summaryrefslogtreecommitdiff
path: root/src/lib/libast/man
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libast/man')
-rw-r--r--src/lib/libast/man/LIBAST.398
-rw-r--r--src/lib/libast/man/aso.3355
-rw-r--r--src/lib/libast/man/ast.3283
-rw-r--r--src/lib/libast/man/astsa.3161
-rw-r--r--src/lib/libast/man/cdt.3617
-rw-r--r--src/lib/libast/man/chr.3126
-rw-r--r--src/lib/libast/man/compat.3103
-rw-r--r--src/lib/libast/man/error.3283
-rw-r--r--src/lib/libast/man/find.389
-rw-r--r--src/lib/libast/man/fmt.3213
-rw-r--r--src/lib/libast/man/fmtls.3143
-rw-r--r--src/lib/libast/man/fs3d.392
-rw-r--r--src/lib/libast/man/ftwalk.3235
-rw-r--r--src/lib/libast/man/getcwd.367
-rw-r--r--src/lib/libast/man/hash.3644
-rw-r--r--src/lib/libast/man/iblocks.362
-rw-r--r--src/lib/libast/man/int.368
-rw-r--r--src/lib/libast/man/ip6.385
-rw-r--r--src/lib/libast/man/magic.3493
-rw-r--r--src/lib/libast/man/mem.398
-rw-r--r--src/lib/libast/man/mime.3117
-rw-r--r--src/lib/libast/man/modecanon.3104
-rw-r--r--src/lib/libast/man/optget.368
-rw-r--r--src/lib/libast/man/path.3391
-rw-r--r--src/lib/libast/man/preroot.3151
-rw-r--r--src/lib/libast/man/proc.3319
-rw-r--r--src/lib/libast/man/re.3214
-rw-r--r--src/lib/libast/man/regex.3163
-rw-r--r--src/lib/libast/man/setenviron.379
-rw-r--r--src/lib/libast/man/sfdisc.3118
-rw-r--r--src/lib/libast/man/sfio.32373
-rw-r--r--src/lib/libast/man/sig.375
-rw-r--r--src/lib/libast/man/spawnveg.397
-rw-r--r--src/lib/libast/man/stak.3169
-rw-r--r--src/lib/libast/man/stk.3165
-rw-r--r--src/lib/libast/man/strcopy.354
-rw-r--r--src/lib/libast/man/strdup.355
-rw-r--r--src/lib/libast/man/strelapsed.377
-rw-r--r--src/lib/libast/man/strerror.353
-rw-r--r--src/lib/libast/man/stresc.353
-rw-r--r--src/lib/libast/man/streval.383
-rw-r--r--src/lib/libast/man/strgid.353
-rw-r--r--src/lib/libast/man/strmatch.3101
-rw-r--r--src/lib/libast/man/stropt.3130
-rw-r--r--src/lib/libast/man/strperm.3109
-rw-r--r--src/lib/libast/man/strsignal.353
-rw-r--r--src/lib/libast/man/strsort.373
-rw-r--r--src/lib/libast/man/strtape.386
-rw-r--r--src/lib/libast/man/strton.397
-rw-r--r--src/lib/libast/man/struid.353
-rw-r--r--src/lib/libast/man/swap.3138
-rw-r--r--src/lib/libast/man/tab.374
-rw-r--r--src/lib/libast/man/tm.3775
-rw-r--r--src/lib/libast/man/tmx.3576
-rw-r--r--src/lib/libast/man/tok.3217
-rw-r--r--src/lib/libast/man/touch.368
-rw-r--r--src/lib/libast/man/tv.3173
-rw-r--r--src/lib/libast/man/vecargs.3126
-rw-r--r--src/lib/libast/man/vmalloc.3640
59 files changed, 12835 insertions, 0 deletions
diff --git a/src/lib/libast/man/LIBAST.3 b/src/lib/libast/man/LIBAST.3
new file mode 100644
index 0000000..380ebde
--- /dev/null
+++ b/src/lib/libast/man/LIBAST.3
@@ -0,0 +1,98 @@
+.fp 5 CW
+.de Af
+.if \\$2 .nr ;G \\$1
+.ie !\\$3 \{\
+\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8"
+\}
+..
+.de aF
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8"
+.ft \\n(;G
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH LIBAST 3
+.UC 4
+.SH NAME
+libast \- introduction to the ast library
+.SH DESCRIPTION
+This section describes the
+.I AST
+(AT&T Software Technology) library functions of the
+.B libast
+library.
+.B libast
+serves three major purposes.
+First, it presents (a subset of) POSIX/ANSI standard headers and interfaces on
+non-compliant systems.
+Second, it provides a portable base of routines that implement concepts
+common to all UNIX system variants.
+Finally, it is a forum for
+modern implementations of features present (or lacking)
+in the standard C libraries.
+Features better provided by separate libraries are omitted from
+.BR libast .
+For example, most terminal driver interface issues are left for
+special purpose libraries such as
+.IR curses (3).
+.PP
+The
+.B libast
+related header files are installed in the directories
+.LR include/ast .
+Routines that do not advertize their own headers are prototyped in
+.LR <ast.h> .
+.L <ast.h>
+is ANSI, K&R and C++ compatible and includes or defines the equivalent of
+.LR <limits.h> ,
+.LR <stddef.h> ,
+.LR <stdlib.h> ,
+.LR <sys/types.h> ,
+.L <string.h>
+and
+.LR <unistd.h> .
+Other libraries that depend on
+.B libast
+may also have headers installed in the
+.L include/ast
+directories.
+.SH FILES
+.nf
+include/ast the \fBast\fP package header directory
+lib/libast.a the \fBlibast\fP library
+lib/libast-g.a the \fBlibast\fP library compiled for debugging
+lib/libast-pg.a the \fBlibast\fP library compiled for profiling
+lib/libast.so.4.0 the \fBlibast\fP shared library
+.fi
+.SH "SEE ALSO"
+intro(3),
+intro(2),
+cc(1)
+.SH CAVEATS
+The library documentation is still under construction.
+Yes, some of it has been in this state for 20 years.
+Thank goodness our commands self-document.
diff --git a/src/lib/libast/man/aso.3 b/src/lib/libast/man/aso.3
new file mode 100644
index 0000000..0e41d30
--- /dev/null
+++ b/src/lib/libast/man/aso.3
@@ -0,0 +1,355 @@
+.fp 5 CW
+.TH LIBASO 3
+.SH NAME
+\fBASO\fP \- Atomic Scalar Operations
+.SH SYNOPSIS
+.de Tp
+.fl
+.ne 2
+.TP
+..
+.de Ss
+.fl
+.ne 2
+.SS "\\$1"
+..
+.de Cs
+.nf
+.ft 5
+..
+.de Ce
+.ft 1
+.fi
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.Cs
+#include <aso.h>
+.Ce
+.Ss "TYPES"
+.Cs
+typedef int (*Asoerror_f)(int, const char*);
+typedef void* (*Asoinit_f)(void*, const char*);
+typedef ssize_t (*Asolock_f)(void*, ssize_t, void volatile*);
+
+typedef struct Asodisc_s
+{
+ uint32_t version;
+ unsigned int hung;
+ Asoerror_f errorf;
+} Asodisc_t;
+
+typedef struct Asometh_s
+{
+ const char* name;
+ int type;
+ Asoinit_f initf;
+ Asolock_f lockf;
+ const char* details;
+} Asometh_t;
+.Ce
+.Ss "OPERATIONS"
+.Cs
+uint8_t asocas8(uint8_t volatile*, int, int);
+uint8_t asoget8(uint8_t volatile*);
+uint8_t asoinc8(uint8_t volatile*);
+uint8_t asodec8(uint8_t volatile*);
+
+uint16_t asocas16(uint16_t volatile*, uint16_t, uint16_t);
+uint16_t asoget16(uint16_t volatile*);
+uint16_t asoinc16(uint16_t volatile*);
+uint16_t asodec16(uint16_t volatile*);
+
+uint32_t asocas32(uint32_t volatile*, uint32_t, uint32_t);
+uint32_t asoget32(uint32_t volatile*);
+uint32_t asoinc32(uint32_t volatile*);
+uint32_t asodec32(uint32_t volatile*);
+
+uint64_t asocas64(uint64_t volatile*, uint64_t, uint64_t);
+uint64_t asoget64(uint64_t volatile*);
+uint64_t asoinc64(uint64_t volatile*);
+uint64_t asodec64(uint64_t volatile*);
+
+unsigned char asocaschar(unsigned char volatile*, int, int);
+unsigned char asogetchar(unsigned char volatile*);
+unsigned char asoincchar(unsigned char volatile*);
+unsigned char asodecchar(unsigned char volatile*);
+
+unsigned short asocasshort(unsigned short volatile*, unsigned short, unsigned short);
+unsigned short asogetshort(unsigned short volatile*);
+unsigned short asoincshort(unsigned short volatile*);
+unsigned short asodecshort(unsigned short volatile*);
+
+unsigned int asocasint(unsigned int volatile*, unsigned int, unsigned int);
+unsigned int asogetint(unsigned int volatile*);
+unsigned int asoincint(unsigned int volatile*);
+unsigned int asodecint(unsigned int volatile*);
+
+unsigned long asocaslong(unsigned long volatile*, unsigned long, unsigned long);
+unsigned long asogetlong(unsigned long volatile*);
+unsigned long asoinclong(unsigned long volatile*);
+unsigned long asodeclong(unsigned long volatile*);
+
+size_t asocassize(size_t volatile*, size_t, size_t);
+size_t asogetsize(size_t volatile*);
+size_t asoincsize(size_t volatile*);
+size_t asodecsize(size_t volatile*);
+
+void* asocasptr(void volatile*, void*, void*);
+void* asogetptr(void volatile*);
+
+void ASODISC(Asodisc_t*, Asoerror_f);
+Asometh_t* asometh(int, void*);
+int asoinit(const char*, Asometh_t*, Asodisc_t*);
+int asolock(unsigned int volatile*, unsigned int, int);
+int asoloop(uintmax_t);
+int asorelax(long);
+.Ce
+.SH DESCRIPTION
+.PP
+\fIASO\fP provides functions to perform atomic scalar operations.
+The functions on the type \f5uint32_t\fP will be fully described below.
+Other functions work similarly on their respective types.
+Some of the functions may be macros that call other functions.
+64 bit operations are provided if the compiler supports 64 bit integers and/or pointers.
+.PP
+.Ss "TYPES"
+.PP
+\f5uint8_t, uint16_t, uint32_t, uint64_t\fP
+
+These are \fIunsigned integer\fP types of different sizes in bits.
+For example, \f5uint32_t\fP represents the type of unsigned integer with 32 bits or 4 bytes.
+.PP
+.Ss "OPERATIONS"
+.PP
+.Ss " uint32_t asoget32(uint32_t* from);"
+This function returns the value \f5*from\fP.
+.PP
+.Ss " uint32_t asoinc32(uint32_t* dest);"
+.Ss " uint32_t asodec32(uint32_t* dest);"
+These functions increment \f5*dest\fP by 1 and decrement \f5*dest\fP by 1 in an atomic step.
+The return value is the old value in \f5*dest\fP.
+
+Consider an example where two concurrent threads/processes call \f5asoinc32()\fP
+on the same \f5dest\fP with values, say \fIv1\fP and \fIv2\fP.
+The eventual value in \f5dest\fP
+will be as if \f5*dest += 2\fP was performed in a single-threaded execution.
+
+That should be constrasted with a situation where, instead of \f5asoinc32()\fP or \f5asodec32()\fP,
+only normal increment (++) or decrement (--) were used.
+Then, the end result could be either \f5*dest += 1\fP or \f5*dest += 2\fP,
+depending on states of the hardware cache and process scheduling.
+.PP
+.Ss " uint32_t asocas32(uint32_t* dest, uint32_t tstval, uint32_t newval);"
+This function provides the atomic \fIcompare-and-swap\fP operation.
+If the current content of \f5dest\fP is equal to \f5tstval\fP then it will be set to \f5newval\fP.
+If multiple threads/processes are performing the same operations only one will succeed with a
+return value of \f5tstval\fP.
+The return value is the old value in \f5*dest\fP.
+.PP
+.Ss " void asorelax(long nsec)"
+This function causes the calling process or thread to briefly pause
+for \f5nsec\fP nanoseconds.
+It is useful to implement tight loops that occasionally yield control.
+.PP
+.Ss " int asolock(unsigned int* lock, unsigned int key, int type)"
+This function uses \f5key\fP, a non-zero unsigned integer, to lock or unlock the \f5lock\fP.
+It returns \f50\fP on success and \f5-1\fP on failure.
+The argument \f5type\fP can take one of the following values:
+.Tp
+\f5ASO_UNLOCK\fP:
+This unlocks the lock if it was locked with \f5key\fP. It is an error to try
+unlocking a lock of a different key.
+.Tp
+\f5ASO_TRYLOCK\fP:
+This makes a single attempt to use the given \f5key\fP to acquire a lock.
+An error will result if the lock is already locked with a different key.
+.Tp
+\f5ASO_LOCK\fP:
+This is a regular locking call. If the lock is locked with a different key,
+this call will wait until the lock is open, then lock it with the given \f5key\fP.
+.Tp
+\f5ASO_SPINLOCK\fP:
+Regardless of what key is currently locking the lock,
+this call will always wait until the lock is open, then lock it with the given \f5key\fP.
+Note that, if the lock is already locked with \f5key\fP, this call can result
+in a deadlock unless that lock can be opened by some other mechanism, e.g.,
+by a different process or thread.
+.PP
+.Ss " int asoloop(uintmax_t iteration);"
+This function is used to implement spin locks that periodically relinquish the processor:
+.Cs
+uintmax_t iteration;
+iteration = 0;
+for (;;) {
+ /* test resource with an aso*() call */
+ if (asoloop(++iteration))
+ /* an error occurred */;
+}
+.Ce
+The value of \f5iteration\fP should be \f51\fP (\fInot\fP \f50\fP) for the first loop iteration.
+\f50\fP is returned on success, \f5-1\fP on failure.
+If \f5iteration mod 4\fP is \f50\fP then \f5asorelax(1)\fP is called to temporarily relinquish
+the processor.
+If \f5Asodisc_t.hung != 0\fP and \f5Asodisc_t.errorf != 0\fP and
+\f5iteration mod (2**Asodisc_t.hung-1)\fP is \f50\fP,
+then \f5Asodisc_t.errorf\fP is called with type \f5ASO_HUNG\fP
+and \f5-1\fP is returned.
+.PP
+.Ss "DISCIPLINE"
+.PP
+The Asodisc_t discipline structure allows the caller to modify default behavior.
+The \fIASO\fP discipline is global for all threads and forked children of the current process.
+The discipline is set and modified by the \f5asoinit()\fP function, described below.
+The structure members are:
+.Tp
+\f5uint32_t version;\fP
+This must be set to \f5ASO_VERSION\fP by the caller and is used by the implementation to detect
+release differences between the caller and the implementation.
+The version is integer of the form \fIYYYYMMDD\fP where \fIYYYY\fP is the release year, \fIMM\fP
+is the release month, and \fIDD\fP is the release day of month.
+This allows the implementation to be forwards and backwards binary compatible with all releases.
+.Tp
+\f5unsigned int hung;\fP
+An error occurs if \f5asoloop\fP() is called \f52**Asometh_t.hung\fP times without gaining access to the loop resource.
+The default value \f50\fP disables the test.
+.Tp
+\f5Asoerror_f errorf;\fP
+\f5int (*errorf)(int type, const char* mesg);\fP
+If \f5errorf\fP != \f50\fP then it is called for each \fIASO\fP fatal library condition.
+\f5type\fP may be one of: \f5ASO_METHOD\fP - a method error; \f5ASO_HUNG\fP - \f5asoloop\fP() was called
+\f52**Asometh_t.hung\fP times with no access to the loop resource.
+\f5mesg\fP is a 0-terminated messsage description.
+.Ss " void ASODISC(Asodisc_t* disc, Asoerror_f errorf);"
+.PP
+This function-like-macro initializes \f5disc->version = ASO_VERSION\fP, \f5disc->errorf = errorf\fP,
+and the remaining \f5disc\fP members to \f50\fP.
+.PP
+.Ss "METHODS"
+.PP
+Several atomic locking methods are implemented for atomic operations
+not supported by \fIintrinsic\fP functions or assembly instructions.
+Methods are controlled by the \f5asometh()\fP and \f5asoinit()\fP
+functions, described below.
+The \fIASO\fP method is global for all threads and forked children of the current process.
+A given method may have multiple types.
+The methods types are:
+.Tp
+\f5ASO_INTRINSIC\fP:
+Some hardware platforms provide machine instructions to implement these operations directly.
+In that case, if a local compiler permits, calls to these \fIintrinsic\fP functions
+may be translated directly into their corresponding machine instructions.
+When necessary the implementation can use only the intrinsic \fIcompare-and-swap\fP
+function on the largest integer type to emulate all other \fIASO\fP operations.
+The \f5ASO_INTRINSIC\fP method type is the default when supported by the compiler.
+It may be used for single-process single-thread, multi-thread, and
+multi-process applications.
+When supported by the hardware / compiler, the library provides the "\fBintrinsic\fP" method with type
+\f5ASO_INTRINSIC|ASO_PROCESS|ASO_THREAD|ASO_SIGNAL\fP.
+.Tp
+\f5ASO_SIGNAL\fP:
+This method type is suitable only for single-process single-thread applications.
+It can be used to provide locking between asyncronous \fBsignal\fP(2) handlers
+and the main program.
+The library provides the "\fBsignal\fP" method with type \f5ASO_SIGNAL\fP.
+This is the default method type when \f5ASO_INTRINSIC\fP is not supported.
+.Tp
+\f5ASO_THREAD\fP:
+This method type is suitable for single-process single-thread, and multi-thread applications.
+It typically requires thread library support, and since the default \f5aso\fP library
+is not linked with a thread library, no \f5ASO_THREAD\fP method is provided by default.
+Threaded applications must link with \fB-ltaso\fP (before \fB-laso\fP or \fB-last\fP)
+in order to access \f5ASO_THREAD\fP methods.
+The \fB-ltaso\fP library provides the "\fBspin\fP" (using \fBpthread_spin_lock\fP(3)) and
+"\fBmutex"\fP (using \fBpthread_mutex_lock\fP(3)) methods with type \f5ASO_THREAD|ASO_SIGNAL\fP.
+.Tp
+\f5ASO_PROCESS\fP:
+This method type is suitable for single-process single-thread, and multi-process applications.
+Some \f5ASO_PROCESS\fP methods may also be suitable for multi-thread applications (if they have the \f5ASO_THREAD\fP type.)
+These methods are typically and noticably \fIslow\fP, up to 2 orders of magnitude slower than
+\f5ASO_INTRINSIC\fP for some applications.
+They are provided as a last resort when other methods are not available.
+The library provides the "\fBsemaphore\fP" method with type \f5ASO_PROCESS|ASO_THREAD|ASO_SIGNAL\fP
+and the "\fBfcntl\fP" method with type \f5ASO_PROCESS|ASO_SIGNAL\fP.
+
+.Ss " Asometh_t* asometh(int type, void* data);"
+This function looks up methods by type or name.
+If type is \f50\fP and \f5data\fP is \f50\fP then the current method is returned; a valid method
+will always be returned for this call.
+If type is \f50\fP then \f5data\fP is treated as a \f50\fP-terminated string method name;
+\f50\fP is returned if no matching method is found.
+The pseudo-type \f5ASO_NEXT\fP generates the list of all methods in successive calls:
+.Cs
+Asometh_t* meth;
+meth = 0;
+while (meth = asometh(ASO_NEXT, meth))
+ /* examine meth->... */
+.Ce
+Otherwise if \f5type\fP is not \f50\fP and not \f5ASO_NEXT\fP it is treated as a combination of the ordered types
+\f5ASO_THREAD\fP, \f5ASO_SIGNAL\fP, \f5ASO_INTRINSIC\fP, \f5ASO_PROCESS\fP:
+the first method with \f5(meth->type & type) != 0\fP is returned;
+\f50\fP is returned if no matching method is found.
+
+Method names are treated as a name, optionally followed by a list of
+\fB,\fP\fIname\fP=\fIvalue\fP details, and optionally ending with \fB,\fP\fIpathname\fP.
+The \fBsemaphore\fP method uses \fBsize\fP=\fInumber\fP to specify
+the number of semaphores and hashes \fIpathname\fP to determine the semaphore IPC key.
+The \fBfcntl\fP method uses \fBsize\fP=\fInumber\fP to specify
+the number of 1 byte file locks and uses \fIpathname\fP as the
+file to lock using \f5fcntl(F_SETLCK[W])\fP.
+
+.Ss " int asoinit(const char* details, Asometh_t* meth, Asodisc_t* disc);"
+This function sets the global discipline to \f5disc\fP,
+closes the current method (releasing its resources),
+temporarily instantiates the default method
+(either \f5ASO_INTRINSIC\fP if available or \f5AS_SIGNAL\fP otherwise),
+and initializes \f5meth\fP and instantiates it as the new method.
+If \f5disc\fP is \f50\fP the the global discpline is not modified.
+If \f5meth\fP is \f50\fP or \f5meth->lockf\fP is \f50\fP and \f5(meth->type & ASO_INTRINSIC) != 0\fP
+then \f5-1\fP is returned and the current method is not changed.
+If an error occurs instantiating \f5meth\fP then the current method is
+set to the default and \f5-1\fP is returned.
+Otherwise \f50\fP is returned on success.
+
+Method resources are released by the next \f5asometh()\fP call,
+or by an \fIASO\fP cleanup function called via \f5atexit\fP(2).
+System global method resources are released on last use;
+this includes removing semaphore keys or
+physical files that may be used by some methods.
+In some cases \fIASO\fP maintains reference counts within
+the resource to determine last use.
+
+An application requiring a specific method must check the default method before
+using any \fIASO\fP operations. For example, a threaded application would
+do something like this:
+.Cs
+void* data = 0 /* \fIor\fP a method name string with optional details */
+Asometh_t* meth;
+if (data || !(asometh(0, 0)->type & (ASO_INTRINSIC|ASO_THREAD))) {
+ if (!(meth = asometh(ASO_INTRINSIC|ASO_THREAD, data)))
+ /* error -- suitable method not found */;
+ else if (asoinit(meth, 0, 0, ASO_VERSION))
+ /* error -- method initialization error */;
+}
+/* ready for \fIASO\fP operaions */
+.Ce
+A multi-process application would check for \f5(ASO_INTRINSIC|ASO_PROCESS)\fP
+instead of \f5(ASO_INTRINSIC|ASO_THREAD)\fP.
+
+.PP
+.SH IMPLEMENTATION NOTES
+Unlike other \fIAST\fP library discipline/method functions which can instantiate
+multiple discpline/method handles within a single process, the \fIASO\fP
+library allows only one discipline and method to be set at a time, with the additional
+restriction that it may only be set by the main and only thread of the calling process.
+For this reason there is no open/close interface with an instantation handle;
+instead the global discipline/method is simply initialized by \f5asoinit()\fP.
+
+\f5ASO_THREAD\fP and \f5ASO_PROCESS\fP methods rely on the \f5Asometh_t.lockf()\fP
+being sufficiently "heavy" to flush the calling thread/process memory cache
+so the subsequent \fIASO\fP operation operates on the physical memory location
+instead of the cached location. There is currently no other portable mechanism
+that guarantees this other than the \f5ASO_INTRINSIC\fP method.
+
+.PP
+.SH AUTHOR
+Kiem-Phong Vo, Adam Edgar, and Glenn Fowler
diff --git a/src/lib/libast/man/ast.3 b/src/lib/libast/man/ast.3
new file mode 100644
index 0000000..8055be9
--- /dev/null
+++ b/src/lib/libast/man/ast.3
@@ -0,0 +1,283 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH AST 3
+.SH NAME
+ast \- miscellaneous libast support
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+char* astconf(const char* \fIname\fP, const char* \fIpath\fP, const char* \fIvalue\fP);
+Ast_confdisc_t astconfdisc(Ast_confdisc_t new_notify);
+void astconflist(Sfio_t* stream, const char* path, int flags);
+off_t astcopy(int \fIrfd\fP, int \fIwfd\fP, off_t \fIn\fP);
+int astquery(int \fIfd\fP, const char* \fIformat\fP , ...);
+.EE
+.SH DESCRIPTION
+.L astconf
+is a string interface to the
+.IR confstr (2),
+.IR pathconf (2),
+and
+.IR sysconf (2)
+calls.
+If
+.I value
+is
+.L 0
+then the configuration parameter value for
+.I name
+is returned.
+Some
+.I name
+configuration parameters may consult the
+.I path
+argument.
+In this case if
+.I path
+is
+.L 0
+then
+\f5"/"\fP
+is used.
+Otherwise if
+.I path
+is not
+.L 0
+then it must exist.
+The string return value for
+.I name
+remains unchanged until the next
+.L astconf
+call on
+.IR name .
+If
+.I value
+is
+.L 0
+then a valid string is always returned;
+\f5""\fP
+is returned if
+.I name
+has no configuration value.
+This simplifies the programming interface:
+.EX
+if (!strcmp(astconf("PATH_RESOLVE", NiL, NiL), "logical"))
+ /* the logical way ... */
+.EE
+If
+.I value
+is not
+.L 0
+then the configuration parameter value for
+.I name
+is set to
+.IR value .
+.L 0
+is returned if the value cannot be set.
+The paradigm is:
+.EX
+universe = astconf("UNIVERSE", 0, "att");
+\|.\|.\|.
+astconf("UNIVERSE", 0, universe);
+.EE
+The settable configuration names are:
+.TP
+.L FS_3D
+.L 1
+if
+.IR 3d (1)
+viewpathing is enabled,
+.L 0
+otherwise.
+This is an alternative to the
+.IR fs3d (3)
+interface.
+.TP
+.L PATH_RESOLVE
+.L logical
+if symbolic links are followed during file tree traversal,
+.L physical
+if symbolic links are not followed during file tree traversal,
+and
+.L metaphysical
+if symbolic links are followed at the top level during file tree traversal.
+These correspond to the generic
+.LR \-L ,
+.LR \-P ,
+and
+.L \-H
+command options.
+.TP
+.L UNIVERSE
+.L ucb
+for
+.I Berkeley
+style and
+.L att
+otherwise.
+This configuration parameter controls the
+.I universe
+setting on machines that support it (e.g., Pyramid).
+.L UNIVERSE
+also controls the behavior of some commands like
+.IR cat (1)
+and
+.IR echo (1).
+.PP
+User defined
+.I name
+values may also be set and queried, but these should probably have
+some form of vendor prefix to avoid being stomped by future standards.
+.PP
+.L astconfdisc
+registers a discipline function
+.EX
+int (*notify)(const char* \fIname\fP, const char* \fIpath\fP, const char* \fIvalue\fP);
+.EE
+that is called just before the configuration parameter
+.I name
+is set to
+.I value
+relative to
+.IR path .
+If
+.I notify
+returns
+.L 0
+then the configuration parameter value is not changed.
+.PP
+.L astconflist
+lists the current configuration names and values of
+.IR stream .
+If
+.I path
+is
+.L 0
+then \f5"/"\fP is used where appropriate.
+If
+.I flags
+is
+.L 0
+or
+.L R_OK|W_OK
+then all configuration parameters are listed.
+.L R_OK
+lists the readonly configuration parameters and
+.L W_OK
+lists the settable configuration parameters.
+.L X_OK
+lists the settable configuration parameters in a form that can be
+snarfed for input to the
+.IR getconf (1)
+command.
+.PP
+.L astcopy
+efficiently copies up to
+.I n
+bytes from the file descriptor
+.I rfd
+to the file descriptor
+.IR wfd .
+The actual number of bytes copied is returned; \-1 is returned on error.
+If
+.I n
+is 0 then an optimal number of bytes (with respect to both
+.I rfd
+and
+.IR wfd )
+is copied.
+.PP
+If possible
+.IR mmap (2)
+is used to do the transfer.
+Some implementations may bypass user buffer copies usually required by the
+.IR read (2)- write (2)
+paradigm.
+.PP
+.L astquery
+outputs an
+.IR sfprintf (3)
+prompt specified by
+.I "format, .\|.\|."
+to the controlling terminal and reads a response from the controlling terminal.
+Offirmative response returns
+.LR 0 ,
+.L EOF
+or quit response returns
+.LR \-1 ,
+otherwise
+.L 1
+is returned.
+If
+.I quit
+is greater than
+.L 0
+then
+.I exit(quit)
+is called on a quit response.
+The responses will eventually be locale specific.
+.PP
+.L astwinsize
+returns the number of rows in
+.I *rows
+and the number of columns
+.I *col
+for the terminal file descriptor
+.IR fd .
+If the number of rows or columns cannot be determined or if
+.I fd
+is not a terminal then
+.I *rows
+and
+.I *cols
+are set to
+.LR 0 .
+If
+.I ioctl (2)
+methods fail then the environment variable
+.L LINES
+is used to set
+.I *rows
+and the environment variable
+.L COLUMNS
+is used to set
+.IR *cols .
+.SH "SEE ALSO"
+getconf(1), confstr(2), mmap(2), pathconf(2), read(2), sysconf(2), write(2)
diff --git a/src/lib/libast/man/astsa.3 b/src/lib/libast/man/astsa.3
new file mode 100644
index 0000000..5b588a8
--- /dev/null
+++ b/src/lib/libast/man/astsa.3
@@ -0,0 +1,161 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.PP
+..
+.de Tp
+.fl
+.ne 3
+.TP
+..
+.de Ss
+.fl
+.ne 3
+.SS "\\$1"
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.TH ASTSA 3
+.SH NAME
+astsa \- standalone libast support
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+#include <ccode.h>
+#include <error.h>
+#include <option.h>
+#include <stk.h>
+.EE
+.SH DESCRIPTION
+.B astsa
+is a standalone subset of
+.BR ast (3)
+that requires only
+.BR cdt (3)
+and
+.BR sfio (3).
+.PP
+.B <ast.h>
+includes
+.BR <ast_common.h> ,
+.B <stdarg.h>
+or
+.BR <varargs.h> ,
+.BR <sfio.h> ,
+.BR <limits.h> ,
+and
+.B <limits.h>
+and
+.B <unistd.h>
+if supported by the local system.
+.PP
+The macros and functions provided by
+.B <ast.h>
+are described below.
+The other headers are described in
+.BR ccode (3),
+.BR error (3),
+.BR optget (3),
+and
+.BR stk (3).
+.SH MACROS
+.Ss "size_t elementsof(\fIx\fP)"
+Evaluates to the number of elements in the array variable
+.IR x .
+.Ss "\fItype\fP* newof(void* old, \fItype\fP, size_t \fIelements\fP, size_t \fIextra\fP)"
+Equivalent to (\fItype\fP*)realloc((char*)\fIold\fP,sizeof(\fItype\fP)*\fIelements\fP+\fIextra\fP)
+if \fIold\fP!=0 and
+(\fItype\fP*)calloc(1,sizeof(\fItype\fP)*\fIelements\fP+\fIextra\fP)
+otherwise.
+.Ss "\fItype\fP* oldof(void* old, \fItype\fP, size_t \fIelements\fP, size_t \fIextra\fP)"
+Equivalent to (\fItype\fP*)realloc((char*)\fIold\fP,sizeof(\fItype\fP)*\fIelements\fP+\fIextra\fP)
+if \fIold\fP!=0 and
+(\fItype\fP*)malloc(1,sizeof(\fItype\fP)*\fIelements\fP+\fIextra\fP)
+otherwise.
+.Ss "size_t roundof(\fIx\fP,\fIy\fP)"
+Evaluates to \fIx\fP rounded up to the next power of 2 boundary \fIy\fP.
+.Ss "int ssizeof(\fIx\fP)"
+Equivalent to (int)sizeof(\fIx\fP).
+.Ss "int streq(\fIa\fP,\fIb\fP)"
+Equivalent to (*(\fIa\fP)==*(\fIb\fP)&&strcmp(\fIa\fP,\fIb\fP)==0).
+.Ss "int strneq(\fIa\fP,\fIb\fP,\fIn\fP)"
+Equivalent to (*(\fIa\fP)==*(\fIb\fP)&&strncmp(\fIa\fP,\fIb\fP,\fIn\fP)==0).
+.SH FUNCTIONS
+.Ss "void astwinsize(int \fIfd\fP, int* \fIplines\fP, int* \fIpcolumns\fP)"
+If \fIplines\fP!=0 then *\fIplines\fP is set to the number of lines on the
+tty device corresponding to \fIfd\fP.
+If \fIpcolumns\fP!=0 then *\fIpcolumns\fP is set to the number of columns
+on the tty device corresponding to \fIfd\fP.
+The default if \fIfd\fP is not a terminal device, or if \fIfd\fP queries fail,
+is 24 lines and 80 columns.
+.Ss "char* fmterror(int \fIerrno\fP)"
+Returns the error message text corresponding to the
+.BR errno (3)
+\fIerrno\fP.
+.Ss "char* strerror(int \fIerrno\fP)"
+Equivalent to fmterror(\fIerrno\fP).
+.Ss "int strgrpmatch(const char* \fIstring\fP, const char* \fIpattern\fP, int* \fIsub\fP, int \fInsub\fP, int \fIflags\fP)"
+Matches the null terminated \fIstring\fP against the null terminated
+.BR ksh (1)
+augmented \fIpattern\fP.
+If \fIsub\fP!=0 then \fIsub\fP[2*\fIi\fP] is set to the start offset and \fIsub\fP[2*\fIi\fP+1] is set
+to the end offset of the \fIi\fP-th parenthesized subexpression.
+\fInsub\fP is 1/2 the number of elements in \fIsub\fP.
+\fIflags\fP controls the matching:
+.Tp
+\f5STR_MAXIMAL\fP:
+Maximal match.
+The default is minimal (first) match.
+.Tp
+\f5STR_LEFT\fP:
+Implicit left anchor.
+.Tp
+\f5STR_RIGHT\fP:
+Implicit right anchor.
+.Tp
+\f5STR_ICASE\fP:
+Ignore case.
+.Tp
+\f5STR_GROUP\fP:
+(|&) inside [@|*|+{n,m}](...) only.
+.Ss "int strmatch(const char* \fIstring\fP, const char* \fIpattern\fP, int* \fIsub\fP, int \fInsub\fP, int \fIflags\fP)"
+Equivalent to strgrpmatch(\fIstring\fP,\fIpattern\fP,0,0,STR_MAXIMAL|STR_LEFT|STR_RIGHT).
+.SH "SEE ALSO"
+.BR ast (3),
+.BR ccode (3),
+.BR cdt (3),
+.BR error (3),
+.BR malloc (3),
+.BR option (3),
+.BR sfio (3),
+.BR stk (3)
diff --git a/src/lib/libast/man/cdt.3 b/src/lib/libast/man/cdt.3
new file mode 100644
index 0000000..46bfe13
--- /dev/null
+++ b/src/lib/libast/man/cdt.3
@@ -0,0 +1,617 @@
+.fp 5 CW
+.TH LIBCDT 3
+.SH NAME
+\fBCdt\fR \- container data types
+.SH SYNOPSIS
+.de Tp
+.fl
+.ne 2
+.TP
+..
+.de Ss
+.fl
+.ne 2
+.SS "\\$1"
+..
+.de Cs
+.nf
+.ft 5
+..
+.de Ce
+.ft 1
+.fi
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.Cs
+#include <cdt.h>
+.Ce
+.Ss "DICTIONARY TYPES"
+.Cs
+Void_t*;
+Dt_t;
+Dtdisc_t;
+Dtmethod_t;
+Dtlink_t;
+Dtstat_t;
+.Ce
+.Ss "DICTIONARY CONTROL"
+.Cs
+Dt_t* dtopen(const Dtdisc_t* disc, const Dtmethod_t* meth);
+int dtclose(Dt_t* dt);
+void dtclear(dt);
+Dtdisc_t* dtdisc(Dt_t* dt, const Dtdisc_t* disc, int type);
+Dtmethod_t* dtmethod(Dt_t* dt, const Dtmethod_t* meth);
+Dt_t* dtview(Dt_t* dt, Dt_t* view);
+int dtcustomize(Dt_t* dt, int type, Void_t* arg);
+int dtoptimize(Dt_t* dt);
+int dtshare(Dt_t* dt, int type);
+int dtlock(Dt_t* dt, unsigned int key, int type);
+.Ce
+.Ss "STORAGE METHODS"
+.Cs
+Dtmethod_t* Dtset;
+Dtmethod_t* Dtbag;
+Dtmethod_t* Dtrhset;
+Dtmethod_t* Dtrhbag;
+Dtmethod_t* Dtoset;
+Dtmethod_t* Dtobag;
+Dtmethod_t* Dtlist;
+Dtmethod_t* Dtstack;
+Dtmethod_t* Dtqueue;
+Dtmethod_t* Dtdeque;
+.Ce
+.Ss "DISCIPLINE"
+.Cs
+#define DTOFFSET(struct_s,member)
+#define DTDISC(disc,key,size,link,makef,freef,comparf,hashf,memoryf,eventf)
+typedef Void_t* (*Dtmake_f)(Dt_t*, Void_t*, Dtdisc_t*);
+typedef void (*Dtfree_f)(Dt_t*, Void_t*, Dtdisc_t*);
+typedef int (*Dtcompar_f)(Dt_t*, Void_t*, Void_t*, Dtdisc_t*);
+typedef unsigned int (*Dthash_f)(Dt_t*, Void_t*, Dtdisc_t*);
+typedef Void_t* (*Dtmemory_f)(Dt_t*, Void_t*, size_t, Dtdisc_t*);
+typedef int (*Dtevent_f)(Dt_t*, int, Void_t*, Dtdisc_t*);
+.Ce
+.Ss "OBJECT OPERATIONS"
+.Cs
+Void_t* dtinsert(Dt_t* dt, Void_t* obj);
+Void_t* dtappend(Dt_t* dt, Void_t* obj);
+Void_t* dtdelete(Dt_t* dt, Void_t* obj);
+Void_t* dtattach(Dt_t* dt, Void_t* obj);
+Void_t* dtdetach(Dt_t* dt, Void_t* obj);
+Void_t* dtsearch(Dt_t* dt, Void_t* obj);
+Void_t* dtmatch(Dt_t* dt, Void_t* key);
+Void_t* dtfirst(Dt_t* dt);
+Void_t* dtnext(Dt_t* dt, Void_t* obj);
+Void_t* dtlast(Dt_t* dt);
+Void_t* dtprev(Dt_t* dt, Void_t* obj);
+Void_t* dtleast(Dt_t* dt, Void_t* obj);
+Void_t* dtmost(Dt_t* dt, Void_t* obj);
+int dtwalk(Dt_t* dt, int (*userf)(Dt_t*, Void_t*, Void_t*), Void_t*);
+Dtlink_t* dtflatten(Dt_t* dt);
+Dtlink_t* dtlink(Dt_t* dt, Dtlink_t* link);
+Void_t* dtobj(Dt_t* dt, Dtlink_t* link);
+Dtlink_t* dtextract(Dt_t* dt);
+Dtlink_t* dtrestore(Dt_t* dt, Dtlink_t* link);
+.Ce
+.Ss "DICTIONARY STATUS"
+.Cs
+Dt_t* dtvnext(Dt_t* dt);
+ssize_t dtvcount(Dt_t* dt);
+Dt_t* dtvhere(Dt_t* dt);
+ssize_t dtsize(Dt_t* dt);
+ssize_t dtstat(Dt_t* dt, Dtstat_t* st);
+.Ce
+.Ss "HASH FUNCTIONS"
+.Cs
+unsigned int dtstrhash(unsigned int h, char* str, int n);
+unsigned int dtcharhash(unsigned int h, unsigned char c);
+.Ce
+.SH DESCRIPTION
+.PP
+\fICdt\fP manages run-time dictionaries using standard container data types:
+unordered set/multiset, ordered set/multiset, list, stack, and queue.
+.PP
+.Ss "DICTIONARY TYPES"
+.PP
+.Ss " Void_t*"
+This type is used to pass objects between \fICdt\fP and application code.
+\f5Void_t\fP is defined as \f5void\fP for ANSI-C and C++
+and \f5char\fP for older C compilation environments.
+.PP
+.Ss " Dt_t"
+This is the type of a dictionary handle.
+.PP
+.Ss " Dtdisc_t"
+This defines the type of a discipline structure which define the lay-out of
+an object and functions to compare, hash, make, delete objects, etc. (see \f5dtdisc()\fP).
+.PP
+.Ss " Dtmethod_t"
+This defines the type of a container method.
+.PP
+.Ss " Dtlink_t"
+This is the type of a dictionary object holder (see \f5dtdisc()\fP.)
+.PP
+.Ss " Dtstat_t"
+This is the type of a structure to return dictionary statistics (see \f5dtstat()\fP.)
+.PP
+.Ss "DICTIONARY CONTROL"
+.PP
+.Ss " Dt_t* dtopen(const Dtdisc_t* disc, const Dtmethod_t* meth)"
+This creates a new dictionary.
+\f5disc\fP is a discipline structure to describe object format.
+\f5meth\fP specifies a manipulation method.
+\f5dtopen()\fP returns the new dictionary or \f5NULL\fP on error.
+See also the events \f5DT_OPEN\fP and \f5DT_ENDOPEN\fP below.
+.PP
+.Ss " int dtclose(Dt_t* dt)"
+This deletes \f5dt\fP and its objects.
+Note that \f5dtclose()\fP fails if \f5dt\fP is being viewed by
+some other dictionaries (see \f5dtview()\fP).
+\f5dtclose()\fP returns \f50\fP on success and \f5-1\fP on error.
+See also the events \f5DT_CLOSE\fP and \f5DT_ENDCLOSE\fP below.
+.PP
+.Ss " void dtclear(Dt_t* dt)"
+This deletes all objects in \f5dt\fP without closing \f5dt\fP.
+.PP
+.Ss " Dtdisc_t* dtdisc(Dt_t* dt, const Dtdisc_t* disc, int type)"
+If \f5disc\fP is \f5NULL\fP, \f5dtdisc()\fP returns the current discipline.
+Otherwise, it changes the discipline of \f5dt\fP to \f5disc\fP.
+Objects may be rehashed, reordered, or removed as appropriate.
+\f5type\fP can be any bit combination of \f5DT_SAMECMP\fP and \f5DT_SAMEHASH\fP.
+\f5DT_SAMECMP\fP means that objects will compare exactly the same as before
+thus obviating the need for reordering or removing new duplicates.
+\f5DT_SAMEHASH\fP means that hash values of objects remain the same
+thus obviating the need to rehash.
+\f5dtdisc()\fP returns the previous discipline on success
+and \f5NULL\fP on error.
+.PP
+.Ss " Dtmethod_t dtmethod(Dt_t* dt, const Dtmethod_t* meth)"
+If \f5meth\fP is \f5NULL\fP, \f5dtmethod()\fP returns the current method.
+Otherwise, it changes the storage method of \f5dt\fP to \f5meth\fP.
+Objects may be rehashed, reordered, or removed as appropriate.
+\f5dtmethod()\fP returns the previous method or \f5NULL\fP on error.
+.PP
+.Ss " Dt_t* dtview(Dt_t* dt, Dt_t* view)"
+A viewpath allows a search or walk starting from a dictionary to continue to another.
+\f5dtview()\fP first terminates any current view from \f5dt\fP to another dictionary.
+Then, if \f5view\fP is \f5NULL\fP, \f5dtview\fP returns the terminated view dictionary.
+If \f5view\fP is not \f5NULL\fP, a viewpath from \f5dt\fP to \f5view\fP is established.
+\f5dtview()\fP returns \f5dt\fP on success and \f5NULL\fP on error.
+.PP
+It is an error to have dictionaries on a viewpath with different storage methods.
+In addition, dictionaries on the same view path should
+treat objects in a consistent manner with respect to comparison or hashing.
+If not, undefined behaviors may result.
+.PP
+.Ss " int dtcustomize(Dt_t* dt, int type, Void_t* arg)"
+This customizes a storage method. The \f5type\fP argument
+indicates the type of customization and \f5arg\fP gives additional
+information for the operation. Here are the types:
+.Tp
+\f5DT_SHARE\fP:
+This turns on/off the share mode for a dictionary.
+Concurrent accesses of a dictionary not in share mode
+may exhibit undefined behaviors including memory segmentation.
+
+Share mode allows multiple accessors, threads or processes, to access objects.
+Such objects could be in the same directory in the case of threads or shared
+memory in the case of processes.
+.Tp
+\f5DT_OPTIMIZE\fP:
+This causes the underlying method to optimize its internal
+data structure. For example, the splay tree underlying \f5Dtoset\fP
+would be balanced.
+.PP
+.Ss " int dtoptimize(Dt_t* dt)"
+This is a short-hand for invoking \f5dtcustomize()\fP with the \f5DT_OPTIMIZE\fP event.
+.PP
+.Ss " int dtshare(Dt_t* dt, int type)"
+This turns on or off share mode for dictionary \f5dt\fP depending on whether \f5type\fP
+is positive or non-positive. It returns -1 on failure.
+.PP
+.Ss " int dtlock(Dt_t* dt, unsigned int key, int type)"
+This globally locks/unlocks a dictionary using the given \f5key\fP.
+It returns 0 on success and -1 on failure.
+The value of \f5key\fP must not be 0.
+The argument \f5type\fP is used as follows:
+.Tp
+\f5type < 0\fP:
+Unlock the dictionary if it was locked with \f5key\fP.
+An error will result if the dictionary was locked with a different key.
+.Tp
+\f5type == 0\fP:
+Attempt to lock the dictionary with \f5key\fP if it is unlocked.
+An error will result if the dictionary was already locked with a different key.
+.Tp
+\f5type > 0\fP:
+Attempt to lock the dictionary with \f5key\fP.
+If the dictionary is already locked with a different key,
+the call will loop and wait until the lock is open to lock it.
+
+.PP
+.Ss "STORAGE METHODS"
+.PP
+Storage methods are of type \f5Dtmethod_t*\fP.
+\fICdt\fP supports the following methods:
+.PP
+.Ss " Dtoset"
+.Ss " Dtobag"
+Objects are ordered by comparisons.
+\f5Dtoset\fP keeps unique objects.
+\f5Dtobag\fP allows repeatable objects.
+.PP
+.Ss " Dtset"
+.Ss " Dtbag"
+Objects are unordered.
+\f5Dtset\fP keeps unique objects.
+\f5Dtbag\fP allows repeatable objects.
+The underlying data structure is a hash table with chaining to handle collisions.
+.PP
+.Ss " Dtrhset"
+.Ss " Dtrhbag"
+These methods are like \f5Dtset\fP and \f5Dtbag\fP but are based on
+a recursive hashing data structure that allows table extension without
+object relocation. The data structure also supports lock-free
+concurrent search operations for share dictionaries.
+.PP
+.Ss " Dtlist"
+Objects are kept in a list.
+\fIA current object\fP is always defined to be either the head of
+the list or an object resulting from a recent search or insert operation.
+The call \f5dtinsert()\fP will insert a new object
+in front of such a current object
+while the call \f5dtappend()\fP will append in back of it.
+.PP
+.Ss " Dtdeque"
+Objects are kept in a deque. This is similar to \f5Dtlist\fP
+except that objects are always inserted at the front and appended at the tail
+of the list.
+.PP
+.Ss " Dtstack"
+Objects are kept in a stack, i.e., in reverse order of insertion.
+Thus, the last object inserted is at stack top
+and will be the first to be deleted.
+.PP
+.Ss " Dtqueue"
+Objects are kept in a queue, i.e., in order of insertion.
+Thus, the first object inserted is at queue head
+and will be the first to be deleted.
+.PP
+.Ss "DISCIPLINE"
+.PP
+Object format and associated management functions are
+defined in the type \f5Dtdisc_t\fP:
+.Cs
+ typedef struct
+ { ssize_t key, size;
+ ssize_t link;
+ Dtmake_f makef;
+ Dtfree_f freef;
+ Dtcompar_f comparf;
+ Dthash_f hashf;
+ Dtmemory_f memoryf;
+ Dtevent_f eventf;
+ } Dtdisc_t;
+.Ce
+.Ss " ssize_t key, size"
+Each object \f5obj\fP is identified by a key used for object comparison or hashing.
+\f5key\fP should be non-negative and defines an offset into \f5obj\fP.
+If \f5size\fP is negative, the key is a null-terminated
+string with starting address \f5*(Void_t**)((char*)obj+key)\fP.
+If \f5size\fP is zero, the key is a null-terminated string with starting address
+\f5(Void_t*)((char*)obj+key)\fP.
+Finally, if \f5size\fP is positive, the key is a byte array of length \f5size\fP
+starting at \f5(Void_t*)((char*)obj+key)\fP.
+.PP
+.Ss " ssize_t link"
+Let \f5obj\fP be an object to be inserted into \f5dt\fP.
+If \f5link\fP is negative, an object holder of type \f5Dtlink_t\fP
+will be allocated to hold \f5obj\fP.
+Otherwise, \f5obj\fP should have
+a \f5Dtlink_t\fP structure embedded \f5link\fP bytes into it,
+i.e., at address \f5(Dtlink_t*)((char*)obj+link)\fP.
+.PP
+.Ss " Void_t* (*makef)(Dt_t* dt, Void_t* obj, Dtdisc_t* disc)"
+If \f5makef\fP is not \f5NULL\fP,
+\f5dtinsert(dt,obj)\fP or \f5dtappend()\fP will call it
+to make a copy of \f5obj\fP suitable for insertion into \f5dt\fP.
+If \f5makef\fP is \f5NULL\fP, \f5obj\fP itself will be inserted into \f5dt\fP.
+.PP
+.Ss " void (*freef)(Dt_t* dt, Void_t* obj, Dtdisc_t* disc)"
+If not \f5NULL\fP,
+\f5freef\fP is used to destroy data associated with \f5obj\fP.
+.PP
+.Ss "int (*comparf)(Dt_t* dt, Void_t* key1, Void_t* key2, Dtdisc_t* disc)"
+If not \f5NULL\fP, \f5comparf\fP is used to compare two keys.
+Its return value should be \f5<0\fP, \f5=0\fP, or \f5>0\fP to indicate
+whether \f5key1\fP is smaller, equal to, or larger than \f5key2\fP.
+All three values are significant for method \f5Dtoset\fP and \f5Dtobag\fP.
+For other methods, a zero value
+indicates equality and a non-zero value indicates inequality.
+If \f5(*comparf)()\fP is \f5NULL\fP, an internal function is used
+to compare the keys as defined by the \f5Dtdisc_t.size\fP field.
+.PP
+.Ss " unsigned int (*hashf)(Dt_t* dt, Void_t* key, Dtdisc_t* disc)"
+If not \f5NULL\fP,
+\f5hashf\fP is used to compute the hash value of \f5key\fP.
+It is required that keys compared equal will also have same hash values.
+If \f5hashf\fP is \f5NULL\fP, an internal function is used to hash
+the key as defined by the \f5Dtdisc_t.size\fP field.
+.PP
+.Ss " Void_t* (*memoryf)(Dt_t* dt, Void_t* addr, size_t size, Dtdisc_t* disc)"
+If not \f5NULL\fP, \f5memoryf\fP is used to allocate and free memory.
+When \f5addr\fP is \f5NULL\fP, a memory segment of size \f5size\fP is requested.
+If \f5addr\fP is not \f5NULL\fP and \f5size\fP is zero, \f5addr\fP is to be freed.
+If \f5addr\fP is not \f5NULL\fP and \f5size\fP is positive,
+\f5addr\fP is to be resized to the given size.
+If \f5memoryf\fP is \f5NULL\fP, \fImalloc(3)\fP is used.
+.PP
+.Ss " int (*eventf)(Dt_t* dt, int type, Void_t* data, Dtdisc_t* disc)"
+If not \f5NULL\fP, \f5eventf\fP announces various events.
+Each event may have particular handling of the return values from \f5eventf\fP.
+But a negative return value typically means failure.
+Following are the events:
+.Tp
+\f5DT_OPEN\fP:
+This event is raised at the start of the process to open a new dictionary.
+The argument \f5data\fP will be a pointer to an object of type \f5Void_t*\fP
+initialized to \f5NULL\fP before the call. The return value of \f5eventf()\fP
+is significant as follows:
+
+On a negative return value, \f5dtopen()\fP will return failure.
+
+On a zero return value, \f5eventf()\fP may set \f5*(Void_t**)data\fP to some non-\f5NULL\fP
+value to indicate that the dictionary structure itself should be allocated
+along with the \f5Dtdisc_t.data\fP section.
+Otherwise, it will be allocated separately with \f5malloc(3)\fP.
+
+On a positive return value, the dictionary is being reconstructed
+based on existing states of some previous dictionary.
+In this case, \f5eventf()\fP should set \f5*(Void_t**)data\fP to point to
+the field \f5Dt_t.data\fP of the corresponding previous dictionary (see \f5DT_CLOSE\fP below).
+If the handle of the previous dictionary was created as discussed above
+in the case of the zero return value, it will be exactly restored.
+Otherwise, a new handle will be allocated with \f5malloc()\fP.
+The ability to create different dictionaries sharing the same set of objects
+allows for managing objects in shared and/or persistent memory.
+.Tp
+\f5DT_ENDOPEN\fP:
+This event is raised at the end of the process to open a dictionary.
+The return value of \f5eventf()\fP will be ignored.
+.Tp
+\f5DT_CLOSE\fP:
+This event is raised at the start of the process to close dictionary \f5dt\fP.
+The return value of \f5eventf\fP is significant as follows:
+
+On a negative return value, \f5dtclose()\fP will return failure.
+
+On a zero return value, all dictionary objects will be deleted and
+and all associated memory will be freed.
+
+On a positive return value, allocated objects and memory will be kept intact.
+This means that \f5dt->data\fP remains intact and can be reused in some future
+dictionary (see \f5DT_OPEN\fP above).
+Note, however, that \f5dt\fP itself would still be freed if it was allocated with \f5malloc(3)\fP.
+.Tp
+\f5DT_ENDCLOSE\fP:
+This event is raised at the end of the process to close a dictionary.
+The return value of \f5eventf()\fP will be ignored.
+.Tp
+\f5DT_DISC\fP:
+The discipline of \f5dt\fP is being changed to a new one given in
+\f5(Dtdisc_t*)data\fP.
+.Tp
+\f5DT_METH\fP:
+The method of \f5dt\fP is being changed to a new one given in
+\f5(Dtmethod_t*)data\fP.
+.Tp
+\f5DT_HASHSIZE\fP:
+This event is applicable to
+the methods \f5Dtset\fP, \f5Dtbag\fP, \f5Dtrhset\fP and \f5Dtrhbag\fP.
+It is typically issued when the respective internal data structure of
+a method is about to be initialized.
+If the return value of the event handling function is positive,
+\f5*(ssize_t*)data\fP is examined for further action;
+else, it is ignored.
+A positive return value means that the event function wishes to suggest a table size.
+It does that by setting \f5*(ssize_t*)data\fP to the desired size.
+Then, the actual table size will be the maximum of the absolute value
+of \f5*(ssize_t*)data\fP and some predefined value set by the method.
+In addition, if \f5*(ssize_t*)data\fP was negative,
+the \f5Dtset\fP and \f5Dtbag\fP methods will never resize the hash table.
+.Tp
+\f5DT_ERROR\fP:
+This event announces an error that occurred during some operations.
+The argument \f5(char*)data\fP is a null-terminated string describing the error.
+.PP
+.Ss "#define DTOFFSET(struct_s,member)"
+This macro function computes the offset of \f5member\fP from the start
+of structure \f5struct_s\fP. It is useful for getting the offset of
+a \f5Dtlink_t\fP embedded inside an object.
+.PP
+.Ss "#define DTDISC(disc,key,size,link,makef,freef,comparf,hashf,memoryf,eventf)"
+This macro function initializes the discipline pointed to by \f5disc\fP
+with the given values.
+.PP
+.Ss "OBJECT OPERATIONS"
+.PP
+.Ss " Void_t* dtinsert(Dt_t* dt, Void_t* obj)"
+.Ss " Void_t* dtappend(Dt_t* dt, Void_t* obj)"
+These functions add an object prototyped by \f5obj\fP into \f5dt\fP.
+\f5dtinsert()\fP and \f5dtappend()\fP perform the same function
+for all methods except for \f5Dtlist\fP (see \f5Dtlist\fP for details).
+If there is an existing object in \f5dt\fP matching \f5obj\fP
+and the storage method is \f5Dtset\fP, \f5Dtrhset\fP or \f5Dtoset\fP,
+\f5dtinsert()\fP and \f5dtappend()\fP will simply return the matching object.
+Otherwise, a new object is inserted according to the method in use.
+See \f5Dtdisc_t.makef\fP for object construction.
+The new object or a matching object as noted will be returned on success
+while \f5NULL\fP is returned on error.
+.PP
+.Ss " Void_t* dtdelete(Dt_t* dt, Void_t* obj)"
+If \f5obj\fP is \f5NULL\fP, methods \f5Dtstack\fP and \f5Dtqueue\fP
+delete respectively stack top or queue head while other methods do nothing.
+If \f5obj\fP is not \f5NULL\fP, an object matching \f5obj\fP is deleted.
+See \f5Dtdisc_t.freef\fP for object destruction.
+\f5dtdelete()\fP returns the deleted object (even if it was deallocated)
+or \f5NULL\fP on error.
+.PP
+.Ss " Void_t* dtattach(Dt_t* dt, Void_t* obj)"
+This function is similar to \f5dtinsert()\fP but \f5obj\fP itself
+will be inserted into \f5dt\fP even if a discipline
+function \f5makef\fP is defined.
+.PP
+.Ss " Void_t* dtdetach(Dt_t* dt, Void_t* obj)"
+This function is similar to \f5dtdelete()\fP but the object to be deleted
+from \f5dt\fP will not be freed (via the discipline \f5freef\fP function).
+.PP
+.Ss " Void_t* dtsearch(Dt_t* dt, Void_t* obj)"
+.Ss " Void_t* dtmatch(Dt_t* dt, Void_t* key)"
+These functions find an object matching \f5obj\fP or \f5key\fP either from \f5dt\fP or
+from some dictionary accessible from \f5dt\fP via a viewpath (see \f5dtview()\fP.)
+\f5dtsearch()\fP and \f5dtmatch()\fP return the matching object or
+\f5NULL\fP on failure.
+.PP
+.Ss " Void_t* dtfirst(Dt_t* dt)"
+.Ss " Void_t* dtnext(Dt_t* dt, Void_t* obj)"
+\f5dtfirst()\fP returns the first object in \f5dt\fP.
+\f5dtnext()\fP returns the object that follows an object matching \f5obj\fP.
+Objects are ordered based on the storage method in use.
+For \f5Dtoset\fP and \f5Dtobag\fP, objects are ordered by object comparisons.
+For \f5Dtstack\fP, objects are ordered in reverse order of insertion.
+For \f5Dtqueue\fP, objects are ordered in order of insertion.
+For \f5Dtlist\fP, objects are ordered by list position.
+For \f5Dtset\fP, \f5Dtbag\fP, \f5Dtrhset\fP and \f5Dtrhbag\fP,
+objects are ordered by some internal order defined at the time when these
+functions are called.
+
+Objects in a dictionary or a viewpath can be walked using
+a \f5for(;;)\fP loop as below.
+.Cs
+ for(obj = dtfirst(dt); obj; obj = dtnext(dt,obj))
+.Ce
+.PP
+.Ss " Void_t* dtlast(Dt_t* dt)"
+.Ss " Void_t* dtprev(Dt_t* dt, Void_t* obj)"
+\f5dtlast()\fP and \f5dtprev()\fP are like \f5dtfirst()\fP and \f5dtnext()\fP
+but work in reverse order.
+For \f5Dtset\fP, \f5Dtbag\fP, \f5Dtrhset\fP and \f5Dtrhbag\fP,
+both reverse and forward orders are the same.
+Note that dictionaries on a viewpath are still walked in the order
+of the viewpath.
+.PP
+.Ss " Void_t* dtleast(Dt_t* dt, Void_t* obj)"
+.Ss " Void_t* dtmost(Dt_t* dt, Void_t* obj)"
+\f5dtleast()\fP returns the smallest object greater or equal to \f5obj\fP.
+\f5dtmost()\fP returns the largest object smaller or equal to \f5obj\fP.
+Again, object ordering depends on the storage method in use.
+For example, with \f5Dtoset\fP and \f5Dtobag\fP, the ordering of objects
+is well-defined and it is possible to call \f5dtleast()\fP or \f5dtmost()\fP
+on an object not in the dictionary and still get a meaningful result.
+On the other hand, with \f5Dtset\fP or \f5Dtrhset\fP, such a call will
+essentially be the same as \f5dtsearch()\fP because without matching
+an object, it cannot be determined what comes before or after.
+.PP
+.Ss " dtwalk(Dt_t* dt, int (*userf)(Dt_t*, Void_t*, Void_t*), Void_t* data)"
+This function calls \f5(*userf)(walk,obj,data)\fP on each object in \f5dt\fP and
+other dictionaries viewable from it.
+\f5walk\fP is the dictionary containing \f5obj\fP.
+If \f5userf()\fP returns a \f5<0\fP value,
+\f5dtwalk()\fP terminates and returns the same value.
+\f5dtwalk()\fP returns \f50\fP on completion.
+.PP
+.Ss " Dtlink_t* dtflatten(Dt_t* dt)"
+.Ss " Dtlink_t* dtlink(Dt_t* dt, Dtlink_t* link)"
+.Ss " Void_t* dtobj(Dt_t* dt, Dtlink_t* link)"
+Using \f5dtfirst()/dtnext()\fP or \f5dtlast()/dtprev()\fP
+to walk a single dictionary can incur significant cost due to function calls.
+For efficient walking of a single directory (i.e., no viewpathing),
+\f5dtflatten()\fP and \f5dtlink()\fP can be used.
+Objects in \f5dt\fP are made into a linked list and walked as follows:
+.Cs
+ for(link = dtflatten(dt); link; link = dtlink(dt,link) )
+.Ce
+.PP
+Note that \f5dtflatten()\fP returns a list of type \f5Dtlink_t*\fP,
+not \f5Void_t*\fP. That is, it returns a dictionary holder pointer,
+not a user object pointer
+(although both are the same if the discipline field \f5link\fP is zero.)
+The macro function \f5dtlink()\fP
+returns the dictionary holder object following \f5link\fP.
+The macro function \f5dtobj(dt,link)\fP
+returns the user object associated with \f5link\fP,
+Beware that the flattened object list is unflattened on any
+dictionary operations other than \f5dtlink()\fP.
+.PP
+.Ss " Dtlink_t* dtextract(Dt_t* dt)"
+.Ss " Dtlink_t* dtrestore(Dt_t* dt, Dtlink_t* list)"
+\f5dtextract()\fP extracts the list of objects from \f5dt\fP and makes it appear empty.
+\f5dtrestore()\fP repopulates \f5dt\fP with
+a list of objects previously obtained via \f5dtextract()\fP.
+It is important that the same discipline and method are in use at both
+extraction and restoration. Otherwise, undefined behaviors may result.
+These functions return \f5NULL\fP on error.
+
+.PP
+.Ss "DICTIONARY INFORMATION"
+.PP
+.Ss " Dt_t* dtvnext(Dt_t* dt)"
+This returns the dictionary that \f5dt\fP is viewing, if any.
+.Ss " ssize_t dtvcount(Dt_t* dt)"
+This returns the number of dictionaries that view \f5dt\fP.
+.Ss " Dt_t* dtvhere(Dt_t* dt)"
+This returns the dictionary \f5v\fP viewable from \f5dt\fP
+where an object was found from the most recent search or walk operation.
+.Ss " ssize_t dtsize(Dt_t* dt)"
+This function returns the number of objects stored in \f5dt\fP.
+.PP
+.Ss " ssize_t dtstat(Dt_t *dt, Dtstat_t* st)"
+This function reports dictionary statistics.
+It returns the number of objects stored in \f5dt\fP.
+.PP
+\f5Dtstat_t\fP contains the below fields:
+.Tp
+\f5int meth\fP:
+This returns the method used for the dictionary, e.g., \f5DT_SET\fP, \f5DT_OSET\fP, etc.
+.Tp
+\f5ssize_t size\fP:
+This has the number of objects in the dictionary.
+.Tp
+\f5ssize_t mlev\fP:
+This returns the maximum number of levels in the data structure used for object storage, i.e.,
+the binary tree or the recursive hash table.
+For a hash table with chaining (i.e., \f5Dtset\fP and \f5Dtbag\fP),
+it gives the length of the longest chain.
+.Tp
+\f5ssize_t lsize[]\fP:
+This gives the object counts at each level.
+For a hash table with chaining (i.e., \f5Dtset\fP and \f5Dtbag\fP),
+a level is defined as objects at that position in their chains.
+Since chains can be arbitrarily long, the report is limited
+to objects at a level less than \f5DT_MAXSIZE\fP.
+.Tp
+\f5ssize_t tsize[]\fP:
+For a hash table using a trie structure, this counts the number of
+sub-tables at each level. For example, \f5tsize[0]\fP should be 1
+only for this hash table type.
+.PP
+.Ss "HASH FUNCTIONS"
+.PP
+.Ss " unsigned int dtcharhash(unsigned int h, char c)"
+.Ss " unsigned int dtstrhash(unsigned int h, char* str, int n)"
+These functions compute hash values from bytes or strings.
+\f5dtcharhash()\fP computes a new hash value from byte \f5c\fP and seed value \f5h\fP.
+\f5dtstrhash()\fP computes a new hash value from string \f5str\fP and seed value \f5h\fP.
+If \f5n\fP is positive, \f5str\fP is a byte array of length \f5n\fP;
+otherwise, \f5str\fP is a null-terminated string.
+.PP
+.SH IMPLEMENTATION NOTES
+\f5Dtlist\fP, \f5Dtstack\fP and \f5Dtqueue\fP are based on doubly linked list.
+\f5Dtoset\fP and \f5Dtobag\fP are based on top-down splay trees.
+\f5Dtset\fP and \f5Dtbag\fP are based on hash tables with
+move-to-front collision chains.
+\f5Dtrhset\fP and \f5Dtrhbag\fP are based on a recursive hashing data structure
+that avoids table resizing.
+.PP
+.SH AUTHOR
+Kiem-Phong Vo, kpv@research.att.com
diff --git a/src/lib/libast/man/chr.3 b/src/lib/libast/man/chr.3
new file mode 100644
index 0000000..8d57648
--- /dev/null
+++ b/src/lib/libast/man/chr.3
@@ -0,0 +1,126 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH CHR 3
+.SH NAME
+chr \- character constant conversion routines
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+int chresc(const char* \fIs\fP, char** \fIe\fP);
+int chrtoi(const char* \fIs\fP);
+.EE
+.SH DESCRIPTION
+.L chresc
+converts and returns the next character constant in the 0-terminated string
+.IR s .
+If
+.I e
+is not 0 then
+.I *e
+is set to point to the next character in
+.I s
+on return.
+0 is returned and
+.I e
+is not modified when the end of
+.I s
+is reached.
+.PP
+.L chrtoi
+converts the 0-terminated string
+.I s
+to an
+.I int
+and returns the value.
+The characters in
+.I s
+are converted in order from the left and shifted into the
+.I int
+value until up to the number of characters in an
+.I int
+is reached.
+This operation is inherently machine-dependent,
+but at least its defined in one place.
+.PP
+The following
+.B \e
+escape sequences are recognized:
+.TP
+.RI \e ooo
+The character represented by the octal code
+.IR ooo .
+.TP
+.RI \ex xx
+The character represented by the hex code
+.IR xx .
+.TP
+.L \ea
+Alert (bell).
+.TP
+.L \eb
+Backspace.
+.TP
+.L \ef
+Formfeed.
+.TP
+.L \en
+Newline.
+.TP
+.L \er
+Carriage return.
+.TP
+.L \et
+Horizontal tab.
+.TP
+.L \ev
+Vertical tab.
+.TP
+.L \eE
+ESC (escape).
+.TP
+.L \e\e
+Backslash.
+.PP
+Other characters following
+.B \e
+are undefined (although for backwards compatibility they
+translate to themselves).
+.SH "SEE ALSO"
+str(3)
diff --git a/src/lib/libast/man/compat.3 b/src/lib/libast/man/compat.3
new file mode 100644
index 0000000..eb5e8a7
--- /dev/null
+++ b/src/lib/libast/man/compat.3
@@ -0,0 +1,103 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH COMPATIBILITY 3
+.SH NAME
+compatibility \- ast library compatibility routines
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+int atexit(void(*)(void));
+char* confstr(int);
+int dup2(int, int);
+long fpathconf(int, int);
+int getgroups(int, int*);
+char* getwd(char*);
+int killpg(int, int);
+int link(const char*, const char*);
+int lstat(const char*, struct stat*);
+int memcmp(const char*, const char*, unsigned int);
+char* memcpy(char*, const char*, int);
+char* memset(char*, char, int);
+int mkdir(const char*, mode_t);
+int mkfifo(const char*, mode_t);
+int mknod(const char*, mode_t);
+char* mktemp(char*);
+int mount(const char*, const char*, int);
+long pathconf(const char*, int);
+int perror(const char*);
+FILE* popen(const char*, const char*);
+int readlink(const char*, char*, int);
+int remove(const char*);
+int rename(const char*, const char*);
+int rmdir(const char*);
+int setpgid(pid_t, pid_t);
+int sigmask(int);
+int sigsetmask(long);
+int sigunblock(int);
+char* strchr(const char*, int);
+char* strrchr(const char*, int);
+double strtod(const char*, char**);
+long strtol(const char*, char**, int);
+int symlink(const char*, const char*);
+long sysconf(int);
+int system(const char*);
+char* tmpnam(char*);
+int unlink(const char*);
+int vfork(void);
+int waitpid(pid_t, int*, int);
+.EE
+.SH DESCRIPTION
+These routines are described in the ANSI C, POSIX, BSD and System V manual
+sections 2 and 3.
+The interfaces are preserved and present in all libast implementations.
+Where conflicts exist the POSIX syntax and semantics are implied.
+The appropriate error value is returned and
+.I errno
+is set to
+.L EINVAL
+when emulation is either too expensive or not possible.
+.SH CAVEATS
+If you
+.L "#undef foo"
+and then call
+.L foo
+you may end up with the local implementation of
+.LR foo ,
+and then you get what you payed for.
diff --git a/src/lib/libast/man/error.3 b/src/lib/libast/man/error.3
new file mode 100644
index 0000000..df47d34
--- /dev/null
+++ b/src/lib/libast/man/error.3
@@ -0,0 +1,283 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH ERROR 3
+.SH NAME
+error \- error and debug trace message formatter
+.SH SYNOPSIS
+.EX
+#include <error.h>
+
+Error_info_t error_info;
+
+void error(int \fIlevel\fP, ...);
+void errorv(const char* \fIlibrary\fP, int \fIlevel\fP, va_alist \fIargs\fP);
+void liberror(const char* \fIlibrary\fP, int \fIlevel\fP, ...);
+
+#include <debug.h>
+
+debug(\fIstatement\fP)
+message((int \fIlevel\fP, ...))
+libmessage((const char* \fIlibrary\fI, int \fIlevel\fP, ...))
+.EE
+.SH DESCRIPTION
+.L error
+is the error and debug trace message formatter.
+.I level
+is the severity level.
+Messages with
+.I "level < error_info.trace"
+are suppressed.
+.I error_info.trace
+is initially
+.LR 0 .
+The remaining arguments are passed on to
+.LR printf .
+A
+.I newline
+is appended to the message text, so none should appear in the
+.L printf
+format.
+If
+.I error_info.id
+is not
+.L 0
+then messages with
+.I "level > 0"
+are prefixed by
+.IR error_info.id: .
+.PP
+Before the message text is output to standard error
+it is passed to the function
+.LR "char* ERROR_translate(const char* \fItext\fP, int \fIflag\fP)" .
+By default
+.L ERROR_translate
+returns the
+.L text
+argument, but on some systems it may do language translation via lookup
+on the original source text.
+.RL ( error
+calls
+.L ERROR_translate
+with a 0
+.L flag
+argument).
+.PP
+.I level
+may be one of:
+.TP
+.B <0
+Negative values are for debug tracing.
+Debug messages are prefixed with
+.BI debug level.
+If
+.I "errno != error_info.last_errno"
+then
+.I error_info.last_errno
+is set to
+.I errno
+and the error text for errno is appended to the message.
+.TP
+.B "ERROR_INFO [0]"
+Information only; no prefixes are added to the message.
+.TP
+.B "ERROR_WARNING [1]"
+.L "warning:"
+is added after
+.L error_info.id
+and
+.I error_info.warnings
+is incremented.
+.TP
+.I "ERROR_ERROR [2]"
+(soft error)
+.I error_info.errors
+is incremented.
+.TP
+.B ">= ERROR_FATAL [3]"
+(hard error)
+.I error_info.errors
+is incremented and
+.L exit(\fIlevel\fP\-2)
+is called after the message is emitted.
+.TP
+.B "ERROR_PANIC [77]"
+(unrecoverable internal error)
+.L "panic:"
+is added after
+.IR error_info.id .
+.PP
+The following may be inclusive-or'd into
+.I level
+for alternate behavior:
+.TP
+.L ERROR_SYSTEM
+The error text for
+.I errno
+is appended to the message.
+.TP
+.L ERROR_OUTPUT
+The next argument is the file descriptor where the error message
+should be emitted.
+.TP
+.L ERROR_SOURCE
+Then next two arguments are a file name and line number that are added
+to the message after
+.IR error_info.id .
+.TP
+.L ERROR_USAGE
+A usage message is emitted.
+.TP
+.L ERROR_PROMPT
+The trailing
+.I newline
+is suppressed.
+.TP
+.L ERROR_NOID
+The
+.I error_info.id
+prefix is suppressed.
+.TP
+.L ERROR_LIBRARY
+The message is from a library routine.
+.SH ENVIRONMENT
+The elements of the global struct
+.I error_info
+control error output and actions.
+Parts of
+.I error_info
+can be initialized from the
+.L ERROR_OPTIONS
+environment variable.
+.L ERROR_OPTIONS
+contains space separated
+.IR name [ =value ]
+options, described below.
+.TP
+.I "int core"
+If
+.I "error_info.core != 0"
+then
+.I "level >= error_info.core"
+generates a core dump.
+Initialized by
+.EX
+ERROR_OPTIONS="core=\fIlevel\fP"
+.EE
+where
+.I level
+can be a number or one of
+.LR error ,
+.LR fatal ,
+or
+.LR panic .
+.I error_info.core
+is a handy way to get a stack trace at the exact point of error.
+.TP
+.I "int error_info.trace"
+If
+.I "error_info.trace != 0"
+and
+.I "level < error_info.trace"
+then the error message text is suppressed.
+.L exit()
+may still be called if appropriate for
+.IR level .
+Initialized by
+.EX
+ERROR_OPTIONS="trace=\fIlevel\fP"
+.EE
+where
+.I error_info.trace
+is set to the negative of
+.IR level .
+.PP
+Library error messages, suppressed by default, are enabled by
+.EX
+ERROR_OPTIONS="library"
+.EE
+The system
+.I errno
+message text can be forced for each message by
+.EX
+ERROR_OPTIONS="system"
+.EE
+.SH "EXTENDED DESCRIPTION"
+.L "<debug.h>"
+provides debugging message macros when
+.L DEBUG
+or
+.L _TRACE_
+are defined
+.RL ( _TRACE_
+is defined by
+.I makerules
+when
+.L CCFLAGS
+contains
+.LR \-g ).
+All of the macros expand to nothing when both
+.L DEBUG
+and
+.L _TRACE_
+are not defined.
+Otherwise
+.L debug
+expands its arg and
+.L libmessage
+and
+.L message
+call
+.L liberror
+and
+.L error
+respectively if
+.IR "error_info.trace<0" .
+Notice that
+.L libmessage
+and
+.L message
+are macro hacks that require double parentheses ((...)) around the
+arguments.
+.SH EXAMPLE
+To enable debugging message level -3, library messages, and system
+.I errno
+text for all commands:
+.EX
+export ERROR_OPTIONS="trace=3 library system"
+.EE
diff --git a/src/lib/libast/man/find.3 b/src/lib/libast/man/find.3
new file mode 100644
index 0000000..a14ceae
--- /dev/null
+++ b/src/lib/libast/man/find.3
@@ -0,0 +1,89 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH FIND 3
+.SH NAME
+find \- fastfind algorithm interface
+.SH SYNOPSIS
+.EX
+#include <find.h>
+
+void* findopen(const char* \fIpattern\fP);
+char* findnext(void* \fIhandle\fP);
+void findclose(void* \fIhandle\fP);
+.EE
+.SH DESCRIPTION
+These routines access the data generated by the
+.I fastfind
+algorithm.
+The data itself is generated by a standalone program that is run daily
+via
+.I cron
+or
+.IR at .
+.PP
+.L findopen
+returns a handle to a
+.I fastfind
+stream for the
+.I ksh
+file pattern
+.IR pattern .
+.L findnext
+returns the next pathname that matches the pattern specified by
+.IR handle .
+.L findnext
+returns
+.L 0
+when no more pathnames match the pattern.
+Finally,
+.L findclose
+closes the
+.I fastfind
+stream for
+.IR handle .
+.SH BUGS
+These rotuines are only as good as the
+.I fastfind
+information which is in the system administration domain.
+.SH "SEE ALSO"
+tw(1),
+find(1),
+strmatch(3)
+.br
+James A. Woods, \fIFast Find Algorithm\fP, Usenix ;login:, February/March, 1983, p. 8
diff --git a/src/lib/libast/man/fmt.3 b/src/lib/libast/man/fmt.3
new file mode 100644
index 0000000..dbe1aab
--- /dev/null
+++ b/src/lib/libast/man/fmt.3
@@ -0,0 +1,213 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH FMT 3
+.SH NAME
+fmt \- string formatting routines
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+#include <ls.h>
+
+char* fmtbase(long \fInumber\fP, int \fIbase\fP, int \fIprefix\fP);
+char* fmtdev(struct stat* \fIst\fP);
+char* fmtelapsed(unsigned long \fIcount\fP, int \fIpersec\fP)
+char* fmterror(int \fIerrno\fP);
+char* fmtesc(const char* \fIstring\fP);
+char* fmtfs(struct stat* \fIst\fP);
+char* fmtgid(int \fIgid\fP);
+char* fmtmatch(const char* \fIre\fP);
+char* fmtmode(int \fImode\fP, int \fIexternal\fP);
+char* fmtperm(int \fIperm\fP);
+char* fmtre(const char* \fIpattern\fP);
+char* fmtsignal(int \fIsig\fP);
+char* fmttime(const char* \fIformat\fP, time_t \fItm\fP);
+char* fmtuid(int \fIuid\fP);
+.EE
+.SH DESCRIPTION
+These routines return a pointer to a formatted string for various numeric
+and string entities.
+Some routines may cache information to speed up the next call.
+Most of the routines return a pointer to a private buffer, the
+contents of which are overwritten on the next call to that routine.
+Most
+.L fmt
+routines have a corresponding
+.L str
+routine that converts in the other direction.
+There is nothing spectacular about this collection other than that
+it provides a single place where the exact format is spelled out.
+.PP
+.L fmtbase
+formats a base
+.I base
+representation for
+.IR number .
+If
+.I "prefix != 0"
+then the base prefix is included in the formatted string.
+If
+.I "number == 0"
+or
+.I "base == 0"
+then the output is signed base 10.
+.PP
+.L fmtdev
+returns the device handle name specified by the
+.L stat
+structure
+.IR st .
+This is the device information displayed by
+.IR "ls \-l" .
+.PP
+.L fmtelapsed
+formats the elapsed time for
+.I (count/persec)
+seconds.
+The two largest time units are used, limiting the return value length
+to at most 6 characters.
+The units are:
+.RS
+.TP
+.B s
+seconds
+.TP
+.B m
+minutes
+.TP
+.B h
+hours
+.TP
+.B days
+.TP
+.B weeks
+.TP
+.B M
+months
+.TP
+.B Y
+years
+.TP
+.B S
+scores
+.RE
+.PP
+.L fmterror
+returns the system error message text for the error number
+.IR errno .
+.PP
+.L fmtesc
+formats non-ASCII characters in
+.I string
+into C-style
+.B \e
+sequences.
+These sequences are understood by
+.L chresc
+and
+.LR chrtoi .
+.PP
+.L fmtfs
+returns the file system type name corresponding to the
+.L stat
+structure
+.IR st .
+.PP
+.L fmtgid
+returns the group name for
+.IR gid .
+.PP
+.L fmtmatch
+returns the
+.L strmatch
+equivalent pattern for the regular expression pattern
+.IR re .
+0 is returned for invalid
+.IR re .
+.PP
+.L fmtmode
+returns the
+.I "ls \-l"
+mode string for the file mode bits in
+.IR mode .
+If
+.I "external != 0"
+then
+.I mode
+is
+.IR modecanon (3)
+canonical.
+.PP
+.L fmtperm
+returns the
+.I chmod
+permission string for the permission bits in
+.IR perm .
+.PP
+.L fmtre
+returns the regular expression
+equivalent pattern for the
+.L strmatch
+pattern
+.IR pattern .
+0 is returned for invalid
+.IR pattern .
+.PP
+.L fmtsignal
+returns the signal name, sans
+.LR SIG ,
+for the signal number
+.IR sig .
+If
+.I "sig < 0"
+then the description text for
+.I \-sig
+is returned.
+.PP
+.L fmttime
+returns the results of
+.I "tmfmt(buf,sizeof(buf),format,tm)"
+in the private buffer
+.IR buf .
+.PP
+.L fmtuid
+returns the user name for
+.IR uid .
+.SH "SEE ALSO"
+modecanon(3),
+str(3)
diff --git a/src/lib/libast/man/fmtls.3 b/src/lib/libast/man/fmtls.3
new file mode 100644
index 0000000..50d872b
--- /dev/null
+++ b/src/lib/libast/man/fmtls.3
@@ -0,0 +1,143 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRLS 3
+.SH NAME
+fmtls \- format file information in buffer
+.SH SYNOPSIS
+.EX
+#include <ls.h>
+
+char* fmtls(char* \fIbuf\fP, char* \fIname\fP, struct stat* \fIst\fP, char* \fIinfo\fP, char* \fIlink\fP, int \fIflags\fP);
+.EE
+.SH DESCRIPTION
+.L fmtls
+formats
+.IR ls (1)
+style file information into the buffer
+.IR buf .
+A pointer to the trailing 0 in
+.I buf
+is returned.
+.I name
+is the file name.
+.I st
+is a pointer to the
+.B "struct stat
+status information for
+.I name
+obtained from one of the
+.IR stat (2)
+routines.
+.I info
+is an additional string that will appear before
+.I name
+in
+.I buf
+and
+.I link
+is the name of the related hard or soft link file.
+Both
+.I info
+and
+.I link
+may be 0.
+.I flags
+controls the format style and may be a combination of the following:
+.PP
+.TP
+.B LS_ATIME
+Use
+.I st->st_atime
+rather than
+.I st->st_mtime
+for
+.BR LS_LONG .
+.TP
+.B LS_CTIME
+Use
+.I st->st_mtime
+rather than
+.I st->st_mtime
+for
+.BR LS_LONG .
+.TP
+.B LS_BLOCKS
+List the number of blocks.
+.TP
+.B LS_INUMBER
+List the file serial number (inode number).
+.TP
+.B LS_LONG
+List the file mode, link count, user and group name,
+size or major/minor device number, and date along with the
+file name.
+.TP
+.B LS_MARK
+The file name is appended with
+.L /
+for directories,
+.L @
+for symbolic links,
+and
+.L *
+for executable files.
+.TP
+.B LS_NOGROUP
+Omit the group name from
+.BR LS_LONG .
+.TP
+.B LS_NOUSER
+Omit the user name from
+.BR LS_LONG .
+.PP
+The user and group fields are each
+.B LS_W_NAME
+characters wide,
+the
+.B LS_INUMBER
+field is
+.B LS_W_INUMBER
+characters wide,
+and the
+.B LS_BLOCKS
+field is
+.B LS_W_BLOCKS
+characters wide.
+.SH "SEE ALSO"
+ls(1), stat(2), strmode(3)
diff --git a/src/lib/libast/man/fs3d.3 b/src/lib/libast/man/fs3d.3
new file mode 100644
index 0000000..7dfd448
--- /dev/null
+++ b/src/lib/libast/man/fs3d.3
@@ -0,0 +1,92 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH FS3D 3
+.SH NAME
+fs3d \- 3D (nDFS) on/off switch
+.SH SYNOPSIS
+.EX
+#include <fs3d.h>
+
+int fs3d(int \fIop\fP);
+.EE
+.SH DESCRIPTION
+.L fs3d
+controls and queries the
+.B 3D
+file system
+.RB (aka nDFS )
+enable state.
+.L 0
+is returned if the current process cannot mount
+.B 3D
+files.
+.I op
+may be one of:
+.TP
+.B FS3D_TEST
+Returns
+.L "FS3D_ON [1]"
+if
+.B 3D
+is enabled,
+.L "FS3D_OFF [0]"
+otherwise.
+.TP
+.B FS3D_ON
+Enables
+.B 3D
+and returns the previous
+.L 3D
+state (either
+.B FS3D_ON
+or
+.BR FS3D_OFF ).
+.TP
+\fBFS3D_LIMIT(\fIlimit\fB)\fR
+Sets the viewpath level limit to
+.IR limit .
+The previous limit is returned.
+.TP
+.B FS3D_INIT
+Re-initialize the
+.B 3D
+tables.
+Used for debugging.
+.SH "SEE ALSO"
+3D(1)
diff --git a/src/lib/libast/man/ftwalk.3 b/src/lib/libast/man/ftwalk.3
new file mode 100644
index 0000000..f339ab7
--- /dev/null
+++ b/src/lib/libast/man/ftwalk.3
@@ -0,0 +1,235 @@
+.fp 5 CW
+.TH FTWALK 3
+.SH NAME
+\fBftwalk\fR \- file tree walker
+.SH SYNOPSIS
+.ta .75i 1.5i 2.25i 3i 3.75i 4.5i 5.25i 6i
+.PP
+.nf
+\f5
+#include <ftwalk.h>
+
+int ftwalk(char* path, int (*userf)(struct FTW* ftw), int options,
+ int (*comparf)(struct FTW* ftw1, struct FTW* ftw2));
+
+
+int ftwflags(void);
+\fR
+.fi
+.SH DESCRIPTION
+.PP
+\fIFtwalk\fR traverses a directory hierarchy using depth-first search.
+Upon visiting each file or directory in the hierarchy, it calls
+the user function \fIuserf\fP to process that file or directory.
+On a directory object, \fIuserf\fR may be called twice, once in preorder
+and once in postorder.
+On a terminal object such as a file or an unreadable directory,
+\fIuserf\fP is called only once.
+Cycles due to hard links or symbolic links are detected
+to avoid infinite loops.
+.PP
+\fIPath\fR is the starting point of the search.
+It may be an absolute path name or a path name relative to
+the current directory.
+If \fIpath\fR is a null pointer or points to an empty string, it is treated
+as if it points to the current (dot) directory.
+.PP
+\fIOptions\fR consists of zero or more of the following bits:
+.IP
+FTW_CHILDREN:
+This implies preorder calls to \fIuserf\fR on directory objects.
+On such a call to \fIuserf\fR,
+the field \fIftw->link\fR (below) points to a link list of
+the children of the respective directory.
+Upon returning from \fIuserf\fP,
+if the field \fIftw->status\fR of any child object
+is set to FTW_SKIP (below), that child is pruned from the search.
+.IP
+FTW_DELAY: When \fBFTW_CHILDREN\fP is turned on,
+the fields \fIftw->statb\fP (\fIstruct stat\fP) of children objects
+remain undefined until these objects are visited.
+.IP
+FTW_DOT: Do not use \fIchdir\fR(2) during the traversal.
+Normally \fIchdir\fR is used so that
+the base name of the object about to be processed can be used
+in accessing its data.
+This can enhance \fIftwalk\fR efficiency but certain program effects
+such as core dumps may be generated in unexpected places
+or may not even be generated at all.
+Whenever \fIchdir\fR generates an error, if possible,
+the current directory is restored to the starting directory
+(see FTW_NAME and FTW_PATH).
+.IP
+FTW_MULTIPLE: The \fIpath\fP argument is treated as a \fIchar**\fP
+pointer to a null-terminated array of path names.
+All hierarchies rooted at these paths will be searched
+.IP
+FTW_POST: Calls to the user function are issued only in postorder.
+That is, \fIuserf\fP is called on a directory only after its descendants have
+been processed.
+The absence of this bit indicates that calls to the user functions
+are issued in preorder. That is, \fIuserf\fP is
+called on a directory before its descendants are processed.
+.IP
+FTW_PHYSICAL: Use \fIlstat\fR(2) instead of \fIstat\fR(2) to get
+file status and allow detection of symbolic links.
+In addition, if each component
+of the absolute path to the starting object has search permission,
+the absolute path is used for early detection of cycles.
+.IP
+FTW_META|FTW_PHYSICAL: Use \fIstat\fR(2) for top level file status and
+\fIlstat\fR(2) for all other files.
+Used to implement the POSIX
+.B \-H
+option for commands like
+.IR ls .
+.IP
+FTW_TWICE: Calls to the user function are issued in both preorder and postorder
+for directory objects.
+.IP
+FTW_USER: The first of 6 user defined option bits.
+These bits are ignored by \fIftwalk\fP.
+.PP
+\fIUserf\fR is a user supplied function that is
+called upon different visits of an object.
+If the return value of \fIuserf\fR is non-zero,
+\fIftwalk\fR returns immediately with the same value.
+The \fIuserf\fP prototype is:
+.PP
+.nf
+ int userf(struct FTW* ftw)
+.fi
+.PP
+\fBstruct FTW\fP contains at least the following elements:
+.PP
+.nf
+ struct FTW* link; /* link list of children */
+ struct FTW* parent; /* parent object on the search path */
+ union
+ {
+ long number; /* local number */
+ void* pointer; /* local pointer */
+ } local; /* user defined */
+ struct stat statb; /* stat buffer of this object */
+ char* path; /* full pathname */
+ short pathlen; /* strlen(path) */
+ unsigned short info; /* type of object */
+ unsigned short status; /* status of object */
+ short level; /* depth of object on the search path */
+ short namelen; /* strlen(name) */
+ char name[]; /* file name of object */
+.fi
+.PP
+The \fIlink\fR field is normally NULL.
+If the option FTW_CHILDREN was turned on,
+it points to the start of the list of children
+of the directory being visited in preorder.
+Finally, if the directory being visited causes a cycle,
+\fIlink\fR points to the object on the search path that is
+identical to this directory. Note that if FTW_PHYSICAL was turned on,
+this may point to a directory that is an ancestor of the starting
+object.
+.PP
+The \fIparent\fR field points to the parent object
+on the search path. For convenience, a parent object is also supplied for
+the starting object.
+In this case, except for the \fIlocal\fR field which is initialized
+to 0 and the \fIlevel\fR field which contains a negative number,
+the rest of the structure may be undefined.
+.PP
+The \fIinfo\fR field indicates the type of the object
+being visited and the type of the visit. The types are:
+.IP
+FTW_D: A directory being visited in preorder, i.e.,
+none of its children has been visited by the search.
+.IP
+FTW_DNX: A directory being visited in preorder that does not have
+search permission.
+.IP
+FTW_DP: A directory being visited in postorder, i.e., all of its
+descendants have been completely processed.
+.IP
+FTW_DC: A directory that causes cycles. This is a terminal object.
+.IP
+FTW_DNR: A directory that cannot be opened for reading. This is a terminal object.
+.IP
+FTW_F: An ordinary file.
+.IP
+FTW_SL: A symbolic link.
+Unless FTW_FOLLOW (below) is issued by the user function,
+this object is terminal.
+.IP
+FTW_NS: \fIStat\fR failed on this object.
+The stat buffer \fIstatb\fR is undefined.
+This object is terminal.
+.PP
+The \fIstatus\fR field of \fIstruct FTW\fR is used to communicate information
+between \fIftwalk\fR and \fIuserf\fR. On calls to \fIuserf\fR, it has one of
+two values:
+.IP
+FTW_NAME: The name of the object as defined in \fIftw->name\fR should be used for
+accessing its file information. This is because \fIchdir\fR(2) has been used
+to set the current directory to a suitable place (see FTW_CHDIR).
+.IP
+FTW_PATH: The argument \fIpath\fR of \fIuserf\fR should be used
+for accessing the file information of the object.
+.PP
+Upon returning, \fIuserf\fR may set the \fIstatus\fR field
+to one of the following values:
+.IP
+FTW_AGAIN: If this is a directory object being visited in postorder,
+it will be processed \fIagain\fR as if it had not been visited.
+.IP
+FTW_NOPOST: If this is a directory object being visited in preorder,
+the user function will not be called on its postorder visit.
+.IP
+FTW_SKIP: This object and its descendants are pruned from the search.
+.IP
+FTW_FOLLOW: If this object is a symbolic link,
+follow the link to its physical counterpart.
+.PP
+\fIComparf\fR, if not NULL, is a pointer to a function
+used to define a search ordering for children of a directory.
+If FTW_CHILDREN is turned on, the ordering of the children of
+a directory is done before the preorder call to \fIuserf\fR on that directory.
+Therefore, in that case, \fIftw->link\fR will point to the smallest child.
+.PP
+The \fIcomparf\fP prototype is:
+.PP
+.nf
+ int comparf(struct FTW* ftw1, struct FTW* ftw2)
+.fi
+.PP
+\fIComparf\fR should return a value <0, 0, or >0 to indicate whether
+\fIftw1\fR is considered smaller, equal, or larger than \fIftw2\fR.
+.PP
+\fIFtwalk\fR normally returns 0.
+On hard errors such as running out of memory, it returns -1.
+\fIFtwalk\fR may also return other values as discussed with respect
+to \fIuserf\fR.
+.PP
+\fIFtwflags\fR returns a combination of \fB0, FTW_META, FTW_PHYSICAL\fR
+according to the
+preferences specified by
+\fBastconf("PATH_RESOLVE",0,0)\fR:
+.TP
+.B logical
+0
+.TP
+.B metaphysical
+.B "FTW_META|FTW_PHYSICAL"
+.TP
+.B physical
+.B FTW_PHYSICAL
+.SH HISTORY
+\fIFtwalk\fR performs similar functions as that of
+the routine \fIftw\fR provided in System V.
+However, it is more general than \fIftw\fR
+and suitable for use as a base in implementing
+popular tools such as \fIls, find, tar, du,\fR and \fIrm\fR.
+\fIFtwalk\fR also handles symbolic links and hard links gracefully.
+.SH AUTHORS
+Phong Vo, Glenn Fowler, Dave Korn
+.SH SEE ALSO
+find(1), rm(1), du(1), ls(1), tar(1), stat(2), symlink(2),
+astfeature(3), ftw(3), pathcd(3)
diff --git a/src/lib/libast/man/getcwd.3 b/src/lib/libast/man/getcwd.3
new file mode 100644
index 0000000..3403296
--- /dev/null
+++ b/src/lib/libast/man/getcwd.3
@@ -0,0 +1,67 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH GETCWD 3
+.SH NAME
+getcwd \- return absolute path to current directory
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+char* getcwd(char* \fIbuf\fP, size_t \fIlen\fP);
+.EE
+.SH DESCRIPTION
+.L getcwd
+copies the absolute path name of the current directory info into
+.I buf
+of length
+.IR len .
+The return path may be longer than
+.LR PATH_MAX .
+If
+.I "buff == 0"
+then space is allocated via
+.IR malloc (3)
+and
+.I len
+extra characters are reserved after the generated path name.
+A pointer to the path name is returned,
+.L 0
+on error.
+.SH "SEE ALSO"
+pathcd(3)
diff --git a/src/lib/libast/man/hash.3 b/src/lib/libast/man/hash.3
new file mode 100644
index 0000000..162d62d
--- /dev/null
+++ b/src/lib/libast/man/hash.3
@@ -0,0 +1,644 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH HASH 3
+.SH NAME
+hash \- hash table support (obsolete: use \fBcdt\fP instead)
+.SH SYNOPSIS
+.L "#include <hash.h>"
+.SH DESCRIPTION
+The
+.I hash
+routines manipulate collections of dynamic, scoped hash tables.
+.PP
+A hash table provides an association between a
+.I key
+and its
+.IR value .
+A
+.I key
+is a sequence of
+.L char
+elements and a
+.I value
+is a user supplied pointer to the value.
+Each hash table has a dynamic number of slots,
+each pointing to the head of a forward linked
+.IR "collision chain" .
+.PP
+Hashing occurs as follows:
+a
+.I "hash function"
+takes a
+.I key
+as an argument and returns a non-negative index.
+The index modulo the table size produces a
+slot that points to a
+.IR "collision chain" .
+The collision chain is sequentially searched until a match is found for
+.IR key .
+The hash tables are automatically resized as new entries are added.
+.PP
+Each hash table has one key type.
+The default key type is a pointer to a null-terminated string.
+The alternate key type is a pointer to a fixed length byte buffer,
+declared for a given table by the
+.L hashalloc()
+function described below.
+.PP
+Hash table information is partitioned into two parts for efficient scoping.
+The
+.I root
+part contains fixed information that is shared among a set of related
+hash tables.
+The remaining information is maintained on a per-table basis.
+.PP
+These basic types are defined in the header file
+.B hash.h
+(alternate names are listed in parenthesis):
+.TP
+.L "Hash_table_t (HASHTABLE)"
+The per-table information.
+The readonly public elements are:
+.RS
+.TP
+.L "int buckets"
+The number of table entries.
+.TP
+.L "char* name"
+The hash table name.
+.TP
+.L "root"
+The root information.
+The public elements are:
+.RS
+.TP
+.L "int root->accesses"
+The number of lookups.
+.TP
+.L "int root->collisions"
+The number of lookup collisions.
+.RE
+.TP
+.L "Hash_table_t* scope"
+The table that this scope covers,
+.L NULL
+if the table is not a scope.
+.TP
+.L "int size"
+The current hash table size.
+.RE
+.TP
+.L "Hash_bucket_t (HASHBUCKET)"
+A collision chain hash bucket.
+The public structure elements are:
+.RS
+.TP
+.L "char* hashname(Hash_bucket_t*)"
+Returns a pointer to the hash bucket key given the bucket pointer.
+.TP
+.L "char* value"
+The value associated with the key.
+.RE
+.TP
+.L "Hash_header_t (HASHHEADER)"
+The hash bucket header that must be the first element in all user defined
+buckets.
+.L HASH_VALUE
+may not be used with user defined buckets.
+.TP
+.L "Hash_position_t (HASHPOSITION)"
+Stores the hash table position for
+.LR hashscan .
+The public elements are:
+.RS
+.TP
+.L "Hash_bucket_t* bucket"
+The current hash bucket pointer.
+.RE
+.PP
+The routines are:
+.TP
+.L "Hash_table_t* hashalloc(Hash_table_t* ref, int op, ...)"
+Creates a new hash table and returns a pointer to the table.
+.IR malloc (3)
+is used to allocate space for the table.
+.L NULL
+is returned if the table cannot be created.
+.L ref
+is a pointer to a reference hash table that provides
+default values for unspecified information.
+The new hash table and
+.L ref
+share the same root information.
+If
+.L ref
+is
+.L NULL
+then new root information is created for the new table.
+The remaining arguments appear in
+.I op-arg
+pairs, followed by a final
+.L 0
+argument.
+The
+.I op-arg
+pairs are:
+.RS
+.TP
+.L "HASH_alloc, (char(*)()) alloc"
+.L alloc
+is a function that is called to process
+.L Hash_bucket_t
+.L value
+assignments.
+The single argument is
+.L "char* value"
+and the processed
+.L char*
+value is returned.
+.TP
+.L "HASH_clear, int flags"
+.L flags
+are the
+.L ref
+flags to be cleared in the new hash table.
+See
+.L HASH_set
+below.
+.TP
+.L "HASH_compare, (int(*)()) compare"
+Specifies an alternate
+.I key
+comparison function.
+The arguments and return value for
+.L compare
+are the same as for
+.IR strncmp (3)
+if
+.L HASH_namesize
+is specified and
+.IR strcmp (3)
+otherwise.
+The first argument is the
+.I key
+from the current hash bucket on the
+.I "collision chain"
+and the second argument is the user supplied
+.IR key .
+.TP
+.L "HASH_free, (int(*)()) free"
+.L free
+is a function that is called when a hash bucket is freed.
+If
+.L HASH_BUCKET
+was set in
+.L hashalloc
+then the hash bucket pointer is passed, otherwise the bucket
+.L value
+pointer is passed.
+.TP
+.L "HASH_hash, (int(*)()) hash"
+Specifies an alternate
+.I key
+hash function.
+A
+.L char*
+key argument (and, if
+.L HASH_namesize
+is specified, an
+.L int
+key size argument) is passed to
+.LR hash .
+The return value must be a non-negative
+.LR int .
+.TP
+.L "HASH_meanchain, int meanchain"
+Specifies the mean collision chain length.
+The hash table is automatically resized when this value is exceeded.
+The default mean chain length is 2.
+.TP
+.L "HASH_name, char* name"
+Associates
+.L name
+with the hash table.
+Used by
+.LR hashdump) .
+.TP
+.L "HASH_namesize, int namesize"
+The fixed size in bytes for
+.I keys
+in the table.
+If
+.L namesize
+is 0 (the default) then the
+.I keys
+are interpreted as null-terminated strings.
+.TP
+.L "HASH_set, int flags"
+Changes the hash table flags by
+.IR or ing
+in
+.LR flags .
+The flags, which may be
+.IR or ed
+together, are:
+.RS
+.TP
+.L HASH_ALLOCATE
+Keys for new hash table entries are to be copied to data areas obtained from
+.IR malloc (3).
+.TP
+.L HASH_FIXED
+Fixes the hash table size, disabling any automatic table resizing.
+.TP
+.L HASH_SCOPE
+The new hash table is a scope that is to be pushed on top of
+.LR ref .
+.L ref
+must be
+.RL non- NULL .
+.RE
+.TP
+.L "HASH_va_list, va_list ap"
+.L ap
+is a
+.L va_list
+variable argument list pointer
+(see
+.LR <stdarg.h> ).
+.RE
+.TP
+.L "Hash_table_t* hashfree(Hash_table_t* tab)"
+The hash table
+.L tab
+is freed.
+The scope covered table pointer is returned,
+.L NULL
+if
+.L tab
+is not a scope.
+.TP
+.L "char* hashlook(Hash_table_t* tab, char* name, int flags, char* value)"
+Operates on the key
+.L name
+in the hash table
+.L tab
+according to
+.L flags
+and
+.LR value .
+A
+.L Hash_bucket_t
+pointer is returned unless otherwise noted.
+There are three basic lookup operations:
+.RS
+.TP
+.L HASH_CREATE
+.L name
+is entered into the top level scope if it does not already exist.
+If
+.L name
+also appears in a lower scope and
+.L HASH_ALLOC
+is set for the table then the new bucket will share the
+.L name
+field value with the lower scope.
+.TP
+.L HASH_DELETE
+.L name
+is deleted from the top level scope if it exists.
+.L NULL
+is returned.
+.TP
+.L HASH_LOOKUP
+The scopes are searched in order from top to bottom for
+.L name .
+The bucket pointer for the first occurrence is returned.
+.L NULL
+is returned if
+.L name
+is not found.
+.RE
+The basic operations may be qualified by the following
+(the qualifiers are restricted to the basic operations in
+the parenthesized list):
+.RS
+.TP
+.L "HASH_BUCKET (HASH_CREATE,HASH_DELETE,HASH_LOOKUP)"
+.L name
+is a pointer to a bucket that has already been entered into the table.
+.TP
+.L "HASH_FIXED (HASH_CREATE)"
+.L value
+is taken to be the size of the hash bucket to be created for
+.L name
+in the top level scope.
+The minimum bucket size is silently restricted to
+.LR sizeof(Hash_header_t) .
+.TP
+.L "HASH_INSTALL (HASH_CREATE)"
+.L name
+is a pointer to a bucket that has not been entered into the table.
+.TP
+.L "HASH_NOSCOPE (HASH_LOOKUP)"
+The lookup is restricted to the top level scope.
+.TP
+.L "HASH_OPAQUE (HASH_CREATE,HASH_DELETE)"
+Sets
+.L (HASH_CREATE)
+or clears
+.L (HASH_DELETE)
+the
+.I opaque
+property for the bucket.
+An opaque bucket is not visible in lower scopes.
+.TP
+.L "HASH_SCOPE (HASH_CREATE,HASH_DELETE)"
+All scopes are searched for the bucket.
+If the bucket is not found for
+.L HASH_CREATE
+then a new bucket is created in the lowest scope.
+.TP
+.L "HASH_VALUE (HASH_CREATE,HASH_LOOKUP)"
+For
+.L HASH_CREATE
+the bucket
+.L value
+field is set to
+.L value
+and the bucket
+.L name
+value is returned.
+For
+.L HASH_LOOKUP
+the bucket
+.L value
+field is returned,
+.L NULL
+if the bucket is not found.
+.RE
+If
+.L name
+.L NULL
+then the name from the most recent
+.L hashlook()
+is used, avoiding recomputation of some internal parameters.
+.TP
+.L "char* hashget(Hash_table_t* tab, char* name)"
+Returns the value
+associated with the key
+.L name
+in the hash table
+.LR tab .
+If
+.L name
+is
+.L NULL
+then the name from the most recent
+.L hashget()
+is used, avoiding recomputation of some internal parameters.
+.L NULL
+is returned if
+.L name
+is not in the table.
+All scope covered tables are searched.
+.TP
+.L "Hash_bucket_t* hashlast(Hash_table_t* tab)"
+Returns a pointer to the most recent hash bucket for
+.LR tab .
+The value is set by
+.LR hashlook() ,
+.L hashscan()
+and
+.LR hashwalk() .
+.TP
+.L "char* hashput(Hash_table_t* tab, char* name, char* value)"
+Set the value of the key
+.L name
+to
+.L value
+in the top level scope of the hash table
+.LR tab .
+.L name
+is entered into the top level scope if necessary.
+The (possibly re-allocated) key name pointer is returned
+(see
+.LR HASH_ALLOCATE ).
+If
+.L name
+is 0 then the most recent lookup
+.L name
+to
+.L hashlook()
+or
+.L hashget()
+is used.
+This eliminates a re-hash and re-lookup of
+.LR name .
+.TP
+.L "int hashwalk(Hash_table_t* tab, int flags, (int(*)()) walker, char* handle)"
+The function
+.L walker
+is applied to each entry (not covered by a scope starting at
+.LR tab )
+in the hash table
+.LR tab .
+If
+.L flags
+is
+.L HASH_NOSCOPE
+then only the top level hash table is used, otherwise the walk includes
+all scope covered tables.
+.L walker
+is called with
+.L char*
+.I key
+as the first argument,
+.L char*
+.I value
+as the second argument, and
+.L char*
+.I handle
+as the third argument.
+.I handle
+may be
+.LR 0 .
+The walk terminates after the last entry or when
+.L walker
+returns a negative value.
+The return value of the last call to
+.L walker
+is returned.
+Only one walk may be active within a collection of scoped tables.
+.TP
+.L "Hash_position_t* hashscan(Hash_table_t* tab, int flags)"
+Returns a
+.L Hash_position_t
+pointer for a sequential scan on the hash table
+.LR tab .
+If
+.L flags
+is
+.L HASH_NOSCOPE
+then only the top level hash table is used, otherwise the scan includes
+all scope covered tables.
+Only one scan may be active within a collection of scoped tables.
+.L hashdone()
+must be called to terminate the scan.
+.L 0
+is returned on error.
+.TP
+.L "Hash_bucket_t* hashnext(Hash_position_t* pos)"
+Returnes a pointer to the next bucket in the sequential scan set up by
+.L hashscan()
+on
+.LR pos .
+If no elements remain then
+.L 0
+is returned.
+.TP
+.L "void hashdone(Hash_position_t* pos)"
+Completes a scan initiated by
+.L hashscan()
+on
+.LR pos .
+.TP
+.L "int hashset(Hash_table_t* tab, int flags)"
+Sets the flags for the hash table
+.L tab
+by
+.IR or ing
+in
+.LR flags .
+Only
+.L HASH_ALLOCATE
+and
+.L HASH_FIXED
+may be set.
+.TP
+.L "int hashclear(Hash_table_t* tab, int flags)"
+Clears the flags for the hash table
+.L tab
+by masking out
+.LR flags .
+Only
+.L HASH_ALLOCATE
+and
+.L HASH_FIXED
+may be cleared.
+.TP
+.L "void hashdump(Hash_table_t* tab, int flags)"
+Dumps hash table accounting info to standard error.
+If
+.L tab
+is
+.L NULL
+then all allocated hash tables are dumped, otherwise only information on
+.L tab
+is dumped.
+If
+.L flags
+is
+.L HASH_BUCKET
+then the hash bucket
+.I key-value
+pairs for each collision chain are also dumped.
+.TP
+.L "void hashsize(Hash_table_t* tab, int size)"
+Changes the size of the hash table
+.L tab
+to
+.L size
+where
+.L size
+must be a power of 2.
+Explicit calls to this routine are not necessary as hash tables
+are automatically resized.
+.TP
+.L "int strhash(char* name)"
+Hashes the null terminated character string
+.L name
+using a linear congruent pseudo-random number generator algorithm
+and returns a non-negative
+.L int
+hash value.
+.TP
+.L "int memhash(char* buf, int siz)"
+Hashes the buffer
+.L buf
+of
+.L siz
+bytes using a linear congruent pseudo-random number generator algorithm
+and returns a non-negative
+.L int
+hash value.
+.TP
+.L "long strsum(char* name, long sum)"
+Returns a running 31-bit checksum of the string
+.L name
+where
+.L sum
+is
+.L 0
+on the first call and
+the return value from a previous
+.L memsum
+or
+.L strsum
+call otherwise.
+The checksum value is consistent across all implementations.
+.TP
+.L "long memsum(char* buf, int siz, long sum)"
+Returns a running 31-bit checksum of buffer
+.L buf
+of
+.L siz
+bytes where
+.L sum
+is
+.L 0
+on the first call and
+the return value from a previous
+.L memsum
+or
+.L strsum
+call otherwise.
+The checksum value is consistent across all implementations.
+.SH "SEE ALSO"
+sum(1)
diff --git a/src/lib/libast/man/iblocks.3 b/src/lib/libast/man/iblocks.3
new file mode 100644
index 0000000..0c615f8
--- /dev/null
+++ b/src/lib/libast/man/iblocks.3
@@ -0,0 +1,62 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH IBLOCKS 3
+.SH NAME
+iblocks \- compute number file blocks used
+.SH SYNOPSIS
+.EX
+#include <ls.h>
+
+long _iblocks(struct stat* \fIst\fP);
+.EE
+.SH DESCRIPTION
+.L _iblocks
+returns the number of blocks used by the file whose
+.IR stat (2)
+information is pointed to by
+.IR st .
+The count includes both data and indirect blocks.
+.PP
+This routine is used by
+.B <ls.h>
+on system without the
+.LI "struct stat" st_blocks
+field.
+.SH "SEE ALSO"
+ls(1), stat(2)
diff --git a/src/lib/libast/man/int.3 b/src/lib/libast/man/int.3
new file mode 100644
index 0000000..8d457c0
--- /dev/null
+++ b/src/lib/libast/man/int.3
@@ -0,0 +1,68 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH INT 3
+.SH NAME
+int \- integral type macros
+.SH SYNOPSIS
+.EX
+#include <int.h>
+.EE
+.SH DESCRIPTION
+This header defines macros for the local integral types.
+.LR int_1 ,
+.LR int_2
+and
+.L int_4
+are always defined to integral types with a size of
+1, 2 and 4 bytes respectively.
+The macros
+.LI int_ n
+where
+.I n
+is a power of 2 greater than 4 are defined if the type is supported.
+.L int_max
+is defined to be the largest support integral type.
+.L int_swap
+is the
+.IR swap (3)
+operation that converts a local
+.L int
+to canonical big-endian representation.
+.SH "SEE ALSO"
+swap(3)
diff --git a/src/lib/libast/man/ip6.3 b/src/lib/libast/man/ip6.3
new file mode 100644
index 0000000..1dadb05
--- /dev/null
+++ b/src/lib/libast/man/ip6.3
@@ -0,0 +1,85 @@
+.fp 5 B
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH IP6 3
+.SH NAME
+ip6 \- IP V6 address support
+.SH SYNOPSIS
+.EX
+#include <ip6.h>
+
+char* fmtip6(unsigned char* addr, int bits);
+int strtoip6(const char* str, char** end, unsigned char* addr, unsigned char* bits);
+.EE
+
+.SH DESCRIPTION
+.L fmtip6()
+formats the IPV6 address
+.L addr
+with optional prefix bits
+.L bits
+(0 if not a prefix) into a thread-specific 0-terminated temporary buffer and returns a pointer
+to the formatted value.
+
+.PP
+.L strtoip6()
+converts a formatted IPV6 address from the 0-terminated string
+.L str
+into a host order IPV6 address in
+.L addr
+which must be a buffer of at least
+.L IP6ADDR
+bytes.
+If
+.L bits
+is not 0 then an optional
+.BI / bits
+(prefix size in bits) is parsed and
+.L *bits
+is set to the number of prefix bits.
+If
+.L end
+is not 0 then
+.L *end
+will point to the first unparsed character in
+.L str
+on return.
+0 is returned on success, -1 on failure.
+
+.SH "SEE ALSO"
+dss(1)
diff --git a/src/lib/libast/man/magic.3 b/src/lib/libast/man/magic.3
new file mode 100644
index 0000000..e8bf0e3
--- /dev/null
+++ b/src/lib/libast/man/magic.3
@@ -0,0 +1,493 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH MAGIC 3
+.SH NAME
+magic \- magic file interface
+.SH SYNOPSIS
+.EX
+#include <magic.h>
+
+Magic_t
+{
+ unsigned long flags;
+};
+
+Magic_t* magicopen(unsigned long \fIflags\fP);
+void magicclose(Magic_t* \fImagic\fP);
+
+int magicload(Magic_t* \fImagic\fP, const char* \fIpath\fP, unsigned long \fIflags\fP);
+int magiclist(Magic_t* \fImagic\fP, Sfio_t* \fIsp\fP);
+
+char* magictype(Magic_t* \fImagic\fP, const char* \fIpath\fP, struct stat* \fIst\fP);
+.EE
+.SH DESCRIPTION
+These routines provide an interface to the
+.IR file (1)
+command magic file.
+.L magicopen
+returns a magic session handle that is passed to all of the other routines.
+.I flags
+may be
+.TP
+.L MAGIC_MIME
+Return the MIME type string rather than the magic file description.
+.TP
+.L MAGIC_PHYSICAL
+Don't follow symbolic links.
+.TP
+.L MAGIC_STAT
+The stat structure
+.I st
+passed to
+.I magictype
+will contain valid
+.I stat (2)
+information.
+See
+.L magictype
+below.
+.TP
+.L MAGIC_VERBOSE
+Enable verbose error messages.
+.PP
+.L magicclose
+closes the magic session.
+.PP
+.L magicload
+loads the magic file named by
+.I path
+into the magic session.
+.I flags
+are the same as with
+.LR magicopen .
+More than one magic file can be loaded into a session;
+the files are searched in load order.
+If
+.I path
+is
+.L 0
+then the default magic file is loaded.
+.PP
+.L magiclist
+lists the magic file contents on the
+.IR sfio (3)
+stream
+.IR sp .
+This is used for debugging magic entries.
+.PP
+.L magictype
+returns the type string for
+.I path
+with optional
+.IR stat (2)
+information
+.IR st .
+If
+.I "st == 0"
+then
+.L magictype
+calls
+.L stat
+on a private stat buffer,
+else if
+.L magicopen
+was called with the
+.L MAGIC_STAT
+flag then
+.I st
+is assumed to contain valid stat information, otherwise
+.L magictype
+calls
+.L stat
+on
+.IR st .
+.L magictype
+always returns a non-null string.
+If errors are encounterd on
+.I path
+then the return value will contain information on those errors, e.g.,
+.LR "cannot stat" .
+.SH FORMAT
+The magic file format is a backwards compatible extension of an
+ancient System V file implementation.
+However, with the extended format it is possible to write a single
+magic file that works on all platforms.
+Most of the net magic files floating around work with
+.LR magic ,
+but they usually double up on
+.I le
+and
+.I be
+entries that are automatically handled by
+.LR magic .
+.PP
+A magic file entry describes a procedure for determining a single file type
+based on the file pathname,
+.I stat (2)
+information, and the file data.
+An entry is a sequence of lines, each line being a record of
+.I space
+separated fields.
+The general record format is:
+.EX
+[op]offset type [mask]expression description [mimetype]
+.EE
+.L #
+in the first column introduces a comment.
+The first record in an entry contains no
+.LR op ;
+the remaining records for an entry contain an
+.LR op .
+Integer constants are as in C:
+.L 0x*
+or
+.L 0X*
+for hexadecimal,
+.L 0*
+for octal and decimal otherwise.
+.PP
+The
+.L op
+field may be one of:
+.TP
+.L +
+The previous records must match but the current record is optional.
+.L >
+is an old-style synonym for
+.LR + .
+.TP
+.L &
+The previous and current records must match.
+.TP
+.L {
+Starts a nesting block that is terminated by
+.LR } .
+A nesting block pushes a new context for the
+.L +
+and
+.L &
+ops.
+The
+.L {
+and
+.L }
+records have no other fields.
+.TP
+\fIid\f5{\fR
+A function declaration and call for the single character identifier
+.IR id .
+The function return is a nesting block end record
+.LR } .
+Function may be redefined.
+Functions have no arguments or return value.
+.TP
+\fIid\f5()\fR
+A call to the function
+.IR id .
+.PP
+The
+.L offset
+field is either the offset into the data upon which the current entry operates
+or a file metadata identifier.
+Offsets are either integer constants or offset expressions.
+An offset expression is contained in (...) and is a combination of
+integral arithmetic operators and the
+.L @
+indirection operator.
+Indirections take the form
+.LI @ integer
+where integer is the data offset for the indirection value.
+The size of the indirection value is taken either from one of the suffixes
+.LR B (byte, 1 char),
+.LR H (short, 2 chars),
+.LR L (long, 4 chars),
+pr
+.LR Q (quead, 8 chars),
+or from the
+.L type
+field.
+Valid file metadata identifiers are:
+.TP
+.L atime
+The string representation of
+.LR stat.st_atime .
+.TP
+.L blocks
+.LR stat.st_blocks .
+.TP
+.L ctime
+The string representation of
+.LR stat.st_ctime .
+.TP
+.L fstype
+The string representation of
+.LR stat.st_fstype .
+.TP
+.L gid
+The string representation of
+.LR stat.st_gid .
+.TP
+The
+.L stat.st_mode
+file mode bits in
+.IR modecanon (3)
+canonical representation (i.e., the good old octal values).
+.TP
+.L mtime
+The string representation of
+.LR stat.st_mtime .
+.TP
+.L nlink
+.LR stat.st_nlink .
+.TP
+.L size
+.LR stat.st_size .
+.TP
+.L name
+The file path name sans directory.
+.TP
+.L uid
+The string representation of
+.LR stat.st_uid .
+.PP
+The
+.L type
+field specifies the type of the data at
+.LR offset .
+Integral types may be prefixed by
+.L le
+or
+.L be
+for specifying exact little-endian or big-endian representation,
+but the internal algorithm automatically loops through the
+standard representations to find integral matches,
+so representation prefixes are rarely used.
+However, this looping may cause some magic entry conflicts; use the
+.L le
+or
+.L be
+prefix in these cases.
+Only one representation is used for all the records in an entry.
+Valid types are:
+.TP
+.L byte
+A 1 byte integer.
+.TP
+.L short
+A 2 byte integer.
+.TP
+.L long
+A 4 byte integer.
+.TP
+.L quad
+An 8 byte integer.
+Tests on this type may fail is the local compiler does not support
+an 8 byte integral type and the corresponding value overflows 4 bytes.
+.TP
+.L date
+The data at
+.L offset
+is interpreted as a 4 byte seconds-since-the-epoch date and
+converted to a string.
+.TP
+.L edit
+The
+.L expression
+field is an
+.IR ed (1)
+style substitution expression
+\fIdel old del new del \fP [ \fI flags \fP ]
+where the substituted value is made available to the
+.L description
+field
+.L %s
+format.
+In addition to the
+.I flags
+supported by
+.IR ed (3)
+are
+.L l
+that converts the substituted value to lower case and
+.L u
+that converts the substituted value to upper case.
+If
+.I old
+does not match the string data at
+.L offset
+then the entry record fails.
+.TP
+.L match
+.L expression
+field is a
+.IR strmatch (3)
+pattern that is matched against the string data at
+.LR offset .
+.TP
+.L string
+The
+.L expression
+field is a string that is compared with the string data at
+.LR offset .
+.PP
+The optional
+.L mask
+field takes the form
+.LI & number
+where
+.I number
+is
+.I anded
+with the integral value at
+.L offset
+before the
+.L expression
+is applied.
+.PP
+The contents of the expression field depends on the
+.LR type .
+String type expression are described in the
+.L type
+field entries above.
+.L *
+means any value and applies to all types.
+Integral
+.L type
+expression take the form [\fIoperator\fP] \fIoperand\P where
+.I operand
+is compared with the data value at
+.L offset
+using
+.IR operator .
+.I operator
+may be one of
+.LR < .
+.LR <= ,
+.LR == ,
+.LR >=
+or
+.LR > .
+.I operator
+defaults to
+.L ==
+if omitted.
+.I operand
+may be an integral constant or one of the following builtin function calls:
+.TP
+.L magic()
+A recursive call to the magic algorithm starting with the data at
+.LR offset .
+.TP
+\f5loop(\fIfunction\fP,\fIoffset\fP,\fIincrement\fP)\fR
+Call
+.I function
+starting at
+.I offset
+and increment
+.I offset
+by
+.I increment
+after each iteration.
+Iteration continues until the description text does not change.
+.PP
+The
+.L description
+field is the most important because it is this field that is presented
+to the outside world.
+When constructing description
+fields one must be very careful to follow the style layed out in the
+magic file, lest yet another layer of inconsistency creep into the system.
+The description for each matching record in an entry are concatenated
+to form the complete magic type.
+If the previous matching description in the current entry does not end with
+.I space
+and the current description is not empty and does not start with
+.I comma ,
+.I dot
+or
+.I backspace
+then a
+.I space
+is placed between the descriptions
+(most optional descriptions start with
+.IR comma .)
+The data value at
+.L offset
+can be referenced in the description using
+.L %s
+for the string types and
+.L %ld
+or
+.L %lu
+for the integral types.
+.PP
+The
+.L mimetype
+field specifies the MIME type, usually in the form
+.IR a / b .
+.SH FILES
+.L ../lib/file/magic
+located on
+.L $PATH
+.SH EXAMPLES
+.EX
+0 long 0x020c0108 hp s200 executable, pure
+o{
++36 long >0 , not stripped
++4 short >0 , version %ld
+}
+
+0 long 0x020c0107 hp s200 executable
+o()
+
+0 long 0x020c010b hp s200 executable, demand-load
+o()
+.EE
+The function
+.LR o() ,
+shared by 3 entries,
+determines if the executable is stripped and also extracts the version number.
+.EX
+0 long 0407 bsd 386 executable
+&mode long &0111!=0
++16 long >0 , not stripped
+.EE
+This entry requires that the file also has execute permission.
+.SH "SEE ALSO"
+file(1), mime(4), tw(1), modecanon(3)
diff --git a/src/lib/libast/man/mem.3 b/src/lib/libast/man/mem.3
new file mode 100644
index 0000000..70da0db
--- /dev/null
+++ b/src/lib/libast/man/mem.3
@@ -0,0 +1,98 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH MEM 3
+.SH NAME
+mem \- fixed string routines
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+void mematoe(void* \fIout\fP, const void* \fIin\fP, size_t \fIn\fP);
+void* memdup(const void* \fIbuf\fP, size_t \fIn\fP)
+void memetoa(void* \fIout\fP, const void* \fIin\fP, size_t \fIn\fP);
+void* memzero(void* \fIbuf\fP, size_t \fIn\fP);
+.EE
+.SH DESCRIPTION
+.L mematoe
+converts
+.I n
+ASCII characters in
+.I in
+to EBCDIC characters in
+.IR out .
+.I in
+and
+.I out
+may be the same.
+.PP
+.L memdup
+copies the
+.I n
+byte buffer
+.I buf
+to a new location provided by
+.IR malloc (3)
+and returns a pointer to the new copy.
+0 is returned if
+.IR malloc (3)
+fails.
+.PP
+.L memetoa
+converts
+.I n
+EBCDIC characters in
+.I in
+to ASCII characters in
+.IR out .
+.I in
+and
+.I out
+may be the same.
+.PP
+.L memzero
+sets the first
+.I n
+bytes in
+.I buf
+to
+.IR 0 .
+.SH "SEE ALSO"
+Proposed Bell Laboratories ASCII/EBCDIC standard, April 16, 1979.
+.br
+str(3), vmalloc(3)
diff --git a/src/lib/libast/man/mime.3 b/src/lib/libast/man/mime.3
new file mode 100644
index 0000000..f540b2e
--- /dev/null
+++ b/src/lib/libast/man/mime.3
@@ -0,0 +1,117 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH MIME 3
+.SH NAME
+mime \- mime/mailcap interface
+.SH SYNOPSIS
+.EX
+#include <mime.h>
+
+Mime_t
+{
+ unsigned long flags;
+};
+
+Mime_t* mimeopen(unsigned long \fIflags\fP);
+void mimeclose(Mime_t* \fImime\fP);
+
+int mimeload(Mime_t* \fImime\fP, const char* \fIpath\fP, unsigned long \fIflags\fP);
+int mimelist(Mime_t* \fImime\fP, Sfio_t* \fIsp\fP, const char* \fIpattern\fP);
+
+char* mimeview(Mime_t* \fImime\fP, const char* \fIview\fP, const char* \fIname\fP, const char* \fItype\fP, const char* \fIopts\fP);
+int mimeset(Mime_t* \fImime\fP, char* \fIline\fP, unsigned long \fIflags\fP);
+.EE
+.SH DESCRIPTION
+These routines provide an interface to the MIME type database.
+.L mimeopen
+returns a mime session handle that is passed to all of the other routines.
+The
+.I flags
+argument is currently unused.
+.PP
+.L mimeclose
+closes the mime session.
+.PP
+.L mimeload
+loads the mime file named by
+.I path
+into the mime session.
+.I flags
+may be one of:
+.TP
+.L MIME_LIST
+The
+.I path
+argument is a
+.B :
+separated list of pathnames, each of which is loaded.
+Non-existent files are ignored
+.L MIME_LIST
+set.
+.TP
+.L MIME_REPLACE
+Replace existing entries by new entries with the same type.
+Otherwise original entries take precedence.
+.PP
+More than one mime file can be loaded into a session;
+the files are searched in load order.
+If
+.I path
+is
+.L 0
+then the default mime file is loaded.
+.PP
+.L mimelist
+lists the mime file contents on the
+.IR sfio (3)
+stream
+.IR sp .
+This is used for debugging mime entries.
+.PP
+.L mimetype
+returns the type string for
+.IR path .
+.L mimetype
+always returns a non-null string.
+If errors are encounterd on
+.I path
+then the return value will be
+.LR "error" .
+.SH "SEE ALSO"
+file(1), mime(4)
diff --git a/src/lib/libast/man/modecanon.3 b/src/lib/libast/man/modecanon.3
new file mode 100644
index 0000000..4a5d128
--- /dev/null
+++ b/src/lib/libast/man/modecanon.3
@@ -0,0 +1,104 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH MODECANON 3
+.SH NAME
+modecanon \- canonical file mode representation
+.SH SYNOPSIS
+#include <modex.h>
+
+int modei(int \fIexternal\fP);
+int modex(int \fIinternal\fP);
+.EE
+.SH DESCRIPTION
+POSIX threw out the file type bit macros and replaced them with
+function-like macros that test file type.
+This is bad in many ways, the worst of which is that it provides
+no way for a user program to synthesize file types in the mode bits.
+.IR pax (1),
+.IR tar (1)
+and
+.IR cpio (1)
+are examples of user programs that must convert between the internal mode
+representation and a private external representation.
+These routines provide a canonical external representation
+with macros to access and synthesize the bits in the external
+representation.
+.PP
+.L modei
+takes an external mode representation
+.I external
+and returns the equivalent internal representation.
+.PP
+.L modex
+takes an internal mode representation
+.I internal
+and returns the equivalent external representation.
+.PP
+The traditional bit access macro (\f5S_\fP prefix changes to \f5X_\fP) are:
+.L X_IFMT ,
+.L X_IFSOCK ,
+.L X_IFLNK ,
+.L X_IFCTG ,
+.L X_IFREG ,
+.L X_IFBLK ,
+.L X_IFDIR ,
+.L X_IFCHR ,
+.L X_IFIFO ,
+.L X_IPERM ,
+.L X_ISUID ,
+.L X_ISGID ,
+.L X_ISVTX ,
+.L X_IRUSR ,
+.L X_IWUSR ,
+.L X_IXUSR ,
+.L X_IRGRP ,
+.L X_IWGRP ,
+.L X_IXGRP ,
+.L X_IROTH ,
+.L X_IWOTH ,
+.L X_IXOTH ,
+.L X_IRWXU ,
+.L X_IRWXG
+and
+.L X_IRWXO .
+.LI X_ITYPE( mode )
+returns the type bits for
+.IR mode .
+.SH "SEE ALSO"
+pax(1), stat(2)
diff --git a/src/lib/libast/man/optget.3 b/src/lib/libast/man/optget.3
new file mode 100644
index 0000000..90afcb8
--- /dev/null
+++ b/src/lib/libast/man/optget.3
@@ -0,0 +1,68 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH OPTGET 3
+.SH NAME
+optget \- option parse assist
+.SH SYNOPSIS
+.EX
+#include <option.h>
+
+Opt_t
+{
+};
+
+Optdisc_t
+{
+ unsigned long version;
+ unsigned long flags;
+ char* catalog;
+ Optinfo_f infof;
+};
+
+Opt_t opt_info;
+
+void optinit(Optdisc_t* \fIdisc\fP, Error_f \fIerrorf\fP);
+int optget(char** \fIargv\fP, const char* \fIusage\fP);
+int optstr(const char* \fIstring\fP, const char* \fIusage\fP);
+int optjoin(char** \fIargv\fP, ... [int (*\fIoptfun\fP)(char** \fIargv\fP, int \fIlast\fP)]);
+char* optusage(const char* \fIopts\fP);
+int optesc(Sfio_t* \fIsp\fP, const char* \fIstring\fP, int \fIflags\fP);
+.EE
+.SH DESCRIPTION
+.SH "SEE ALSO"
diff --git a/src/lib/libast/man/path.3 b/src/lib/libast/man/path.3
new file mode 100644
index 0000000..8721888
--- /dev/null
+++ b/src/lib/libast/man/path.3
@@ -0,0 +1,391 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH PATH 3
+.SH NAME
+path \- file path routines
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+char* pathaccess(char* \fIpath\fP, const char* \fIdirs\fP, const char* \fIa\fP, const char* \fIb\fP, int \fImode\fP);
+char* pathbin(void);
+char* pathcanon(char* \fIpath\fP, int \fIflags\fP);
+char* pathcat(char* \fIpath\fP, const char* \fIdirs\fP, int \fIsep\fP, const char* \fIa\fP, const char* \fIb\fP);
+char* pathcd(char* \fIpath\fP, const char* \fIhome\fP);
+int pathcheck(const char* \fIpackage\fP, const char* \fItool\fP, Pathcheck_t* \fIpc\fP);
+int pathgetlink(const char* \fIname\fP, char* \fIbuf\fP, int \fIsiz\fP);
+char* pathkey(char* \fIkey\fP, char* \fIattr\fP, const char* \fIlang\fP, const char* \fIpath\fP);
+char* pathnext(char* \fIpath\fP, char* \fIextra\fP, long* \fIvisits\fP);
+char* pathpath(char* \fIpath\fP, const char* \fIp\fP, const char* \fIa\fP, int \fImode\fP);
+char* pathprobe(char* \fIpath\fP, char* \fIattr\fP, const char* \fIlang\fP, const char* \fItool\fP, const char* \fIproc\fP, int \fIop\fP);
+char* pathrepl(char* \fIpath\fP, const char* \fImatch\fP, const char* \fIreplace\fP);
+int pathsetlink(const char* \fItext\fP, char* \fIname\fP);
+char* pathshell(void);
+int pathstat(const char* \fIpath\fP, struct stat* \fIst\fP);
+char* pathtemp(char* \fIpath\fP, const char* \fIdir\fP, const char* \fIpfx\fP);
+.EE
+.SH DESCRIPTION
+These routines operate on file path names.
+Path buffers are assumed to be of size
+.LR PATH_MAX .
+.L <ast.h>
+always defines
+.LR PATH_MAX ,
+even if it indeterminant on the local system.
+Yes, this was probably a bad choice, but it was made about 10 years ago.
+We will probably move to a <stk.h> based implementation.
+.PP
+.L pathaccess
+constructs a path in
+.L path
+to the file
+.L a/b
+with access
+.L mode
+using the
+.L :
+separated directories in
+.IR dirs .
+Both
+.I a
+and
+.I b
+may be
+.LR 0 .
+.L mode
+is the inclusive-or of:
+.TP
+.L F_OK
+File exists.
+.TP
+.L R_OK
+Read permission on file.
+.TP
+.L W_OK
+Write permission on file.
+.TP
+.L X_OK
+Execute permission on file.
+.TP
+.L PATH_REGULAR
+A regular file.
+.TP
+.L PATH_ABSOLUTE
+Generated path name is rooted at
+.LR / .
+.I path
+is returned, 0 on error.
+.PP
+.L pathbin
+returns a pointer to the
+.L :
+separated list of directories to search for executable commands.
+The
+.L PATH
+environment variable is first consulted.
+If not defined then
+.L confstr(_CS_PATH,...)
+is used.
+A valid string is always returned.
+.PP
+.L pathcanon
+canonicalizes the path
+.I path
+in place.
+A pointer to the trailing 0 in the canonicalized path is returned.
+A canonical path has:
+redundant
+.L .
+and
+.L /
+removed;
+.L ..
+moved to the front;
+.L /..
+preserved for super root hacks;
+.L ...
+resolved if
+.IR fs3d (3)
+is enabled.
+.I flags is the inclusive-or of:
+.TP
+.L PATH_DOTDOT
+Each
+.L ..
+is checked for access.
+.TP
+.L PATH_EXISTS
+Path must exist at each component.
+.TP
+.L PATH_PHYSICAL
+Symbolic links are resolved at each component.
+.PP
+0 is returned on error.
+If an error occurs and either of
+.L PATH_DOTDOT
+or
+.L PATH_EXISTS
+is set then
+.I path
+will contain the components following the failure point.
+.PP
+.L pathcat
+concatenates the first
+.I sep
+separated path component in
+.I dirs
+with the path components
+.I a
+and
+.I b
+into
+.LR path .
+The path is constructed in
+.I path
+by separating each path component with
+.IR / .
+Both
+.I a
+and
+.I b
+may be
+.LR 0 .
+A pointer to the next
+.I sep
+separated component in
+.I dirs
+is returned,
+.L 0
+when there are no more components.
+.L pathcat
+is used by
+.LR pathaccess .
+.PP
+.L pathcd
+sets the current working directory to
+.I path
+via
+.IR chdir (2).
+If
+.I path
+is longer than
+.L PATH_MAX
+then it is split up into a sequence of relative paths and
+.I chdir
+is called on each of these.
+For any given system, if you got to a directory, then
+.L pathcd
+can get you back, modulo permission and link changes.
+.PP
+.L pathcheck
+is a stub for license libraries.
+See
+.IR license (3).
+.PP
+.L pathgetlink
+returns the 0-terminated symbolic link text for
+.I path
+in the buffer
+.I bu
+of size
+.IR siz .
+The link text length is returned on success, \-1 on error.
+Weird
+.I universe (1)
+interactions with dynamic symbolic links are handled
+by converting non-standard dynamic link text to
+.LI .../$( UNIVERSE )/...
+.L pathsetsymlink
+converts in the other direction.
+.PP
+.L pathkey
+generates in
+.I key
+a 14 character lookup key (plus terminating 0) for the language
+.I lang
+processor in
+.IR path .
+A poihter to the key is returned, 0 on error.
+If
+.I "key == 0"
+then space is allocated via
+.IR malloc (3).
+Key specific attribute
+.I name=value
+pairs are copied into
+.I attr
+if
+.IR "attr != 0" .
+.PP
+.L pathpath
+constructs in
+.I path
+a path to
+.I p
+with
+.IR access (2)
+mode
+.I mode
+using the directories from
+.LR pathbin() .
+If \fIa != 0\fP then
+.IR a ,
+.IR argv [0]
+(if available via
+.IR optget (3)),
+and the
+.L _
+environment variable (set by
+.IR ksh (1) )
+are used for related root searching.
+If
+.I p
+also contains a
+.L /
+then
+.I ../p
+is searched for.
+.PP
+.L pathprobe
+generates in
+.I path
+the full path name of the
+.I tool
+specific
+.IR probe (1)
+information file for the
+.I lang
+langauge processor
+.IR proc .
+If
+.I "path == 0"
+then space is allocated via
+.IR malloc (3).
+Probe attribute
+.I name=value
+pairs are copied into
+.I attr
+if
+.IR "attr != 0" .
+.I op
+may be one of:
+.TP
+.B \-1
+return the path name with no access checks or generation
+.TP
+.B 0
+message emitted information must be generated via
+.IR probe (1)
+.TP
+.B 1
+no message emitted information must be probed via
+.IR probe (1)
+.PP
+0 is returned if the information does not exist and cannot be generated.
+.PP
+.L pathrepl
+does an in-place replacement of the first occurrence of
+.I /match/
+with
+.I /replace/
+in
+.IR path .
+.PP
+.L pathsetlink
+creates a symbolic link
+.I text
+in the path
+.IR name .
+See
+.L pathgetlink
+above for weird
+.IR universe (1)
+interactions hidden by this routine.
+.PP
+.L pathshell
+returns a pointer to the pathname for the shell for the current process.
+The
+.L SHELL
+environment variable is first consulted, but is rejected under suspicious
+ownership/setuid conditions of if it seems to point to
+.IR csh (1) ;
+otherwise
+.L confstr(_CS_SHELL,...)
+is used.
+A valid string is always returned.
+.PP
+.L pathstat
+first tries
+.LI stat( path,st )
+and if that fails it tries
+.LI lstat( path,st ).
+The
+.L stat
+or
+.L lstat
+return value is returned.
+.PP
+.L pathtemp
+generates in
+.I path
+a temporary file path name of the form
+.I dir/pfx<pid>.<suf>
+where the length of
+.IR pfx ,
+if !=0, is limited to 5, the length of
+.I <pid>
+(the base 64 representation of the current process id)
+is limited to 3, and
+.I <suf>
+(an internally generated suffix that avoid file confilicts)
+is limited to 3.
+The generated path name conforms to the classic UNIX 14 char and the DOS
+.LR 8.3
+limitations.
+Both
+.I dir
+and
+.I pfx
+may be
+.LR 0 .
+.IR access (2)
+is used to avoid file conflicts but the generated path name is not created,
+so you could lose in a race.
+.SH "SEE ALSO"
+3d(1), access(2), confstr(3), fs3d(3), lstat(2), stat(2)
diff --git a/src/lib/libast/man/preroot.3 b/src/lib/libast/man/preroot.3
new file mode 100644
index 0000000..025fc22
--- /dev/null
+++ b/src/lib/libast/man/preroot.3
@@ -0,0 +1,151 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH PREROOT 3
+.SH NAME
+preroot \- preroot support
+.SH SYNOPSIS
+.EX
+#include <preroot.h>
+
+char* getpreroot(char* \fIpath\fP, char* \fIcmd\fP);
+int ispreroot(char* \fIdir\fP);
+int realopen(char* \fIpath\fP, int \fImode\fP, int \fIperm\fP);
+void setpreroot(char** \fIargv\fP, char* \fIdir\fP);
+.EE
+.SH DESCRIPTION
+The
+.I preroot
+routines manipulate the process preroot.
+.I preroot
+is a kernel supported per-process two level viewpath.
+All pathnames rooted at
+.L /
+are first searched for in the process preroot directory
+and then in the system root directory.
+Setting the process preroot is a priveleged operation controlled by the
+.IR /etc/preroot (1)
+command.
+.PP
+.L <preroot.h>
+defines the symbol
+.B FS_PREROOT
+for those systems that support preroot.
+The following routines are valid only when
+.B FS_PREROOT
+is defined:
+.TP
+.L getpreroot
+returns a pointer to the absolute pathname of the preroot directory
+for the executable
+.IR cmd .
+The result is placed in
+.IR path .
+If
+.I path
+is
+.B 0
+then
+.IR malloc (3)
+is used to allocate the pathname space.
+.B 0
+is returned if
+.I cmd
+has no preroot or if an error was encountered.
+In this case
+.I errno
+is set to indicate the error.
+.TP
+.L ispreroot
+Non-zero is returned if
+.I dir
+is the current process preroot.
+If
+.I dir
+is
+.B 0
+then non-zero is returned if the current process has a preroot.
+.TP
+.L realopen
+temporarily disables the process preroot and does an
+.IR open (3)
+relative to the system root directory.
+The return value from
+.I open
+is returned.
+If there is no preroot then
+.I realopen
+is equivalent to
+.IR open .
+.TP
+.L setpreroot
+calls
+.IR execvp (3)
+as
+.L "execvp(a\fIrgv\fP[0],\fIargv\fP)"
+with the process preroot set to
+.IR dir .
+.I argv
+must be a
+.BR 0 -terminated
+argument array.
+If
+.I argv
+is
+.B 0
+then the value of
+.I opt_argv
+from
+.IR optget (3)
+is used.
+.L setpreroot
+returns immediately if
+.I dir
+is already the process preroot.
+.SH "SEE ALSO"
+/etc/preroot(1)
+.SH BUGS
+Preroot semantics should be preserved when reading directories.
+The
+.I ast
+.IR directory (3)
+routines do this.
+.IR 3d (1)
+viewpathing does
+.I preroot
+the right way.
diff --git a/src/lib/libast/man/proc.3 b/src/lib/libast/man/proc.3
new file mode 100644
index 0000000..64c1a6e
--- /dev/null
+++ b/src/lib/libast/man/proc.3
@@ -0,0 +1,319 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH PROC 3
+.SH NAME
+proc \- process control routines
+.SH SYNOPSIS
+.EX
+#include <proc.h>
+
+Proc_t* procopen(const char* \fIcommand\fP, char** \fIargv\fP, char** \fIenvv\fP, long* \fIopv\fP, long \fIflags\fP);
+int procfree(Proc_t* \fIproc\fP);
+int procclose(Proc_t* \fIproc\fP);
+int procrun(const char* \fIcommand\fP, char** \fIargv\fP);
+.EE
+.SH DESCRIPTION
+These routines provide a portable interface to process creation and execution.
+They work on systems with
+.IR fork (2)
+and
+.IR exec (2)
+as well as on systems with only
+.IR spawnve (2)
+or
+.IR spanwveg (3).
+.PP
+.L procopen
+runs
+.I command
+with arguments
+.IR argv ,
+environment modifications in
+.IR envv ,
+file descriptor, signal and process group operations in
+.I opv
+and flags in
+.IR flags .
+.PP
+.I command
+is searched for using the
+.L PATH
+environment variable from the calling environment.
+If
+.I command
+is
+.L 0
+then the current shell is used (see
+.IR pathshell (3)).
+If
+.I envv
+is not
+.L 0
+then it is a
+.L 0
+terminated vector of
+\fIname\fP[=\fIvalue\fP]
+strings that are added to the
+.I command
+environment using
+.IR setenviron (3).
+If
+.I name
+appears in the parent environment then its value is replaced with the new
+.IR value .
+If
+.RI = value
+is omitted then
+.I name
+is removed from the child environment.
+The
+.L _
+environment variable is set to contain the pathname for
+.I command
+and will appear at the top of the child environment.
+.PP
+If
+.I opv
+is not
+.L 0
+then it is a 0 terminaled vector of operations to perform.
+In the following
+.I context
+is a combination of
+.L PROC_FD_CHILD
+and
+.L PROC_FD_PARENT
+for the child and parent process context respectively.
+Valid operations are:
+.TP
+\f5PROC_FD_CLOSE(\fIfd\fP,\fIcontext\fP)\fR
+The file descriptor
+.I fd
+is closed in
+.IR context .
+.TP
+\f5PROC_FD_DUP(\fIfrom\fP,\fIto\fP,\fIcontext\fP)\fR
+The file descriptor
+.I from
+is
+.IR dup (2)'d
+into the file descriptor
+.I to
+in
+.IR context .
+.TP
+\f5PROC_SIG_DFL(\fIsig\fP)\fR
+The signal handler for
+.I sig
+is set to
+.L SIG_DFL
+in the child context.
+.TP
+\f5PROC_SIG_IGN(\fIsig\fP)\fR
+The signal handler for
+.I sig
+is set to
+.L SIG_IGN
+in the child context.
+.TP
+\f5PROC_SYS_PGRP(\fIpgid\fP)\fR
+The child process group is set to
+.IR pgid .
+.I pgid
+may have the following values:
+.TP
+.L <0
+The child process becomes a session leader.
+.TP
+.L 0
+The child process is in the parent process group.
+.TP
+.L 1
+The child process becomes a process group leader.
+.TP
+.L >1
+The child process joins the process group
+.IR pgid .
+.TP
+\f5PROC_SYS_UMASK(\fImask\fP)\fR
+The child process group file creation mask is set to
+.IR mask .
+.PP
+.I flags
+is the inclusive-or of the following:
+.TP
+.L PROC_ARGMOD
+.I "argv[-1]"
+and
+.I "argv[0]"
+may be modified.
+This is an optimization that avoids an environment vector
+.I realloc(3)
+when
+.I command
+is a shell script.
+.TP
+.L PROC_BACKGROUND
+Standard shell
+.L &
+setup is done for the child process.
+.TP
+.L PROC_CLEANUP
+Parent process redirection file discriptors are closed on error.
+.TP
+.L PROC_DAEMON
+Standard daemon setup is done for the child process.
+.TP
+.L PROC_ENVCLEAR
+The child environment is cleared before
+.I envv
+is added.
+.TP
+.L PROC_GID
+The child effective group id is set to the real group id.
+.TP
+.L PROC_IGNORE
+Parent pipe errors are ignored.
+.TP
+.L PROC_OVERLAY
+The current process is overlayed by
+.I command
+if possible
+(i.e., the
+.IR fork (2)
+call is omitted).
+.TP
+.L PROC_PARANOID
+Paranoid:
+.I command
+is searched using the default standard
+.LR PATH ;
+the child environment variable
+.L PATH
+is set to the default standard;
+the
+.L PROC_GID
+and
+.L PROC_UID
+modes are set;
+only
+.L /bin/sh
+is used to execute
+.I command
+if it is a shell script.
+.TP
+.L PROC_PRIVELEGED
+If the effective user id is
+.L 0
+then the child real user id is set to
+.L 0
+and the child real group id is set to the effective group id.
+.TP
+.L PROC_READ
+.I proc.rfd
+is connected to
+.IR command 's
+standard output.
+.TP
+.L PROC_SESSION
+The child process becomes a session group leader.
+(Equivalent to the
+.I opv
+entry
+.LR PROC_SYS_PGRP(-1) .)
+.TP
+.L PROC_UID
+The child effective user id is set to the real user id.
+.TP
+.L PROC_WRITE
+.I proc.wfd
+is connected to
+.IR commands 's
+standard input.
+.PP
+The return value is a pointer to a structure with the following members:
+.TP
+.L "pid_t \fIpid\fP"
+The child process id.
+.TP
+.L "pid_t \fIpgrp\fP"
+The child process group.
+.TP
+.L "int \fIrfd\fP"
+A read file descriptor connected to
+.IR command 's
+standard output.
+.TP
+.L "int \fIwfd\fP"
+A write file descriptor connected to
+.IR command 's
+standard input.
+.PP
+If an error occurs then
+.L 0
+is returned.
+.PP
+.L procclose
+waits for the process
+.I proc
+to complete and then closes the command stream
+.IR proc .
+The command exit status is returned.
+.L -1
+is returned if the child portion of
+.L procopen
+failed.
+.PP
+.L procfree
+frees the process stream without waiting for
+.I command
+to complete.
+Presumably some other mechanism will be used to wait for
+.IR proc.pid .
+.PP
+.L procrun
+combines
+.L procopen
+and
+.L procclose
+with the flags
+.L PROC_GID|PROC_UID
+and returns the command exit status.
+.SH "SEE ALSO"
+popen(3), sfpopen(3), spawnveg(3), system(3)
diff --git a/src/lib/libast/man/re.3 b/src/lib/libast/man/re.3
new file mode 100644
index 0000000..2e1010e
--- /dev/null
+++ b/src/lib/libast/man/re.3
@@ -0,0 +1,214 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH RE 3
+.SH NAME
+recomp, reexec, ressub, refree, reerror \(mi regular expression library
+.SH SYNOPSIS
+.EX
+#include <re.h>
+
+Re_program_t* recomp(char* \fIpattern\fP, int \fIflags\fP);
+int reexec(Re_program_t* \fIre\fP, char* \fIsource\fP);
+void ressub(Re_program_t* \fIre\fP, Sfio_t* \fIsp\fP, char* \fIold\fP, char* \fInew\fP, int \fIflags\fP);
+void reerror(char* \fImessage\fP);
+void refree(Re_program_t* \fIre\fP);
+.EE
+.SH DESCRIPTION
+.L recomp
+compiles a regular expression in
+.I pattern
+and returns a pointer to the compiled regular expression.
+The space is allocated by
+.IR malloc (3)
+and may be released by
+.LR refree .
+Regular expressions are as in
+.IR egrep (1)
+except that newlines are treated as ordinary
+characters and
+.L $
+matches the end of a null-terminated string.
+.I flags
+may be
+.L RE_EDSTYLE
+which specifies
+.IR ed (1)
+style special characters,
+.LR \e( ,
+.LR \e) ,
+.LR \e? ,
+.L \e+
+and
+.L \e|
+for the
+.IR egrep (1)
+.LR ( ,
+.LR ) ,
+.LR ? ,
+.L +
+and
+.LR | ,
+respectively.
+.PP
+.L reexec
+matches the null-terminated
+.I source
+string against the compiled regular expression
+.I re
+from a previous call to
+.LR recomp .
+If it matches,
+.L reexec
+returns a non-zero value.
+If
+.I flags
+is
+.L RE_MATCH
+then the array
+.I re\->match
+is filled with character pointers to the substrings of
+.I source
+that correspond to the
+parenthesized subexpressions of
+.IR pattern :
+.I re\->match[i].sp
+points to the beginning and
+.I re\->match[i].ep
+points just beyond
+the end of substring
+.IR i .
+(Subexpression
+.I i
+begins at the
+.IR i th
+matched left parenthesis, counting from 1.)
+Pointers in
+.I re\->match[0]
+pick out the substring that corresponds to
+the entire regular expression.
+Unused elements of
+.I re\->match
+are filled with zeros.
+Matches involving
+.LR * ,
+.LR + ,
+and
+.L ?
+are extended as far as possible.
+A maximum of 9 subexpressions will be matched.
+The structure of elements of
+.I re\->match
+is:
+.nf
+.ta 8n
+ typedef struct
+ {
+ char* sp;
+ char* ep;
+ } rematch;
+.fi
+.LP
+.L ressub
+places in the
+.IR sfio (3)
+stream
+.I sp
+a substitution instance of
+.I old
+to
+.I new
+in
+.I source
+in the context of the last
+.L reexec
+performed on
+.IR re\->match .
+Each instance of
+.LI \e n ,
+where
+.I n
+is a digit, is replaced by the
+string delimited by
+.LI re\->match[ n ].sp
+and
+.LI re\->match[ n ].ep .
+Each instance of
+.L &
+is replaced by the string delimited by
+.I re\->match[0].sp
+and
+.IR re\->match[0].ep .
+If
+.L RE_ALL
+is set in
+.I flags
+then all occurrences of
+.I old
+are replaced by
+.IR new .
+If
+.L RE_LOWER
+.RL [ RE_UPPER ]
+is set in
+.I flags
+then
+.I old
+is converted to lower [upper] case.
+.LP
+.L reerror,
+called whenever an error is detected in
+.L recomp,
+.L reexec,
+or
+.L ressub,
+writes the string
+.I msg
+on the standard error file and exits.
+.L reerror
+may be replaced to perform
+special error processing.
+.SH DIAGNOSTICS
+.L recomp
+returns 0 for an invalid expression or other failure.
+.L reexec
+returns 1 if
+.I source
+is accepted, 0 otherwise.
+.SH "SEE ALSO"
+ed(1), grep(1), expr(1)
diff --git a/src/lib/libast/man/regex.3 b/src/lib/libast/man/regex.3
new file mode 100644
index 0000000..7c15d21
--- /dev/null
+++ b/src/lib/libast/man/regex.3
@@ -0,0 +1,163 @@
+.fp 5 B
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH REGEX 3
+.SH NAME
+regex \- regular expression interface
+.SH SYNOPSIS
+.EX
+#include <regex.h>
+
+int regcomp(regex_t* \fIre\fP, const char* \fIregex\fP, int \fIcflags\fP);
+int regexec(const regex_t* \fIre\fP, const char* \fIstring\fP, size_t \fInmatch\fP, regmatch_t \fIpmatch\fP[], int \fIeflags\fP);
+size_t regerror(int \fIcode\fP, const regex_t* \fIre\fP, char* \fIerrbuf\fP, size_t \fIerrbuf_size\fP);
+void regfree(regex_t* \fIre\fP);
+
+regclass_t regclass(const char* \fIstr\fP, char** \fIend\fP);
+int regaddclass(const char* \fIname\fP, regclass_t \fIclassf\fP);
+int regcollate(const char* \fIstr\fP, char** \fIend\fP, char* \fIbuf\fP, int \fIsize\fP);
+
+int regcomb(regex_t* \fIre_1\fP, regex_t* \fIre_2\fP);
+size_t regdecomp(regex_t* \fIre\fP, regflags_t \fIflags\fP, char* \fIbuf\fP, size_t \fIsize\fP);
+int regdup(regex_t* \fIre_old\fP, regex_t* \fIre_new\fP);
+regstat_t* regstat(const regex_t* \fIre\fP);
+
+regex_t* regcache(const char* \fIpattern\fP, regflags_t \fIflags\fP, int* \fIpcode\fP);
+
+int regncomp(regex_t* \fIre\fP, const char* \fIpattern\fP, size_t \fIsize\fP, regflags_t \fIflags\fP);
+int regnexec(const regex_t* \fIre\fP, const char* \fIsubject\fP, size_t \fIsize\fP, size_t \fInmatch\fP, regmatch_t* \fImatch\fP, regflags_t \fIflags\fP);
+int regrecord(const regex_t* \fIre\fP);
+int regrexec(const regex_t* \fIre\fP, const char* \fIbuf\fP, size_t \fIsize\fP, size_t \fInmatch\fP, regmatch_t* \fImatch\fP, regflags_t \fIflags\fP, int \fIsep\fP, void* \fIhandle\fP, regrecord_t \fIrecordf\fP);
+void regfatal(regex_t* \fIre\fP, int \fIlevel\fP, int \fIcode\fP);
+void regfatalpat(regex_t* \fIre\fP, int \fIlevel\fP, int \fIcode\fP, const char* \fIpattern\fP);
+
+int regsubcomp(regex_t* \fIre\fP, const char* \fIstr\fP, const regflags_t* \fImap\fP, int \fIminmatch\fP, regflags_t \fIflags\fP);
+int regsubexec(const regex_t* \fIre\fP, const char* \fIsubject\fP, size_t \fInmatch\fP, regmatch_t* match);
+int regsubflags(regex_t* \fIre\fP, const char* \fIstr\fP, char** \fIend\fP, int \fIdelim\fP, const regflags_t* \fImap\fP, int* \fIpm\fP, regflags_t* \fIpf\fP);
+void regsubfree(regex_t* \fIre\fP);
+.EE
+
+.SH DESCRIPTION
+.LR regcomp() ,
+.LR regexec() ,
+.LR regerror() ,
+and
+.L regfree()
+are the POSIX regular expression functions.
+The remaining functions are
+.B ast
+extensions.
+.B ast
+also provides
+.I flags
+extensions to the
+.LR regcomp() ,
+.LR regexec()
+functions and
+.I code
+extensions to the
+.L regerror()
+function.
+
+.PP
+.L regcache()
+maintains a cache of compiled regular expressions for patterns of size
+255 bytes or less.
+The initial cache size is 8.
+.L pattern
+and
+.L flags
+are passed to
+.L regcomp()
+with an
+.L re
+pointer maintained by
+.LR regcache() .
+.LR pcode ,
+if not 0, points to the return value of the
+.L regcomp()
+call.
+If the
+.L regcomp()
+call fails,
+.L regcache()
+returns 0 and
+.L pcode
+will point to the non-zero error code.
+Do not call
+.L regfree()
+on the
+.L re
+returned by
+.LR regcache() .
+Both
+.L pattern
+and
+.L flags
+are used to match entries in the cache.
+When the cache is full the least recently used
+.L re
+is freed (via
+.LR regfree() )
+to make space for the new pattern.
+Any
+.L re
+previously returned by
+.L regcache()
+may be freed (invalidated) on the next call to
+.LR regcache() .
+If
+.L pattern
+is longer that 255 bytes then it is still passed on to
+.LR regcomp() ,
+but it will not be cached.
+If
+.L pattern
+is 0 then the cache is flushed.
+In addition, if the integer value of
+.L flags
+is greater than the current cache size, the cache size is increased
+to that integer value.
+0 is always returned when
+.L pattern
+is 0;
+.L pcode
+will point to a non-zero value on error.
+
+.SH "SEE ALSO"
+strmatch(3)
diff --git a/src/lib/libast/man/setenviron.3 b/src/lib/libast/man/setenviron.3
new file mode 100644
index 0000000..818f7fc
--- /dev/null
+++ b/src/lib/libast/man/setenviron.3
@@ -0,0 +1,79 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH LIBAST 3
+.SH NAME
+setenviron \- set environment value
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+char* setenviron(const char* \fIkey\fP);
+.EE
+.SH DESCRIPTION
+.L setenviron
+controls environment
+.I name=value
+pairs.
+.L setenviron("\fIname=value\fP")
+adds
+.I name
+to the environment and returns a pointer to a
+.IR strdup (3)
+copy of
+.IR name=value .
+.L setenviron("\fIname\fP")
+removes
+.I name
+from the environment and returns the empty string.
+.L setenviron(0)
+reserves a few slots in an internal array and is usually called by
+a parent process that expects many children.
+0 is returned on error.
+.L setenviron
+preserves the
+.IR ksh (1)
+convention of
+.L _
+as the first environment variable name.
+.SH "SEE ALSO"
+env(1), exec(2)
+.SH BUGS
+POSIX will eventually settle on an interface.
+It has already picked a few of the names we did in .2 drafts.
+This is about the third name change for ours.
diff --git a/src/lib/libast/man/sfdisc.3 b/src/lib/libast/man/sfdisc.3
new file mode 100644
index 0000000..f0ce5b0
--- /dev/null
+++ b/src/lib/libast/man/sfdisc.3
@@ -0,0 +1,118 @@
+.fp 5 CW
+.TH SFDISC 3 "16 June 1993"
+.SH NAME
+\fBsfdisc\fR \- \fBsfio\fP disciplines
+.SH SYNOPSIS
+.de Tp
+.fl
+.ne 2
+.TP
+..
+.de Ss
+.fl
+.ne 2
+.SS "\\$1"
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.nf
+.ft 5
+#include <sfdisc.h>
+
+extern Sfdisc_t* dcnewskable(Sfio_t* f);
+extern int dcdelskable(Sfdisc_t* disc);
+
+extern Sfdisc_t* dcnewtee(Sfio_t* tee);
+extern int dcdeltee(Sfdisc_t* disc);
+
+extern Sfdisc_t* dcnewfilter(char* cmd);
+extern int dcdelfilter(Sfdisc_t* disc);
+
+extern Sfdisc_t* dcnewsubstream(Sfio_t* f, long offset, long extent);
+extern int dcdelsubstream(Sfdisc_t* disc);
+
+extern Sfdisc_t* dcnewlzw(void);
+extern int dcdellzw(Sfdisc_t* disc);
+
+extern Sfdisc_t* dcnewunion(Sfio_t** flist, int n);
+extern int dcdelunion(Sfdisc_t* disc);
+.ft 1
+.fi
+.SH DESCRIPTION
+.PP
+I/O disciplines are used to extend the data processing power of
+\fIsfio\fP streams. The convention for using the disciplines
+in this package is to use the call \f5dcnewXXX()\fP to create
+a discipline of the type \f5XXX\fP and to use \f5dcdelXXX()\fP
+to destroy it.
+A discipline is enable by inserting it into the desired
+stream using the \f5sfdisc()\fP call. A discipline can be used on only
+one stream. It is unsafe to share a discipline on two or more streams
+since the discipline may maintain states between successive IO calls.
+For multiple uses, \f5dcnewXXX()\fP should be used
+to create a distinct discipline for each stream.
+Each discipline structure is equipped with an exception handler
+that causes self-destruction when the associated stream is closed.
+.PP
+.Ss " Sfdisc_t* dcnewskable(Sfio_t* f);"
+.Ss " int dcdelskable(Sfdisc_t* disc);"
+\f5dcnewskable()\fP creates a discipline that when inserted
+on the stream \f5f\fP will ensure that \f5f\fP is seekable.
+If \f5f\fP is originally unseekable, data will be shadowed
+in a temporary file stream to allow seekability.
+\f5dcnewskable()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.Ss " Sfdisc_t* dcnewtee(Sfio_t* tee);"
+.Ss " int dcdeltee(Sfdisc_t* disc);"
+\f5dcnewtee()\fP creates a discipline that
+when inserted into a stream \f5f\fP will duplicate to the stream \f5tee\fP
+any data written to \f5f\fP.
+\f5dcnewtee()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.Ss " Sfdisc_t* dcnewfilter(char* cmd);"
+.Ss " int dcdelfilter(Sfdisc_t* disc);"
+\f5dcnewfilter()\fP creates a discipline that
+when inserted into a stream \f5f\fP will run the command \f5cmd\fP
+to process any input data before making it available to the application.
+For example, \f5dcnewfilter("uncompress")\fP is an equivalent but slower
+alternative to the lzw discipline below.
+\f5dcnewfilter()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.Ss " Sfdisc_t* dcnewsubstream(Sfio_t* base, long offset, long extent);"
+.Ss " int dcdelsubstream(Sfdisc_t* disc);"
+\f5dcnewsubstream()\fP creates a discipline that
+reserves a portion of the stream \f5base\fP starting at \f5offset\fP
+with length \f5extent\fP and makes this portion appear as if it is
+a stream. When this discipline is inserted into a stream, it will make
+cause all IO operations on this stream to take place in the reserved
+portion of the \f5base\fP stream.
+\f5dcnewsubstream()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.Ss " Sfdisc_t* dcnewlzw(void);
+.Ss " int dcdellzw(Sfdisc_t* disc);"
+\f5dcnewlzw()\fP creates a discipline that when inserted into
+a stream \f5f\fP will run the \fBuncompress\fP algorithm
+on input data from \f5f\fP before making it available to the
+application. This is useful to allow applications to process
+data from a file packed with the UNIX \fBcompress\fP utility
+as if the data is in plain text.
+\f5dcnewlzw()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.Ss " Sfdisc_t* dcnewunion(Sfio_t** list, int n);
+.Ss " int dcdelunion(Sfdisc_t* disc);"
+\f5dcnewunion()\fP creates a discipline that concatenates
+input data from all \f5n\fP streams in \f5list\fP.
+When inserted into a stream \f5f\fP, this discipline will cause
+all input operations on \f5f\fP to come from the merged data stream.
+\f5dcnewunion()\fP returns the discipline on success and \f5NULL\fP on failure.
+
+.SH ACKNOWLEDGMENTS
+Dave Korn contributed the substream discipline.
+Jim Arnold contributed the lzw discipline.
+
+.SH NOTES
+Since we are not sure of the legal responsibilities concerning the lzw patent,
+the lzw discipline is not currently distributed with any release of sfio
+outside of AT&T.
+
+.SH AUTHOR
+Kiem-Phong Vo, kpv@research.att.com.
diff --git a/src/lib/libast/man/sfio.3 b/src/lib/libast/man/sfio.3
new file mode 100644
index 0000000..f22d0b7
--- /dev/null
+++ b/src/lib/libast/man/sfio.3
@@ -0,0 +1,2373 @@
+.fp 5 CW
+.TH SFIO 3 "01 June 2008"
+.SH NAME
+\fBsfio\fR \- safe/fast string/file input/output
+.SH SYNOPSIS
+.de Tp
+.fl
+.ne 3
+.TP
+..
+.de Ss
+.fl
+.ne 3
+.SS "\\$1"
+..
+.ta 1.0i 2.0i 3.0i 4.0i 5.0i
+.Ss "LIBRARIES"
+.nf
+.ft 5
+#include <sfio.h>
+
+libsfio.a -lsfio
+libstdio.a -lstdio
+libsfio-mt.a -lsfio-mt
+libstdio-mt.a -lstdio-mt
+.ft 1
+.fi
+.Ss "DATA TYPES"
+.nf
+.ft 5
+Void_t;
+Sfoff_t;
+Sflong_t;
+Sfulong_t;
+Sfdouble_t;
+
+Sfio_t;
+
+Sfdisc_t;
+ssize_t (*Sfread_f)(Sfio_t*, Void_t*, size_t, Sfdisc_t*);
+ssize_t (*Sfwrite_f)(Sfio_t*, const Void_t*, size_t, Sfdisc_t*);
+Sfoff_t (*Sfseek_f)(Sfio_t*, Sfoff_t, int, Sfdisc_t*);
+int (*Sfexcept_f)(Sfio_t*, int, Void_t*, Sfdisc_t*);
+
+Sffmt_t;
+int (*Sffmtext_f)(Sfio_t*, Void_t*, Sffmt_t*);
+int (*Sffmtevent_f)(Sfio_t*, int, Void_t*, Sffmt_t*);
+
+SFIO_VERSION
+.ft 1
+.fi
+.Ss "BIT FLAGS"
+.nf
+.ft 5
+SF_STRING
+SF_READ
+SF_WRITE
+SF_APPENDWR (SF_APPEND)
+SF_LINE
+SF_SHARE
+SF_PUBLIC
+SF_MALLOC
+SF_STATIC
+SF_IOCHECK
+SF_WHOLE
+SF_MTSAFE
+SF_IOINTR
+.ft 1
+.fi
+.Ss "OPENING/CLOSING STREAMS"
+.nf
+.ft 5
+Sfio_t* sfnew(Sfio_t* f, Void_t* buf, size_t size, int fd, int flags);
+Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode);
+Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode);
+Sfio_t* sftmp(size_t size);
+int sfclose(Sfio_t* f);
+
+
+.ft 1
+.fi
+.Ss "THREAD SAFETY"
+.nf
+.ft 5
+int sfmutex(Sfio_t* f, int type);
+
+SFMTX_LOCK
+SFMTX_TRYLOCK
+SFMTX_UNLOCK
+SFMTX_CLRLOCK
+.ft 1
+.fi
+.Ss "INPUT/OUTPUT OPERATIONS"
+.nf
+.ft 5
+int sfgetc(Sfio_t* f);
+int sfputc(Sfio_t* f, int c);
+int sfnputc(Sfio_t* f, int c, int n);
+int sfungetc(Sfio_t* f, int c);
+
+Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max);
+int sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max);
+Sfulong_t sfgetu(Sfio_t* f);
+int sfputu(Sfio_t* f, Sfulong_t v);
+Sflong_t sfgetl(Sfio_t* f);
+int sfputl(Sfio_t* f, Sflong_t v);
+Sfdouble_t sfgetd(Sfio_t* f);
+int sfputd(Sfio_t* f, Sfdouble_t v);
+
+char* sfgetr(Sfio_t* f, int rsc, int type);
+ssize_t sfputr(Sfio_t* f, const char* s, int rsc);
+Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc);
+
+ssize_t sfread(Sfio_t* f, Void_t* buf, size_t n);
+ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n);
+Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type);
+Void_t* sfreserve(Sfio_t* f, ssize_t n, int type);
+.ft 1
+.fi
+.Ss "DATA FORMATTING"
+.nf
+.ft 5
+int sfscanf(Sfio_t* f, const char* format, ...);
+int sfsscanf(const char* s, const char* format, ...);
+int sfvsscanf(const char* s, const char* format, va_list args);
+int sfvscanf(Sfio_t* f, const char* format, va_list args);
+
+int sfprintf(Sfio_t* f, const char* format, ...);
+char* sfprints(const char* format, ...);
+char* sfvprints(const char* format, va_list args);
+ssize_t sfaprints(char** sp, const char* format, ...);
+ssize_t sfvaprints(char** sp, const char* format, va_list args);
+int sfsprintf(char* s, int n, const char* format, ...);
+int sfvsprintf(char* s, int n, const char* format, va_list args);
+int sfvprintf(Sfio_t* f, const char* format, va_list args);
+
+Sffmt_t;
+
+SFFMT_LEFT
+SFFMT_SIGN
+SFFMT_BLANK
+SFFMT_ZERO
+SFFMT_THOUSAND
+SFFMT_LONG
+SFFMT_LLONG
+SFFMT_SHORT
+SFFMT_LDOUBLE
+SFFMT_IFLAG
+SFFMT_JFLAG
+SFFMT_CENTER
+SFFMT_CHOP
+SFFMT_ALTER
+SFFMT_SKIP
+SFFMT_ARGPOS
+SFFMT_VALUE
+
+int (*Sffmtext_f)(Sfio_t* f, Void_t* v, Sffmt_t* fe);
+int (*Sffmtevent_f)(Sfio_t* f, int type, Void_t* v, Sffmt_t* fe);
+void va_copy(va_list to, va_list fr);
+long sffmtversion(Sffmt_t* fe, type);
+.ft 1
+.fi
+.Ss "BUFFERING, SYNCHRONIZATION"
+.nf
+.ft 5
+Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size);
+int sfsync(Sfio_t* f);
+int sfpoll(Sfio_t** flist, int n, int timeout);
+Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode);
+int sfpurge(Sfio_t* f);
+.ft 1
+.fi
+.Ss "DISCIPLINE, EVENT HANDLING"
+.nf
+.ft 5
+Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc);
+int sfraise(Sfio_t* f, int type, Void_t* data);
+ssize_t sfrd(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc);
+ssize_t sfwr(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc);
+Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc);
+
+SF_NEW
+SF_READ
+SF_WRITE
+SF_SEEK
+SF_CLOSING (SF_CLOSE)
+SF_DPUSH
+SF_DPOP
+SF_DPOLL
+SF_DBUFFER
+SF_SYNC
+SF_PURGE
+SF_FINAL
+SF_READY
+SF_LOCKED
+SF_ATEXIT
+SF_EVENT
+.ft 1
+.fi
+.Ss "STREAM CONTROL"
+.nf
+.ft 5
+int sfresize(Sfio_t* f, Sfoff_t size);
+int sfset(Sfio_t* f, int flags, int i);
+int sfsetfd(Sfio_t* f, int fd);
+Sfio_t* sfstack(Sfio_t* base, Sfio_t* top);
+Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2);
+.ft 1
+.fi
+.Ss "STREAM INFORMATION"
+.nf
+.ft 5
+Sfoff_t sfsize(Sfio_t* f);
+Sfoff_t sftell(Sfio_t* f);
+ssize_t sfvalue(Sfio_t* f);
+int sffileno(Sfio_t* f);
+
+int sfstacked(Sfio_t* f);
+int sfeof(Sfio_t* f);
+int sferror(Sfio_t* f);
+int sfclrerr(Sfio_t* f);
+int sfclrlock(Sfio_t* f);
+
+int sfnotify(void (*notify)(Sfio_t* f, int type, Void_t* data));
+
+int sfwalk(Sfwalk_f walkf, Void_t* data, int type);
+.ft 1
+.fi
+.Ss "MISCELLANEOUS FUNCTIONS"
+.nf
+.ft 5
+ssize_t sfmaxr(ssize_t maxr, int s);
+ssize_t sfslen();
+int sfulen(Sfulong_t v);
+int sfllen(Sflong_t v);
+int sfdlen(Sfdouble_t v);
+ssize_t sfpkrd(int fd, Void_t* buf, size_t n,
+ int rsc, long tm, int action);
+.ft 1
+.fi
+.Ss "FULL STRUCTURE SFIO_T"
+.nf
+.ft 5
+#include <sfio_t.h>
+#define SFNEW(buf,size,file,flags,disc)
+.ft 1
+.fi
+.Ss "EXAMPLE DISCIPLINES"
+.nf
+.ft 5
+#include <sfdisc.h>
+
+int sfdcdio(Sfio_t* f, size_t bufsize);
+int sfdcdos(Sfio_t* f);
+int sfdcfilter(Sfio_t* f, const char* cmd);
+int sfdcseekable(Sfio_t* f);
+int sfdcslow(Sfio_t* f);
+int sfdcsubstream(Sfio_t* f, Sfio_t* parent,
+ Sfoff_t offset, Sfoff_t extent);
+int sfdctee(Sfio_t* f, Sfio_t* tee);
+int sfdcunion(Sfio_t* f, Sfio_t** array, int n);
+int sfdclzw(Sfio_t* f);
+int sfdcgzip(Sfio_t* f, int flags);
+.ft 1
+.fi
+.Ss "STDIO-COMPATIBILITY"
+.nf
+.ft 5
+#include <stdio.h>
+cc ... -lstdio -lsfio
+cc ... -lstdio-mt -lsfio-mt
+.ft 1
+.fi
+.SH DESCRIPTION
+.PP
+Sfio provides I/O functions to manage buffered streams.
+Each Sfio stream is a \fIfile stream\fP, representing a file (see \f5open(2)\fP),
+or a \fIstring stream\fP, representing a memory segment.
+Beyond the usual I/O operations on streams,
+Sfio provides I/O disciplines for extended data processing,
+stream stacks for recursive stream processing, and
+stream pools for automatic data synchronization.
+Applications can extend the \f5sfprintf()/sfscanf()\fP functions
+to define their own conversion patterns as well as redefine existing ones.
+.PP
+A discipline defines analogues of
+the system calls \f5read(2), write(2)\fP and \f5lseek(2)\fP.
+Such system calls or their discipline replacements are used to process stream data.
+Henceforth, ``\fIsystem call\fP'' will refer to either a system call
+or its discipline replacement.
+.PP
+A system call is said to cause an exception if its return value is non-positive.
+Unless overridden by exception handlers (see \f5sfdisc()\fP),
+an interrupted system call (\f5errno == EINTR\fP on UNIX systems)
+will be automatically reinvoked to continue the ongoing operation.
+.PP
+The buffer of a stream is typically a memory segment allocated via \f5malloc(3)\fP
+or supplied by the application.
+File streams may also use memory mapping (\f5mmap(2)\fP) if that is more efficient.
+When memory mapping is used,
+the underlying file should not be truncated while the stream is active.
+Memory mapping can be turned off using \f5sfsetbuf()\fP.
+.PP
+There are three \fIstandard streams\fP:
+\f5sfstdin\fP for input (file descriptor \f50\fP on UNIX systems),
+\f5sfstdout\fP for normal output (file descriptor \f51\fP), and
+\f5sfstderr\fP for error output (file descriptor \f52\fP).
+
+.PP
+.Ss "LIBRARIES"
+.PP
+This version of Sfio can be built and used for both uni-threaded and multi-threaded
+environments. In the former case, streams are not protected from
+simultaneous accesses by different threads. In the latter case, a stream
+is typically locked with a mutex during access so that another thread
+trying to access the same stream will block until the mutex is released.
+
+A program that does not use multiple threads can link with \fBlibsfio.a\fP
+while a program that uses multiple threads should link with \fBlibsfio-mt.a\fP.
+The libraries \fBlibstdio.a\fP and \fBlibstdio-mt.a\fP provide
+corresponding Stdio functions to link with code already compiled using the
+native header \fBstdio.h\fP instead of the one provided by Sfio.
+
+.PP
+.Ss "DATA TYPES"
+.PP
+.Ss " Void_t*"
+This defines a type suitable to exchange
+data of unknown types between application and Sfio.
+\f5Void_t\fP is a macro defined as \f5void\fP for ANSI-C and C++ and
+\f5char\fP for other compilation environments.
+.PP
+.Ss " Sfoff_t"
+This defines an integral type suitable to address
+the largest possible file extent.
+.PP
+.Ss " Sfulong_t, Sflong_t, Sfdouble_t"
+These are respectively the largest
+unsigned integer, signed integer, and floating point value types on the local platform.
+.PP
+.Ss " Sfio_t"
+This defines the type of a stream handle.
+.PP
+.Ss " Sfdisc_t"
+.Ss " ssize_t (*Sfread_f)(Sfio_t*, Void_t*, size_t, Sfdisc_t*)"
+.Ss " ssize_t (*Sfwrite_f)(Sfio_t*, const Void_t*, size_t, Sfdisc_t*)"
+.Ss " Sfoff_t (*Sfseek_f)(Sfio_t*, Sfoff_t, int, Sfdisc_t*)"
+.Ss " int (*Sfexcept_f)(Sfio_t*, int, Void_t*, Sfdisc_t*)"
+\f5Sfdisc_t\fP defines a stream discipline structure.
+\f5Sfread_f\fP, \f5Sfwrite_f\fP and \f5Sfseek_f\fP are the types
+of discipline functions to replace the system calls:
+\f5read(2)\fP, \f5write(2)\fP and \f5lseek(2)\fP.
+\f5Sfexcept_f\fP is the type of an event-handling function.
+See \f5sfdisc()\fP for more details.
+.PP
+.Ss " Sffmt_t"
+.Ss " int (*Sffmtext_f)(Sfio_t*, Void_t*, Sffmt_t*)"
+.Ss " int (*Sffmtevent_f)(Sfio_t*, int, Void_t*, Sffmt_t*)"
+\f5Sffmt_t\fP defines a formatting environment that can be used
+to extend scanning and printing in the \f5sfprint()/sfscanf()\fP
+functions. \f5Sffmtext_f\fP and \f5Sffmtevent_f\fP define the types
+of extension functions definable in \f5Sffmt_t\fP.
+See \f5Sffmt_t\fP below for more details.
+.PP
+.Ss " SFIO_VERSION"
+This is a macro value of type \f5long int\fP that defines
+the current version number of Sfio. For example, the Sfio2000's
+version number is \f520000515L\fP
+(which also indicates its latest version date: 05/15/2000).
+
+.Ss "BIT FLAGS"
+A number of bit flags control stream operations.
+They are set either at stream initialization or by calling \f5sfset()\fP.
+Following are the flags:
+.Tp
+\f5SF_STRING\fP:
+The stream is memory-based.
+.Tp
+\f5SF_READ\fP, \f5SF_WRITE\fP, \f5SF_APPENDWR\fP (\f5SF_APPEND\fP):
+Flags \f5SF_READ\fP and \f5SF_WRITE\fP indicate readability and writability.
+Flag \f5SF_APPENDWR\fP asserts that the stream is a file opened in append mode
+(see \f5open(2)\fP and \f5fcntl(2)\fP)
+so that data is always output at the end of file.
+On systems without direct support for append mode,
+Sfio uses \f5lseek(2)\fP or its discipline replacement
+to approximate this behavior.
+.Tp
+\f5SF_LINE\fP:
+The stream is line-oriented.
+For a \f5SF_WRITE\fP stream,
+this means that buffered data is flushed
+whenever a new-line character, \f5\en\fP, is output.
+For a \f5SF_READ\fP stream, \f5SF_LINE\fP is only
+significant during calls to functions in the \f5sfscanf()\fP family.
+\f5SF_LINE\fP is set on initialization of
+any stream representing a terminal device.
+.Tp
+\f5SF_SHARE\fP, \f5SF_PUBLIC\fP:
+Flag \f5SF_SHARE\fP means that the underlying file descriptor
+is shared by independent entities (for example, multiple processes).
+
+For a seekable file stream, \f5SF_SHARE\fP means that
+the logical stream and the physical file positions will be made the same
+before a system call to perform physical I/O.
+There are different possibilities.
+If \f5SF_PUBLIC\fP is not set,
+the physical file position is made equal to the logical stream position.
+If \f5SF_PUBLIC\fP is set, there are two cases.
+If the physical file position has changed from its last known position,
+the logical stream position is made equal to the new physical file position.
+Finally, if the physical file location remains the same as its last known position,
+the physical file position is made the same as the logical stream position.
+
+For an unseekable stream (e.g., pipes or terminal devices), if possible,
+\f5SF_SHARE\fP means that
+the block and record I/O operations (\f5sfread()\fP, \f5sfwrite()\fP, \f5sfmove()\fP,
+\f5sfgetr()\fP, \f5sfputr()\fP, \f5sfreserve()\fP, \f5sfscanf()\fP
+and \f5sfvprintf()\fP) will ensure:
+(1) after each writing operation, the stream is synchronized and
+(2) each reading operation only reads the requested amount.
+Note, however, that (2) is not always possible
+without proper OS facilities such as \f5recv(2)\fP or \f5streamio(4)\fP.
+
+A standard stream that is seekable will be initialized with \f5SF_SHARE|SF_PUBLIC\fP.
+.Tp
+\f5SF_MALLOC\fP:
+The stream buffer was obtained via \f5malloc(3)\fP
+and can be reallocated or freed.
+.Tp
+\f5SF_STATIC\fP:
+The stream structure should not be freed when closed (\f5sfclose()\fP).
+This flag is used by an applications that allocate their own
+stream structures. Such applications must use the header file \f5sfio_t.h\fP
+instead of \f5sfio.h\fP.
+.Tp
+\f5SF_IOCHECK\fP:
+If the stream has a discipline exception handler,
+exceptions will be raised in \f5sfsync()\fP, \f5sfpurge()\fP
+or before a system call \f5read(2)\fP or \f5write(2)\fP (see \f5sfdisc()\fP).
+.Tp
+\f5SF_WHOLE\fP:
+This flag guarantees that data written in any single \f5sfwrite()\fP or
+\f5sfputr()\fP call will always be output as a whole to the output device.
+This is useful in certain applications (e.g., networking) where a complex object
+must be output without being split in different system calls.
+Note that the respective stream still buffers data as much as the buffer can accomodate.
+.Tp
+\f5SF_MTSAFE\fP:
+This flag indicates that the respective stream may be accessed by more than one threads.
+A mutex lock will be used to ensure that only one thread at a time can access
+the stream. Note that this flag can only be set at stream opening time
+(see \f5sfopen()\fP, \f5sfpopen()\fP and \f5sfnew()\fP).
+Certain fast macro functions such as \f5sfgetc()\fP and \f5sfputc()\fP will
+no longer behave as macros. Thus, an application that requires such fast macro functions
+should leave \f5SF_MTSAFE\fP off and performs explicit locking with \f5sfmutex()\fP.
+.Tp
+\f5SF_IOINTR\fP:
+This flag indicates that I/O system calls should not be resumed
+after being interrupted by signals. It is useful for
+aborting I/O operations on such interruptions. Note, however,
+than certain operating systems (e.g., BSD Unix systems) may automatically
+resume interrupted system calls outside the scope of the library. On such systems,
+\f5SF_IOINTR\fP will be ineffective.
+
+.PP
+.Ss "OPENING/CLOSING STREAMS"
+.PP
+.Ss " Sfio_t* sfnew(Sfio_t* f, Void_t* buf, size_t size, int fd, int flags)"
+This function creates or renews a stream.
+It returns the new stream on success and \f5NULL\fP on error.
+.Tp
+\f5f\fP:
+If \f5f\fP is \f5NULL\fP, a new stream is created.
+Otherwise, \f5f\fP is reused.
+In this case, if \f5flags\fP does not have \f5SF_EOF\fP,
+\f5f\fP shall be closed via \f5sfclose()\fP before being reused.
+During a stream renewal, buffer, pool and discipline stack are preserved.
+Note that, except for \f5SF_STATIC\fP streams,
+renewing a stream already closed will result in undefined behavior.
+.Tp
+\f5buf\fP, \f5size\fP:
+These determine a buffering scheme.
+See \f5sfsetbuf()\fP for more details.
+.Tp
+\f5fd\fP:
+If \f5SF_STRING\fP is specified in \f5flags\fP, this is ignored.
+Otherwise, \f5fd\fP is a file descriptor (e.g., from \f5open(2)\fP)
+to use for raw data I/O.
+Note that Sfio supports unseekable file descriptors
+opened for both read and write, e.g., sockets.
+.Tp
+\f5flags\fP:
+This is composed from \f5SF_EOF\fP and
+bit values defined in the \fBBIT FLAGS\fP section.
+Note, in particular, that a multi-threaded application should
+set the bit \f5SF_MTSAFE\fP to protect the new stream from
+being simultaneously accessed by multiple threads.
+
+.Ss " Sfio_t* sfopen(Sfio_t* f, const char* string, const char* mode)"
+
+If \f5string\fP is \f5NULL\fP,
+\f5f\fP is a file stream and
+\f5mode\fP does not imply a string stream,
+\f5sfopen()\fP changes the modes of \f5f\fP according to \f5mode\fP.
+In this case, \f5sfopen()\fP returns \f5f\fP on success and \f5NULL\fP on error.
+This somewhat unusual usage of \f5sfopen()\fP is good for
+resetting certain predefined modes in standard streams including
+\fItext/binary\fP and \fIappend\fP that are inherited from some parent process.
+Note also that \f5SF_READ\fP and \f5SF_WRITE\fP can only be reset if the stream
+is not yet initialized.
+
+\f5sfopen()\fP is normally used to create a new stream or renew a stream.
+In this case, it returns the new stream on success and \f5NULL\fP on error.
+Below are the meanings of the arguments:
+.Tp
+\f5f\fP:
+This is treated as in \f5sfnew()\fP.
+.Tp
+\f5string\fP:
+This is a file name or a string to perform I/O on.
+See above for when this is \f5NULL\fP.
+.Tp
+\f5mode\fP:
+This is composed from the set of letters \f5{s, r, w, +, a, b, t, x, m, u}\fP.
+When conflicting options are present in the same \f5mode\fP string,
+the last one will take effect.
+
+\f5s\fP specifies opening a string stream.
+\f5string\fP can be a null-terminated string or \f5NULL\fP.
+Specifying \f5s\fP alone is equivalent to specifying \f5sr\fP.
+If \f5s\fP is not specified, \f5string\fP defines a file name.
+
+\f5r\fP and \f5w\fP specify read and write modes.
+Write mode creates and/or truncates the given file to make an empty file.
+The \f5+\fP modifier indicates that the stream is opened for both read and write.
+
+\f5a\fP specifies append mode, i.e., data is always output at end of file.
+
+\f5b\fP and \f5t\fP specify binary and text modes.
+
+\f5x\fP specifies exclusive mode, i.e.,
+a file opened for writing should not already exist.
+
+\f5m\fP specifies that the stream needs to be protected from
+simultaneous accesses by multiple threads.
+This turns on the bit flag \f5SF_MTSAFE\fP.
+
+\f5u\fP specifies that the stream is guaranteed to be accessed
+by only one thread at a time. The bit flag \f5SF_MTSAFE\fP is left off.
+The absence of option \f5m\fP is the same as the presence of option \f5u\fP.
+
+.Ss " Sfio_t* sfpopen(Sfio_t* f, const char* cmd, const char* mode)"
+This function opens a stream that corresponds to the coprocess \f5cmd\fP.
+The argument \f5mode\fP should be composed from \f5r\fP, \f5w\fP, and \f5+\fP.
+The argument \f5f\fP, if not \f5NULL\fP, is a stream to be renewed (see \f5sfnew()\fP).
+\f5sfpopen()\fP returns the new stream or \f5NULL\fP on error.
+
+The standard input/output of \f5cmd\fP
+is connected to the application via a pipe if the stream is opened for writing/reading.
+If the stream is opened for both reading and writing,
+there will be two different associated file descriptors, one for each type of I/O
+(note the effect on \f5sffileno()\fP).
+
+On opening a coprocess for writing (i.e., \f5mode\fP contains \f5w\fP or \f5+\fP),
+the signal handler for \f5SIGPIPE\fP in the parent application
+will be set to \f5SIG_IGN\fP if it is \f5SIG_DFL\fP at that time.
+This protects the parent application from being accidentally killed
+on writing to a coprocess that closes its reading end.
+Applications that need to detect such write errors should use
+disciplines and exception handlers (see \f5sfdisc()\fP).
+
+The command \f5cmd\fP
+is executed by an \fIinterpreter\fP which is either \f5/bin/sh\fP
+or an executable command defined by the environment variable \f5SHELL\fP.
+In either case, the interpreter is invoked with 2 arguments, respectively \f5-c\fP
+and the given command \f5cmd\fP. When the interpreter is \f5/bin/sh\fP or
+\f5/bin/ksh\fP, \f5sfpopen()\fP may execute the command \f5cmd\fP itself
+if there are no shell meta-characters in \f5cmd\fP.
+
+.Ss " Sfio_t* sftmp(size_t size)"
+This function creates a stream for temporary data.
+It returns the new stream or \f5NULL\fP on error.
+
+A stream created by \f5sftmp()\fP can be completely or partially memory-resident.
+If \f5size\fP is \f5SF_UNBOUND\fP, the stream is a pure string stream.
+If \f5size\fP is zero, the stream is a pure file stream.
+Otherwise, the stream is first created as a string stream but when
+its buffer grows larger than \f5size\fP or on any attempt to change disciplines,
+a temporary file is created.
+Two environment variables, \f5TMPPATH\fP and \f5TMPDIR\fP,
+direct where temporary files are created.
+\f5TMPPATH\fP, if defined,
+specifies a colon-separated set of directories to be
+used in a round-robin fashion to create files.
+If \f5TMPPATH\fP is undefined,
+\f5TMPDIR\fP can be used to specify a single directory to create files.
+If neither of \f5TMPPATH\fP and \f5TMPDIR\fP are defined, \f5/tmp\fP is used.
+
+.Ss " int sfclose(Sfio_t* f)"
+This function closes the stream \f5f\fP and frees its resources.
+\f5SF_STATIC\fP should be used if the stream space is to be preserved.
+If \f5f\fP is the base of a stream stack (see \f5sfstack()\fP),
+all streams on the stack are closed.
+If \f5f\fP is a \f5sfpopen\fP-stream,
+\f5sfclose()\fP waits until the associated command terminates
+and returns its exit status.
+\f5sfclose()\fP returns \f5-1\fP for failure and \f50\fP for success.
+
+\f5SF_READ|SF_SHARE\fP and \f5SF_WRITE\fP streams
+are synchronized before closing (see \f5sfsync()\fP).
+If \f5f\fP has disciplines,
+their exception handlers will be called twice.
+The first exception handler call has the \f5type\fP argument as one of
+\f5SF_CLOSING\fP or \f5SF_NEW\fP (see \f5sfdisc()\fP.)
+The latter, \f5SF_NEW\fP is used when a stream is being closed via \f5sfnew()\fP
+so that it can be renewed.
+The second call uses \f5type\fP as \f5SF_FINAL\fP
+and is done after all closing operations have succeeded but before
+the stream itself is deallocated.
+In either case, if the exception handler returns a negative value,
+\f5sfclose()\fP will immediately return this value.
+If the exception handler returns a positive value,
+\f5sfclose()\fP will immediately return a zero value.
+
+.PP
+.Ss "THREAD SAFETY"
+.PP
+The libraries \f5libsfio.a\fP and \f5libstdio.a\fP (providing binary
+compatibility to Stdio-based code) only support uni-threaded code.
+Multi-threaded applications should link with
+\f5libsfio-mt.a\fP and \f5libstdio-mt.a\fP.
+When this is done, certain platforms may require additional
+thread libraries for linkage. For example, Linux, Irix and Solaris
+require \f5-lpthread\fP while HPUX requires \f5-lcma\fP.
+Aside from linkage differences, the Sfio API remains identical in all cases.
+
+Note that unlike Stdio streams which are in thread-safe mode by default.
+Sfio streams can be opened in either uni-threaded or multi-threaded mode.
+A uni-threaded stream is more efficient than a multi-threaded one.
+For example, functions such as \f5sfgetc()\fP and \f5sfputc()\fP
+remain as macro or inline functions for a uni-threaded stream while
+they will act as full function calls in a multi-threaded case.
+The three standard streams \f5sfstdin/sfstdout/sfstderr\fP
+are in multi-threaded mode by default
+(however, see \f5sfopen()\fP for how this may be changed).
+Other Sfio streams are normally opened uni-threaded unless
+the flag \f5SF_MTSAFE\fP or the option \f5m\fP were specified.
+Stdio-based code can also make a Stdio stream uni-threaded by
+using the option \f5u\fP when opening a file.
+
+.PP
+.Ss "int sfmutex(Sfio_t* f, int type)"
+This function acquires or releases a mutex
+(mutually exclusive) lock on the stream \f5f\fP.
+It can be used by a thread to serialize a sequence of I/O operations
+executed together in some critical section.
+\f5sfmutex()\fP is implicitly used by
+all Sfio operations on a stream with the flag \f5SF_MTSAFE\fP to
+protect it from concurrent accesses via multiple threads.
+\f5sfmutex()\fP returns \f50\fP on success and some non-zero value on failure.
+
+Each stream has a lock count which starts at \f50\fP.
+When the count is positive, a single thread holds the stream.
+Only this thread can further lock or unlock the stream.
+A different thread attempting to acquire such a locked stream will suspend
+until the lock count returns to \f50\fP.
+Each successful locking operation increases the lock count
+while each successful unlocking operation decreases it,
+thus, allowing nesting of matching lock/unlock operations.
+
+The \f5type\fP argument of \f5sfmutex()\fP takes on the below values:
+.Tp
+\f5SFMTX_LOCK\fP:
+Locking a stream if it is unlocked or increasing the lock count of the stream
+if it is already locked by the same thread. This call will block until it is
+possible to lock the stream.
+.Tp
+\f5SFMTX_TRYLOCK\fP:
+This is the non-blocking version of \f5SFMTX_LOCK\fP.
+If the stream is already locked by a different thread, \f5sfmutex()\fP will
+immediately return with an error status.
+.Tp
+\f5SFMTX_UNLOCK\fP:
+Decreasing the lock count and releasing the stream when the lock count reaches 0.
+An attempt to unlock a stream without a previously successful lock may
+result in undefined behavior in certain implementations.
+The current Sfio implementation returns an error status.
+.Tp
+\f5SFMTX_CLRLOCK\fP:
+Resetting the lock count to \f50\fP and releasing the stream.
+As with \f5SFMTX_LOCK\fP,
+an attempt to clear the lock count without a previously successful lock
+may result in undefined behavior.
+.PP
+.Ss "INPUT/OUPUT OPERATIONS"
+.PP
+.Ss " int sfgetc(Sfio_t* f)"
+.Ss " int sfputc(Sfio_t* f, int c)"
+These functions read/write a byte from/to stream \f5f\fP.
+\f5sfgetc()\fP returns the byte read or \f5-1\fP on error.
+\f5sfputc()\fP returns \f5c\fP on success and \f5-1\fP on error.
+
+.Ss " ssize_t sfnputc(Sfio_t* f, int c, size_t n)"
+This function attempts to write the byte \f5c\fP to \f5f\fP \f5n\fP times.
+It returns the number of bytes actually written or \f5-1\fP on failure.
+
+.Ss " int sfungetc(Sfio_t* f, int c)"
+This function pushes the byte \f5c\fP back into \f5f\fP.
+If \f5c\fP matches the byte immediately before the current position in buffered data,
+the current position is simply backed up (note the effect on \f5sftell()\fP and
+\f5sfseek()\fP). There is no theoretical limit on the number of bytes that
+can be pushed back into a stream. Pushed back bytes not part of
+buffered data will be discarded on any operation that implies
+buffer synchronization.
+\f5sfungetc()\fP returns \f5c\fP on success and \f5-1\fP on failure.
+
+.Ss " Sfulong_t sfgetm(Sfio_t* f, Sfulong_t max)"
+.Ss " int sfputm(Sfio_t* f, Sfulong_t v, Sfulong_t max)"
+These functions read and write \f5Sfulong_t\fP values
+encoded in a portable format given that the values are at most \f5max\fP.
+Portability across a write architecture and a read architecture
+requires that the bit order in a byte is the same on both architectures and
+the written value is storable in an \f5Sfulong_t\fP on the read architecture.
+\f5sfgetm()\fP returns the value read or \f5-1\fP on error.
+\f5sfputm()\fP returns the number of bytes written or \f5-1\fP on error.
+
+.Ss " Sfulong_t sfgetu(Sfio_t* f)"
+.Ss " int sfputu(Sfio_t* f, Sfulong_t v)"
+These functions read and write \f5Sfulong_t\fP values
+in a compact variable-length portable format.
+Portability across a write architecture and a read architecture
+requires that the bit order in a byte is the same on both architectures and
+the written value is storable in an \f5Sfulong_t\fP on the read architecture.
+\f5sfgetu()\fP returns the value read or \f5-1\fP on error.
+\f5sfputu()\fP returns the number of bytes written or \f5-1\fP on error.
+See also \f5sfulen()\fP.
+
+.Ss " Sflong_t sfgetl(Sfio_t* f)"
+.Ss " int sfputl(Sfio_t* f, Sflong_t v)"
+These functions are similar to \f5sfgetu()\fP and \f5sfputu()\fP
+but for reading and writing (signed) \f5Sflong_t\fP values.
+See also \f5sfllen()\fP.
+
+.Ss " Sfdouble_t sfgetd(Sfio_t* f)"
+.Ss " int sfputd(Sfio_t* f, Sfdouble_t v)"
+These functions read and write \f5Sfdouble_t\fP values.
+In this case, portability depends on the input and output architectures
+having the same floating point value representation.
+Values are coded and decoded using \f5ldexp(3)\fP and \f5frexp(3)\fP
+so they are constrained to the sizes supported by these functions.
+See also \f5sfdlen()\fP.
+
+.Ss " char* sfgetr(Sfio_t* f, int rsc, int type)"
+This function reads a record of data ending in the record separator \f5rsc\fP.
+After \f5sfgetr()\fP returns, the length of the record even if it is incomplete
+can be retrieved with \f5sfvalue()\fP.
+\f5sfgetr()\fP returns the record on success and \f5NULL\fP on error.
+See also \f5sfmaxr()\fP for limiting the amount of data read to construct a record.
+
+The \f5type\fP argument is composed of some subset of the below bit flags:
+.Tp
+\f5SF_STRING\fP:
+A null byte will replace the record separator to make the record into a C string.
+Otherwise, the record separator is left alone.
+.Tp
+\f5SF_LOCKR\fP:
+Upon successfully obtaining a record \f5r\fP,
+the stream will be locked from further access until it is released with
+a call \f5sfread(f,r,0)\fP.
+.Tp
+\f5SF_LASTR\fP:
+This should be used only after a failed \f5sfgetr()\fP to retrieve
+the last incomplete record. In this case, \f5rsc\fP is ignored.
+
+.Ss " ssize_t sfputr(Sfio_t* f, const char* s, int rsc)"
+This function writes the null-terminated string \f5s\fP to \f5f\fP.
+If \f5rsc\fP is non-negative, \f5(unsigned char)rsc\fP is output after the string.
+\f5sfputr()\fP returns the number of bytes written or \f5-1\fP on failure.
+
+.Ss " Sfoff_t sfmove(Sfio_t* fr, Sfio_t* fw, Sfoff_t n, int rsc)"
+This function moves objects
+from input stream \f5fr\fP to output stream \f5fw\fP.
+\f5sfmove()\fP returns the number of objects moved or \f5-1\fP on failure.
+
+An object can be either a byte if the record separator argument
+\f5rsc\fP is negative or a record of \f5rsc\fP is non-negative.
+In the latter case, a record is incomplete if it does not end in \f5rsc\fP.
+Generally speaking, a stream can have at most one incomplete record.
+If \f5n\fP is negative, all complete objects of \f5fr\fP will be moved.
+Otherwise, \f5n\fP indicates the number of objects to move.
+If either \f5fr\fP or \f5fw\fP is \f5NULL\fP, it acts
+as if it is a stream corresponding to \f5/dev/null\fP,
+the UNIX device that has no read data and throws away any write data.
+For example, the call \f5sfmove(f,(Sfio_t*)0,(Sfoff_t)(-1),'\en')\fP
+counts the number of complete lines in stream \f5f\fP.
+
+.Ss " ssize_t sfread(Sfio_t* f, Void_t* buf, size_t n)"
+This function reads up to \f5n\fP bytes from \f5f\fP into buffer \f5buf\fP.
+It returns the number of bytes actually read or \f5-1\fP on error.
+
+.Ss " ssize_t sfwrite(Sfio_t* f, const Void_t* buf, size_t n)"
+This function writes \f5n\fP bytes from \f5buf\fP to \f5f\fP.
+If \f5f\fP is \f5SF_STRING\fP, and the buffer is not large enough,
+an \f5SF_WRITE\fP exception shall be raised.
+\f5sfwrite()\fP returns the number of bytes written or \f5-1\fP on failure.
+
+.Ss " Sfoff_t sfseek(Sfio_t* f, Sfoff_t offset, int type)"
+This function sets a new I/O position for \f5f\fP.
+It returns the new position or \f5-1\fP on failure.
+
+If the stream is a \f5SF_STRING\fP stream and the new
+address is beyond the current buffer extent,
+an \f5SF_SEEK\fP exception will be raised (see \f5sfdisc()\fP).
+
+The new position is determined based on \f5offset\fP and
+\f5type\fP which is composed from the bit flags:
+.Tp
+\f50\fP or \f5SEEK_SET\fP:
+\f5offset\fP is the desired position.
+.Tp
+\f51\fP or \f5SEEK_CUR\fP:
+\f5offset\fP is relative to the current position (see \f5SF_PUBLIC\fP below).
+.Tp
+\f52\fP or \f5SEEK_END\fP:
+\f5offset\fP is relative to the physical end of file.
+.Tp
+\f5SF_SHARE\fP:
+The stream is treated as if it has the control bit \f5SF_SHARE\fP on.
+This implies that a system call seek will be done to ensure that the
+location seeking to is valid.
+.Tp
+\f5SF_PUBLIC\fP:
+The stream is treated as if it has the control bit \f5SF_PUBLIC\fP on.
+If the physical file position has changed from its last known location,
+the current position is taken as the new physical position.
+Otherwise, the current position is the logical stream position.
+
+.Ss " Void_t* sfreserve(Sfio_t* f, ssize_t n, int type)"
+This function reserves a data block from the stream \f5f\fP.
+It returns the reserved data block on success and \f5NULL\fP on failure.
+
+If \f5f\fP is a \f5SF_READ\fP stream, the data block is a segment of input data.
+If \f5f\fP is a \f5SF_WRITE\fP stream, the data block is a buffer
+suitable for writing output data.
+For consistency, if \f5f\fP is opened with \f5SF_READ|SF_WRITE\fP,
+it will normally be treated as if it is a \f5SF_READ\fP stream
+(see \f5sfset()\fP for forcing a particular mode) but the returned
+buffer can also be written into (more below).
+However, it is possible to bias to \f5SF_WRITE\fP when the \f5type\fP
+argument is non-negative by adding the \f5SF_WRITE\fP bit \f5type\fP.
+In any case, a reserved data block is guaranteed to be valid only until
+a future access to the stream \f5f\fP.
+
+When \f5f\fP is \f5SF_READ\fP, \f5SF_SHARE\fP and unseekable,
+\f5sfreserve()\fP will attempt to peek at input data without
+consuming it. This enables separate processes to share in reading
+input from unseekable file descriptors (e.g., pipes or devices).
+However, this use of \f5sfreserve()\fP may fail
+on certain platforms that do not properly support
+peeking on unseekable file descriptors.
+
+After a \f5sfreserve()\fP call, whether or not it succeeds,
+\f5sfvalue(f)\fP gives the size of the available data block.
+Any partially reserved data block after a failed \f5sfreserve()\fP
+call can be obtained in another \f5sfreserve()\fP call with the argument
+\f5type\fP being \f5SF_LASTR\fP. The second argument \f5n\fP
+to \f5sfreserve()\fP will be ignored in this case.
+
+A \f5sfreserve()\fP call is successful if it can obtain a data block
+of size at least the absolute value of \f5n\fP.
+For a \f5SF_READ\fP atream, the argument \f5n\fP is treated as follows:
+.Tp
+\f5n < 0\fP:
+\f5sfreserve()\fP attempts to get \fIat least\fP \f5|n|\fP bytes
+into the buffer.
+.Tp
+\f5n == 0\fP:
+If the argument \f5type\fP is \f50\fP,
+\f5sfreserve()\fP attempts to get \fIat least\fP \f51\fP byte into the buffer
+but does not consume it (as consistent with \f5n == 0\fP).
+If \f5type != 0\fP, no attempt will be made to read data into the buffer.
+For example, the call \f5sfreserve(f, 0, -1)\fP only returns the buffer status,
+i.e., size of existing buffered data and pointer to such data, if any.
+The call \f5sfreserve(f, 0, SF_LOCKR)\fP is similar but also locks the stream.
+.Tp
+\f5n > 0\fP:
+\f5sfreserve()\fP will use attempt to get \fIat most\fP \f5n\fP bytes into
+the buffer. Further, if \f5type == \f5SF_LOCKR\fP (see below), read attempts
+end on a positive amount.
+
+For a successful reservation, the argument \f5type\fP dictates treatment
+as follows:
+.Tp
+\f5type == SF_LASTR\fP:
+After a \f5sfreserve()\fP call with \f5type != SF_LOCKR\fP fails,
+there may be some left over data not accessible via conventional Sfio calls.
+Immediately after such a failed call,
+another call to \f5sfreserve\fP with \f5type == SF_LASTR\fP will return any left over
+data and also advance the stream I/O position by the amount of returned data.
+.Tp
+\f5type < 0\fP:
+If \f5n > 0\fP, the stream I/O position is advanced by \f5n\fP.
+If \f5n < 0\fP, the stream I/O position is advanced by the amount
+of available data.
+For example, a successful \f5sfreserve(f, -1, -1)\fP call will return a
+buffer of data and simultanously advance the stream I/O position by the amount
+indicated by \f5sfvalue(f)\fP.
+.Tp
+\f5type == SF_LOCKR\fP:
+The stream I/O position remains unchanged.
+In addition, \f5f\fP will be locked from further access.
+As appropriate to the stream type (\f5SF_READ\fP, \f5SF_WRITE\fP or both),
+\f5f\fP can be unlocked later
+with one of \f5sfread(f,rsrv,size)\fP or \f5sfwrite(f,rsrv,size)\fP
+where \f5rsrv\fP is the reserved data block and \f5size\fP is the amount of
+data to be consumed. For example, if \f5f\fP is a locked \f5SF_READ\fP stream,
+the call \f5sfread(f,rsrv,1)\fP will reopen the stream and simultaneously
+advance the stream I/O position by \f51\fP.
+Finally, a stream opened for both reading and writing
+can release the lock with either call (with associated operational semantics!)
+For example, the below code reads 10 bytes of data from a stream
+opened with both \f5SF_READ\fP and \f5SF_WRITE\fP, modifies the data in place,
+then rewrites the new data back to the stream:
+
+.nf
+.ft 5
+ rsrv = sfreserve(f, 10, 1);
+ for(i = 0; i < 10; ++i)
+ rsrv[i] = toupper(rsrv[i]);
+ sfwrite(f, rsrv, 10);
+.ft 1
+.fi
+
+.ne 6
+.PP
+.Ss "DATA FORMATTING"
+.PP
+Data printing and scanning are done via the
+\f5sfprintf()\fP and \f5sfscanf()\fP family of functions.
+These functions are similar to their
+ANSI-C \f5fprintf()\fP and \f5fscanf()\fP counterparts.
+However, the Sfio versions have been extended for both portability and generality.
+In particular, a notion of a formatting environment stack is introduced.
+Each formatting element on the stack
+defines a separate \fIformatting pair\fP of a format specification string,
+\f5char* format\fP (the usual second argument in the formatting
+functions), and an argument list, \f5va_list args\fP (the third argument
+in functions \f5sfvprintf()\fP and \f5sfvscanf()\fP).
+A formatting environment element may also specify extension functions
+to obtain or assign arguments and to provide new semantics for pattern processing.
+To simplify the description below, whenever we talk
+about an argument list, unless noted otherwise,
+it is understood that this means either the true
+argument list when there is no extension function or the action to be taken
+by such a function in processing arguments.
+The manipulation of the formatting environment stack is done
+via the pattern \f5!\fP discussed below.
+
+.Ss "%! and Sffmt_t"
+The pattern \f5%!\fP manipulates the formatting environment stack to
+(1) change the top environment to a new environment,
+(2) stack a new environment on top of the current top,
+or (3) pop the top environment.
+The bottom of the environment stack always contains a virtual environment with the
+original formatting pair and without any extension functions.
+
+The top environment of a stack, say \f5fe\fP, is automatically popped whenever
+its format string is completely processed.
+In this case, its event-handling function (if any) is called
+as \f5(*eventf)(f,SF_FINAL,NIL(Void_t*),fe)\fP.
+The top environment
+can also be popped by giving an argument \f5NULL\fP to \f5%!\fP
+or by returning a negative value in an extension function.
+In these cases, the event-handling function is called
+as \f5(*eventf)(f,SF_DPOP,form,fe)\fP where \f5form\fP is the remainder
+of the format string. A negative return value from the event handling function
+will prevent the environment from being popped.
+
+A formatting environment is a structure of type \f5Sffmt_t\fP
+which contains the following elements:
+
+.nf
+.ft 5
+ Sffmtext_f extf; /* extension processor */
+ Sffmtevent_f eventf; /* event handler */
+
+ char* form; /* format string to stack */
+ va_list args; /* corresponding arg list */
+
+ int fmt; /* pattern being processed */
+ ssize_t size; /* object size */
+ int flags; /* formatting control flags */
+ int width; /* width of field */
+ int precis; /* precision required */
+ int base; /* conversion base */
+
+ char* t_str; /* extfdata string */
+ int n_str; /* length of t_str */
+.ft 1
+.fi
+
+The first four elements of \f5Sffmt_t\fP must be defined by the application
+before the structure is passed to a formatting function.
+The two function fields should not be changed during processing.
+Other elements of \f5Sffmt_t\fP are set by the respective formatting function
+before it calls the extension function \f5Sffmt_t.extf\fP and, subsequently,
+can be modified by this function to redirect formatting or scanning.
+For example, consider a call from a \f5sfprintf()\fP function to process an
+unknown pattern \f5%t\fP (which we may take to mean ``time'') based on a
+formatting environment \f5fe\fP.
+\f5fe->extf\fP may reset \f5fe->fmt\fP to `\f5d\fP' upon returing
+to cause \f5sfprintf()\fP to process the value being formatted as an integer.
+
+Below are the fields of \f5Sffmt_t\fP:
+.Tp
+\f5extf\fP:
+\f5extf\fP is a function to extend scanning and formatting patterns.
+Its usage is discussed below.
+.Tp
+\f5eventf\fP:
+This is a function to process events as discussed earlier.
+.Tp
+\f5form\fP and \f5args\fP:
+This is the formatting pair of a specification string and corresponding argument list.
+When an environment \f5fe\fP is being inserted into the stack,
+if \f5fe->form\fP is \f5NULL\fP, the top environment is changed to \f5fe\fP
+and its associated extension functions
+but processing of the current formatting pair continues.
+On the other hand, if \f5fe->form\fP is not \f5NULL\fP,
+the new environment is pushed onto the stack
+so that pattern processing will start with the new formatting pair as well as
+any associated extension functions.
+During processing, whenever \f5extf\fP is called,
+\f5form\fP and \f5args\fP will be set to the current values of
+the formatting pair in use.
+.Tp
+\f5fmt\fP:
+This is set to the pattern being processed or one of '.', 'I', '('.
+.Tp
+\f5size\fP:
+This is the size of the object being processed.
+.Tp
+\f5flags\fP:
+This is a collection of bits defining the formatting flags specified for the pattern.
+The bits are:
+
+\f5SFFMT_LEFT\fP: Flag \f5-\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_SIGN\fP: Flag \f5+\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_BLANK\fP: Flag \fIspace\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_ZERO\fP: Flag \f50\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_THOUSAND\fP: Flag \f5'\fP in \f5sfprintf()\fP.
+
+\f5SFFMT_LONG\fP: Flag \f5l\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_LLONG\fP: Flag \f5ll\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_SHORT\fP: Flag \f5h\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_LDOUBLE\fP: Flag \f5L\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_IFLAG\fP: flag \f5I\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_JFLAG\fP: flag \f5j\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_CENTER\fP: flag \f5=\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_CHOP\fP: flag \f5-\fP in \fIprecis\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_ALTER\fP: Flag \f5#\fP in \f5sfprintf()\fP and \f5sfscanf()\fP.
+
+\f5SFFMT_SKIP\fP: Flag \f5*\fP in \f5sfscanf()\fP.
+
+\f5SFFMT_ARGPOS\fP: This indicates argument processing for \f5pos$\fP.
+
+\f5SFFMT_VALUE\fP: This is set by \f5fe->extf\fP
+to indicate that it is returning a value to be formatted or
+the address of an object to be assigned.
+
+.Tp
+\f5width\fP:
+This is the field width.
+.Tp
+\f5precis\fP:
+This is the precision.
+.Tp
+\f5base\fP:
+This is the conversion base.
+.Tp
+\f5t_str\fP and \f5n_str\fP:
+This is the type string and its size.
+
+.Ss " int (*Sffmtext_f)(Sfio_t* f, Void_t* v, Sffmt_t* fe)"
+This is the type of the extension function \f5fe->extf\fP to process
+patterns and arguments.
+Arguments are always processed in order and
+\f5fe->extf\fP is called exactly once per argument.
+Note that, when \f5pos$\fP (below) is not used anywhere in a format string,
+each argument is used exactly once per a corresponding pattern.
+In that case, \f5fe->extf\fP is called
+as soon as the pattern is recognized and before any scanning or formatting.
+On the other hand, when \f5pos$\fP is used in a format string,
+an argument may be used multiple times.
+In this case, all arguments shall be processed in order
+by calling \f5fe->extf\fP exactly once per argument before any pattern processing.
+This case is signified by the flag \f5SFFMT_ARGPOS\fP in \f5fe->flags\fP.
+
+In addition to the predefined formatting patterns and other application-defined
+patterns, \f5fe->extf\fP may be called with \f5fe->fmt\fP being one
+of `\f5(\fP' (left parenthesis), `\f5.\fP' (dot), and `\f5I\fP'.
+
+The left parenthesis requests a string to be used as the \f5extfdata\fP string discussed below.
+In this case, upon returning, \f5fe->extf\fP should set the \f5fe->size\fP field
+to be the length of the string or a negative value to indicate a null-terminated string.
+
+The `\f5I\fP' requests an integer to define the object size.
+
+The dot requests an integer for width, precision, base, or a separator.
+In this case, the \f5fe->size\fP field will indicate how many dots have appeared
+in the pattern specification. Note that, if the actual conversion pattern is 'c' or 's',
+the value \f5*form\fP will be one of these characters.
+.Tp
+\f5f\fP:
+This is the input/output stream in the calling formatting function.
+During a call to \f5fe->extf\fP, the stream shall be unlocked
+so that \f5fe->extf\fP can read from or write to it as appropriate.
+.Tp
+\f5v\fP:
+For both \f5sfscanf()\fP and \f5sfprintf()\fP functions,
+\f5v\fP points to a location suitable for storing any scalars or pointers.
+On return, \f5fe->extf\fP treats \f5v\fP as discussed below.
+.Tp
+\f5fe\fP:
+This is the current formatting environment.
+.PP
+The return value \f5rv\fP of \f5fe->extf\fP directs further processing.
+There are two cases.
+When \f5pos$\fP is present, a negative return value means to ignore \f5fe\fP
+in further argument processing while a non-negative return value is treated
+as the case \f5rv == 0\fP below.
+When \f5pos$\fP is not present, \f5fe->extf\fP is called per argument
+immediately before pattern processing and its return values are treated
+as below:
+.Tp
+\f5rv < 0:\fP
+The environment stack is immediately popped.
+.Tp
+\f5rv == 0:\fP
+The extension function has not consumed (in a scanning case) or
+output (in a printing case) data out of or into the given stream \f5f\fP.
+The fields \f5fmt\fP, \f5flags\fP, \f5size\fP,
+\f5width\fP, \f5precis\fP and \f5base\fP of \f5fe\fP
+shall direct further processing.
+
+For \f5sfprintf()\fP functions, if \f5fe->flags\fP
+has the bit \f5SFFMT_VALUE\fP,
+\f5fe->extf\fP should have set \f5*v\fP to the value to be processed;
+otherwise, a value should be obtained from the argument list.
+Likewise, for \f5sfscanf()\fP functions,
+\f5SFFMT_VALUE\fP means that
+\f5*v\fP should have a suitable address; otherwise,
+an address to assign value should be obtained from the argument list.
+
+When \f5pos$\fP is present,
+if \f5fe->extf\fP changes \f5fe->fmt\fP, this pattern shall be used regardless of
+the pattern defined in the format string. On the other hand, if \f5fe->fmt\fP
+is unchanged by \f5fe->extf\fP, the pattern in the format string is used.
+In any case, the effective pattern should be one of the standardly defined pattern.
+Otherwise, it shall be treated as unmatched.
+.Tp
+\f5rv > 0:\fP
+The extension function has accessed the stream \f5f\fP
+to the extent of \f5rv\fP bytes.
+Processing of the current pattern ceases except that,
+for scanning functions, if \f5fe->flags\fP does not contain
+the bit \f5SFFMT_SKIP\fP, the assignment count shall increase by 1.
+
+.Ss "void va_copy(va_list to, va_list fr)"
+This macro function portably copies the argument list \f5fr\fP to
+the argument list \f5to\fP. It should be used to set the field \f5Sffmt_t.args\fP.
+
+.Ss "long sffmtversion(Sffmt_t* fe, int type)"
+This macro function initializes
+the formatting environment \f5fe\fP with a version number if \f5type\fP is
+non-zero. Otherwise, it returns the current value of the version number of \f5fe\fP.
+This is useful for applications to find out
+when the format of the structure \f5Sffmt_t\fP changes.
+Note that the version number corresponds to the Sfio version number
+which is defined in the macro value \f5SFIO_VERSION\fP.
+
+.Ss " int sfprintf(Sfio_t* f, const char* format, ...);"
+.Ss " char* sfprints(const char* format, ...);"
+.Ss " char* sfvprints(const char* format, va_list args);"
+.Ss " ssize_t sfaprints(char** sp, const char* format, ...);"
+.Ss " ssize_t sfvaprints(char** sp, const char* format, va_list args);"
+.Ss " int sfsprintf(char* s, int n, const char* format, ...)"
+.Ss " int sfvsprintf(char* s, int n, const char* format, va_list args);"
+.Ss " int sfvprintf(Sfio_t* f, const char* format, va_list args);"
+These functions format output data.
+\f5sfprintf()\fP and \f5sfvprintf()\fP write to output stream \f5f\fP.
+\f5sfsprintf()\fP and \f5sfvsprintf()\fP write to buffer \f5s\fP
+which is of size \f5n\fP.
+\f5sfprints()\fP and \f5sfvprints()\fP construct data in some Sfio-defined buffer.
+\f5sfaprints()\fP and \f5sfvaprints()\fP are similar to \f5sfprints()\fP
+and \f5sfvprints()\fP
+but they return a string constructed via \f5malloc()\fP in \f5*sp\fP
+and expect this string to be freed by the caller when no longer needed.
+\f5sfvprintf()\fP is the underlying primitive for the other functions.
+Except for \f5sfprints()\fP and \f5sfvprints()\fP
+which return a null-terminated string or \f5NULL\fP,
+other functions return the number of output bytes or \f5-1\fP on failure.
+
+The length of string constructed by \f5sfprints()\fP, \f5sfsprintf()\fP, or
+\f5sfvsprintf()\fP can be retrieved by \f5sfslen()\fP.
+.PP
+The standard patterns are:
+\f5n, s, c, %, h, i, d, p, u, o, x, X, g, G, e, E, f\fP and \f5!\fP.
+Except for \f5!\fP which shall be described below,
+see the ANSI-C specification of \f5fprintf(3)\fP for details on the other patterns.
+Let \f5z\fP be some pattern type. A formatting pattern is defined as below:
+
+.nf
+.ft 5
+ %[pos$][flag][width][.precision[.base]][(extfdata)]z
+.ft 1
+.fi
+
+.Tp
+\f5pos$\fP:
+A pattern can specify which argument in the argument list to use.
+This is done via \f5pos$\fP where \f5pos\fP is the argument position.
+Arguments are numbered so that the first argument after \f5format\fP is at position 1.
+If \f5pos\fP is not specified, the argument following the most recently used one
+will be used.
+The pattern \f5%!\fP (see below) cannot be used subsequent to a usage of \f5pos$\fP.
+Doing so may cause unexpected behaviors.
+.Tp
+\f5flag\fP:
+The flag characters are
+\f5h\fP, \f5hh\fP, \f5l\fP, \f5ll\fP, \f5L\fP, \f5I\fP, \f5j\fP, \f5t\fP, \f5z\fP,
+\f5\-\fP, \f5+\fP, \fIspace\fP, \f50\fP, \f5'\fP, \f5=\fP and \f5#\fP.
+
+Flag \f5I\fP defines the size or type of the object being formatted.
+There are two cases: (1) \f5I\fP by itself and (2) \f5I\fP
+followed by either a decimal number or `*'.
+
+In the first case, for integer and floating point patterns,
+the object type is taken to be the largest appropriate type
+(i.e., one of \f5Sflong_t\fP, \f5Sfulong_t\fP or \f5Sfdouble_t\fP).
+For conversion specifiers \f5s\fP and \f5c\fP, the flag is ignored.
+
+In the second case, a given decimal value would define a size while
+`*' would cause the size to be obtained from the argument list.
+Then, if the conversion specifier is \f5s\fP, this size defines the
+length of the string or strings being formatted (see the discussion of \f5base\fP below).
+For integer and floating point patterns,
+the size is used to select a type from one of the below lists as
+indicated by the conversion specifier:
+
+.nf
+.ft 5
+ Sflong_t, long, int, short
+ Sfulong_t, unsigned long, unsigned int, unsigned short
+ Sfdouble_t, double, float
+.ft 1
+.fi
+
+The selection algorithm always matches types from left to right in any given list.
+Although selection is generally based on sizes in bytes,
+for compatibility with Microsoft-C, the size 64
+is matched with an appropriate type with the same number of bits, if any.
+If the given size does not match any of the listed types,
+it shall match one of \f5int\fP, \f5unsigned int\fP, and \f5double\fP
+as defined by the formatting pattern.
+
+Below are a few examples of using the \f5I\fP flag.
+The first example prints an \f5Sflong_t\fP integer.
+This example is actually not portable and
+only works on platforms where \f5sizeof(Sflong_t)\fP is 8.
+The second example shows how to that portably.
+The third example specifies printing a string of length 16.
+This length shall be used regardless of whether or not the given string
+is shorter or longer than 16.
+The last example shows the use of the pattern \f5%n\fP to assign the amount
+of data already output into a \f5short\fP integer \f5n_output\fP.
+
+.nf
+.ft 5
+ sfprintf(sfstdout,"%I8d", Sflong_obj);
+ sfprintf(sfstdout,"%I*d", sizeof(Sflong_obj), Sflong_obj);
+ sfprintf(sfstdout,"%I*s", 16, s);
+ sfprintf(sfstdout,"%d%I*n", 1001, sizeof(short), &n_output);
+.ft 1
+.fi
+
+Flags \f5h\fP, \f5l\fP, \f5j\fP and \f5L\fP are the ANSI-C conventions to
+select the types of input objects.
+For example, \f5%hd\fP indicates a \f5short int\fP,
+while \f5%ld\fP indicates a \f5long int\fP.
+
+Flag \f5hh\fP addresses the byte value types, i.e., \f5char\fP and \f5unsigned char\fP.
+
+Flags \f5z\fP, \f5t\fP and \f5j\fP address respectively
+the types \f5size_t\fP, \f5ptrdiff_t\fP and \f5Sflong_t\fP.
+
+Flags \f5ll\fP and \f5L\fP address respectively
+the largest integer and floating value types, i.e.,
+\f5Sfulong_t\fP, \f5Sflong_t\fP, and \f5Sfdouble_t\fP.
+
+Flag \f5-\fP left-justifies data within the field (otherwise, right-justification).
+
+Flag \f5+\fP means that a signed conversion will always begin with a plus or minus sign.
+
+Flag \fIspace\fP is ignored if \f5+\fP is specified; otherwise,
+it means that if the first character of a signed conversion
+is not a sign or if the result is empty, a space will be prepended.
+
+Flag \f50\fP means padding with zeros on the left.
+
+Flag \f5'\fP outputs thousands-separator used by the current locale.
+\f5setlocale(3)\fP should have been used to set the desired locale.
+
+Flag \f5=\fP centers data within the field.
+
+Flag \f5#\fP indicates an alternative format processing.
+For \f5%o\fP, the first digit is always a zero.
+For \f5%x\fP and \f5%X\fP, a non-zero result will have a prefix
+\f50x\fP or \f50X\fP. For \f5%e\fP, \f5%E\fP, \f5%f\fP, \f5%g\fP, and \f5%G\fP,
+the result always contains a decimal point. For \f5%g\fP and \f5%G\fP,
+trailing zeros will not be removed. For \f5%d\fP, \f5%i\fP and \f5%u\fP,
+the form is \fIbase#number\fP where \fIbase\fP is the conversion base
+and \fInumber\fP is represented by digits for this \fIbase\fP.
+For example, a base \f52\fP conversion with \f5%#..2d\fP for \f510\fP
+is \f52#1010\fP instead of \f51010\fP as printed with \f5%..2d\fP.
+Finally, for \f5%c\fP, bytes will be printed in the C format.
+For example, when the ASCII character set is used,
+the byte value 10 will be printed as \f5\\n\fP while 255 is printed
+as \f5\\377\fP.
+.Tp
+\f5width\fP:
+This defines the width of the printing field. A value to be printed will
+be justified and padded if necessary to fill out the field width.
+.Tp
+\f5precis\fP:
+After a first dot appears, an integral value defines a precision.
+For floating point value patterns, precision is the number of precision digits.
+For \f5%c\fP, precision defines the number of times to repeat the
+character being formatted.
+For \f5%s\fP, precision defines the maximum number of characters to output;
+-\f5precis\fP also defines the maximum number of characters to output, but
+retains the rightmost \f5precis\fP characters.
+.Tp
+\f5base\fP:
+This is defined after exactly two dots have appeared.
+
+For \f5%i\fP, \f5%d\fP, and \f5%u\fP,
+\f5base\fP should be an integer value in the inclusive range \f5[2,64]\fP
+and defines a conversion base.
+If \f5base\fP is not in this range, it is defined to be \f510\fP.
+The digits to represent numbers are:
+
+.nf
+.ft 5
+ 01234567890
+ abcdefghijklmnopqrstuvwxyz
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
+.ft 1
+.fi
+
+For \f5%s\fP and \f5%c\fP, \f5base\fP defines a separator.
+Then, for \f5%s\fP, the input argument is taken to be a NULL-terminated array of strings
+while, for \f5%c\fP, this is a null-terminated array of characters.
+The strings or characters will be formatted one of a time based
+on the usual width and precision rules.
+After each formatted string or character, except for the last one,
+the separator \f5base\fP is output if it is a non-zero.
+
+There are further restrictions on the syntax of \f5%s\fP and \f5%c\fP when
+a separator is defined.
+Below are the legitimate sequences for \f5%s\fP and \f5%c\fP after the second dot:
+
+.nf
+\f5 s c\fP
+\f5 *s *c\fP
+\f5 \fP\fIz\fP\f5s \fP\fIz\fP\f5c\fP
+.fi
+
+In the first case, no separator is defined so \f5base\fP is set to zero.
+In the second case, \f5base\fP is obtained from the argument list.
+In the third case, the character \fIz\fP
+must be non-alphanumeric and \f5base\fP will be set to this character.
+
+The below example shows both the call and the result
+of printing a \f5NULL\fP-terminated array
+of three strings \f5apple\fP, \f5orange\fP, and \f5grape\fP:
+
+.nf
+.ft 5
+ sfprintf(sfstdout,"|%8..:s|",list);
+ | apple: orange: grape|
+.ft 1
+.fi
+
+.Tp
+\f5(extfdata)\fP:
+This defines a string \f5extfdata\fP
+to be passed to the extension function \f5Sffmt_t.extf\fP.
+Parentheses shall be balanced.
+If \f5extfdata\fP is \f5*\fP, the string is obtained from the argument list.
+
+.PP
+.Ss " int sfscanf(Sfio_t* f, const char* format, ...)"
+.Ss " int sfsscanf(const char* s, const char* format, ...)"
+.Ss " int sfvsscanf(const char* s, const char* format, va_list args)"
+.Ss " int sfvscanf(Sfio_t* f, const char* format, va_list args)"
+These functions scan data items.
+\f5sfscanf()\fP scans from the input stream \f5f\fP
+while \f5sfsscanf()\fP and \f5sfvsscanf()\fP
+scan from the null-terminated string \f5s\fP.
+\f5sfvscanf()\fP is the underlying primitive that performs the actual scanning.
+Item types are determined from patterns in string \f5format\fP.
+These functions return
+the number of items successfully scanned or \f5-1\fP on error.
+.PP
+A white space character (blank, tab, or new-line) in \f5format\fP
+normally matches a maximal sequence of input white space characters.
+However, if the input stream is in \f5SF_LINE\fP mode (see \f5sfset()\fP),
+a new-line character only matches white spaces up to an input new-line character.
+This is useful to avoid blocking when scanning typed inputs.
+.PP
+The standard scan patterns are:
+\f5i, d, u, o, x, X, p, n, f, e, E, g, G, c, %, s, []\fP and \f5!\fP.
+Except for \f5!\fP which shall be described below,
+see the ANSI-C specification of \f5fscanf(3)\fP for details on other patterns.
+Let \f5z\fP be some pattern type. A formatting pattern is specified as below:
+
+.nf
+.ft 5
+ %[*][pos$][width][.width.base][(extfdata)][flag]z
+.ft 1
+.fi
+
+.Tp
+\f5pos$\fP:
+A pattern can specify which argument in the argument list to use.
+This is done via \f5pos$\fP where \f5pos\fP is the argument position.
+Arguments are numbered so that the first argument after \f5format\fP is at position 1.
+If \f5pos\fP is not specified, the argument following the most recently used one
+will be used.
+The pattern \f5%!\fP (see below) cannot be used subsequent to a usage of \f5pos$\fP.
+.Tp
+\f5*:\fP
+This discards the corresponding scanned item.
+.Tp
+\f5width\fP and \f5base\fP:
+\f5width\fP defines the maximum number of bytes to scan
+and \f5base\fP defines the base of an integral value being scanned.
+The `.' (dot) notation also allows specifying a `*' (star) to obtain
+the value from the argument list. The below example specifies scanning
+4 bytes to obtain the value of an integer in base 10. At the end of scanning,
+the variable \f5v\fP should have the value \f51234\fP.
+
+.nf
+.ft 5
+ sfsscanf("12345678","%.*.*d", 4, 10, &v);
+.ft 1
+.fi
+
+.Tp
+\f5(extfdata)\fP:
+This defines a string \f5extfdata\fP
+to be passed to the extension function \f5Sffmt_t.extf\fP.
+Parentheses shall be balanced.
+If \f5extfdata\fP is \f5*\fP, the string is obtained from the argument list.
+.Tp
+\f5flag:\fP
+This is \f5#\fP, \f5I\fP, or some sequence of \f5h\fP, \f5l\fP, and \f5L\fP.
+
+Flag \f5#\fP is significant for pattern \f5%i\fP and \f5%[\fP.
+For \f5%i\fP, it means that the \f5#\fP symbol does not have its usual
+meaning in an input sequence \f5base#value\fP.
+For example, the scanning result of \f5%#i\fP on input \f52#1001\fP is \f52\fP
+and the next \f5sfgetc()\fP call will return \f5#\fP.
+For \f5%[\fP, if the next character in the input stream does not match
+the given scan set of characters, \f5#\fP causes a match to a null string
+instead of a failure.
+
+Flag \f5I\fP defines the size or type of the object being formatted.
+There are two cases: (1) \f5I\fP by itself and (2) \f5I\fP
+followed by either a decimal number or `*'.
+
+In the first case, for integer and floating point patterns,
+the object type is taken to be the largest appropriate type
+(i.e., one of \f5Sflong_t\fP, \f5Sfulong_t\fP or \f5Sfdouble_t\fP).
+For string patterns such as \f5%s\fP, the flag is ignored.
+
+In the second case, a given decimal value would define a size while
+`*' would cause the size to be obtained from the argument list.
+For string patterns such as \f5%s\fP or \f5%[\fP, this size defines the
+length of the buffer to store scanned data.
+Specifying a buffer size only limits the amount of data copied into the buffer.
+Scanned data beyond the buffer limit will be discarded.
+For integer and floating point patterns,
+the size is used to select a type from one of the below lists as
+indicated by the conversion specifier:
+
+.nf
+.ft 5
+ Sflong_t, long, int, short
+ Sfulong_t, unsigned long, unsigned int, unsigned short
+ Sfdouble_t, double, float
+.ft 1
+.fi
+
+The selection algorithm always matches types from left to right in any given list.
+Although selection is generally based on sizes in bytes,
+for compatibility with Microsoft-C, the size 64
+is matched with an appropriate type with the same number of bits, if any.
+If the given size does not match any of the listed types,
+it shall match one of \f5int\fP, \f5unsigned int\fP, and \f5double\fP
+as indicated by the formatting pattern.
+
+Below are examples of using the \f5I\fP flag.
+The first example scans a 64-bit integer.
+The second scans some floating point value
+whose size is explicitly computed and given.
+The last example scans a string into a buffer with the given size 128.
+Note that if the scanned string is longer than 127, only the first 127
+bytes shall be copied into the buffer. The rest of the scanned data
+shall be discarded.
+
+.nf
+.ft 5
+ sfscanf(sfstdin,"%I64d", &int64_obj);
+ sfscanf(sfstdin,"%I*f", sizeof(float_obj), &float_obj);
+ sfscanf(sfstdin,"%I*s", 128, buffer);
+.ft 1
+.fi
+
+Flags \f5h\fP, \f5l\fP, and \f5L\fP are the ANSI-C conventions
+for indicating the type of a scanned element.
+For example, \f5%hd\fP means scanning a \f5short int\fP.
+The flags \f5ll\fP and \f5L\fP mean respectively scanning an
+integer or a floating point value with largest size
+(i.e, \f5Sflong_t\fP or \f5Sfdouble_t\fP).
+.PP
+The \f5%i\fP, \f5%d\fP and \f5%u\fP patterns scan numbers in bases
+from \f52\fP to \f564\fP.
+\f5%i\fP scans integral values in self-describing formats.
+Except for octal, decimal and hexadecimal numbers with the usual formats,
+numbers in general bases are assumed to be of the form: \fIbase#value\fP
+where \fIbase\fP is a number in base 10 and \fIvalue\fP
+is a number in the given base.
+For example, \f52#1001\fP is the binary representation of the decimal value \f59\fP.
+If \fIbase\fP is \f536\fP or less,
+the digits for \fIvalue\fP can be any combination of \f5[0-9], [a-z], [A-Z]\fP
+where upper and lower case digits are not distinguishable.
+If \fIbase\fP is larger than \f536\fP, the set of digits is:
+
+.nf
+.ft 5
+ 0123456789
+ abcdefghijklmnopqrstuvwxyz
+ ABCDEFGHIJKLMNOPQRSTUVWXYZ @_
+.ft 1
+.fi
+
+.PP
+.Ss "BUFFERING, SYNCHRONIZATION"
+.PP
+.Ss " Void_t* sfsetbuf(Sfio_t* f, Void_t* buf, size_t size)"
+This function changes the buffering scheme for the stream \f5f\fP.
+The stream will be synchronized before any buffer modification.
+If a new buffer is successfully set and the old buffer has not been freed,
+\f5sfsetbuf()\fP returns the old buffer. Otherwise, it returns \f5NULL\fP.
+After a \f5sfsetbuf()\fP call,
+\f5sfvalue()\fP returns the size of the returned buffer.
+
+Sfio attempts to read data in blocks likely to be serviced fast by the file system.
+This means block sizes being multiples of a suitable alignment value
+(e.g., 512, 1024 or 8192). By default, the alignment value
+is computed via some internal mechanism depending on the local platform but
+it can also be explicitly set via the call \f5sfsetbuf(f, (Void_t*)f, size)\fP.
+
+In invocations of \f5sfsetbuf()\fP other than the above case,
+the \f5size\fP argument is treated as follows:
+.Tp
+\f5size == SF_UNBOUND\fP:
+Sfio will pick a suitable buffer size.
+If \f5buf\fP is \f5NULL\fP,
+Sfio will also pick a suitable buffering scheme (such as memory mapping.)
+If \f5buf\fP is not \f5NULL\fP, its actual value is ignored
+but the buffer will be allocated via \f5malloc(3)\fP.
+This can be used to avoid memory mapping.
+.Tp
+\f5size > 0\fP:
+This is the suggested size to use for buffering or memory mapping.
+If \f5buf\fP is \f5NULL\fP,
+Sfio will pick a suitable buffering scheme as discussed above.
+If \f5buf\fP is not \f5NULL\fP, then \f5buf\fP and \f5size\fP determine
+a buffer of the given size.
+.Tp
+\f5size == 0\fP:
+If \f5buf\fP is \f5NULL\fP, the stream will be unbuffered.
+If \f5buf\fP is not \f5NULL\fP,
+\f5sfsetbuf()\fP simply returns the stream buffer.
+In this case, no attempt will be made to synchronize the stream.
+
+.Ss " int sfsync(Sfio_t* f)"
+This function synchronizes the logical and physical views of stream \f5f\fP.
+It returns a negative value for failure and \f50\fP for success.
+
+For a \f5SF_WRITE\fP stream, synchronization means to write out any buffered data.
+For a seekable \f5SF_READ\fP file stream,
+the physical file position is aligned with the logical stream position and,
+if \f5SF_SHARE\fP is on, buffered data is discarded.
+If \f5f\fP is \f5NULL\fP, all streams are synchronized.
+If \f5f\fP is the base of a stream stack (see \f5sfstack()\fP),
+all stacked streams are synchronized.
+Note that a stacked stream can only be synchronized this way.
+If \f5f\fP is in a pool (see \f5sfpool()\fP) but not being the head,
+the pool head is synchronized.
+
+If \f5f\fP has flag \f5SF_IOCHECK\fP, the \f5SF_SYNC\fP event is raised
+before and after synchronization. See \f5sfdisc()\fP for details.
+
+.Ss " int sfpoll(Sfio_t** flist, int n, int timeout)"
+This function polls a set of streams to see if I/O operations
+can be performed on them without blocking.
+This is useful for multiplexing I/O over a set of streams.
+If a stream has a discipline, the exception function may be called
+before and after the stream is polled (see \f5sfdisc()\fP for details).
+After a successful \f5sfpoll()\fP call,
+for each ready stream \f5f\fP, \f5sfvalue(f)\fP returns
+a bit combination of \f5SF_READ\fP and \f5SF_WRITE\fP to tell which I/O
+mode is available. If \f5SF_READ\fP is available, an attempt to read
+a byte will not block. If \f5SF_WRITE\fP is available,
+an attempt to flush will not block.
+\f5sfpoll()\fP returns the number of ready streams or \f5-1\fP on failure.
+.Tp
+\f5flist\fP and \f5n\fP:
+\f5flist\fP is an array of \f5n\fP streams to be polled.
+Upon return, ready streams are moved to the front
+of \f5flist\fP in the same relative order.
+.Tp
+\f5timeout\fP:
+This defines an elapse time in milliseconds
+to wait to see if any stream is ready for I/O.
+If \f5timeout\fP is negative, \f5sfpoll()\fP will block until some stream become ready.
+Note that \f5SF_STRING\fP and normal file streams never block
+and are always ready for I/O.
+If a stream with discipline is being polled and
+its readiness is as yet undetermined (e.g., empty buffer,)
+the discipline exception function will be called with \f5SF_DPOLL\fP
+before querying the operating system.
+
+.Ss " Sfio_t* sfpool(Sfio_t* f, Sfio_t* poolf, int mode)"
+This function manipulates pools of streams.
+In a pool, only one stream is at the head and can have buffered data.
+All other streams in the pool will be synchronized.
+A stream becomes head when it is used for some I/O operation.
+\f5sfpool()\fP returns \f5NULL\fP on failure.
+.Tp
+\f5f\fP and \f5poolf\fP:
+If \f5f\fP is \f5NULL\fP,
+\f5sfpool()\fP simply returns the head of the pool containing \f5poolf\fP.
+If \f5f\fP is not \f5NULL\fP and \f5poolf\fP is \f5NULL\fP,
+\f5f\fP is deleted from its pool.
+In this case, if no other stream from the same pool can become head,
+\f5sfpool()\fP will return \f5NULL\fP; otherwise, it returns some stream
+from the remainder of the pool.
+If both \f5f\fP and \f5poolf\fP are not \f5NULL\fP,
+\f5f\fP is moved from its current pool (if any)
+into the same pool with \f5poolf\fP.
+In this case, \f5poolf\fP is returned.
+.Tp
+\f5mode\fP:
+If \f5poolf\fP is already in a pool, \f5mode\fP is ignored.
+Otherwise, \f5mode\fP should be \f50\fP or \f5SF_SHARE\fP.
+A \f5SF_SHARE\fP pool contains streams with \f5SF_WRITE\fP mode.
+In addition, on change to a new head stream,
+buffered write data of the current head
+is transferred to the new head.
+
+.Ss " int sfpurge(Sfio_t* f)"
+This function discards all buffered data
+unless \f5f\fP is a \f5SF_STRING\fP stream.
+Note that if \f5f\fP is a \f5SF_READ\fP stream based on an unseekable device,
+purged data will not be recoverable.
+If \f5f\fP is a \f5sfpopen\fP-stream opened for both read and write,
+data of both the read and write pipe ends will be purged
+(see \f5sfset()\fP to selectively turn off read or write mode
+if one set of data is to be preserved.)
+After purging, if \f5f\fP has flag \f5SF_IOCHECK\fP,
+the event \f5SF_PURGE\fP is raised.
+\f5sfpurge()\fP returns \f5-1\fP for failure and \f50\fP for success.
+
+.PP
+.Ss "DISCIPLINE, EVENT-HANDLING"
+.PP
+A file stream uses the system calls \f5read(2)\fP, \f5write(2)\fP
+and \f5lseek(2)\fP to read, write and position in the underlying file.
+Disciplines enable application-defined I/O methods including exception handling and
+data pre/post-processing.
+
+.Ss " Sfdisc_t* sfdisc(Sfio_t* f, Sfdisc_t* disc)"
+Each stream has a discipline stack whose bottom is a virtual discipline
+representing the actual system calls.
+\f5sfdisc()\fP manipulates the discipline stack of stream \f5f\fP.
+\f5f\fP will be synchronized before any discipline stack manipulation.
+After a successful discipline stack manipulation,
+the stream I/O position (see \f5sfseek()\fP and \f5sftell()\fP)
+and extent (see \f5sfsize()\fP) are updated
+to reflect that defined by the top discipline.
+\f5sfdisc()\fP returns \f5NULL\fP on failure.
+
+If the value of \f5disc\fP is identical to the value of \f5f\fP,
+then the top discipline on the discipline
+stack is returned without any further action.
+An application can then use this feature of \f5sfdisc()\fP
+and the field \f5disc\fP (below) of the discipline structure
+to traverse the entire discipline stack of a stream \f5f\fP as follows:
+
+.nf
+.ft 5
+ for(disc = sfdisc(f, (Sfdisc_t*)f); disc; disc = disc->disc)
+.ft 1
+.fi
+
+If \f5disc\fP is \f5SF_POPDISC\fP or \f5(Sfdisc_t*)0\fP,
+the top element of the stack, if any, is popped and its address is returned.
+Otherwise, \f5disc\fP is pushed onto the discipline stack.
+In this case, if successful, \f5sfdisc()\fP returns
+the discipline that was pushed down.
+
+Note that a discipline can be used on only one stream at a time.
+An application should take care to allocate different discipline
+structures for use with different streams.
+A discipline structure is of the type \f5Sfdisc_t\fP which
+contains the following public fields:
+
+.nf
+.ft 5
+ Sfread_f readf;
+ Sfwrite_f writef;
+ Sfseek_f seekf;
+ Sfexcept_f exceptf;
+ Sfdisc_t* disc;
+.ft 1
+.fi
+
+.PP
+The first three fields of \f5Sfdisc_t\fP specify alternative I/O functions.
+If any of them is \f5NULL\fP, it is inherited
+from a discipline pushed earlier on the stack.
+Note that a file stream always
+has \f5read(2)\fP, \f5write(2)\fP, \f5lseek(2)\fP and \f5NIL(Sfexcept_f)\fP
+as the \fIlogical bottom discipline\fP.
+Arguments to I/O discipline functions
+have the same meaning as that of the
+functions \f5sfrd()\fP, \f5sfwr()\fP and \f5sfsk()\fP described below.
+.PP
+The exception function, \f5(*exceptf)()\fP announces exceptional events during
+I/O operations.
+It is called as \f5(*exceptf)(Sfio_t* f, int type, Void_t* value, Sfdisc_t* disc)\fP.
+Unless noted otherwise, the return value of \f5(*exceptf)()\fP is used as follows:
+.Tp
+\f5<0\fP:
+The on-going operation shall terminate.
+.Tp
+\f5>0\fP:
+If the event was raised due to an I/O error,
+the error has been repaired and the on-going operation shall continue normally.
+For some events, e.g., \f5SF_DPOLL\fP, the return value may also have
+additional meanings.
+.Tp
+\f5=0\fP:
+The on-going operation performs default actions with respect to the raised event.
+For example, on a reading error or reaching end of file, the top stream of a stack
+will be popped and closed and the on-going operation continue with the new top
+stream.
+.PP
+The argument \f5type\fP of \f5(*exceptf)()\fP
+identifies the particular exceptional event:
+.Tp
+\f5SF_LOCKED\fP:
+The stream cannot be accessed.
+Note that this lock state is not related to the mutex lock
+that protects a stream from multiple accesses by different threads
+(see section THREAD SAFETY). Rather, the stream was frozen by
+certain operations such as \f5sfreserve()\fP or \f5sfstack()\fP.
+Thus, a stream can be in this state even if the application is uni-threaded.
+.Tp
+\f5SF_READ\fP, \f5SF_WRITE\fP:
+These events are raised around reading and writing operations.
+
+If \f5SF_IOCHECK\fP is on, \f5SF_READ\fP and \f5SF_WRITE\fP
+are raised immediately before \f5read(2) and write(2)\fP calls.
+In this case, \f5*((ssize_t*)value)\fP is the amount of data to be processed.
+The return value of \f5(*exceptf)()\fP, if negative,
+indicates that the stream is not ready for I/O
+and the calling operation will abort with failure.
+If it is positive, the stream is ready for I/O
+but the amount should be restricted to the amount specified by this value.
+If the return value is zero, the I/O operation is carried out normally.
+
+\f5SF_READ\fP and \f5SF_WRITE\fP are also raised on operation failures.
+In such a case, \f5*((ssize_t*)value)\fP
+is the return value from the failed operation.
+.Tp
+\f5SF_SEEK\fP:
+This event is raised when a seek operation fails.
+.Tp
+\f5SF_NEW\fP, \f5SF_CLOSING\fP (\f5SF_CLOSE\fP), \f5SF_FINAL\fP:
+These events are raised during a stream closing.
+\f5SF_NEW\fP is raised for a stream about to be closed to be renewed (see \f5sfnew()\fP).
+\f5SF_CLOSING\fP is raised for a stream about to be closed.
+\f5SF_FINAL\fP is raised after a stream has been closed and before
+its space is to be destroyed (see \f5sfclose()\fP).
+For these events, a non-zero return value from \f5(*exceptf)()\fP causes
+\f5sfclose()\fP to return immediately with the same value.
+.Tp
+\f5SF_DPUSH\fP, \f5SF_DPOP\fP, \f5SF_DBUFFER\fP:
+Events \f5SF_DPUSH\fP and \f5SF_DPOP\fP are raised when a
+discipline is about to be pushed or popped.
+\f5(Sfdisc_t*)value\fP is the to-be top discipline, if any.
+
+A stream buffer is always synchronized before pushing or popping a discipline.
+If this synchronization fails, \f5SF_DBUFFER\fP will be raised with
+\f5*((size_t*)value)\fP being the amount of data still in the buffer.
+If the return value of \f5exceptf\fP is non-negative,
+the push or pop operation will continue normally;
+otherwise, \f5sfdisc()\fP returns failure.
+.Tp
+\f5SF_DPOLL\fP:
+This event is raised by
+\f5sfpoll()\fP to see if the stream is ready for I/O.
+\f5*((int*)value)\fP indicates a time-out interval to wait.
+A negative return value from the exception function means blocking.
+A zero return value means that \f5sfpoll()\fP should
+query the underlying file descriptor.
+A positive return value means non-blocking. In addition,
+this value will be a bit combination of \f5SF_READ\fP and \f5SF_WRITE\fP
+to indicate what I/O modes are ready.
+.Tp
+\f5SF_READY\fP:
+This event is raised by \f5sfpoll()\fP for each ready stream.
+The third argument to the event handler is an integer composed with
+the two bits \f5SF_READ\fP and \f5SF_WRITE\fP to indicate which
+I/O modes are ready.
+.Tp
+\f5SF_SYNC\fP, \f5SF_PURGE\fP:
+If \f5SF_IOCHECK\fP is set,
+these events are raised respectively for a \f5sfsync()\fP or \f5sfpurge()\fP call.
+In each case, the respective event is raised once before the appropriate
+operation (synchronization or purging) with \f5((int)value)\fP being \f51\fP
+and once after with \f5((int)value)\fP being \f50\fP.
+Note that \f5sfsync()\fP is called for each
+\f5SF_WRITE\fP or \f5SF_SHARE|SF_READ\fP stream on closing.
+
+.Tp
+\f5SF_ATEXIT\fP:
+This event is raised for each open stream before the process exits.
+
+.Ss " int sfraise(Sfio_t* f, int type, Void_t* data)"
+If \f5f\fP is non-\f5NULL\fP, \f5sfraise()\fP calls all exception handlers
+of \f5f\fP with the event \f5type\fP and associated \f5data\fP.
+If an exception handler returns a non-zero value,
+\f5sfraise()\fP immediate returns the same value.
+Application-defined events should start from the value \f5SF_EVENT\fP
+so as to avoid confusion with system-defined events,
+\f5sfraise()\fP returns \f50\fP on success and \f5-1\fP on failure.
+
+If \f5f\fP is \f5NULL\fP, \f5sfraise()\fP iterates over all streams
+and raise events as described above. In this case,
+\f5sfraise()\fP returns \f50\fP on success and a negative value
+on failure. The absolute value of the return value tells how many
+streams failed on raising the given event.
+
+.Ss " ssize_t sfrd(Sfio_t* f, Void_t* buf, size_t n, Sfdisc_t* disc)"
+.Ss " ssize_t sfwr(Sfio_t* f, const Void_t* buf, size_t n, Sfdisc_t* disc)"
+.Ss " Sfoff_t sfsk(Sfio_t* f, Sfoff_t offset, int type, Sfdisc_t* disc)"
+These functions provides safe methods for a discipline I/O function to invoke
+earlier discipline I/O functions and to properly handle exceptions.
+They should not be used in any other context.
+\f5sfrd()\fP and \f5sfwr()\fP return the number of bytes read or written.
+\f5sfsk()\fP returns the new seek position.
+On error, all three functions return a negative value which should be \f5-1\fP
+or the value returned by the exception handler.
+
+.PP
+.Ss "STREAM CONTROL"
+.PP
+.Ss " int sfresize(Sfio_t* f, Sfoff_t size)"
+This function resizes the stream \f5f\P so that its extent is \f5size\fP.
+If the stream corresponds to a file, the file size is set to \f5size\fP
+via the system call \f5ftruncate()\fP.
+When a stream is made larger, the new data space is filled with zero's.
+\f5sfresize()\fP returns \f50\fP on success and a negative value on failure.
+
+.Ss " int sfset(Sfio_t* f, int flags, int set)"
+This function sets control flags for the stream \f5f\fP.
+It returns the previous set of flags or \f50\fP on error.
+
+Settable flags are:
+\f5SF_READ\fP, \f5SF_WRITE\fP, \f5SF_IOCHECK\fP,
+\f5SF_LINE\fP, \f5SF_SHARE\fP, \f5SF_PUBLIC\fP, \f5SF_MALLOC\fP and
+\f5SF_STATIC\fP.
+Note that \f5SF_READ\fP and \f5SF_WRITE\fP can be turned on or off only
+if the stream was opened as \f5SF_READ|SF_WRITE\fP.
+Turning off one of them means that the stream is to be treated exclusively
+in the other mode. It is not possible to turn off both.
+If legal, an attempt to turn on either \f5SF_READ\fP or \f5SF_WRITE\fP
+will cause the stream to be in the given I/O mode.
+.Tp
+\f5set == 0:\fP
+If \f5flags\fP is zero, the current set of flags is simply returned.
+Note that when a stream is first opened, not
+all of its flags are initialized yet (more below). If \f5flags\fP is
+non-zero, an attempt is made to turn off the specified flags.
+.Tp
+\f5set != 0:\fP
+If \f5flags\fP is zero, the stream is initialized if not yet done so.
+Then the current set of flags is returned.
+If \f5flags\fP is non-zero, an attempt is made to turn on the
+specified flags.
+
+.Ss " int sfsetfd(Sfio_t* f, int fd)"
+This function changes the file descriptor of \f5f\fP.
+Before a change is realized,
+\f5(*notify)(f,SF_SETFD,newfd)\fP (see \f5sfnotify()\fP) is called.
+\f5sfsetfd()\fP returns \f5-1\fP on failure and the new file descriptor on success.
+.Tp
+\f5fd >= 0\fP:
+If the current file descriptor is non-negative,
+it will be changed using \f5dup(3)\fP to a value larger or equal to \f5fd\fP.
+Upon a successful change, the previous file descriptor will be closed.
+If the current file descriptor is negative, it will be set to \f5fd\fP and
+the stream will be reinitialized.
+.Tp
+\f5fd < 0\fP:
+The stream is synchronized (see \f5sfsync()\fP) and its
+file descriptor will be set to this value.
+Then, except for \f5sfclose()\fP, the stream will be inaccessible
+until a future \f5sfsetfd()\fP call resets the file descriptor to a non-negative value.
+Thus, \f5sfsetfd(f,-1)\fP can be used to avoid closing the file descriptor
+of \f5f\fP when \f5f\fP is closed.
+
+.Ss " Sfio_t* sfstack(Sfio_t* base, Sfio_t* top)"
+This function stacks or unstacks stream.
+Every stream stack is identified by a base stream
+via which all I/O operations are performed.
+However, an I/O operation always takes effect on the top stream.
+If the top stream reaches the end of file or
+has an unrecoverable error condition,
+it is automatically popped and closed
+(see also \f5sfdisc()\fP for alternative handling of these conditions).
+.Tp
+\f5base\fP:
+This is the base stream of the stack.
+If it is \f5NULL\fP, \f5sfstack()\fP does nothing and returns \f5top\fP.
+.Tp
+\f5top\fP:
+If this is \f5SF_POPSTACK\fP or \f5(Sfio_t*)0\fP,
+the stack is popped and \f5sfstack()\fP returns the popped stream.
+Otherwise, \f5top\fP is pushed on top of the stack identified by \f5base\fP
+and \f5sfstack()\fP returns the \f5base\fP stream.
+
+.Ss " Sfio_t* sfswap(Sfio_t* f1, Sfio_t* f2)"
+This function swaps contents of \f5f1\fP and \f5f2\fP.
+This fails if either stream is in a stream stack but not being a base stream.
+If \f5f2\fP is \f5NULL\fP, a new stream is constructed as a duplicate of \f5f1\fP.
+\f5sfswap()\fP returns \f5f2\fP or \f5f1\fP duplicate on success and
+\f5NULL\fP on failure.
+
+.PP
+.Ss "STREAM INFORMATION"
+.PP
+.Ss " Sfoff_t sfsize(Sfio_t* f)"
+This function returns the size of stream \f5f\fP (see \f5sfnew()\fP).
+If \f5f\fP is not seekable or if its size is not determinable,
+\f5sfsize()\fP returns \f5-1\fP.
+
+.Ss " Sfoff_t sftell(Sfio_t* f)"
+This function returns the current I/O position in stream \f5f\fP.
+Note that if \f5f\fP is \f5SF_APPEND\fP
+and a writing operation was just performed,
+the current I/O position is at the physical end of file.
+If \f5f\fP is unseekable, \f5sftell\fP returns the number of bytes
+read from or written to \f5f\fP.
+See also \f5sfungetc()\fP.
+
+.Ss " ssize_t sfvalue(Sfio_t* f)"
+This function returns the string or buffer length
+for \f5sfreserve()\fP, \f5sfsetbuf()\fP, and \f5sfgetr()\fP.
+
+.Ss " int sffileno(Sfio_t* f)"
+This function returns the file descriptor of stream \f5f\fP.
+
+.Ss " int sfstacked(Sfio_t* f)"
+This function returns a non-zero value
+if stream \f5f\fP has been stacked.
+
+.Ss " int sfeof(Sfio_t* f)"
+.Ss " int sferror(Sfio_t* f)"
+.Ss " int sfclrerr(Sfio_t* f)"
+\f5sfeof()\fP tells whether or not the stream has an end-of-file condition.
+\f5sferror()\fP tells whether or not the stream has an error condition.
+\f5sfclrerr()\fP clears both end-of-file and error conditions.
+The end-of-file and error conditions are also cleared on an I/O operation.
+
+.Ss " int sfclrlock(Sfio_t* f)"
+This function restores the stream back to a normal state.
+This means clearing locks and possibly throwing away unprocessed data.
+As such, this operation is unsafe and should be used with care.
+For example, it may be used before a long jump (\f5longjmp(3)\fP)
+out of some discipline I/O function to restore the internal stream states.
+\f5sfclrlock()\fP returns the current set of flags.
+
+.Ss " int sfnotify((void(*)notify)(Sfio_t*, int, void*) )"
+This sets a function \f5(*notify)()\fP to be called
+as \f5(*notify)(f, type, data)\fP on various stream events.
+Arguments \f5type\fP and \f5data\fP indicate the reason for the call and accompanying data:
+.Tp
+\f5SF_NEW\fP:
+\f5f\fP is being opened and \f5data\fP is the underlying file descriptor.
+.Tp
+\f5SF_CLOSING\fP (\f5SF_CLOSE\fP):
+\f5f\fP is the stream being closed and \f5data\fP is the underlying file descriptor.
+.Tp
+\f5SF_SETFD\fP:
+The file descriptor of \f5f\fP is being changed to the one
+defined by \f5data\fP (see \f5sfsetfd()\fP.)
+.Tp
+\f5SF_READ\fP:
+An attempt to change \f5f\fP to read mode failed.
+\f5data\fP is the file descriptor of the stream.
+.Tp
+\f5SF_WRITE\fP:
+An attempt to change \f5f\fP to write mode failed.
+\f5data\fP is the file descriptor of the stream.
+.Tp
+\f5SF_MTACCESS\fP:
+When a notifying function was registered (see \f5sfnotify()\fP),
+every Sfio call on a stream with flag \f5SF_MTSAFE\fP will
+invoke the notifying function
+once on entry after the stream is locked
+as \f5(*notify)(f, SF_MTACCESS, Sfio_t** fp), and
+once on return before unlocking as
+as \f5(*notify)(f, SF_MTACCESS, (Sfio_t**)0).
+In the call entry case,
+the notification function could use the argument \f5fp\fP
+to set a stream that would be used for performing the actual I/O operation.
+In this way, certain global streams such as the standard streams \f5sfstdin\fP,
+\f5sfstdout\fP and \f5sfstderr\fP could be made to act differently when used
+in different streams.
+
+.Ss " int sfwalk(Sfwalk_f walkf, Void_t* data, int type)"
+This function invokes \f5(*walkf)(f, data)\fP on every open stream \f5f\fP
+whose flags as defined by \f5sfset()\fP contains all bit flags given in \f5type\fP.
+On such a call, if the return value is negative, \f5sfwalk()\fP will terminate.
+\f5sfwalk()\fP returns 0 if no stream was processed.
+Otherwise, it returns the return value from the last invocation of \f5walkf()\fP.
+
+As an example, the call \f5sfwalk(walkf, data, SF_READ)\fP will iterate over all streams
+opened for reading. Similarly, \f5sfwalk(walkf, data, SF_READ|SF_WRITE)\fP
+iterates over all streams opened for both reading and writing.
+Lastly, \f5sfwalk(walkf, data, 0)\fP iterates over all streams.
+
+.PP
+.Ss "MISCELLANEOUS FUNCTIONS"
+.PP
+.Ss " ssize_t sfmaxr(ssize_t maxr, int set)"
+Certain records may require too much memory for storage, thus, causing
+undesirable side effects. Therefore, the library normally bounds the amount
+of memory used by \f5sfgetr()\fP. A different memory bound
+can be set via \f5sfmaxr()\fP. While a positive \f5maxr\fP hints to \f5sfgetr()\fP
+to use only about that much memory to construct a record, a non-positive bound
+allows \f5sfgetr()\fP to use as much memory as necessary.
+\f5sfmaxr()\fP sets the value only if \f5set\fP is non-zero.
+It returns the value before setting or the current value if not setting.
+
+.Ss " ssize_t sfslen()"
+This function returns the length of a string just constructed
+by \f5sfsprintf()\fP or \f5sfprints()\fP. See also \f5sfvalue()\fP.
+
+.Ss " int sfulen(Sfulong_t v)"
+.Ss " int sfllen(Sflong_t v)"
+.Ss " int sfdlen(Sfdouble_t v)"
+These functions return respectively the number of bytes required to code the
+\f5Sfulong_t\fP, \f5Sflong_t\fP or \f5Sfdouble_t\fP value \f5v\fP by \f5sfputu()\fP,
+\f5sfputl()\fP or \f5sfputd()\fP.
+
+.Ss " ssize_t sfpkrd(int fd, char* buf, size_t n, int rsc, long tm, int action)"
+This function acts directly on the file descriptor \f5fd\fP.
+It does a combination of peeking on incoming data and a time-out read.
+Upon success, it returns the number of bytes received.
+A return value of \f50\fP means that the end-of-file condition has been detected.
+A negative value represents an error.
+.Tp
+\f5buf\fP, \f5n\fP:
+These define a buffer and its size to read data into.
+.Tp
+\f5rsc\fP:
+If \f5>=0\fP, this defines a record separator.
+If the last returned byte is not the record separator, then
+the read data did not contain a complete record. Otherwise,
+it contains one or more records.
+See also \f5action\fP below.
+.Tp
+\f5tm\fP:
+If \f5>=0\fP, this defines a time interval in milliseconds to wait for incoming data.
+.Tp
+\f5action\fP:
+If \f5action > 0\fP, \f5sfpkrd()\fP will peek on incoming data but
+will not read past it. Therefore, a future \f5sfpkrd()\fP or \f5read(2)\fP will see
+the same data again.
+If \f5action <= 0\fP, \f5sfpkrd()\fP will not peek and there are two cases.
+If \f5rsc < 0\fP, an attempt is made to read \f5n\fP bytes.
+If \f5rsc >= 0\fP, an attempt is made to read one record.
+
+.PP
+.Ss "FULL STRUCTURE SFIO_T"
+.PP
+.Ss " #include <sfio_t.h>"
+Most applications based on Sfio only need to include
+the header file \f5sfio.h\fP which defines an abbreviated \f5Sfio_t\fP
+structure without certain fields private to Sfio.
+However, there are times (e.g., debugging)
+when an application may require more details about the full \f5Sfio_t\fP structure.
+In such cases, the header file \f5sfio_t.h\fP can be used in place of \f5sfio.h\fP.
+Note that an application doing this will become sensitive to changes
+in the internal architecture of Sfio.
+
+.Ss " #define SFNEW(buf,size,file,flags,disc)"
+This macro function is defined in \f5sfio_t.h\fP for
+use in static initialization of an \f5Sfio_t\fP structure.
+It requires five arguments:
+.Tp
+\f5buf, size\fP:
+These define a buffer and its size.
+.Tp
+\f5file\fP:
+This defines the underlying file descriptor if any.
+.Tp
+\f5flags\fP:
+This is composed from bit flags described above.
+.Tp
+\f5disc\fP:
+This defines a discipline if any.
+
+.PP
+.Ss "EXAMPLE DISCIPLINES"
+.PP
+The below functions create disciplines and insert them into
+the given streams \f5f\fP. These functions return \f50\fP
+on success and \f5-1\fP on failure.
+
+.Ss "int sfdcdio(Sfio_t* f, size_t bufsize)"
+This creates a discipline that uses the direct IO feature
+available on file systems such as SGI's XFS to speed up IO.
+The argument \f5bufsize\fP suggests a buffer size to use for data transfer.
+
+.Ss "int sfdcdos(Sfio_t* f)"
+This creates a discipline to read DOS text files.
+It basically transforms pairs of \er\en to \en.
+
+.Ss "int sfdcfilter(Sfio_t* f, const char* cmd)"
+This creates a discipline that sends data from \f5f\fP
+to the given command \f5cmd\fP to process, then reads back the processed data.
+
+.Ss "int sfdcseekable(Sfio_t* f)"
+This creates a discipline that makes an unseekable reading stream seekable.
+
+.Ss "int sfdcslow(Sfio_t* f)"
+This creates a discipline that makes all Sfio operations return immediately
+on interrupts. This is useful for dealing with slow devices.
+
+.Ss "int sfdcsubstream(Sfio_t* f, Sfio_t* parent, Sfoff_t offset, Sfoff_t extent)"
+This creates a discipline that makes \f5f\fP acts as if it
+corresponds exactly to the subsection of \f5parent\fP
+starting at \f5offset\fP with size \f5extent\fP.
+
+.Ss "int sfdctee(Sfio_t* f, Sfio_t* tee)"
+This creates a discipline that copies to the stream \f5tee\fP
+any data written to \f5f\fP.
+
+.Ss "int sfdcunion(Sfio_t* f, Sfio_t** array, int n)"
+This creates a discipline that makes \f5f\fP act as if it is
+the concatenation of the \f5n\fP streams given in \f5array\fP.
+
+.Ss "int sfdclzw(Sfio_t* f)"
+This creates a discipline that would decompress data in \f5f\fP.
+The stream \f5f\fP should have data from a source compressed by
+the Unix \fBcompress\fP program.
+
+.Ss "int sfdcgzip(Sfio_t* f, int opt)"
+This creates a discipline for reading/writing data compressed by zlib.
+The argument \f5opt\fP defines the optimization level.
+
+.PP
+.Ss "STDIO-COMPATIBILITY"
+.PP
+Sfio provides compatibility functions for all various popular
+Stdio implementations at source and binary level.
+The source Stdio-compatibility interface
+provides the header file \f5stdio.h\fP that defines
+a set of macros or inlined functions to map Stdio calls to Sfio ones.
+This mapping may benignly extend or change the meaning of certain
+original Stdio operations. For example, the Sfio's version of
+\f5popen()\fP allows a coprocess to be opened for both reading and writing
+unlike the original call which only allows a coprocess to be opened for a single mode.
+Similarly, the Sfio's \f5fopen()\fP call can be used to create
+string streams in addition to file streams.
+.PP
+The standard streams \f5stdin\fP, \f5stdout\fP and \f5stderr\fP
+are mapped via \f5#define\fP to \f5sfstdin\fP, \f5sfstdout\fP and \f5sfstderr\fP.
+The latter are typically declared of the type \f5Sfio_t*\fP.
+Certain older Stdio applications require these to be declared
+as addresses of structures so that static initializations of
+the sort ``\f5FILE*\ f\ =\ stdin;\fP'' would work.
+Such applications should use the compile time flag \f5SF_FILE_STRUCT\fP
+to achieve the desired effect.
+.PP
+The binary Stdio-compatibility libraries, \f5libstdio.a\fP and \f5libstdio-mt.a\fP,
+provide complete implementations of Stdio functions suitable
+for linking applications already compiled with native header \f5stdio.h\fP.
+These functions are also slightly altered or extended
+as discussed above.
+.PP
+Below are the supported Stdio functions:
+.PP
+.nf
+.ft 5
+FILE* fopen(const char* file, const char* mode);
+FILE* freopen(const char* file, const char* mode, FILE* stream);
+FILE* fdopen(int filedesc, const char* mode);
+FILE* popen(const char* command, const char* mode);
+FILE* tmpfile();
+int fclose(FILE* stream);
+int pclose(FILE* stream);
+
+void flockfile(FILE* stream)
+int ftrylockfile(FILE* stream)
+void funlockfile(FILE* stream)
+
+void setbuf(FILE* stream, char* buf);
+int setvbuf(FILE* stream, char* buf, int mode, size_t size);
+void setbuffer(FILE* stream, char* buf, size_t size);
+int setlinebuf(FILE* stream);
+int fflush(FILE* stream);
+int fpurge(FILE* stream);
+
+int fseek(FILE* stream, long offset, int whence);
+void rewind(FILE* stream);
+int fgetpos(FILE* stream, fpos_t* pos);
+int fsetpos(FILE* stream, fpos_t* pos);
+long ftell(FILE* stream);
+
+int getc(FILE* stream);
+int fgetc(FILE* stream);
+int getchar(void);
+int ungetc(int c, FILE* stream);
+int getw(FILE* stream);
+char* gets(char* s);
+char* fgets(char* s, int n, FILE* stream);
+size_t fread(Void_t* ptr, size_t size, size_t nelt, FILE* stream);
+
+int putc(int c, FILE* stream);
+int fputc(int c, FILE* stream);
+int putchar(int c);
+int putw(int w, FILE* stream);
+int puts(const char* s, FILE* stream);
+int fputs(const char* s, FILE* stream);
+size_t fwrite(const Void_t* ptr, size_t size, size_t nelt, FILE* stream);
+
+int fscanf(FILE* stream, const char* format, ...);
+int vfscanf(FILE* stream, const char* format, va_list args);
+int _doscan(FILE* stream, const char* format, va_list args);
+int scanf(const char* format, ...);
+int vscanf(const char* format, va_list args);
+int sscanf(const char* s, const char* format, ...);
+int vsscanf(const char* s, const char* format, va_list args);
+
+int fprintf(FILE* stream, const char* format, ...);
+int vfprintf(FILE* stream, const char* format, va_list args);
+int _doprnt(FILE* stream, const char* format, va_list args);
+int printf(const char* format, ...);
+int vprintf(const char* format, va_list args);
+int sprintf(const char* s, const char* format, ...);
+int snprintf(const char* s, int n, const char* format, ...);
+int vsprintf(const char* s, const char* format, va_list args);
+int vsnprintf(const char* s, int n, const char* format, va_list args);
+
+int feof(FILE* stream);
+int ferror(FILE* stream);
+int clearerr(FILE* stream);
+.ft 1
+.fi
+
+.PP
+.Ss "RECENT CHANGES"
+.PP
+A few exception types have been added. In particular, exception handlers shall
+be raised with \f5SF_LOCKED\fP on accessing a stream frozen either by
+an ongoing operation or a previous operation (e.g., \f5sfgetr()\fP).
+Before a process exits, the event \f5SF_ATEXIT\fP is raised for each open stream.
+.PP
+A number of disciplines were added for various processing functions.
+Of interests are disciplines to use the direct I/O feature on IRIX6.2,
+read DOS text files, and decompress files compressed by Unix \fIcompress\fP.
+.PP
+Various new stream and function flags have been added. For example,
+the third argument of \f5sfgetr()\fP is now a set of bit flags and not
+just a three-value object. However, the old semantics of this argument
+of \f5sfgetr()\fP is still supported.
+.PP
+The \f5sfopen()\fP call has been extended so that sfopen(f,NULL,mode) can be
+used to changed the mode of a file stream before any I/O operations.
+This is most useful for changing the modes of the standard streams.
+.PP
+The buffering strategy has been significantly enhanced for streams
+that perform many seek operations. Also, the handling of stream and
+file positions have been better clarified so that applications that
+share file descriptors across streams and/or processes can be sure that
+the file states will be consistent.
+.PP
+The strategy for mapping between Sfio and Stdio streams in the binary
+compatibility package has been significantly enhanced for efficiency.
+For most platforms, the mapping is now constant time per look-up.
+.PP
+The \f5SF_BUFCONST\fP flag was deleted. This is largely unused anyway.
+.PP
+The library can be built for thread-safety. This is based largely on
+Posix pthread mutexes except for on UWIN where native Windows APIs
+are used.
+.PP
+The functions \f5sfgetm()\fP and \f5sfputm()\fP were added to encode
+unsigned integer values with known ranges.
+.PP
+The flag \f5SF_APPEND\fP is identical to \f5SF_APPENDWR\fP.
+However it conflicts with a different token of the same name
+defined in the system header \f5stat.h\fP of BSDI Unix systems.
+On such systems, we shall not define \f5SF_APPEND\fP and this
+symbol may be removed in a future release.
+.PP
+Similarly, the exception \f5SF_CLOSE\fP is identical to \f5SF_CLOSING\fP.
+However it conflicts with a different token of the same name
+defined in the system header \f5socket.h\fP of AIX Unix systems.
+On such systems, we shall not define \f5SF_CLOSE\fP and this
+symbol may be removed in a future release.
+.PP
+The printing and scanning functions were extended to handle multibyte characters
+and to conform to the C99 standard.
+.PP
+The function \f5sfpoll()\fP was rehauled to make it useful
+for writing servers that must commnunicate with multiple streams
+without blocking.
+.PP
+The formatting pattern \f5%c\fP for \f5sf*printf\fP was extended
+to allow the flag \f5#\fP to print unprintable character values
+using the C convention. For example, \f5%#c\fP prints the octal value 012
+as \f5\\n\fP.
+
+.SH AUTHORS
+Kiem-Phong Vo, kpv@research.att.com,
+.br
+David G. Korn, dgk@research.att.com, and
+.br
+Glenn S. Fowler, gsf@research.att.com.
diff --git a/src/lib/libast/man/sig.3 b/src/lib/libast/man/sig.3
new file mode 100644
index 0000000..db41a2b
--- /dev/null
+++ b/src/lib/libast/man/sig.3
@@ -0,0 +1,75 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH SIG 3
+.SH NAME
+sig \- signal interface routines
+.SH SYNOPSIS
+.L "#include <ast.h>"
+.L "#include <sig.h>"
+.sp
+.L "int sigunblock(int sig);"
+.L "int sigcritical(int op);"
+.SH DESCRIPTION
+.L sigunblock
+is called to
+unblocks the signal
+.L sig
+from within a handler currently servicing
+.LR sig .
+.PP
+.L sigcritical
+controls a critical region for the
+.LR SIGINT ,
+.L SIGQUIT
+and
+.L SIGHUP
+signals.
+.L "op > 0"
+pushes the region,
+.L "op == 0"
+pops the region, and
+.L "op < 0"
+returns non-zero if any signals are being held in the current
+critical region.
+Signal critical regions may be nested.
+The current critical region level is returned,
+.L \-1
+on error.
+.SH "SEE ALSO"
+signal(2)
diff --git a/src/lib/libast/man/spawnveg.3 b/src/lib/libast/man/spawnveg.3
new file mode 100644
index 0000000..3dfd424
--- /dev/null
+++ b/src/lib/libast/man/spawnveg.3
@@ -0,0 +1,97 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH SPAWNVEG 3
+.SH NAME
+spawnveg \- process spawn with process group and session control
+.SH SYNOPSIS
+.L "#include <ast.h>"
+.sp
+.L "int spawnveg(const char* command, char** argv, char** envv, pid_t pgid);"
+.SH DESCRIPTION
+.L spwanveg
+combines
+.IR fork (2),
+.IR exec (2),
+.IR setpgid (2)
+and
+.IR setsid (2)
+into a single call.
+.PP
+.LR command ,
+.L argv
+and
+.L envv
+are as in
+.IR execve (2).
+.L pgid
+controls the new process group and session:
+.TP
+.L <0
+The new process becomes a session leader.
+is called in the child context.
+.TP
+.L 0
+The new process is in the callers process group.
+.TP
+.L 1
+The new process becomes a process group leader.
+.TP
+.L >1
+The new process joins the process group
+.IR pgid .
+.SH COMMENTS
+It is possible to code all process creation (except for
+.IR vfork (2)
+hack like in
+.IR csh (1))
+using
+.LR spawnveg .
+The
+.IR proc (3)
+routines and
+.IR ksh (1)
+do this on systems that don't support
+.IR fork (2).
+This makes porting to NT and Windows a snap: a simple
+.IR iffe (1)
+probe provides a
+.L spawnveg
+implementation using the NT or Windows process primitives.
+.SH "SEE ALSO"
+fork(2), exec(2), setpgid(2), setsid(2), spawnve(2)
diff --git a/src/lib/libast/man/stak.3 b/src/lib/libast/man/stak.3
new file mode 100644
index 0000000..5feac69
--- /dev/null
+++ b/src/lib/libast/man/stak.3
@@ -0,0 +1,169 @@
+.fp 5 CW
+.TH STAK 3
+.SH NAME
+\fBstak\fR \- data stack storage library (obsolete: use \fBstk\fR instead)
+.SH SYNOPSIS
+.ta .75i 1.5i 2.25i 3i 3.75i 4.5i 5.25i 6i
+.PP
+.nf
+\f5
+#include <stak.h>
+
+Stak_t *stakcreate(int \fIflags\fP);
+Stak_t *stakinstall(Stak_t *\fIstack\fP, char *(\fIoverflow\fP)(int));
+int stakdelete(Stak_t *\fIstack\fP);
+void staklink(Stak_t *\fIstack\fP)
+
+char *stakalloc(unsigned \fIsize\fP);
+char *stakcopy(const char *\fIstring\fP);
+char *stakset(char *\fIaddress\fP, unsigned \fIoffset\fP);
+
+char *stakseek(unsigned \fIoffset\fP);
+int stakputc(int \fIc\fP);
+int stakputs(const char *\fIstring\fP);
+int stakwrite(const char *\fIaddress\fP, unsigned \fIsize\fP);
+int staktell(void);
+char *stakptr(unsigned \fIoffset\fP);
+char *stakfreeze(unsigned \fIextra\fP);
+\fR
+.fi
+.SH DESCRIPTION
+.PP
+\f5stak\fP is a package of routines designed to provide efficient
+stack oriented dynamic storage.
+A stack abstraction consists of an ordered list of contiguous
+memory regions, called stack frames, that can hold objects of
+arbitrary size.
+A stack is represented by the type \f5Stak_t\fP
+defined in header \f5<stak.h>\fP.
+At any instant there is one active stack.
+Variable size objects can be
+added to the active stack
+and programs can reference these objects directly with pointers.
+In addition, the last object on the stack
+(referred to here as the current object)
+can be built incrementally.
+The current object has an associated offset that determines its
+current size.
+While the current object is being built incrementally,
+its location might
+change so that it is necessary to reference the object with
+relative offsets ranging from zero to the current offset of the object.
+.PP
+There is a preset initial active stack.
+To use an additional stack, it is necessary to create it and to
+install it as the active stack.
+A stack is created with the \f5stakcreate\fP() function.
+A \fIflags\fP argument of \f5STAK_SMALL\fP indicates that unused
+space on the stack should be freed whenever this stack ceases
+to be the active stack.
+If successful,
+\f5stakcreate\fP() returns a pointer to a stack whose reference
+count is 1.
+Otherwise, \f5stakcreate\fP() returns a null pointer.
+The \f5staklink\fP() function increases the reference count for the
+given \fIstack\fP.
+The \f5stakinstall\fP() function
+makes the specified \fIstack\fP the active stack and returns a pointer
+to the previous active stack.
+When the \fIoverflow\fP argument is not null,
+it specifies a function that will
+be called whenever \f5malloc\fP(3) fails while trying to grow the
+stack.
+The \fIoverflow\fP function will be called with the size that was passed
+to \f5malloc\fP(3).
+The \fIoverflow\fP function can call \f5exit\fP(3), call \f5longjmp\fP(3)
+or return.
+If the \f5overflow\fP function returns,
+it must return a pointer to a memory region of the given size.
+The default action is to write an error to standard error and to
+call \f5exit\fP(2) with a non-zero exit value.
+When \fIstack\fP is a null pointer,
+the active stack is not changed
+but the \fIoverflow\fP function for the active stack can be changed
+and a pointer to the active stack is returned.
+The \f5stakdelete\fP() function decrements the reference count and
+frees the memory associated with
+the specified stack
+when the reference count is zero.
+The effect of subsequent references to objects
+on the stack are undefined.
+.PP
+The
+\f5stakalloc\fP() function returns an aligned pointer to space on the
+active stack that can be used to hold any object of the given \fIsize\fP.
+\f5stakalloc\fP() is similar to \f5malloc\fP(3) except that individual
+items returned by \f5stakalloc\fP() can not be freed.
+\f5stakalloc\fP() causes the offset of the current object to be set to
+zero.
+.PP
+The
+\f5stakcopy\fP() function copies the given string onto the stack
+and returns a pointer to the \fIstring\fP on the stack.
+\f5stakcopy\fP() causes the offset of the current object to be set to
+zero.
+.PP
+The \f5stakset\fP() function finds the frame containing the given
+\fIaddress\fP, frees all frames that were created after the one containing
+the given \fIaddress\fP, and sets the current object to the given
+\fIaddress\fP.
+The top of the current object is set to \fIoffset\fP bytes from
+current object.
+If \fIaddress\fP is not the address of an object on the
+stack the result is undefined.
+.PP
+The remaining functions are used to build the current object incrementally.
+An object that is built incrementally on the stack will
+always occupy contiguous memory within a stack frame but
+until \f5stakfreeze\fP() is called,
+the location in memory for the object can change.
+There is a current offset associated with the current object that
+determines where subsequent operations apply.
+Initially, this offset is zero, and the offset changes as a result
+of the operations you specify.
+The \f5stakseek\fP() function is used set the offset for the
+current object.
+The \fIoffset\fP argument to \f5stakseek\fP() specifies the new
+offset for the current object.
+The frame will be extended or moved
+if \f5offset\fP causes the new current offset to extend beyond the
+current frame.
+\f5stakseek\fP() returns a pointer to the beginning of the current object.
+The \f5staktell\fP() function gives the offset of the current object.
+.PP
+The \f5stakputc\fP() function adds a given character to the current object
+on the stack.
+The current offset is advanced by 1.
+The \f5stakputs\fP() function appends the given \fIstring\fP onto the current
+object in the stack and returns the length of the string.
+The current offset is advanced by the length of the string.
+The \f5stakwrite\fP() function appends the given \fIsize\fP byte memory
+region starting at \fIaddress\fP onto the current
+object in the stack and advances the current offset by \fIsize\fP.
+The current offset is returned.
+.PP
+The \f5stakptr\fP() function converts the given \f5offset\fP
+for the current object into a memory address on the stack.
+This address is only valid until another stack operation is given.
+The result is not defined if \fIoffset\fP exceeds the size of the current
+object.
+The \f5stakfreeze\fP()
+function terminates the current object on the
+stack and returns a pointer to the beginning of this object.
+If \fIextra\fP is non-zero, \fIextra\fP bytes are added to the stack
+before the current object is terminated. The first added byte will
+contain zero and the contents of the remaining bytes are undefined.
+.PP
+.SH HISTORY
+The
+\f5stak\fP
+interface was derived from similar routines in the KornShell code
+that is used for building parse trees and carrying out expansions.
+It provides an efficient mechanism for grouping dynamically allocated
+objects so that they can be freed all at once rather than individually.
+.SH AUTHOR
+ David Korn
+.SH SEE ALSO
+\f5exit(2)\fP
+\f5longjmp(3)\fP
+\f5malloc(3)\fP
diff --git a/src/lib/libast/man/stk.3 b/src/lib/libast/man/stk.3
new file mode 100644
index 0000000..3e65821
--- /dev/null
+++ b/src/lib/libast/man/stk.3
@@ -0,0 +1,165 @@
+.fp 5 CW
+.TH STK 3
+.SH NAME
+\fBstk\fR \- data stack storage library
+.SH SYNOPSIS
+.ta .75i 1.5i 2.25i 3i 3.75i 4.5i 5.25i 6i
+.PP
+.nf
+\f5
+#include <stk.h>
+
+Stk_t *stkopen(int \fIflags\fP);
+Stk_t *stkinstall(Stk_t *\fIstack\fP, char *(\fIoverflow\fP)(int));
+int stkclose(Stk_t *\fIstack\fP);
+void stklink(Stk_t *\fIstack\fP)
+
+char *stkalloc(Stk_t *\fIstack\fP, unsigned \fIsize\fP);
+char *stkcopy(Stk_t *\fIstack\fP, const char *\fIstring\fP);
+char *stkset(Stk_t *\fIstack\fP, char *\fIaddress\fP, unsigned \fIoffset\fP);
+
+char *stkseek(Stk_t *\fIstack\fP, unsigned \fIoffset\fP);
+int stktell(Stk_t *\fIstack\fP);
+char *stkptr(Stk_t *\fIstack\fP, unsigned \fIoffset\fP);
+char *stkfreeze(Stk_t *\fIstack\fP, unsigned \fIextra\fP);
+int stkon(Stk *\fIstack\fP, char* \fIaddr\fP)
+\fR
+.fi
+.SH DESCRIPTION
+.PP
+\f5stk\fP is a package of routines designed to provide efficient
+stack oriented dynamic storage.
+A stack abstraction consists of an ordered list of contiguous
+memory regions, called stack frames, that can hold objects of
+arbitrary size.
+A stack is represented by the type \f5Stk_t\fP
+defined in header \f5<stk.h>\fP.
+The type \f5Stk_t\fP is compatible with the type \f5Sfio_t\fP
+defined by the \f5sfio\fP(3) library.
+At any instant there is one active stack which can be referenced
+by the constant \f5stkstd\fP.
+Variable size objects can be
+added to the active stack
+and programs can reference these objects directly with pointers.
+In addition, the last object on the stack
+(referred to here as the current object)
+can be built incrementally.
+The current object has an associated offset that determines its
+current size.
+While the current object is being built incrementally,
+its location might
+change so that it is necessary to reference the object with
+relative offsets ranging from zero to the current offset of the object.
+.PP
+There is a preset initial active stack.
+To use an additional stack, it is necessary to create it and to
+install it as the active stack.
+A stack is created with the \f5stkopen\fP() function.
+A \fIflags\fP argument of \f5STK_SMALL\fP indicates that unused
+space on the stack should be freed whenever this stack ceases
+to be the active stack.
+If successful,
+\f5stkopen\fP() returns a pointer to a stack whose reference
+count is 1.
+Otherwise, \f5stkopen\fP() returns a null pointer.
+The \f5stklink\fP() function increases the reference count for the
+given \fIstack\fP.
+The \f5stkinstall\fP() function
+makes the specified \fIstack\fP the active stack and returns a pointer
+to the previous active stack.
+When the \fIoverflow\fP argument is not null,
+it specifies a function that will
+be called whenever \f5malloc\fP(3) fails while trying to grow the
+stack.
+The \fIoverflow\fP function will be called with the size that was passed
+to \f5malloc\fP(3).
+The \fIoverflow\fP function can call \f5exit\fP(3), call \f5longjmp\fP(3)
+or return.
+If the \f5overflow\fP function returns,
+it must return a pointer to a memory region of the given size.
+The default action is to write an error to standard error and to
+call \f5exit\fP(2) with a non-zero exit value.
+When \fIstack\fP is a null pointer,
+the active stack is not changed
+but the \fIoverflow\fP function for the active stack can be changed
+and a pointer to the active stack is returned.
+The \f5stkclose\fP() function decrements the reference count and
+frees the memory associated with
+the specified stack
+when the reference count is zero.
+The effect of subsequent references to objects
+on the stack are undefined.
+.PP
+The
+\f5stkalloc\fP() function returns an aligned pointer to space on the
+active stack that can be used to hold any object of the given \fIsize\fP.
+\f5stkalloc\fP() is similar to \f5malloc\fP(3) except that individual
+items returned by \f5stkalloc\fP() can not be freed.
+\f5stkalloc\fP() causes the offset of the current object to be set to
+zero.
+.PP
+The
+\f5stkcopy\fP() function copies the given string onto the stack
+and returns a pointer to the \fIstring\fP on the stack.
+\f5stkcopy\fP() causes the offset of the current object to be set to
+zero.
+.PP
+The \f5stkset\fP() function finds the frame containing the given
+\fIaddress\fP, frees all frames that were created after the one containing
+the given \fIaddress\fP, and sets the current object to the given
+\fIaddress\fP.
+The top of the current object is set to \fIoffset\fP bytes from
+current object.
+If \fIaddress\fP is not the address of an object on the
+stack the result is undefined.
+.PP
+The \f5sfio\fP(3) output functions can be used to build
+current object incrementally.
+An object that is built incrementally on the stack will
+always occupy contiguous memory within a stack frame but
+until \f5stkfreeze\fP() is called,
+the location in memory for the object can change.
+There is a current offset associated with the current object that
+determines where subsequent operations apply.
+Initially, this offset is zero, and the offset changes as a result
+of the operations you specify.
+The \f5stkseek\fP() function is used set the offset for the
+current object.
+The \fIoffset\fP argument to \f5stkseek\fP() specifies the new
+offset for the current object.
+The frame will be extended or moved
+if \f5offset\fP causes the new current offset to extend beyond the
+current frame.
+\f5stkseek\fP() returns a pointer to the beginning of the current object.
+The \f5stktell\fP() function gives the offset of the current object.
+.PP
+The \f5stkptr\fP() function converts the given \f5offset\fP
+for the current object into a memory address on the stack.
+This address is only valid until another stack operation is given.
+The result is not defined if \fIoffset\fP exceeds the size of the current
+object.
+The \f5stkfreeze\fP()
+function terminates the current object on the
+stack and returns a pointer to the beginning of this object.
+If \fIextra\fP is non-zero, \fIextra\fP bytes are added to the stack
+before the current object is terminated. The first added byte will
+contain zero and the contents of the remaining bytes are undefined.
+.PP
+The \f5stkon\fP()
+function returns non-zero if the address given by \fIaddr\fP is
+on the stack \fIstack\fP and \f50\fP otherwise.
+.PP
+.SH HISTORY
+The
+\f5stk\fP
+interface was derived from similar routines in the KornShell code
+that is used for building parse trees and carrying out expansions.
+It provides an efficient mechanism for grouping dynamically allocated
+objects so that they can be freed all at once rather than individually.
+.SH AUTHOR
+ David Korn
+.SH SEE ALSO
+\f5exit(2)\fP
+\f5longjmp(3)\fP
+\f5malloc(3)\fP
+\f5sfio(3)\fP
diff --git a/src/lib/libast/man/strcopy.3 b/src/lib/libast/man/strcopy.3
new file mode 100644
index 0000000..c08d885
--- /dev/null
+++ b/src/lib/libast/man/strcopy.3
@@ -0,0 +1,54 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRCOPY 3
+.SH NAME
+strcopy \- copy strings
+.SH SYNOPSIS
+.L "char* strcopy(char* a, char* b)"
+.SH DESCRIPTION
+.I strcopy
+copies the nul-terminated string
+.I b
+into
+.IR a .
+A pointer to the 0 character in
+.I a
+is returned.
+.SH "SEE ALSO"
+strcpy(3)
diff --git a/src/lib/libast/man/strdup.3 b/src/lib/libast/man/strdup.3
new file mode 100644
index 0000000..3b26ec4
--- /dev/null
+++ b/src/lib/libast/man/strdup.3
@@ -0,0 +1,55 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRDUP 3
+.SH NAME
+strdup \- duplicate nul-terminated string
+.SH SYNOPSIS
+.L "char* strdup(char* s)"
+.SH DESCRIPTION
+.I strdup
+copies the nul-terminated string
+.I s
+to a new location provided by
+.IR malloc (3)
+and returns a pointer to the new copy.
+0 is returned if
+.IR malloc (3)
+failed.
+.SH "SEE ALSO"
+malloc(3), memdup(3)
diff --git a/src/lib/libast/man/strelapsed.3 b/src/lib/libast/man/strelapsed.3
new file mode 100644
index 0000000..8c3fa4a
--- /dev/null
+++ b/src/lib/libast/man/strelapsed.3
@@ -0,0 +1,77 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRELAPSED 3
+.SH NAME
+strelapsed \- parse elapsed time expression
+.SH SYNOPSIS
+.L "unsigned long strelapsed(char* buf, char** next, int persec)"
+.SH DESCRIPTION
+.I strelapsed
+returns a pointer to a string representation of the elapsed time for
+.L (count/persec)
+seconds.
+The two largest time units are used, limiting the return value length
+to at most 6 characters.
+The units are:
+.TP
+.B s
+seconds
+.TP
+.B m
+minutes
+.TP
+.B h
+hours
+.TP
+.B days
+.TP
+.B weeks
+.TP
+.B M
+months
+.TP
+.B Y
+years
+.TP
+.B S
+scores
+.SH "SEE ALSO"
+strelapsed(3)
+.SH CAVEATS
+The return value points to a static area that is overwritten on each call.
diff --git a/src/lib/libast/man/strerror.3 b/src/lib/libast/man/strerror.3
new file mode 100644
index 0000000..0084e3a
--- /dev/null
+++ b/src/lib/libast/man/strerror.3
@@ -0,0 +1,53 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRERROR 3
+.SH NAME
+strerror \- return error message string given error number
+.SH SYNOPSIS
+.L "char* strerror(int err)"
+.SH DESCRIPTION
+.I strerror
+returns the error message string corresponding to the error message number
+.IR err .
+.BI "Error " nnn
+is returned if
+.I err
+is invalid.
+.SH "SEE ALSO"
+error(3)
diff --git a/src/lib/libast/man/stresc.3 b/src/lib/libast/man/stresc.3
new file mode 100644
index 0000000..c09a0e9
--- /dev/null
+++ b/src/lib/libast/man/stresc.3
@@ -0,0 +1,53 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRESC 3
+.SH NAME
+stresc \- convert character constants in string
+.SH SYNOPSIS
+.L "int stresc(char* s)"
+.SH DESCRIPTION
+.I stresc
+converts
+.L \e
+character constant expressions in the nul-terminated string
+.I s
+in place and returns the length of the converted
+.IR s .
+.SH "SEE ALSO"
+chresc(3), ctoi(3)
diff --git a/src/lib/libast/man/streval.3 b/src/lib/libast/man/streval.3
new file mode 100644
index 0000000..2b491c8
--- /dev/null
+++ b/src/lib/libast/man/streval.3
@@ -0,0 +1,83 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STREVAL 3
+.SH NAME
+streval \- long integer arithmetic expression evaluator
+.SH SYNOPSIS
+.L "long streval(char* s, char** e, long (*conv)(char* cs, char** ce))"
+.SH DESCRIPTION
+.I streval
+evaluates the long integer arithmetic expression in the nul-terminated string
+.I s
+and returns the result.
+If
+.I e
+is not 0 then
+.I *e
+is set to point to the first unknown character in the expression.
+.PP
+If
+.I conv
+is not 0 then it is called when an unknown token is encountered in
+.IR s .
+.I cs
+points to the beginning of the unknown token.
+The return value is the long integer value of the unknown token and
+.I ce
+must be set to point to the first character after the unknown token.
+If an expression syntax error is encountered the
+.I conv
+is called with
+.I cs
+set to 0 and
+.I *ce
+pointing to the error message text.
+.PP
+In addition to the normal C expressions and integer constant styles,
+numbers in any base
+.I b
+<= 2 <=36
+may be represented as
+.IR b # nnnn ,
+where the extra digits in
+.I nnnn
+are taken from
+.BR [A-Z] .
+.SH "SEE ALSO"
+strtol(3)
diff --git a/src/lib/libast/man/strgid.3 b/src/lib/libast/man/strgid.3
new file mode 100644
index 0000000..d7a2663
--- /dev/null
+++ b/src/lib/libast/man/strgid.3
@@ -0,0 +1,53 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRGID 3
+.SH NAME
+strgid \- return group name given group number
+.SH SYNOPSIS
+.L "char* strgid(int gid)"
+.SH DESCRIPTION
+.I strgid
+returns the group id name string given the group number
+.IR gid .
+.I strgid
+maintains an internal cache to avoid repeated password database scans
+by the low level
+.IR getgrgid (3).
+.SH "SEE ALSO"
+getgrent(3)
diff --git a/src/lib/libast/man/strmatch.3 b/src/lib/libast/man/strmatch.3
new file mode 100644
index 0000000..5f5af89
--- /dev/null
+++ b/src/lib/libast/man/strmatch.3
@@ -0,0 +1,101 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRMATCH 3
+.SH NAME
+strmatch \- match shell file patterns
+.SH SYNOPSIS
+.L "int strmatch(char* s, char* p)"
+.br
+.L "char* submatch(char* s, char* p, int m)"
+.SH DESCRIPTION
+.I strmatch
+compares the string
+.I s
+with the shell pattern
+.I p
+and returns 1 for match and 0 otherwise.
+.I submatch
+does a leading substring match of the shell pattern
+.I p
+with the string
+.IR s .
+If
+.I m
+is 0 then the match is minimal, otherwise a maximal match is done.
+A pointer to the first character after the matched substring is returned,
+.I 0
+if there is no match.
+.PP
+Except for
+.I &
+and
+.IR ! ,
+each shell pattern has an equivalent
+.IR egrep (1)
+construct.
+.EX
+ \fBsh pattern egrep RE description\fP
+ * .* 0 or more chars
+ ? . any single char
+ [.] [.] char class
+ [!.] [^.] negated char class
+ *(.) (.)* 0 or more of
+ +(.) (.)+ 1 or more of
+ ?(.) (.)? 0 or 1 of
+ (.) (.) 1 of
+ @(.) (.) 1 of
+ a|b a|b a or b
+ a&b a and b
+ !(.) none of
+.EE
+.L \e
+is used to escape *, ?, (, |, &, ), [, and \e
+outside of [...].
+.SH "SEE ALSO"
+grep(1)
+.SH BUGS
+An unbalanced
+.L )
+terminates the top level pattern.
+.br
+Nested
+.L &
+and
+.L !
+constructs are non-intuitive and are computationally intensive.
diff --git a/src/lib/libast/man/stropt.3 b/src/lib/libast/man/stropt.3
new file mode 100644
index 0000000..f2a8dae
--- /dev/null
+++ b/src/lib/libast/man/stropt.3
@@ -0,0 +1,130 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STROPT 3
+.SH NAME
+stropt \- table driven option expression evaluator
+.SH SYNOPSIS
+.L "#include <namval.h>"
+.br
+.L "int stropt(char* s, struct namval* tab,
+.br
+.L " int (*fun)(void* a, struct namval* p, int n, char* v),"
+.br
+.L " void* a)"
+.SH DESCRIPTION
+.I stropt
+parses option expressions in the nul-terminated string
+.I s
+using the option names in
+.IR tab .
+.I tab
+is an array of
+.B "struct namval"
+name value pairs:
+.EX
+char* name;
+int value;
+.EE
+The last entry must be followed by a sentinel with
+.B name
+set to 0.
+.PP
+An option expression contains 0 or more of [\fBno\fP]\fIname\fP[=\fIvalue\fP]
+separate by
+.B space
+or
+.BR tab ,
+where
+.I name
+must be one of the option names in
+.IR tab ,
+.I value
+is an optional value, and
+.B no
+is for Boolean options.
+Each option is passed to
+.I fun
+for processing.
+.I a
+is the
+.L void*
+pointer that is passed from the
+.I stropt
+call but is otherwise not interpreted.
+.I p
+points to the option name value pair from
+.IR tab .
+.I n
+is 0 if
+.B no
+preceded the option
+.I name
+and
+.I v
+points to the beginning of the option
+.I value
+in
+.IR s .
+and
+If
+.I name
+is not found in
+.I tab
+then
+.I fun
+is called with
+.I p
+pointing to an internal
+.I namval
+entry with
+.I p\->name
+pointing to the unknown option and
+.I p\->value
+set to the
+.I value
+of the sentinel entry in
+.IR tab .
+.PP
+If
+.I fun
+returns non-zero then this value is returned and no further
+options are processed.
+Otherwise
+.I stropt
+returns 0 after processing all options.
diff --git a/src/lib/libast/man/strperm.3 b/src/lib/libast/man/strperm.3
new file mode 100644
index 0000000..9b68946
--- /dev/null
+++ b/src/lib/libast/man/strperm.3
@@ -0,0 +1,109 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRPERM 3
+.SH NAME
+strperm \- evaluate file permission expression
+.SH SYNOPSIS
+.L "int strperm(char* s, char** e, int p)"
+.SH DESCRIPTION
+.I strperm
+applies a file permission expression in the nul-terminated string
+.I s
+to the initial file permission mask
+.IR p .
+The new permission mask is returned.
+If
+.I e
+not 0 then
+.I *e
+is set to point to the first unrecognized character in
+.IR s .
+.PP
+A tape device specification is composed of one or more
+.I who-op-permission
+terms separated by
+.BR , .
+.I who
+selects portions of the permission bits and may be any combination of:
+.TP 3
+.B u
+the user permission bits;
+.TP
+.B g
+the group permission bits;
+.TP
+.B o
+the `other' permission bits;
+.TP
+.B a
+all permission bits.
+.PP
+If omitted, all permission bits are selected.
+.I op
+specifies how the original permission
+.I p
+is to be modified:
+.TP 3
+.B +
+.br
+.ns
+.B |
+the new bits are set in
+.IR p ;
+.TP 3
+.B \-
+the new bits are cleared in
+.IR p ;
+.TP
+.B &
+the new bits are and'd with
+.IR p ;
+.TP
+.B =
+the select bits in
+.I p
+are set equal to the new bits
+.PP
+A permission expression term may also be an octal number.
+Octal specifications are inherently non-portable.
+Refer to
+.IR chmod (1)
+for an explanation of this form.
+.SH "SEE ALSO"
+chmod(1), ls(1), strmode(3)
diff --git a/src/lib/libast/man/strsignal.3 b/src/lib/libast/man/strsignal.3
new file mode 100644
index 0000000..4141980
--- /dev/null
+++ b/src/lib/libast/man/strsignal.3
@@ -0,0 +1,53 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRSIGNAL 3
+.SH NAME
+strsignal \- return signal description string given signal number
+.SH SYNOPSIS
+.L "char* strsignal(int sig)"
+.SH DESCRIPTION
+.I strsignal
+returns the signal description string corresponding to the signal number
+.IR sig .
+.BI "Signal " nnn
+is returned if
+.I sig
+is invalid.
+.SH "SEE ALSO"
+signal(2), sigvec(2)
diff --git a/src/lib/libast/man/strsort.3 b/src/lib/libast/man/strsort.3
new file mode 100644
index 0000000..c48cfc2
--- /dev/null
+++ b/src/lib/libast/man/strsort.3
@@ -0,0 +1,73 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH HSORT 3
+.SH NAME
+hsort \- array heap sort
+.SH SYNOPSIS
+.EX
+#include <ast.h>
+
+void strsort(char** \fIarray\fP, int \fIelements\fP, int (*\fIcompare\fP)(const char* \fIa\fP, const char* \fIb\fP));
+.EE
+.SH DESCRIPTION
+.L strsort
+does a heap sort on the array of pointers
+.I array
+with
+.I elements
+elements using the comparison function
+.IR compare .
+.I compare
+returns
+.L \-1
+if
+.I a
+is lexicographically less than
+.IR b ,
+.L 0
+if
+.I a
+is equal to
+.IR b ,
+and
+.L 1
+if
+.I a
+is lexicographically greater than
+.IR b .
diff --git a/src/lib/libast/man/strtape.3 b/src/lib/libast/man/strtape.3
new file mode 100644
index 0000000..06c33ba
--- /dev/null
+++ b/src/lib/libast/man/strtape.3
@@ -0,0 +1,86 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRTAPE 3
+.SH NAME
+strtape \- convert string to tape device pathname
+.SH SYNOPSIS
+.L "char* strtape(char* s, char** e)"
+.SH DESCRIPTION
+.I strtape
+converts the generic tape device specification in the nul-terminated string
+.I s
+to a local tape device pathname.
+A pointer to the device pathname is returned.
+If
+.I e
+not 0 then
+.I *e
+is set to point to the first unrecognized character in
+.IR s .
+.PP
+A tape device specification is composed of
+.IR unit-density-rewind .
+All are optional.
+.I unit
+is a unit number in the range
+.BR [0-7] .
+The default unit is
+.BR 1 .
+Density may be one of:
+.TP 3
+.B l
+for low;
+.TP 3
+.B m
+for medium, and
+.TP
+.B h
+for high.
+.PP
+The default is
+.BR m .
+.I rewind
+is
+.B n
+for no-rewind on device close.
+The default is to rewind on device close.
+.SH "SEE ALSO"
+pax(1), tar(1)
+.SH CAVEATS
+The return value points to a static area that is overwritten on each call.
diff --git a/src/lib/libast/man/strton.3 b/src/lib/libast/man/strton.3
new file mode 100644
index 0000000..bfcf891
--- /dev/null
+++ b/src/lib/libast/man/strton.3
@@ -0,0 +1,97 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRTON 3
+.SH NAME
+strton \- convert string to long integer
+.SH SYNOPSIS
+.L "long strton(char* s, char** e)"
+.SH DESCRIPTION
+.I strton
+converts the nul-terminated string
+.I s
+to a long integer.
+If
+.I e
+not 0 then
+.I *e
+is set to point to the first unrecognized character in
+.IR s .
+Leading spaces in
+.I s
+are ignored.
+.PP
+A number is composed of
+.IR sign-base-number-suffix .
+All but
+.I number
+are optional.
+.I sign
+may be \+ or \-.
+.I base
+may be:
+.TP
+.B 0x
+for hexadecimal;
+.TP
+.B 0
+for octal, or
+.TP
+.IR nn #
+for base
+2 \(le
+.I nn
+\(le 36.
+.PP
+For bases greater than 10 the additional digits are take from the set
+.BR [a-zA-Z] .
+The suffix multiplies the converted number and may be:
+.TP
+.B b
+block (512)
+.TP
+.B g
+giga (1024 * 1024 * 1024)
+.TP
+.B k
+kilo (1024)
+.TP
+.B m
+mega (1024 * 1024)
+.SH "SEE ALSO"
+atoi(3), scanf(3), strtod(3)
diff --git a/src/lib/libast/man/struid.3 b/src/lib/libast/man/struid.3
new file mode 100644
index 0000000..522deb5
--- /dev/null
+++ b/src/lib/libast/man/struid.3
@@ -0,0 +1,53 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH STRUID 3
+.SH NAME
+struid \- return user name given user number
+.SH SYNOPSIS
+.L "char* struid(int uid)"
+.SH DESCRIPTION
+.I struid
+returns the user id name string given the user number
+.IR uid .
+.I struid
+maintains an internal cache to avoid repeated password database scans
+by the low level
+.IR getpwuid (3).
+.SH "SEE ALSO"
+getpwent(3)
diff --git a/src/lib/libast/man/swap.3 b/src/lib/libast/man/swap.3
new file mode 100644
index 0000000..82176de
--- /dev/null
+++ b/src/lib/libast/man/swap.3
@@ -0,0 +1,138 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH SWAP 3
+.SH NAME
+swap \- integral representation conversion routines
+.SH SYNOPSIS
+.L "#include <swap.h>"
+.sp
+.L "int swapop(const void* internal, const void* external, int width);
+.L "int_max swapget(int op, const void* from, int width);"
+.L "void* swapput(int op, void* to, int width, int_max value);"
+.L "void* swapmem(int op, const void* from, void* to, size_t n);"
+.SH DESCRIPTION
+These routines convert integral constants between internal and
+external representations.
+They are used to handle binary data generated by foreign programs.
+New binary data representations should use the compact canonical form
+provided by the
+.IR sfio (3)
+routines
+.L sfputu
+and
+.LR sgetu .
+.PP
+.L swapop
+returns the swap operation required to convert the
+.L width
+byte integer
+.L external
+to the
+.L width
+byte integer
+.LR internal .
+The swap operation is a bit mask:
+.TP
+.L 0
+No swapping necessary.
+.TP
+.L 1
+Swap byte
+.L 0
+with byte
+.LR 1 .
+.TP
+.L 2
+Swap bytes
+.L 0
+and
+.L 1
+with bytes
+.L 2
+and
+.LR 3 .
+.TP
+.L 4
+Swap bytes
+.L 0-3
+with bytes
+.LR 4-7 ,
+and so on.
+The largest native integral type is defined by the macro
+.L int_max
+in the header
+.L <int.h>
+described in
+.IR int (3).
+.PP
+.L swapget
+returns the
+.L width
+byte integer in the buffer
+.LR from ,
+swapped according to
+.LR op .
+.PP
+.L swapput
+copies the
+.L width
+byte integer
+.L value
+into the buffer
+.LR to ,
+swapped according to
+.LR op .
+.L to
+is returned.
+.PP
+.L swapmem
+swaps
+.L n
+bytes from the buffer
+.L from
+to the buffer
+.L to
+according to
+.LR op .
+.L to
+and
+.L from
+may be the same.
+.SH "SEE ALSO"
+int(3)
diff --git a/src/lib/libast/man/tab.3 b/src/lib/libast/man/tab.3
new file mode 100644
index 0000000..e1c76f3
--- /dev/null
+++ b/src/lib/libast/man/tab.3
@@ -0,0 +1,74 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TAB 3
+.SH NAME
+tab \- simple table lookup routines
+.SH SYNOPSIS
+.L "#include <ast.h>"
+.sp
+.L "int tabindex(const void* tab, int size, const char* name);"
+.L "void* tablook(const void* tab, int size, const char* name);"
+.SH DESCRIPTION
+These routines do linear lookups in
+.I small
+tables (on the order of 32 elements).
+Each table element has a size of
+.L size
+bytes and the beginning of the element points to a name that is
+matched by the lookup routines.
+.PP
+.L tabindex
+returns the index of the table element in
+.L tab
+that matches
+.LR name .
+If there is no match then
+.L \-1
+is returned.
+.PP
+.L tablook
+returns a pointer to the table element in
+.L tab
+that matches
+.LR name .
+If there is no match then
+.L 0
+is returned.
+.SH "SEE ALSO"
+hash(3)
diff --git a/src/lib/libast/man/tm.3 b/src/lib/libast/man/tm.3
new file mode 100644
index 0000000..12eb4ec
--- /dev/null
+++ b/src/lib/libast/man/tm.3
@@ -0,0 +1,775 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TM 3
+.SH NAME
+tm \- seconds resolution time conversion support
+.SH SYNOPSIS
+.L "#include <tm.h>"
+.SH DESCRIPTION
+The
+.I tm
+library supports conversion between
+string date specifications,
+seconds reolution
+.L time_t
+clock values and
+.LR Tm_t .
+.L Tm_t
+contains the elements of
+.L "struct tm"
+along with these additions:
+.TP
+.L "unsigned _ast_int4_t tm_nsec"
+The subsecond portion of the time in nanoseconds.
+.TP
+.L "Tm_zone_t* tm_zone"
+The time zone name.
+.PP
+.L localtime()
+and
+.L gmtime()
+(see
+.BR ctime (3))
+are used to determine local time zone and savings time information.
+.PP
+.L time_t
+values are the number of seconds since the epoch,
+.BR "Jan 1 00:00:00 GMT 1970" ,
+with leap seconds omitted.
+.PP
+The global variable
+.L "int tm_info.flags"
+contains flags that allow all programs using the library
+to be controlled in a consistent manner.
+.L tm_info.flags
+is initialized by the
+.L tminit()
+routine described below, and may be explicitly reset after
+.L tminit()
+is called.
+The flags are:
+.TP
+.L TM_ADJUST
+Set by
+.L tminit()
+if
+.L localtime()
+and
+.L gmtime()
+do not compensate for leap seconds.
+.TP
+.L TM_LEAP
+.L time_t
+values are interpreted as if they include leap seconds.
+Set by
+.L tminit()
+if the
+.L leap
+option is set in the
+.L TM_OPTIONS
+environment variable.
+.TP
+.L TM_UTC
+Times are relative to
+.B UTC
+(universal coordinated time, i.e.,
+.BR GMT .)
+Otherwise times are relative to the local time zone.
+Set by
+.L tminit()
+if the time zone name matches one of
+.L tm_info.format[43]
+through
+.L tm_info.format[46]
+described below.
+If the time zone name is not determined by
+.L localtime()
+then the environment variables
+.L TZNAME
+(as described in BSD 4.3) and
+.L TZ
+(as described in System V)
+are checked, in order.
+If this fails then the time zone name is constructed using
+the local time zone offset.
+.PP
+The routines are:
+.TP
+.L "time_t tmdate(const char* date, char** end, time_t* clock)"
+Parses the date specification
+.L date
+using the
+.L tm_info.format
+string table (described below)
+and returns the equivalent
+.L time_t
+value.
+If
+.RL non- NULL ,
+.L end
+is set to the position of the first unrecognized character in
+.LR date .
+.L clock
+is used to provide default values for omitted components in
+.LR date .
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+.TP
+.L "Tm_t* tmfix(Tm_t* tp)"
+Corrects any out of bounds fields in
+.L tp
+and returns
+.L tp
+as its value.
+The corrections start with
+.L tp->tm_sec
+and propagate down to
+.LR tp->tm_year .
+For example, if
+.L tp->tm_sec
+were 61 then it would change to 1 and
+.L tp->tm_min
+would be incremented by 1, and so on.
+.L tp->tm_isdst
+is not changed -- call
+.L tmtime()
+to determine its proper value after the
+.L tmfix()
+adjustments.
+.TP
+.L "char* tmfmt(char* buf, size_t len, const char* format, time_t* clock)"
+Formats the date pointed to by
+.L clock
+into the buffer
+.L buf
+with size
+.L len
+bytes according to the format specification
+.LR format .
+If
+.L format
+is
+.L NULL
+or empty then the string
+.L tm_info.format[40]
+is used.
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+A pointer to the end of
+.L buf
+(i.e., the terminating
+.LR "'\e0'" )
+is returned.
+.RS
+.PP
+.L format
+is in
+.BR printf (3)
+style, where
+.RI % field
+names a fixed size field, zero padded if necessary,
+and
+.I \ec
+and
+.I \ennn
+sequences are as in C. Invalid
+.RI % field
+specifications and all other characters are copied
+without change.
+.I field
+may be preceded by
+.B %-
+to turn off padding or
+.B %_
+to pad with space, otherwise numeric fields
+are padded with
+.B 0
+and string fields are padded with space.
+.I field
+may also be preceded by
+.B E
+for alternate era representation or
+.B O
+for alternate digit representation (if supported by the current locale.)
+Finally, an integral
+.I width
+preceding
+.I field
+truncates the field to
+.I width
+characters.
+sequences are interpreted as in the C language.
+String field values are taken from the
+.L tm_info.format
+string table.
+The
+.I fields
+are:
+.TP
+.PD 0
+.B %
+.B %
+character.
+.TP
+.B a
+Abbreviated weekday name.
+.TP
+.B A
+Full weekday name.
+.TP
+.B b
+Abbreviated month name.
+.TP
+.B c
+.BR ctime (3)
+style date without the trailing
+.BR newline .
+.TP
+.B C
+.BR date (1)
+style date.
+.TP
+.B d
+Day of month number.
+.TP
+.B D
+Date as
+.IR mm / dd / yy .
+.TP
+.B e
+Blank padded day of month number.
+.TP
+.B E
+Unpadded day of month number.
+.TP
+.B f
+Locale default override date format.
+.TP
+.B F
+Locale default date format
+.RL ( tm_info.format[40] .)
+.TP
+.B h
+Abbreviated month name.
+.TP
+.B H
+24-hour clock hour.
+.TP
+.B i
+International
+.BR date (1)
+date that includes the time zone type name
+.RL ( tm_info.format[107] .)
+.TP
+.B I
+12-hour clock hour.
+.TP
+.B j
+1-offset Julian date.
+.TP
+.B J
+0-offset Julian date.
+.TP
+.B k
+.BR date (1)
+style date
+.RL ( tm_info.format[106] .)
+.TP
+.B K
+Language neutral, all numeric, no embedded space date
+with larger to smaller time units from left to right,
+suitable for sorting:
+.LR '"%Y-%m-%d+%H:%M:%S"' .
+.TP
+.B l
+.BR ls (1)
+.B \-l
+date that lists recent dates with
+.L %g
+and distant dates with
+.BR %G .
+.TP
+.B m
+Month number.
+.TP
+.B M
+Minutes.
+.TP
+.B n
+.B newline
+character.
+.TP
+.B N
+The time zone type or nation code.
+.TP
+.B p
+Meridian (e.g.,
+.B AM
+or
+.BR PM .)
+.TP
+.B q
+The nanosecond part of the time.
+.TP
+\fB%Q\fP\fI<delim>recent<delim>distant<delim>\fP
+Recent dates are formatted with
+.I recent
+and distand dates are formatted with
+.IR distant ,
+where
+.I <delim>
+is any character not appearing in
+.I recent
+or
+.IR distant .
+.TP
+.B r
+12-hour time as
+.IR hh : mm : ss
+.IR meridian .
+.TP
+.B R
+24-hour time as
+.IR hh : mm .
+.TP
+.B s
+Seconds since the epoch.
+.RI . prec
+preceding
+.B s
+appends
+.I prec
+nanosecond digits,
+.B 9
+if
+.I prec
+is omitted.
+.TP
+.B S
+.I seconds.subseconds
+since the epoch.
+.TP
+.B t
+.B tab
+character.
+.TP
+.B T
+24-hour time as
+.IR hh : mm : ss .
+.TP
+.B u
+Weeday number with 1 for Monday, 7 for Sunday.
+.TP
+.B U
+Week number with Sunday as the first day.
+.TP
+.B V
+ISO week number (i18n is \fIfun\fP.)
+.TP
+.B w
+Weekday number with 0 for Sunday, 6 for Saturday.
+.TP
+.B W
+Week number with Monday as the first day.
+.TP
+.B x
+Local date style, using
+.LR tm_info.format[39] ,
+that includes the month, day and year.
+.TP
+.B X
+Local time style, using
+.LR tm_info.format[38] ,
+that includes the hours and minutes.
+.TP
+.B y
+2-digit year (you'll be sorry.)
+.TP
+.B Y
+4-digit year.
+.TP
+.B z
+Time zone
+.I SHHMM
+west of GMT offset where
+.I S
+is
+.B +
+or
+.BR - .
+.TP
+.B Z
+Time zone name.
+.TP
+=[=]][-+]]\fIflag\fP
+Set (default or +) or clear (-)
+.I flag
+in
+.L tm_info.flags
+for the remainder of
+.IR format ,
+or for the remainder of the process if
+.B ==
+is specified.
+.I flag
+may be:
+.RS
+.TP
+.B l
+.L (TM_LEAP)
+Enable leap second adjustments.
+.TP
+.B s
+.L (TM_SUBSECOND)
+Append nanosecond
+.B .%N
+to
+.BR %S .
+.TP
+.B u
+.L (TM_UTC)
+UTC time zone.
+.RE
+.TP
+.B #
+Equivalent to
+.BR %s .
+.TP
+\fP?\fP\fIalternate\fP
+Use
+.I alternate
+format is a default format override has not been specified.
+e.g.,
+.BR ls (1)
+uses
+.BR %?%l .
+Export
+\f5TM_OPTIONS="format='\fP\fIoverride\fP\f5'"\fP
+to override the default.
+.PD
+.RE
+.TP
+.L "void tminit(Tm_zone_t* zone)"
+Implicitly called by the other
+.I tm
+library routines to initialize global data, including the
+.L tm_info.format
+table and the
+.L tm_info.flags
+global flags.
+Global data should only be modified after an explicit call to
+.LR tminit .
+If
+.L "zone != 0"
+then it specifies a time zone other that the local time zone.
+.TP
+.L "void tmset(Tm_zone_t* zone);"
+.L tmset
+sets the reference timezoe to
+.LR zone .
+.L tm_info.local
+points to the local timezone and
+.L tm_info.zone
+points to the current reference timezone.
+.TP
+.L "time_t tmleap(time_t* clock)"
+Returns a
+.L time_t
+value for the time pointed to by
+.L clock
+with leap seconds adjusted for external
+routines that do not handle leap seconds.
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+Adjustments are only done if the
+.L TM_ADJUST
+flag is set in
+.LR tm_info.flags .
+.TP
+.L "Tm_t* tmmake(time_t* clock)"
+Returns a pointer to the
+.L Tm_t
+struct corresponding to the time pointed to by
+.LR clock .
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+.TP
+.L "time_t tmtime(Tm_t* tp, int west)"
+Returns the
+.L time_t
+value corresponding to
+.LR tp .
+If
+.L west
+is
+.L TM_LOCALZONE
+then
+.L tm
+is relative to the local time zone,
+otherwise
+.L west
+is the number of minutes west of
+.B UTC
+with daylight savings time taken into account.
+.LR tp->tm_wday ,
+.LR tp->tm_yday
+and
+.L tp->tm_isdst
+are ignored in the conversion.
+.PP
+The library routines use a table of date strings pointed to by
+.LR "char** tm_info.format" .
+The indices in
+.L tm_info.format
+are fixed by category.
+.L tm_info.format
+may be changed to point to other tables
+according to local language and date conventions.
+The contents by index (showing the USA English values) are:
+.RS
+.TP
+.PD 0
+.B 0-11
+3-character abbreviated month names.
+.TP
+.B 12-23
+Full month names.
+.TP
+.B 24-30
+3-character abbreviated weekday names.
+.TP
+.B 31-37
+Full weekday names.
+.TP
+.B 38
+.L tmfmt()
+local time format used by the
+.B %X
+field.
+.TP
+.B 39
+.L tmfmt()
+local date format used by the
+.B %x
+field.
+.TP
+.B 40
+.L tmfmt()
+format used if the
+.L format
+argument is
+.L NULL
+or empty.
+.TP
+.B 41-42
+Meridian names: AM, PM.
+.TP
+.B 43-46
+.B UTC
+time zone names: GMT, UTC, UCT, CUT.
+.TP
+.B 47-50
+Daylight savings time suffix names: DST.
+.TP
+.B 51-54
+Suffixes to be ignored when matching strings in
+.LR tmfmt() .
+.TP
+.B 55-61
+Time part names: second, hour, minute, day, week, month, year.
+.TP
+.B 62-65
+Hours of the day names: midnight, morning, noon, evening.
+.TP
+.B 66-68
+Relative day names: yesterday, today, tomorrow.
+.TP
+.B 69-71
+Past relative time references: last, ago, past.
+.TP
+.B 72-75
+Current relative time references: this, now, current.
+.TP
+.B 75-77
+Future relative time references: next, hence, coming.
+.TP
+.B 78-80
+Exact relative time references: exactly.
+.TP
+.B 81-84
+Noise words to be ignored: at, in, on.
+.TP
+.B 85-94
+Ordinal suffixes: st, nd, rd, th, th, th, th, th, th, th.
+.TP
+.B 95-104
+Digit names.
+.TP
+.B 105
+The
+.L tmfmt()
+format equivalent for
+.BR ctime (3):
+.LR '"%a %b %e %T %Y"' .
+.TP
+.B 106
+The
+.L tmfmt()
+.BR date (1)
+default format:
+.LR '"%a %b %e %T %Z %Y"' .
+.TP
+.B 107
+The
+.L tmfmt()
+.BR date (1)
+international format:
+.LR '"%a %b %e %T %z %Z %Y"' .
+.TP
+.B 108
+The
+.L tmfmt()
+.BR ls (1)
+recent date format:
+.LR '"%b %e %H:%M"' .
+.TP
+.B 109
+The
+.L tmfmt()
+.BR ls (1)
+distant date format:
+.LR '"%b %e %Y"' .
+.TP
+.B 110
+The
+.L tmfmt()
+.BR date (1)
+meridian date format:
+.LR '"%I:%M:%S %p"' .
+.TP
+.B 111
+The ERA name.
+.TP
+.B 112
+ERA alternative for
+.BR 39 .
+.TP
+.B 113
+ERA alternative for
+.BR 38 .
+.TP
+.B 114
+ERA alternative for
+.BR 40 .
+.TP
+.B 115
+The ERA year.
+.TP
+.B 116-125
+Ordinal names: first, \fIno second!\fP, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth.
+.TP
+.B 126-128
+Final time references, as in \fIthe last in the list\fP: final, ending, nth.
+.PD
+.RE
+.PP
+Low level support functions and data are described in
+.LR <tm.h> .
+.SH EXAMPLES
+.EX
+#include <tm.h>
+main() {
+ int i;
+ time_t t;
+ char buf[128];
+ struct {
+ char* date;
+ char* format;
+ } x[] = {
+ "now", "%i",
+ "2 months ago", "%C",
+ "this Wednesday noon", "%x %I:%M %p",
+ "last December 25", "%A",
+ 0, 0
+ };
+ for (i = 0; x[i].date; i++) {
+ t = tmdate(x[i].date, (char*)0, (time_t*)0);
+ (void)tmfmt(buf, sizeof(buf), x[i].format, &t);
+ puts(buf);
+ }
+}
+.EE
+produces
+.EX
+Fri Sep 30 12:10:14 USA EDT 1988
+Fri Jul 1 00:00:00 EDT 1988
+10/05/88 12:00 PM
+Friday
+.EE
+.SH "SEE ALSO"
+.BR date (1),
+.BR time (2),
+.BR ctime (3)
+.SH BUGS
+The C library static
+.L "struct tm"
+values may get clobbered by
+.I tm
+library routines as the
+.BR ctime (3)
+and
+.BR localtime (3)
+routines typically return pointers to a single static
+.L "struct tm"
+area.
+.L tmdate()
+uses an internal international time zone name table that will
+probably always be incomplete.
diff --git a/src/lib/libast/man/tmx.3 b/src/lib/libast/man/tmx.3
new file mode 100644
index 0000000..268d40a
--- /dev/null
+++ b/src/lib/libast/man/tmx.3
@@ -0,0 +1,576 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TM 3
+.SH NAME
+tm \- time conversion support
+.SH SYNOPSIS
+.L "#include <tm.h>"
+.SH DESCRIPTION
+The
+.I tm
+library supports conversion between
+string date specifications,
+.L time_t
+clock values and
+.L "struct tm"
+values.
+.L localtime()
+and
+.L gmtime()
+(see
+.IR ctime (3))
+are used to determine local time zone information.
+.PP
+.L time_t
+values are the number of seconds since the epoch,
+.BR "Jan 1 00:00:00 GMT 1970" ,
+with leap seconds omitted.
+.PP
+The global variable
+.L "int tm_info.flags"
+contains flags that allow all programs using the library
+to be controlled in a consistent manner.
+.L tm_info.flags
+is initialized by the
+.L tminit()
+routine described below, and may be explicitly reset after
+.L tminit()
+is called.
+The flags are:
+.TP
+.L TM_ADJUST
+Set by
+.L tminit()
+if
+.L localtime()
+and
+.L gmtime()
+do not compensate for leap seconds.
+.TP
+.L TM_LEAP
+.L time_t
+values are interpreted as if they include leap seconds.
+Set by
+.L tminit()
+if the
+.L leap
+option is set in the
+.L TM_OPTIONS
+environment variable.
+.TP
+.L TM_UTC
+Times are relative to
+.B UTC
+(universal coordinated time, i.e.,
+.BR GMT ).
+Otherwise times are relative to the local time zone.
+Set by
+.L tminit()
+if the time zone name matches one of
+.L tm_info.format[43]
+through
+.L tm_info.format[46]
+described below.
+If the time zone name is not determined by
+.L localtime()
+then the environment variables
+.L TZNAME
+(as described in BSD 4.3) and
+.L TZ
+(as described in System V)
+are checked, in order.
+If this fails then the time zone name is constructed using
+the local time zone offset.
+.PP
+The routines are:
+.TP
+.L "time_t tmdate(const char* date, char** end, time_t* clock)"
+Parses the date specification
+.L date
+using the
+.L tm_info.format
+string table (described below)
+and returns the equivalent
+.L time_t
+value.
+If
+.RL non- NULL ,
+.L end
+is set to the position of the first unrecognized character in
+.LR date .
+.L clock
+is used to provide default values for omitted components in
+.LR date .
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+.TP
+.L "struct tm* tmfix(struct tm* tp)"
+Corrects any out of bounds fields in
+.L tp
+and returns
+.L tp
+as its value.
+The corrections start with
+.L tp->tm_sec
+and propagate down to
+.LR tp->tm_year .
+For example, if
+.L tp->tm_sec
+were 61 then it would change to 1 and
+.L tp->tm_min
+would be incremented by 1, and so on.
+.LR tp->tm_wday ,
+.LR tp->tm_yday
+and
+.L tp->tm_isdst
+are not changed as these can be computed from the other fields.
+.TP
+.L "char* tmfmt(char* buf, size_t len, const char* format, time_t* clock)"
+Formats the date pointed to by
+.L clock
+into the buffer
+.L buf
+with size
+.L len
+bytes according to the format specification
+.LR format .
+If
+.L format
+is
+.L NULL
+or empty then the string
+.L tm_info.format[40]
+is used.
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+A pointer to the end of
+.L buf
+(i.e., the terminating
+.LR "'\e0'" )
+is returned.
+.RS
+.PP
+.L format
+is in the style of
+.IR printf (3),
+where
+.BI % field
+causes the corresponding fixed size field to be placed in
+.LR buf ,
+zero padded if necessary, and \e\fIc\fP and \e\fInnn\fP
+sequences are interpreted as in the C language.
+Otherwise invalid
+.BI % field
+specifications and all other characters in
+.L format
+are copied into
+.L buf
+without change.
+String field values are taken from the
+.L tm_info.format
+string table.
+The
+.I fields
+are:
+.TP
+.PD 0
+.B %
+.B %
+character.
+.TP
+.B a
+Abbreviated weekday name.
+.TP
+.B A
+Full weekday name.
+.TP
+.B b
+Abbreviated month name.
+.TP
+.B c
+.IR ctime (3)
+style date without the trailing
+.BR newline .
+.TP
+.B C
+.IR date (1)
+style date.
+.TP
+.B d
+Day of month number.
+.TP
+.B D
+Date as
+.IR mm / dd / yy .
+.TP
+.B e
+Blank padded day of month number.
+.TP
+.B E
+Unpadded day of month number.
+.TP
+.B h
+Abbreviated month name.
+.TP
+.B H
+24-hour clock hour.
+.TP
+.B i
+International
+.IR date (1)
+date that includes the time zone type name.
+.TP
+.B I
+12-hour clock hour.
+.TP
+.B j
+1-offset Julian date.
+.TP
+.B J
+0-offset Julian date.
+.TP
+.B l
+.IR ls (1)
+.B \-l
+date that lists recent dates with
+.IR hh : mm
+and distant dates with
+.IR yyyy .
+.TP
+.B m
+Month number.
+.TP
+.B M
+Minutes.
+.TP
+.B n
+.B newline
+character.
+.TP
+.B p
+Meridian (e.g.,
+.B AM
+or
+.BR PM ).
+.TP
+.B r
+12-hour time as
+.IR hh : mm : ss
+.IR meridian .
+.TP
+.B R
+24-hour time as
+.IR hh : mm .
+.TP
+.B S
+Seconds.
+.TP
+.B t
+.B tab
+character.
+.TP
+.B T
+24-hour time as
+.IR hh : mm : ss .
+.TP
+.B U
+Week number with Sunday as the first day.
+.TP
+.B w
+Weekday number.
+.TP
+.B W
+Week number with Monday as the first day.
+.TP
+.B x
+Local date style, using
+.LR tm_info.format[39] ,
+that includes the month, day and year.
+.TP
+.B X
+Local time style, using
+.LR tm_info.format[38] ,
+that includes the hours and minutes.
+.TP
+.B y
+2-digit year.
+.TP
+.B Y
+4-digit year.
+.TP
+.B z
+Time zone type name.
+.TP
+.B Z
+Time zone name.
+.TP
+.BI + flag
+.TP
+.BI \- flag
+Temporarily (until
+.L tmform()
+returns) sets (+) or clears (\-) the
+.L tm_info.flags
+flags specified by
+.IR flag :
+.RS
+.TP
+.B l
+.L TM_LEAP
+.TP
+.B u
+.L TM_UTC
+.RE
+.TP
+.B #
+Number of seconds since the epoch.
+.PD
+.RE
+.TP
+.L "void tminit(Tm_zone_t* zone)"
+Implicitly called by the other
+.I tm
+library routines to initialize global data, including the
+.L tm_info.format
+table and the
+.L tm_info.flags
+global flags.
+Global data should only be modified after an explicit call to
+.LR tminit .
+If
+.L "zone != 0"
+then it specifies a time zone other that the local time zone.
+.TP
+.L "void tmset(Tm_zone_t* zone);"
+.L tmset
+sets the reference timezoe to
+.LR zone .
+.L tm_info.local
+points to the local timezone and
+.L tm_info.zone
+points to the current reference timezone.
+.TP
+.L "time_t tmleap(time_t* clock)"
+Returns a
+.L time_t
+value for the time pointed to by
+.L clock
+with leap seconds adjusted for external
+routines that do not handle leap seconds.
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+Adjustments are only done if the
+.L TM_ADJUST
+flag is set in
+.LR tm_info.flags .
+.TP
+.L "struct tm* tmmake(time_t* clock)"
+Returns a pointer to the
+.L tm
+struct corresponding to the time pointed to by
+.LR clock .
+If
+.L clock
+is
+.L NULL
+then the current time is used.
+.TP
+.L "time_t tmtime(struct tm* tp, int west)"
+Returns the
+.L time_t
+value corresponding to
+.LR tp .
+If
+.L west
+is
+.L TM_LOCALZONE
+then
+.L tm
+is relative to the local time zone,
+otherwise
+.L west
+is the number of minutes west of
+.B UTC
+with daylight savings time taken into account.
+.LR tp->tm_wday ,
+.LR tp->tm_yday
+and
+.L tp->tm_isdst
+are ignored in the conversion.
+.PP
+The library routines use a table of date strings pointed to by
+.LR "char** tm_info.format" .
+The indices in
+.L tm_info.format
+are fixed by category.
+.L tm_info.format
+may be changed to point to other tables
+according to local language and date conventions.
+The contents by index (showing the USA English values) are:
+.RS
+.TP
+.PD 0
+.B 0-11
+3-character abbreviated month names.
+.TP
+.B 12-23
+Full month names.
+.TP
+.B 24-30
+3-character abbreviated weekday names.
+.TP
+.B 31-37
+Full weekday names.
+.TP
+.B 38
+.L tmform()
+local time format used by the
+.B %X
+field.
+.TP
+.B 39
+.L tmform()
+local date format used by the
+.B %x
+field.
+.TP
+.B 40
+.L tmform()
+format used if the
+.L format
+argument is
+.L NULL
+or empty.
+.TP
+.B 41-42
+Meridian names: AM, PM.
+.TP
+.B 43-46
+.B UTC
+time zone names: GMT, UTC, UCT, CUT.
+.TP
+.B 47-50
+Daylight savings time suffix names: DST.
+.TP
+.B 51-54
+Suffixes to be ignored when matching strings in
+.LR tmform() .
+.TP
+.B 55-61
+Time part names: second, hour, minute, day, week, month, year.
+.TP
+.B 62-65
+Hours of the day names: midnight, morning, noon, evening.
+.TP
+.B 66-68
+Relative day names: yesterday, today, tomorrow.
+.TP
+.B 69-71
+Past relative time references: last, ago, past.
+.TP
+.B 72-75
+Current relative time references: this, now, current.
+.TP
+.B 75-77
+Future relative time references: next, hence, coming.
+.TP
+.B 78-80
+Exact relative time references: exactly.
+.TP
+.B 81-85
+Noise words to be ignored: at, in, on.
+.PD
+.RE
+.PP
+Low level support functions and data are described in
+.LR <tm.h> .
+.SH EXAMPLES
+.EX
+#include <tm.h>
+main() {
+ int i;
+ time_t t;
+ char buf[128];
+ struct {
+ char* date;
+ char* format;
+ } x[] = {
+ "now", "%i",
+ "2 months ago", "%C",
+ "this Wednesday noon", "%x %I:%M %p",
+ "last December 25", "%A",
+ 0, 0
+ };
+ for (i = 0; x[i].date; i++) {
+ t = tmdate(x[i].date, (char*)0, (time_t*)0);
+ (void)tmform(buf, x[i].format, &t);
+ puts(buf);
+ }
+}
+.EE
+produces
+.EX
+Fri Sep 30 12:10:14 USA EDT 1988
+Fri Jul 1 00:00:00 EDT 1988
+10/05/88 12:00 PM
+Friday
+.EE
+.SH "SEE ALSO"
+date(1), time(2), ctime(3)
+.SH BUGS
+.L "struct tm"
+values may get clobbered by the
+.I tm
+library routines as the
+.IR ctime (3)
+routines typically return pointers to a single static
+.L "struct tm"
+area.
+.L tmdate()
+uses an internal international time zone name table that will
+probably always be incomplete.
diff --git a/src/lib/libast/man/tok.3 b/src/lib/libast/man/tok.3
new file mode 100644
index 0000000..46fbff9
--- /dev/null
+++ b/src/lib/libast/man/tok.3
@@ -0,0 +1,217 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TOK 3
+.SH NAME
+tok \- space separated token stream routines
+.SH SYNOPSIS
+.L "#include <ast.h>"
+.sp
+.L "void* tokopen(char* string)"
+.L "char* tokread(void* tok)"
+.L "void tokclose(void* tok)"
+.sp
+.L "int tokscan(char* string, char** next, const char* format, ...);"
+.sp
+.L "Sfio_t* tokline(const char* input, int flags, int* line);"
+.SH DESCRIPTION
+.L tokopen
+returns a pointer to a space separated token stream on the 0 terminated
+string
+.LR string .
+.L tokread
+returns a pointer to the next
+space separated token in the token stream
+.L tok
+as returned by
+.LR tokopen .
+0 is returned when no tokens remain.
+.L tokread
+temporarily modifies
+.L string
+by inserting 0's to terminate each token.
+.L tokclose
+closes the token stream and restores
+.L string
+to its original state.
+.PP
+.L tokscan
+scans the string
+.L string
+for tokens specified in
+.LR format .
+It is a more forgiving
+.IR sscanf (3).
+If
+.L "next != 0"
+then it will point to the next unread character in
+.L string
+on return.
+The number of scanned tokens is returned.
+.L \-1
+is returned if
+.L string
+was not empty and
+.L format
+failed to match and tokens.
+.PP
+.I space
+in
+.L format
+matches 0 or more
+.I space
+or
+.I tab
+characters.
+.I newline
+in format eats the remainder of the current line in
+.LR string .
+"...", '...' and \e\fIcharacter\fP quotes are interpreted.
+A quoted
+.I carriage-return
+is converted to
+.IR newline .
+.I newline
+in
+.L string
+is equivalent to end of string except when quoted.
+.I \enewline
+is a line splice.
+.PP
+.L %
+in
+.L format
+prefixes format conversion characters; each conversion character
+corresponds to a
+.L tokscan
+argument following the
+.L format
+argument.
+The format conversions are:
+.TP
+.L %c
+A single
+.LR char .
+.TP
+.L "%hd %d %ld"
+[short, int, long] base 10 integer.
+.TP
+.L "%hn %n %ln"
+[short, int, long] C-style base integer.
+.TP
+.L "%ho %o %lo"
+[short, int, long] base 8 integer.
+.TP
+.L %s
+String.
+.TP
+.L "%hu %u %lu"
+[short, int, long] C-style base unsigned integer.
+.TP
+.L %v
+The next two arguments are a pointer to a
+.L char**
+argument vector and the maximum number of elements in the vector.
+.TP
+.L "%hx %x %lx"
+[short, int, long] base 16 integer.
+.PP
+.L %s
+and
+.L %v
+data may also be counted length strings of the form
+\f5(\fIcount\fP:\fIdata\fP)\fR
+where
+.I count
+is the number of characters in
+.I data
+and the terminating
+.L )
+may also be a
+.IR tab ,
+or the data may be
+.L (null)
+which represents the
+.L NULL
+string.
+.PP
+.L tokline
+returns an
+.IR sfio (3)
+stream to a file or string that splices
+.I \enewline
+into single lines,
+allows "..." and '...' to quotes to span
+.I newlines
+(done by translating quoted
+.I newline
+to
+.IR carriage-return ;
+.L tokscan
+above converts quoted
+.I carriage-return
+back to
+.IR newline ),
+and deletes
+.I "# ... newline"
+comments.
+This is done by pushing an
+.I sfio
+discipline onto a string or file stream.
+Seeks are disabled on the resulting stream.
+If
+.L "flags == SF_READ"
+then
+.L input
+is a file name;
+If
+.L "flags == SF_STRING"
+then
+.L input
+is a 0 terminated string;
+otherwise
+.L input
+is an open
+.L Sfio_t*
+stream.
+If
+.L "line != 0"
+then it points to a line count that is initialized to 0
+and is incremented for each input line.
+.SH "SEE ALSO"
+sfio(3)
diff --git a/src/lib/libast/man/touch.3 b/src/lib/libast/man/touch.3
new file mode 100644
index 0000000..908a8b1
--- /dev/null
+++ b/src/lib/libast/man/touch.3
@@ -0,0 +1,68 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TOUCH 3
+.SH NAME
+touch \- set file access and modify times
+.SH SYNOPSIS
+.L "#include <ast.h>"
+.sp
+.L "int touch(const char* path, time_t atime, time_t mtime, int force);"
+.SH DESCRIPTION
+.L touch
+sets the access and modify times of the file named by
+.LR path .
+If
+.L "force != 0"
+then the file is created if it doesn't exist;
+otherwise the file is not created and
+.L \-1
+is returned.
+If
+.L "force < 0"
+then
+.L atime
+and
+.L mtime
+are taken verbatim; otherwise
+.L "(time_t)(-1)"
+retains the current value for the file and
+.L "(time_t)(0)"
+uses the current time.
+.SH CAVEATS
+By default the change time is always changed to the current time.
diff --git a/src/lib/libast/man/tv.3 b/src/lib/libast/man/tv.3
new file mode 100644
index 0000000..b90a559
--- /dev/null
+++ b/src/lib/libast/man/tv.3
@@ -0,0 +1,173 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH TM 3
+.SH NAME
+tv \- high resolution time support
+.SH SYNOPSIS
+.L "#include <tv.h>"
+.SH DESCRIPTION
+The
+.I tv
+library supports high resolution
+.B Tv_t
+time specifications.
+.SS Tv_t
+contains these elements:
+.TP
+.L unsigned
+.L _ast_int4_t
+.L tv_sec
+Seconds since the epoch.
+.TP
+.L unsigned
+.L _ast_int4_t
+.L tv_nsec
+Nanosecond resolution.
+.PP
+In practice resolution is much coarser than 1 nanosecond.
+Systems that only support 1 second resolution always set
+.L tv_nsec
+to 0.
+.SS "int tvgettime(Tv_t* tv)"
+Sets
+.L tv
+to the current time.
+.L 0
+is returned on success,
+.L -1
+on error.
+.SS "int tvsettime(const Tv_t* tv)"
+Sets the system time to
+.LR tv .
+The caller must have sufficient privilege.
+.L 0
+is returned on success,
+.L -1
+on error.
+.SS "int tvcmp(const Tv_t* av, const Tv_t* bv)"
+Compares the times
+.L av
+and
+.L bv
+and returns
+.L -1
+if
+.L av
+is less than
+.LR bv ,
+.L 0
+if
+.L av
+is equal to
+.LR bv ,
+and
+.L 1
+if
+.L av
+is greater than
+.LR bv .
+.SS "time_t tvgetatime(Tv_t* tv, const struct stat* st)"
+.SS "time_t tvgetmtime(Tv_t* tv, const struct stat* st)"
+.SS "time_t tvgetctime(Tv_t* tv, const struct stat* st)"
+These macros set
+.L tv
+to the
+.L st
+the access, modify, or change time, respectively.
+The seconds portion of
+.L tv
+is returned.
+.SS "time_t tvsetatime(Tv_t* tv, struct stat* st)"
+.SS "time_t tvsetmtime(Tv_t* tv, struct stat* st)"
+.SS "time_t tvsetctime(Tv_t* tv, struct stat* st)"
+These macros set the
+.L st
+access, modify, or change time, respectively, to
+.LR tv .
+The seconds portion of
+.L tv
+is returned.
+.SS "int tvtouch(const char* path, const Tv_t* av, const Tv_t* mv, const Tv_t* cv, int copy)"
+Sets the file
+.L path
+access time from
+.LR av ,
+modify time from
+.LR mv ,
+and change time from
+.LR cv .
+Any of
+.LR av ,
+.LR mv ,
+and
+.L cv
+may be 0; the corresponding file time will retain the previous value if
+.L path
+exists and
+.L copy
+is
+.L 1 ;
+otherwise the corresponding file time will be set to the current time.
+.L 0
+is returned on success,
+.L -1
+on error.
+.SS "int tvsleep(const Tv_t* tv, Tv_t* rv)"
+Pauses execution for
+.L tv
+time.
+.L 0
+is returned if the full
+.L tv
+amount has expired.
+Otherwise
+.L -1
+is returned and
+.LR rv ,
+if not 0, is set to the sleep time remaining.
+.SH "RETURN VALUE"
+Except for
+.LR tvcmp() ,
+an error return of
+.L -1
+also sets
+.L errno
+to the corresponding error code.
+.SH "SEE ALSO"
+tm(3)
diff --git a/src/lib/libast/man/vecargs.3 b/src/lib/libast/man/vecargs.3
new file mode 100644
index 0000000..29e492b
--- /dev/null
+++ b/src/lib/libast/man/vecargs.3
@@ -0,0 +1,126 @@
+.fp 5 CW
+.de Af
+.ds ;G \\*(;G\\f\\$1\\$3\\f\\$2
+.if !\\$4 .Af \\$2 \\$1 "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+..
+.de aF
+.ie \\$3 .ft \\$1
+.el \{\
+.ds ;G \&
+.nr ;G \\n(.f
+.Af "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7" "\\$8" "\\$9"
+\\*(;G
+.ft \\n(;G \}
+..
+.de L
+.aF 5 \\n(.f "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de LR
+.aF 5 1 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de RL
+.aF 1 5 "\\$1" "\\$2" "\\$3" "\\$4" "\\$5" "\\$6" "\\$7"
+..
+.de EX \" start example
+.ta 1i 2i 3i 4i 5i 6i
+.PP
+.RS
+.PD 0
+.ft 5
+.nf
+..
+.de EE \" end example
+.fi
+.ft
+.PD
+.RE
+.PP
+..
+.TH VECARGS 3
+.SH NAME
+vecargs \- command argument vector insertion routines
+.SH SYNOPSIS
+.L "#include <vecargs.h>"
+.sp
+.L "char** vecload(char* string);"
+.L "char** vecfile(const char* path);"
+.L "char** vecstring(const char* string);"
+.L "void vecfree(char**, int);"
+.L "int vecargs(char** vec, int* argcp, char*** argvp);"
+.SH DESCRIPTION
+.L vecload
+loads a string vector from lines in
+.LR string .
+.L string
+may be modified upon return.
+Each line in
+.L string
+is treated as a new vector element.
+Lines with
+.L #
+as the first character are comments.
+.I \enewline
+joins consecutive lines.
+A string vector pointer is returned, 0 on error.
+.PP
+.L vecfile
+constructs a string vector by calling
+.L vecload
+on the contents of the file named by
+.LR path .
+The string vector pointer is returned, 0 on error.
+.PP
+.L vecstring
+constructs a string vector by calling
+.L vecload
+on a copy of
+.LR string .
+The string vector pointer is returned, 0 on error.
+.PP
+.L vecfree
+frees a string vector allocated by
+.LR vecfile ,
+.L vecload
+or
+.LR vecstring .
+.PP
+.L vecargs
+inserts the string vector
+.L vec
+(as returned by
+.LR vecfile ,
+.L vecload
+or
+.LR vecstring )
+between
+.L "(*argvp)[0]"
+and
+.LR "(*argvp)[1]" ,
+sliding
+.L "(*argvp)[1] ..."
+over.
+NULL and empty string args in
+.L vec
+are not copied.
+.L "vecfree(vec)"
+is called before the return.
+.L \-1
+is returned if the insertion failed.
+.SH EXAMPLES
+.L vecargs
+is commonly used to modify command
+.L argv
+from fixed files.
+For example,
+.IR make (1)
+checks for the files
+.L ./Makeargs
+and
+.L ./makeargs
+to modify its arguments on startup.
+Its a handy way to override default options on a directory by directory basis
+without modify the standard control files
+(\f5Makefile\fP in this case.)
+.SH CAVEATS
+This paradigm is not recommended for all commands; only a few exceptions
+make sense.
diff --git a/src/lib/libast/man/vmalloc.3 b/src/lib/libast/man/vmalloc.3
new file mode 100644
index 0000000..3bc6413
--- /dev/null
+++ b/src/lib/libast/man/vmalloc.3
@@ -0,0 +1,640 @@
+.fp 5 CW
+.de MW
+\f5\\$1\fP
+..
+.TH VMALLOC 3 "1 May 1998"
+.SH NAME
+vmalloc \- virtual memory allocation
+.SH SYNOPSIS
+.MW "#include <vmalloc.h>"
+.SS Regions
+.nf
+.MW "Vmalloc_t* vmopen(Vmdisc_t* disc, Vmethod_t* meth, int flags);"
+.MW "int vmclose(Vmalloc_t*);"
+.MW "int vmclear(Vmalloc_t*);"
+.MW "int vmcompact(Vmalloc_t* region);"
+.MW "int vmset(Vmalloc_t* region, int flags, int type);"
+.MW "Vmalloc_t* Vmheap;"
+.MW "Vmdisc_t* vmdisc(Vmalloc_t* region, Vmdisc_t* disc);"
+.MW "Vmalloc_t* vmmopen(char* file, int project, ssize_t size);"
+.MW "Void_t* vmmvalue(Vmalloc_t* vm, int key, Void_t* value, int op);"
+.MW "Void_t* vmmcleanup(Vmalloc_t* vm);"
+.MW "Void_t* vmmaddress(size_t size);"
+.fi
+.SS "Allocation functions"
+.nf
+.MW "Void_t* vmalloc(Vmalloc_t* region, size_t size);"
+.MW "Void_t* vmalign(Vmalloc_t* region, size_t size, size_t align);"
+.MW "Void_t* vmresize(Vmalloc_t* region, Void_t* addr, size_t size, int type);"
+.MW "int vmfree(Vmalloc_t* region, Void_t* addr);"
+.MW "Void_t* vmnewof(Vmalloc_t* region, Void_t* addr, type, size_t n, size_t x);"
+.MW "Void_t* vmoldof(Vmalloc_t* region, Void_t* addr, type, size_t n, size_t x);"
+.MW "Void_t* vmgetmem(Vmalloc_t* region, Void_t* addr, size_t size);"
+.fi
+.SS "Debugging"
+.nf
+.MW "int vmdebug(int);"
+.MW "int vmdbcheck(Vmalloc_t* vm);"
+.MW "int vmdbwatch(Void_t* addr);"
+.MW "static void vmdbwarn(Vmalloc_t*, char* mesg, int n);"
+.fi
+.SS "Profiling"
+.nf
+.MW "void vmprofile(Vmalloc_t* vm, int fd);"
+.fi
+.SS "Information and statistics"
+.nf
+.MW "int vmbusy(Vmalloc_t* region);"
+.MW "Vmalloc_t* vmregion(Void_t* addr);"
+.MW "Void_t* vmsegment(Vmalloc_t* region, Void_t* addr);"
+.MW "int vmwalk(Vmalloc_t* region, int(*walkf)(Vmalloc_t*, Void_t*, size_t, Vmdisc_t*);"
+.MW "long vmaddr(Vmalloc_t* region, Void_t* addr);"
+.MW "long vmsize(Vmalloc_t* region, Void_t* addr);"
+.MW "int vmstat(Vmalloc_t* vm, Vmstat_t* statb);"
+.MW "int vmtrace(int fd);"
+.MW "int vmtrbusy(Vmalloc_t* vm);"
+.MW "Void_t* vmdata(Vmalloc_t* vm);"
+.fi
+.SS "Malloc-compatible functions"
+.nf
+.MW "Void_t* malloc(size_t size);"
+.MW "Void_t* realloc(Void_t* addr, size_t size);"
+.MW "Void_t* calloc(size_t n_obj, size_t s_obj);"
+.MW "int cfree(Void_t* addr);"
+.MW "void free(Void_t* addr);"
+.MW "Void_t* memalign(size_t align, size_t size);"
+.MW "Void_t* valloc(size_t size);"
+.MW "int setregmax(int regmax);"
+.fi
+.SH DESCRIPTION
+These functions for dynamic storage allocation work in
+\fIregions\fP of memory.
+Each region has an \fIallocation method\fP
+for parceling out blocks of storage and a
+\fImemory discipline\fP for obtaining raw space.
+Automatic locking prevents interference by reentrant
+access to a region.
+.PP
+Pointers to space have type \f5Void_t*\fP
+where \f5Void_t\fP is \f5#define\fPd as \f5void\fP if possible, otherwise \f5char\fP.
+Space is counted in type \f5size_t\fP.
+
+.ne 4
+.SS Regions
+Regions have type \f5Vmalloc_t\fP.
+Two predefined regions are pointed to by:
+.TP
+.MW Vmheap
+A general-purpose region, with best-fit
+allocation, and system memory discipline \f5Vmdcsystem\fP.
+.PP
+These functions manipulate regions:
+.PP
+.I vmopen
+creates a region with memory discipline \fIdisc\fP,
+allocation method \fImeth\fP,
+and a setting for control \fIflags\fP.
+It returns a pointer to the region on success and \f5NULL\fP on failure.
+The flags, represented by bit values or-ed together, are:
+.TP
+.MW VM_SHARE
+This region may be accessed concurrently by multiple threads or processes.
+.TP
+.MW VM_TRACE
+Place tracing messages for each allocation event
+on the tracing file established by \fIvmtrace\fP.
+.TP
+\f5VM_DBCHECK\fP, \f5VM_DBABORT\fP
+.br
+See \fBDebugging\fP below.
+.PP
+.I vmclose
+closes a \fIregion\fP and releases all associated memory
+according to the region's discipline.
+The first segment obtained from the discipline's
+\f5memoryf\fP function (see `Disciplines' below) will be the last released.
+\fIvmclose\fP returns \-1 on failure and a non-negative value otherwise.
+.PP
+.I vmclear
+frees all allocated blocks in \fIregion\fP regardless of methods.
+It returns \-1 on failure and a non-negative value otherwise.
+.PP
+.I vmcompact
+releases as much of a \fIregion\fP's
+free space to its discipline's \f5memoryf\fP
+function as possible.
+It returns a nonnegative value on success and \-1 on failure.
+.PP
+.I vmset
+adjusts and queries a \fIregion\fP's \fIflags\fP.
+The indicated flags are turned on if \fItype\fP is nonzero, off if zero.
+\fIvmset\fP returns the previous value of all flags.
+Thus, \f5vmset(region,0,0)\fP queries the flags without changing them.
+In addition to the settable flags, one of
+\f5VM_MTBEST\fP, \f5VM_MTDEBUG\fP, \f5VM_MTPROFILE\fP,
+\f5VM_MTPOOL\fP, or \f5VM_MTLAST\fP
+is returned to indicate the method used in creating the \fIregion\fP.
+.PP
+.I vmdisc
+changes the discipline of \fIregion\fP to the given new discipline
+\fIdisc\fP if \fIdisc\fP is not \f5NULL\fP and its \f5memoryf\fP function
+is the same as the current discipline. If the current discipline
+has an \f5exceptf\fP function, it will be called with event \f5VM_DISC\fP.
+This function always returns the current discipline.
+.PP
+.I vmmopen
+creates a region to allocate memory obtained via either
+\fImmap(2)\fP when \fIproject < 0\fP or \fIshmget(2)\fP when \fIproject >= 0\fP.
+The region is built from a single memory segment
+guaranteed to be at least as large as \fIsize\fP.
+When \fIproject >= 0\fP,
+\fIfile\fP and \fIproject\fP are used in a call to \fIftok(3)\fP
+to get a key suitable for getting a shared memory segment via \fIshmget(2)\fP.
+Otherwise, \fIfile\fP is the backing store for the mapped data.
+In this case, not only the region may be used concurrently by different processes,
+it is also persistent. That is, process could even exit, move the file to
+a different but similar machine then restart and open the same
+region to continue working.
+.PP
+Note that Vmalloc can protect concurrent accesses only on region entry and exit
+for memory allocation operations.
+This means that at the time when regions are being opened or closed, there will be no
+protection for the memory segments being attached into or detached from process memory space.
+This limitation has a special impact on \fIvmmopen()\fP as follows.
+.PP
+A shared memory segment opened via \fIvmmopen()\fP corresponds uniquely
+to a combination of the \fIfile\fP and \fIproject\fP parameters.
+Thus, if multiple \fIvmmopen()\fP calls are done in the same process using a
+same combination of \fIfile\fP and \fIproject\fP,
+the joined behavior of such regions will be unpredictable when opening and closing
+are done concurrently with other memory allocation operations.
+Beware that this effect can be subtle with library functions that may attempt
+to create their own memory allocation regions.
+.PP
+.I vmmvalue
+manages pairs of \fIkey\fP and \fIvalue\fP in a region opened via \fIvmopen()\fP.
+If \fIop\fP is \f5VM_MMGET\fP, the value associated with \f5key\fP is returned.
+If \fIop\fP is \f5VM_MMSET\fP, the value associated with \f5key\fP will be
+set to \fIvalue\fP.
+If \fIop\fP is \f5VM_MMADD\fP, the value associated with \f5key\fP will be
+treated as a signed long value to which \f5val\fP (also treated as a signed long value)
+will be added.
+The call always returns the updated data value associated with \fIkey\fP.
+.PP
+.I vmmcleanup
+sets region up to remove backing store or \fIshmid\fP on closing.
+.PP
+.I vmmaddress
+computes an address suitable for attaching a shared memory segment or
+memory mapping a segment of file data of the given \fIsize\fP.
+The address is chosen with hope to minimize collision with other activities
+related to memory such as growth of stack space or space used
+for dynamically linked libraries, etc.
+
+.SS "Allocation functions"
+.I vmalloc
+returns a pointer to a block of the requested \fIsize\fP
+in a \fIregion\fP, aligned to the \fIstrictest alignment\fP
+that is suitable for the needs of any basic data type.
+It returns \f5NULL\fP on failure.
+.PP
+.I vmalign
+works like \fIvmalloc\fP, but returns a block aligned to a common
+multiple of \fIalign\fP and the \fIstrictest alignment\fP.
+.PP
+.I vmresize
+attempts to change the length of the block pointed to by
+\fIaddr\fP to the specified \fIsize\fP.
+If that is impossible and \fItype\fP has
+at least one of \f5VM_RSMOVE\fP and \f5VM_RSCOPY\fP,
+a new block is allocated and the old block is freed.
+The bit \f5VM_RSCOPY\fP also causes
+the new block to be initialized with
+as much of the old contents as will fit.
+When a resized block gets larger, the new space will be cleared
+if \fItype\fP has the bit \f5VM_RSZERO\fP.
+\fIvmresize\fP
+returns a pointer to the final block, or \f5NULL\fP on failure.
+If \fIaddr\fP is \f5NULL\fP, \fIvmresize\fP behaves like \fIvmalloc\fP;
+otherwise, if \fIsize\fP is 0, it behaves like \fIvmfree\fP.
+.PP
+.I vmfree
+makes the currently allocated block pointed to by
+\fIaddr\fP available for future allocations in its \fIregion\fP.
+If \fIaddr\fP is \f5NULL\fP, \fIvmfree\fP does nothing.
+It returns \-1 on error, and nonnegative otherwise.
+.PP
+.I vmnewof
+is a macro function that attempts to change the length of
+the block pointed to by \fIaddr\fP to the size \f5n*sizeof(type)+x\fP.
+If the block is moved, new space will be initialized with as much of the
+old content as will fit.
+Additional space will be set to zero.
+.PP
+.I vmoldof
+is similar to \fIvmnewof\fP but it neither copies data nor clears space.
+.PP
+.I vmgetmem
+provides a handy function to creat/close regions and allocate/free memory
+based on chunks of memory obtained from the heap region \fIVmheap\fP.
+.TP
+.MW "vmgetmem(0,0,0)"
+This call opens a new region.
+.TP
+.MW "vmgetmem(region, 0, 0)"
+This call closes the given \f5region\fP.
+.TP
+.MW "vmgetmem(region,0,n)"
+This call allocates a block of length \f5n\fP and clears it to zeros.
+.TP
+.MW "vmgetmem(region,p,0)"
+This call frees the block \f5p\fP.
+.TP
+.MW "vmgetmem(region,p,n)"
+This call resizes the block \f5p\fP to length \f5n\fP
+and clears the new memory to zeros if the block grows.
+The block may be moved as deemed necessary by the allocator.
+.PP
+.SS "Memory disciplines"
+Memory disciplines have type \f5Vmdisc_t\fP,
+a structure with these members:
+.in +.5i
+.nf
+.MW "Void_t* (*memoryf)(Vmalloc_t *region, Void_t* obj,"
+.ti +.5i
+.MW "size_t csz, size_t nsz, Vmdisc_t *disc);"
+.MW "int (*exceptf)(Vmalloc_t *region, int type, Void_t* obj, Vmdisc_t *disc);"
+.MW "int round;"
+.fi
+.in -.5i
+.TP
+.MW round
+If this value is positive, all size arguments to the
+\f5memoryf\fP function will be multiples of it.
+.TP
+.MW memoryf
+Points to a function to get or release segments of space for the
+\fIregion\fP.
+.TP
+.MW exceptf
+If this pointer is not \f5NULL\fP,
+the function it points to is called to announce
+events in a \fIregion\fP.
+.PP
+There are two standard disciplines, both with \f5round\fP being 0 and \f5exceptf\fP being \f5NULL\fP.
+.TP
+.MW Vmdcsystem
+A discipline whose \f5memoryf\fP function gets space from the operation system
+via different available methods which include \fImmap(2)\fP, \fIsbrk(2)\fP and
+functions from the WIN32 API.
+For historical reason, \fIVmdcsbrk\fP is also available and functions like \fIVmdcsystem\fP.
+.TP
+.MW Vmdcheap
+A discipline whose \f5memoryf\fP function gets space from the region \f5Vmheap\fP.
+A region with \f5Vmdcheap\fP discipline and \f5Vmlast\fP
+allocation is good for building throwaway data structures.
+.PP
+A \fImemoryf\fP
+function returns a pointer to a memory segment on success, and \f5NULL\fP on failure.
+When \fInsz >= 0\fP and \fIcsz > 0\fP,
+the function first attempts to change the current segment \fIaddr\fP to fit \fInsz\fP
+(for example, \fInsz == 0\fP means deleting the segment \fIaddr\fP).
+If this attempt is successful, it should return \fIaddr\fP.
+Otherwise, if \fInsz > csz\fP, the function may try to allocate a new segment
+of size \fInsz-csz\fP. If successful, it should return the address of the new segment.
+In all other cases, it should return NULL.
+.PP
+An \fIexceptf\fP
+function is called for events identified by \fItype\fP, which is coded thus:
+.TP
+.MW VM_OPEN
+This event is raised at the start of the process to open a new region.
+Argument \fIobj\fP will be a pointer to an object of type \f5Void_t*\fP
+initialized to NULL before the call. The return value of \fIexceptf\fP
+is significant as follows:
+
+On a negative return value, \fIvmopen\fP will terminate with failure.
+
+On a zero return value, \fIexceptf\fP may set \f5*((Void_t**)obj)\fP
+to some non-NULL value to tell \fIvmopen\fP
+to allocate the region handle itself via \fImemoryf\fP. Otherwise,
+the region handle will be allocated from the \f5Vmheap\fP region.
+
+On a positive return value,
+the new region is being reconstructed
+based on existing states of some previous region.
+In this case, \fIexceptf\fP should set \f5*(Void_t**)\fP\fIobj\fP to point to
+the field \f5Vmalloc_t.data\fP of the corresponding previous region
+(see \f5VM_CLOSE\fP below).
+If the handle of the previous region was allocated
+via \fImemoryf\fP as discussed above in the case of the zero return value,
+then it will be exactly restored. Otherwise, a new handle will be allocated from \f5Vmheap\fP.
+The ability to create regions sharing the same states allows for
+managing shared and/or persistent memory.
+.TP
+.MW VM_ENDOPEN
+This event is raised at the end of the process to open a new region.
+The return value of \fIexceptf\fP will be ignored.
+.TP
+.MW VM_CLOSE
+This event is raised at the start of the process to close a region,
+The return value of \fIexceptf\fP is significant as follows:
+
+On a negative return value, \fIvmclose\fP immediately returns with failure.
+
+On a zero return value, \fIvmclose\fP proceeds normally by calling \f5memoryf\fP to free
+all allocated memory segments and also freeing the region itself.
+
+On a positive return value, \fIvmclose\fP will only free the region
+without deallocating the associated memory segments. That is,
+the field \fIVmalloc_t.data\fP of the region handle remains intact.
+This is useful for managing shared and/or persistent memory (see \f5VM_OPEN\fP above).
+.TP
+.MW VM_ENDCLOSE
+This event is raised at the end of the process to close a region.
+The return value of \fIexceptf\fP will be ignored.
+.TP
+.MW VM_NOMEM
+An attempt to extend the region by the amount
+\f5(size_t)\fP\fIobj\fP failed. The region is unlocked, so the
+\fIexceptf\fP function may free blocks.
+If the function returns a positive value the memory
+request will be repeated.
+.TP
+.MW VM_DISC
+The discipline structure is being changed.
+
+.SS "Allocation methods"
+Methods are of type \f5Vmethod_t*\fP.
+.TP
+.MW Vmbest
+An approximately best-fit allocation strategy.
+.TP
+.MW Vmlast
+A strategy for building structures that are only deleted in whole.
+Only the latest allocated block can be freed.
+This means that as soon as a block \f5a\fP is allocated,
+\fIvmfree\fP calls on blocks other than \c5a\fP are ignored.
+.TP
+.MW Vmpool
+A strategy for blocks of one size,
+set by the first \fIvmalloc\fP call after \fIvmopen\fP or \fIvmclear\fP.
+.TP
+.MW Vmdebug
+An allocation strategy with extra-stringent checking and locking.
+It is useful for finding misuses of dynamically allocated
+memory, such as writing beyond the boundary of a block, or
+freeing a block twice.
+.ne 3
+.TP
+.MW Vmprofile
+An allocation method that records and prints summaries of memory usage.
+
+.SS Debugging
+The method \f5Vmdebug\fP is used to debug common memory violation problems.
+When a problem is found,
+a warning message is written to file descriptor 2 (standard error).
+In addition, if flag \f5VM_DBABORT\fP is on,
+the program is terminated by calling \fIabort\fP(2).
+Each message is a line of self-explanatory fields separated by colons.
+The optional flag \f5-DVMFL\fP, if used during compilation,
+enables recording of file names and line numbers.
+The following functions work with method \f5Vmdebug\fP.
+.PP
+.I vmdebug
+resets the file descriptor to write out warnings to the given argument.
+By default, this file descriptor is 2, the standard error.
+\fIvmdebug\fP returns the previous file descriptor.
+.PP
+.I vmdbcheck
+checks a region using \f5Vmdebug\fP or \f5Vmbest\fP for integrity.
+If \f5Vmdebug\fP, this also checks for block overwriting errors.
+On errors, \fIvmdbwarn\fP is called.
+If flag \f5VM_DBCHECK\fP is on,
+\fIvmdbcheck\fP is called at each invocation of
+\fIvmalloc\fP, \fIvmfree\fP, or \fIvmresize\fP.
+.PP
+.I vmdbwatch
+causes address \fIaddr\fP
+to be watched, and reported whenever met in
+\fIvmalloc\fP, \fIvmresize\fP or \fIvmfree\fP.
+The watch list has finite size and if it becomes full,
+watches will be removed in a first-in-first-out fashion.
+If \fIaddr\fP is \f5NULL\fP,
+all current watches are canceled.
+\fIvmdbwatch\fP returns the watch bumped out due to an insertion
+into a full list or \f5NULL\fP otherwise.
+.PP
+.I vmdbwarn
+is an internal function that processes
+warning messages for discovered errors.
+It can't be called from outside the \fIvmalloc\fP package,
+but is a good place to plant debugger traps because
+control goes there at every trouble.
+
+.SS "Profiling"
+The method \f5Vmprofile\fP is used to profile memory usage.
+Profiling data are maintained in private memory of a process so
+\f5Vmprofile\fP should be avoided from regions manipulating
+persistent or shared memory.
+The optional flag \f5-DVMFL\fP, if used during compilation,
+enables recording of file names and line numbers.
+.PP
+.I vmprofile
+prints memory usage summary.
+The summary is restricted to region \fIvm\fP if \fIvm\fP is not \f5NULL\fP;
+otherwise, it is for all regions created with \f5Vmprofile\fP.
+Summary records are written to file descriptor \fIfd\fP as lines with
+colon-separated fields. Here are some of the fields:
+.TP
+.I n_alloc,n_free:
+Number of allocation and free calls respectively. Note that a resize
+operation is coded as a free and an allocation.
+.TP
+.I s_alloc,s_free:
+Total amounts allocated and freed. The difference between these numbers
+is the amount of space not yet freed.
+.TP
+.I max_busy, extent:
+These fields are only with the summary record for region.
+They show the maximum busy space at any time and the extent of the region.
+
+.SS "Information and statistics"
+.I vmbusy
+returns the busy status of a region.
+A region is busy if some allocation operation is accessing it.
+.PP
+.I vmregion
+returns the region to which the block pointed to by
+\fIaddr\fP belongs.
+This works only in regions that allocate with
+\f5Vmbest\fP, \f5Vmdebug\fP or \f5Vmprofile\fP.
+.PP
+.I vmsegment
+finds if some segment of memory in \fIregion\fP
+contains the address \fIaddr\fP.
+It returns the address of a found segment or \f5NULL\fP if none found.
+.PP
+.I vmwalk
+walks all segments in \fIregion\fP or if \fIregion\fP is \f5NULL\fP,
+all segments in all regions.
+At each segment, \fI(*walkf)(vm,addr,size,disc)\fP
+is called where \fIvm\fP is the region, \fIaddr\fP is the segment,
+\fIsize\fP is the size of the segment, and \fIdisc\fP is the region's discipline.
+If \fIwalkf\fP returns a negative value, the walk stops and returns the same value.
+On success, \fIvmwalk\fP returns 0; otherwise, it returns \-1.
+.PP
+.I vmaddr
+checks whether \fIaddr\fP
+points to an address within some allocated block of the given region.
+If not, it returns \-1.
+If so, it returns the offset from the beginning of the block.
+The function does not work for a \f5Vmlast\fP region except
+on the latest allocated block.
+.PP
+.I vmsize
+returns the size of the allocated block pointed to by \fIaddr\fP.
+It returns \-1 if \fIaddr\fP
+does not point to a valid block in the region.
+Sizes may be padded beyond that requested; in
+particular no block has size 0.
+The function does not work for a \f5Vmlast\fP region except
+on the latest allocated block.
+.PP
+.I vmstat
+gathers statistics on the given \fIregion\fP.
+If \f5region\fP is NULL, it computes statistics for the \fIMalloc\fP calls.
+This may include summing statistics from more than one regions constructed to avoid blocking
+due to parallel or asynchronous operations.
+If \fIstatb\fP is not NULL, \fIvmstat\fP computes and stores the statistics in \fIstatb\fP then returns 0.
+If \fIstatb\fP is NULL, no statistics will be computed and
+the returned value is either 1 if the region is busy, i.e.,
+being accessed by some allocation call or 0 otherwise.
+
+A \f5Vmstat_t\fP structure has at least these members:
+.in +.5i
+.nf
+.ta \w'\f5size_t \fP'u +\w'\f5extent \fP'u
+.MW "int n_busy; /* # of busy blocks */
+.MW "int n_free; /* # of free blocks */
+.MW "size_t s_busy; /* total busy space */
+.MW "size_t s_free; /* total free space */
+.MW "size_t m_busy; /* maximum busy block size */
+.MW "size_t m_free; /* maximum free block size */
+.MW "int n_seg; /* count of segments */
+.MW "size_t extent; /* memory extent of region */
+.MW "int n_region; /* total Malloc regions */
+.MW "int n_open; /* non-blocked operations */
+.MW "int n_lock; /* blocked operations */
+.MW "int n_probe; /* region searches */
+.fi
+.in -.5i
+.PP
+Bookeeping overhead is counted in \f5extent\fP,
+but not in \f5s_busy\fP or \f5s_free\fP.
+.PP
+.I vmtrace
+establishes file descriptor \fIfd\fP
+as the trace file and returns
+the previous value of the trace file descriptor.
+The trace descriptor is initially invalid.
+Output is sent to the trace file by successful allocation
+events when flag \f5VM_TRACE\fP is on.
+.PP
+Tools for analyzing traces are described in \fImtreplay\fP(1).
+The trace record for an allocation event
+is a line with colon-separated fields, four numbers and one string.
+.TP
+.I old
+Zero for a fresh allocation;
+the address argument for freeing and resizing.
+.TP
+.I new
+Zero for freeing;
+the address returned by allocation or resizing.
+.TP
+.I size
+The size argument for allocation or resizing;
+the size freed by freeing.
+Sizes may differ due to padding for alignment.
+.TP
+.I region
+The address of the affected region.
+.TP
+.I method
+A string that tells the region's method:
+\f5best\fP, \f5last\fP, \f5pool\fP, \f5profile\fP, or \f5debug\fP.
+.PP
+.I vmtrbusy
+outputs a trace of all currently busy blocks in region \f5vm\fP.
+This only works with the \f5Vmbest\fP, \f5Vmdebug\fP and \f5Vmprofile\fP methods.
+.PP
+.I vmdata
+returns the core data of the given region.
+The core data hold the data structures for allocated and free blocks.
+Depending on the region discipline,
+the core data of a region may be in shared or persistent memory even
+if the region pointer created with \fIvmopen\fP is always in private process memory.
+
+.SS "Malloc-compatible functions"
+This set of functions implement \fImalloc\fP(3).
+They allocate via the \fIVmregion\fP region which is initially set
+to be \fIVmheap\fP.
+
+Concurrent accesses are supported unless an application
+change \fIVmregion\fP to something other than \fIVmheap\fP.
+New regions may be created on the fly to avoid blocking.
+The maximum number of regions that can be created
+this way is set to 64 by default. An application could
+reduce this number by calling \fIsetregmax(regmax)\fP to
+set the maximum number of these extra regions to \fIregmax\fP.
+\fIsetregmax()\fP always returns the previous value.
+.PP
+These functions are instrumented for run-time debugging, profiling and tracing.
+For accurate reporting of files and line numbers,
+application code should include \f5vmalloc.h\fP and compile with \f5-DVMFL\fP.
+The following environment variables can be set before any memory allocation
+(e.g., before a process starts) to drive different modes:
+.TP
+.I VMETHOD
+This defines the method to use for allocation.
+Its value should be one of the strings:
+\fIVmbest, Vmdebug, Vmprofile, Vmlast, Vmpool\fP.
+The 'V' can be in lower case.
+.TP
+.I VMDEBUG
+This is ignored if
+a method other than \f5Vmdebug\fP has been selected with \fIVMETHOD\fP.
+\fIVMDEBUG\fP can be any combination of `a',
+a decimal number and a list of hexadecimal numbers.
+`a' causes the program to abort on any discovered allocation error.
+A hexadecimal number starts with either \fI0x\fP or \fI0X\fP
+and defines an address to watch (see \fIvmdbwatch\fP).
+Any other number is taken to be decimal and defines a period \fIp\fP
+to check the arena for integrity. The default period is 1, ie, the
+arena is checked on every call to a \fImalloc\fP function.
+Other letters not part of the defined set are ignored.
+.TP
+.I VMPROFILE
+This is ignored if a method other than \f5Vmprofile\fP
+has been selected by \fIVMETHOD\fP or \fIVMDEBUG\fP.
+\fIVMPROFILE\fP defines a file name to store profile data.
+Each instance of the pattern `%p' found in \fIVMPROFILE\fP
+is transformed to the process id of the running process.
+If the file cannot be created, file descriptor 2 (standard error)
+is used for output.
+.TP
+.I VMTRACE
+If this defines a valid writable file, trace messages of all allocation calls
+are written to the given file (see \fIvmopen()\fP and \fIvmtrace()\fP).
+Similar to \fIVMPROFILE\fP, each instance of the pattern `%p' found
+in \fIVMTRACE\fP is turned to the process id of the running process.
+
+.SH RECENT CHANGES
+\f5Vmlast\fP: allocated blocks are now allowed to be resized (09/1998).
+
+.SH SEE ALSO
+\fImtreplay\fP(1), \fImalloc\fP(3).
+
+.SH AUTHOR
+Kiem-Phong Vo, kpv@research.att.com