From b6b9d458c78364bdbbd7fbd7ec543bc364014b45 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sun, 20 Jun 1999 23:31:26 +0000 Subject: [PATCH] Add a slew of manual pages. Organization: Straylight/Edgeware From: mdw --- man/alloc.3 | 68 ++++++++++ man/base64.3 | 89 +++++++++++++ man/bits.3 | 172 ++++++++++++++++++++++++ man/conn.3 | 76 +++++++++++ man/crc32.3 | 48 +++++++ man/dspool.3 | 85 ++++++++++++ man/dstr.3 | 368 +++++++++++++++++++++++++++++++++++++++++++++++++++ man/exc.3 | 250 ++++++++++++++++++++++++++++++++++ man/lock.3 | 57 ++++++++ man/quis.3 | 40 ++++++ man/report.3 | 41 ++++++ man/sel.3 | 258 ++++++++++++++++++++++++++++++++++++ man/selbuf.3 | 89 +++++++++++++ man/str.3 | 136 +++++++++++++++++++ man/sub.3 | 100 ++++++++++++++ man/sym.3 | 214 ++++++++++++++++++++++++++++++ man/tv.3 | 160 ++++++++++++++++++++++ man/url.3 | 121 +++++++++++++++++ 18 files changed, 2372 insertions(+) create mode 100644 man/alloc.3 create mode 100644 man/base64.3 create mode 100644 man/bits.3 create mode 100644 man/conn.3 create mode 100644 man/crc32.3 create mode 100644 man/dspool.3 create mode 100644 man/dstr.3 create mode 100644 man/exc.3 create mode 100644 man/lock.3 create mode 100644 man/quis.3 create mode 100644 man/report.3 create mode 100644 man/sel.3 create mode 100644 man/selbuf.3 create mode 100644 man/str.3 create mode 100644 man/sub.3 create mode 100644 man/sym.3 create mode 100644 man/tv.3 create mode 100644 man/url.3 diff --git a/man/alloc.3 b/man/alloc.3 new file mode 100644 index 0000000..c0793f0 --- /dev/null +++ b/man/alloc.3 @@ -0,0 +1,68 @@ +.\" -*-nroff-*- +.TH alloc 3mLib "8 May 1999" "mLib" +.SH NAME +alloc \- mLib low-level memory allocation +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void *xmalloc(size_t " sz ); +.BI "void *xrealloc(void *" p ", size_t " sz ); +.BI "char *xstrdup(const char *" s ); +.fi +.SH DESCRIPTION +These functions allocate and return blocks of memory. If insufficient +memory is available, an +.B EXC_NOMEM +exception is raised. +.PP +The +.B xmalloc +and +.B xrealloc +functions are veneers over the standard +.BR malloc (3) +and +.BR realloc (3) +functions. +.B xmalloc +allocates a block of +.I sz +bytes and returns a pointer to the base of the block; +.B xrealloc +is given a pointer +.I p +to a block of memory, and returns the address of a new block of size +.I sz +bytes with the same contents as the original one (either truncated, if +the new block is smaller, or padded with rubbish at the end if bigger). +.PP +The +.B xstrdup +function allocates and returns a block of memory containing a copy of +the null-terminated string +.IR p . +The block is just large enough for the string and its null terminator. +.PP +The memory blocks allocated by these functions can be released by +calling the standard +.BR free (3) +function. +.SH "RETURN VALUE" +The +.BR xmalloc , +.B xrealloc +and +.B xstrdup +functions return pointers to the blocks they allocated. They do not +return if a block couldn't be allocated; instead, the exception +.B EXC_NOMEM +is raised. +.SH "SEE ALSO" +.BR malloc (3), +.BR realloc (3), +.BR free (3), +.BR exc (3mLib). +.SH AUTHOR +Mark Wooding, + diff --git a/man/base64.3 b/man/base64.3 new file mode 100644 index 0000000..4e6186b --- /dev/null +++ b/man/base64.3 @@ -0,0 +1,89 @@ +.\" -*-nroff-*- +.TH base64 3mLib "20 June 1999" mLib +.SH NAME +base64 \- conversion to and from base64 encoding +.SH SYNOPSIS +.nf +.B "#include + +.BI "void base64_encode(base64_ctx *" ctx , +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); +.BI "void base64_decode(base64_ctx *" ctx , +.BI " const void *" p ", size_t " sz , +.BI " dstr *" d ); +.BI "void base64_init(base64_ctx *" ctx ); +.fi +.SH DESCRIPTION +The +.B base64 +functions perform base64 encoding and decoding of arbitrary binary +strings. The base64 encoding is defined by RFC2045. +.PP +Before encoding or decoding a string, a +.I context +(of type +.BR base64_ctx ) +must be initialized, by passing it to +.BR base64_init . +The context contains data which must be retained between calls to encode +or decode substrings. The +.B base64_init +function sets up initial values for the data, and sets up defaults for +the output formatting settings (see below). +.PP +Encoding of a string is performed by the +.B base64_encode +function. It is passed a pointer to a context block +.IR ctx , +the input substring to encode passed by address +.I p +and length +.IR sz , +and a pointer to a dynamic string +.I d +in which to write its output (see +.BR dstr (3mLib) +for details on dynamic strings). Once all the input data has been +passed through +.B base64_encode +it is necessary to flush the final few bytes of output. This is +achieved by passing +.B base64_encode +a null pointer as its source argument. It is an error to attempt to +continue encoding after flushing output. +.PP +The output of the +.B base64_encode +function is formatted into lines using values from the context +structure. The +.B indent +member is a pointer to a null-terminated string which is used to +separate the output lines. The default indent string contains only a +newline character. The +.B maxline +member gives the maxmimum length of line that +.B base64_encode +is allowed to produce. If this is not a multiple of 4, it is rounded +up to the next highest multiple of four before use. A value of zero +instructs +.B base64_encode +not to perform line splitting: the output will be a single (possibly +very long) output line. The default maximum line length is 72 +characters. You may set these parameters by direct assignment to the +context structure once it has been initialized. +.PP +Decoding is performed similarly by the +.B base64_decode +function. The comments above about flushing output apply equally to +decoding. +.PP +Decoding ignores all whitespace characters in the encoded string. It +also ignores +.RB ` = ' +characters in the string and works out the final block length +automatically based on the input size. +.SH "SEE ALSO" +.BR dstr (3mLib). +.SH AUTHOR +Mark Wooding, diff --git a/man/bits.3 b/man/bits.3 new file mode 100644 index 0000000..77eeede --- /dev/null +++ b/man/bits.3 @@ -0,0 +1,172 @@ +.\" -*-nroff-*- +.TH bits 3mLib "20 June 1999" mLib +.SH NAME +bits \- portable bit manipulation macros +.SH SYNOPSIS +.nf +.B "#include " + +.BI "octet U8(" v ); +.BI "uint16 U16(" v ); +.BI "uint32 U32(" v ); + +.BI "octet LSL8(" v ", " s ); +.BI "octet LSR8(" v ", " s ); +.BI "uint16 LSL16(" v ", " s ); +.BI "uint16 LSR16(" v ", " s ); +.BI "uint32 LSL32(" v ", " s ); +.BI "uint32 LSR32(" v ", " s ); + +.BI "octet ROL8(" v ", " s ); +.BI "octet ROR8(" v ", " s ); +.BI "uint16 ROL16(" v ", " s ); +.BI "uint16 ROR16(" v ", " s ); +.BI "uint32 ROL32(" v ", " s ); +.BI "uint32 ROR32(" v ", " s ); + +.BI "octet GETBYTE(" p ", " o ); +.BI "void PUTBYTE(" p ", " o ", " v ); + +.BI "octet LOAD8(" p ); +.BI "void STORE8(" p ", " v ); + +.BI "uint16 LOAD16_B(" p ); +.BI "uint16 LOAD16_L(" p ); +.BI "uint16 LOAD16(" p ); +.BI "void STORE16_B(" p ", " v ); +.BI "void STORE16_L(" p ", " v ); +.BI "void STORE16(" p ", " v ); + +.BI "uint32 LOAD32_B(" p ); +.BI "uint32 LOAD32_L(" p ); +.BI "uint32 LOAD32(" p ); +.BI "void STORE32_B(" p ", " v ); +.BI "void STORE32_L(" p ", " v ); +.BI "void STORE32(" p ", " v ); +.fi +.SH DESCRIPTION +The header file +.B +contains a number of useful definitions for portably dealing with bit- +and byte-level manipulation of larger quantities. It declares three +types: +.TP +.B octet +Equivalent to +.BR "unsigned char" . +This is intended to be used when a character array is used to represent +the octets of some external data format. Note that on some +architectures the +.B "unsigned char" +type may occupy more than 8 bits. +.TP +.B uint16 +Equivalent to +.BR "unsigned short" . +Intended to be used when a 16-bit value is required. This type is +always capable of representing any 16-bit unsigned value, but the actual +type may be wider than 16 bits and will require masking. +.TP +.B uint32 +Equivalent to some (architecture-dependent) standard type. Capable of +representing any unsigned 32-bit value, although the the actual type may +be wider than 32 bits. +.PP +The constants +.BR MASK8 , +.B MASK16 +and +.B MASK32 +contain bitmasks appropriate for discarding additional bits from a value +before use as an 8-, 16- or 32-bit quantity. The macros +.BR U8 , +.B U16 +and +.B U32 +perform masking and type-coercion on a value, and may be more useful in +general. For example, +.B U16(x) +yields a value of type +.B uint16 +which contains (only) the least-significant 16 bits of +.BR x . +.PP +The macros +.BI LSL n +and +.BI LSR n +perform left and right logical shift operations on values of width +.IR n , +where +.I n +is one of +.BR 8 , +.B 16 +or +.BR 32 . +The first argument, written +.IR v , +is the value to shift, and the second, written +.IR s , +is the number of bits to shift. The value +.I s +is reduced modulo +.I n +before use. Similarly, the macros +.BI ROL n +and +.BI ROR n +perform left and right rotations (cyclic shifts) on values of width +.IR n . +These macros are all written in such a way as to maximize portability. +A compiler may be able to spot that simple machine instructions will +suffice in many cases, although that's not the main objective. +.PP +The macros +.BI LOAD n +and +.BI STORE n +(where again +.I n +is one of +.BR 8 , +.B 16 +or +.BR 32 ) +perform transformations between +.IR n -bit +quantities and arrays of octets. For example, +.B LOAD32(q) +returns the 32-bit value stored in the four octets starting at address +.BR q , +and +.B STORE16(q, x) +stores the 16-bit value +.B x +in the 2 octets starting at address +.BR q . +Values are loaded and stored such that the most significant octet is +assigned the lowest address (i.e., they use network, or big-endian byte +order). Macros +.BI LOAD n _L +and +.BI STORE n _L +are also provided for +.I n +either +.B 16 +or +.BR 32 : +they use little-endian byte order. There are +explicit big-endian macros +.BI LOAD n _B +and +.BI STORE n _B +too. The pointer arguments don't have to be pointers to octets; the +value arguments don't have to be of the right type. The macros perform +all appropriate type casting and masking. Again, these macros are +written with portability foremost in mind, although it seems they don't +actually perform at all badly in real use. +.SH AUTHOR +Mark Wooding, + diff --git a/man/conn.3 b/man/conn.3 new file mode 100644 index 0000000..60c9510 --- /dev/null +++ b/man/conn.3 @@ -0,0 +1,76 @@ +.\" -*-nroff-*- +.TH conn 3mLib "23 May 1999" mLib +.SH NAME +conn \- selector for nonblocking connections +.SH SYNOPSIS +.nf +.B "#include + +.BI "void conn_init(conn *" c ", sel_state *" s ", int " fd , +.BI " struct sockaddr *" dst ", int " dsz , +.BI " void (*" func ")(int " fd ", void *" p ), +.BI " void *" p ); + +.BI "void conn_kill(conn *" c ); +.fi +.SH DESCRIPTION +The +.B conn +selector manages a nonblocking connection to a remote socket. The +selector's state is maintained in an object of type +.BR conn . +.PP +Before use, a +.B conn +selector must be initialized. This requires a call to +.B conn_init +with a fairly large number of arguments: +.TP +.I c +Pointer to +.B conn +object which needs to be initialized. +.TP +.I s +Pointer to a multiplexor object (type +.BR sel_state ) +to which this selector should be attached. See +.BR sel (3mLib) +for more details about multiplexors, and how this whole system works. +.TP +.I fd +File descriptor for the socket you want to connect. This becomes the +`property' of the +.B conn +selector until the connection attempt finishes. For example, if there's +an error, the descriptor will be closed. +.TP +.I dst +Pointer to destination socket address for the connection. Make sure +that the address has the right family. +.TP +.I dsz +Size of the destination socket address. +.TP +.I func +A function to call when the connection is complete. It is passed the +file descriptor of the connected socket, and the pointer passed +to +.B conn_init +as the +.I p +argument. If the connection failed, a file descriptor value of \-1 is +passed, and the global +.B errno +value is set appropriately. +.TP +.I p +An arbitrary pointer whose value is passed to the handler function when +the connection finishes. +.PP +If you want to cancel the connection attempt before it finishes, call +.B conn_kill +with the address of the selector. The file descriptor is closed, and +the selector becomes safe to be discarded. +.SH AUTHOR +Mark Wooding, diff --git a/man/crc32.3 b/man/crc32.3 new file mode 100644 index 0000000..911cf5f --- /dev/null +++ b/man/crc32.3 @@ -0,0 +1,48 @@ +.\" -*-nroff-*- +.TH crc32 3mLib "8 May 1999" "mLib" +.SH NAME +crc32 \- calculate 32-bit CRC +.SH SYNOPSIS +.nf +.B "#include " + +.BI "int crc32(unsigned long " crc ", const void *" buf ", size_t " sz ); +.BI CRC32( result ", " crc ", " buf ", " sz ) +.fi +.SH DESCRIPTION +The +.B crc32 +function calculates a 32-bit cyclic redundancy check of the data block +at address +.I buf +and size +.I sz +passed to it. +.PP +The function is restartable. For a first call, pass zero as the value +of the +.I crc +argument; for subsequent blocks, pass in the previous output. The final +answer is equal to the result you'd get from computing a CRC over the +concatenation of the individual blocks. +.PP +The +.B CRC32 +macro calculates a CRC inline. The calculated CRC value is placed in +the variable named by +.IR result . +Only use the macro version when efficiency is a major concern: it makes +the code rather harder to read. +.PP +Note that a CRC is not cryptographically strong: it's fairly easy for an +adversary to construct blocks of data with any desired CRC, or to modify +a given block in a way which doesn't change its (unknown) CRC. +.PP +The exact behaviour of the CRC is beyond the scope of this manual; +suffice to say that the result is, in some suitable representation, the +remainder after division in the finite field GF(2^32) of the block by a +carefully chosen polynomial of order 32. +.SH "RETURN VALUE" +The return value is the 32-bit CRC of the input block. +.SH AUTHOR +Mark Wooding, diff --git a/man/dspool.3 b/man/dspool.3 new file mode 100644 index 0000000..72465dc --- /dev/null +++ b/man/dspool.3 @@ -0,0 +1,85 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.in +5n +.ft B +.nf +.. +.de VE +.ft R +.in -5n +.sp 1 +.fi +.. +.TH dspool 3mLib "20 June 1999" mLib +.SH NAME +dspool \- pools of preallocated dynamic strings +.SH SYNOPSIS +.nf +.B "#include + +.BI "void dspool_create(dspool *" p ", size_t " isz ); +.BI "void dspool_destroy(dspool *" p ); +.BI "dstr *dspool_get(dspool *" p ); +.BI "void dspool_put(dspool *" p ", dstr *" d ); + +.BI "dstr *DSGET(dspool *" p ", d ); +.BI "void DSPUT(dspool *" p ", dstr *" d ); +.fi +.SH DESCRIPTION +A dynamic string pool maintains a collection of `spare' dynamic +strings. Some pieces of code require high turnover of strings, and +allocating and freeing them entails a large amount of overhead. A +dynamic string pool keeps a list of dynamic strings which have been +allocated but are not currently in use. +.PP +A pool is created by the function +.BR dspool_create . +It is passed the address of a pool structure +.I p +and the initial size +.I izs +to allocate for new dynamic strings obtained from the pool. A newly +created pool contains no strings. Once a pool is no longer required, +the function +.B dspool_destroy +will release all the strings in the pool, such that the pool can safely +be thrown away. +.PP +A string is obtained from a pool by calling +.BR dspool_get . +If the pool is empty, a new string is allocated; otherwise a string is +chosen from those currently in the pool. +.PP +A string is returned to the pool by the +.B dspool_put +function. It is passed the address of a pool and the address of a +string to return. The string must have been allocated from +.I some +dynamic string pool, although it's not actually necessary to return it +to the pool from which it was allocated. +.PP +The macro call +.VS +DSGET(p, d); +.VE +is equivalent to the assignment +.VS +d = dspool_get(p); +.VE +(except that it's probably quicker). The macro +.B DSPUT +is entirely equivalent to the function +.B dspool_put +except for improved performance. +.SH CAVEATS +The string pool allocator requires the suballocator (see +.BR sub (3mLib) +for details). You must ensure that +.B sub_init +is called before any strings are allocated from a string pool. +.SH SEE ALSO +.BR dstr (3mLib), +.BR sub (3mLib). +.SH AUTHOR +Mark Wooding, diff --git a/man/dstr.3 b/man/dstr.3 new file mode 100644 index 0000000..3eec044 --- /dev/null +++ b/man/dstr.3 @@ -0,0 +1,368 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS 5 +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.de HP +.IP +.ft B +\h'-\w'\\$1\ 'u'\\$1\ \c +.ft P +.. +.ie t .ds o \(bu +.el .ds o o +.TH dstr 3mLib "8 May 1999" "mLib" +dstr \- a simple dynamic string type +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void dstr_create(dstr *" d ); +.BI "void dstr_destroy(dstr *" d ); +.BI "void dstr_reset(dstr *" d ); + +.BI "void dstr_ensure(dstr *" d ", size_t " sz ); +.BI "void dstr_tidy(dstr *" d ); + +.BI "void dstr_putc(dstr *" d ", char " ch ); +.BI "void dstr_putz(dstr *" d ); +.BI "void dstr_puts(dstr *" d ", const char *" s ); +.BI "int dstr_vputf(dstr *" d ", va_list " ap ); +.BI "int dstr_putf(dstr *" d ", ...); +.BI "void dstr_putd(dstr *" d ", const dstr *" p ); +.BI "void dstr_putm(dstr *" d ", const void *" p ", size_t " sz ); +.BI "int dstr_putline(dstr *" d ", FILE *" fp ); +.BI "size_t dstr_write(const dstr *" d ", FILE *" fp ); + +.BI "void DCREATE(dstr *" d ); +.BI "void DDESTROY(dstr *" d ); +.BI "void DRESET(dstr *" d ); +.BI "void DENSURE(dstr *" d ", size_t " sz ); +.BI "void DPUTZ(dstr *" d ); +.BI "void DPUTS(dstr *" d ", const char *" s ); +.BI "void DPUTD(dstr *" d ", const dstr *" p ); +.BI "void DPUTM(dstr *" d ", const void *" p ", size_t " sz ); +.BI "size_t DWRITE(const dstr *" d ", FILE *" fp ); +.fi +.SH SUMMARY +The header +.B dstr.h +declares a type for representing dynamically extending strings, and a +small collection of useful operations on them. None of the operations +returns a failure result on an out-of-memory condition; instead, the +exception +.B EXC_NOMEM +is raised. +.PP +Many of the functions which act on dynamic strings have macro +equivalents. These equivalent macros may evaluate their arguments +multiple times. +.SH "UNDERLYING TYPE" +A +.B dstr +object is a small structure with the following members: +.VS +typedef struct dstr { + char *buf; /* Pointer to string buffer */ + size_t sz; /* Size of the buffer */ + size_t len; /* Length of the string */ +} dstr; +.VE +The +.B buf +member points to the actual character data in the string. The data may +or may not be null terminated, depending on what operations have +recently been performed on it. None of the +.B dstr +functions depend on the string being null-terminated; indeed, all of +them work fine on strings containing arbitrary binary data. You can +force null-termination by calling the +.B dstr_putz +function, or the +.B DPUTZ +macro. +.PP +The +.B sz +member describes the current size of the buffer. This reflects the +maximum possible length of string that can be represented in +.B buf +without allocating a new buffer. +.PP +The +.B len +member describes the current length of the string. It is the number of +bytes in the string which are actually interesting. The length does +.I not +include a null-terminating byte, if there is one. +.PP +The following invariants are maintained by +.B dstr +and must hold when any function is called: +.HP \*o +If +.B sz +is nonzero, then +.B buf +points to a block of memory of length +.BR sz . +If +.B sz +is zero, then +.B buf +is a null pointer. +.HP \*o +At all times, +.BI sz " >= " len\fR. +.PP +Note that there is no equaivalent of the standard C distinction between +the empty string (a pointer to an array of characters whose first +element is zero) and the nonexistant string (a null pointer). Any +.B dstr +whose +.B len +is zero is an empty string. +.SH "CREATION AND DESTRUCTION" +The caller is responsible for allocating the +.B dstr +structure. It can be initialized in any of the following ways: +.HP \*o +Using the macro +.B DSTR_INIT +as an initializer in the declaration of the object. +.HP \*o +Passing its address to the +.B dstr_create +function. +.HP \*o +Passing its address to the (equivalent) +.B DCREATE +macro. +.PP +The initial value of a +.B dstr +is the empty string. +.PP +The additional storage space for a string's contents may be reclaimed by +passing it to the +.B dstr_destroy +function, or the +.B DDESTROY +macro. After destruction, a string's value is reset to the empty +string: +.I "it's still a valid" +.BR dstr . +However, once a string has been destroyed, it's safe to deallocate the +underlying +.B dstr +object. +.PP +The +.B dstr_reset +function empties a string +.I without +deallocating any memory. Therefore appending more characters is quick, +beause the old buffer is still there and doesn't need to be allocated. +Calling +.VS +dstr_reset(d); +.VE +is equivalent to directly assinging +.VS +d->len = 0; +.VE +There's also a macro +.B DRESET +which does the same job as the +.B dstr_reset +function. +.SH "EXTENDING A STRING" +All memory allocation for strings is done by the function +.BR dstr_ensure . +Given a pointer +.I d +to a +.B dstr +and a size +.IR sz , +the function ensures that there are at least +.I sz +unused bytes in the string's buffer. The current algorithm for +extending the buffer is fairly unsophisticated, but seems to work +relatively well \- see the source if you really want to know what it's +doing. +.PP +Extending a string never returns a failure result. Instead, if there +isn't enough memory for a longer string, the exception +.B EXC_NOMEM +is raised. See +.BR exc (3mLib) +for more information about +.BR mLib 's +exception handling system. +.PP +Note that if an ensure operation needs to reallocate a string buffer, +any pointers you've taken into the string become invalid. +.PP +There's a macro +.B DENSURE +which does a quick inline check to see whether there's enough space in +a string's buffer. This saves a procedure call when no reallocation +needs to be done. The +.B DENSURE +macro is called in the same way as the +.B dstr_ensure +function. +.PP +The function +.B dstr_tidy +`trims' a string's buffer so that it's just large enough for the string +contents and a null terminating byte. This might raise an exception due +to lack of memory. (There are two possible ways this might happen. +Firstly, the underlying allocator might just be braindamaged enough to +fail on reducing a block's size. Secondly, tidying an empty string with no +buffer allocated for it causes allocation of a buffer large enough for +the terminating null byte.) +.SH "CONTRIBUTING DATA TO A STRING" +There are a collection of functions which add data to a string. All of +these functions add their new data to the +.I end +of the string. This is good, because programs usually build strings +left-to-right. If you want to do something more clever, that's up to +you. +.PP +Several of these functions have equivalent macros which do the main work +inline. (There still might need to be a function call if the buffer +needs to be extended.) +.PP +Any of these functions might extend the string, causing pointers into +the string buffer to be invalidated. If you don't want that to happen, +pre-ensure enough space before you start. +.PP +The simplest function is +.B dstr_putc +which appends a single character +.I ch +to the end of the string. It has a macro equivalent called +.BR DPUTC . +.PP +The function +.B dstr_putz +places a zero byte at the end of the string. It does +.I not +affect the string's length, so any other data added to the string will +overwrite the null terminator. This is useful if you want to pass your +string to one of the standard C library string-handling functions. The +macro +.B DPUTZ +does the same thing. +.PP +The function +.B dstr_puts +writes a C-style null-terminated string to the end of a dynamic string. +A terminating zero byte is also written, as if +.B dstr_putz +were called. The macro +.B DPUTS +does the same job. +.PP +The function +.B dstr_putf +works similarly to the standard +.BR sprintf (3) +function. It accepts a +.BR print (3)-style +format string and an arbitrary number of arguments to format and writes +the resulting text to the end of a dynamic string, returning the number +of characters so written. A terminating zero byte is also appended. +The formatting is intended to be convenient and safe rather than +efficient, so don't expect blistering performance. Similarly, there may +be differences between the formatting done by +.B dstr_putf +and +.BR sprintf (3) +because the former has to do most of its work itself. In particular, +.B dstr_putf +doesn't (and probably never will) understand the +.RB ` n$ ' +positional paramter notation accepted by many Unix C libraries. There +is no macro equivalent of +.BR dstr_putf . +.PP +The function +.B dstr_vputf +provides access to the `guts' of +.BR dstr_putf : +given a format string and a +.B va_list +pointer, it will format the arguments according to the format string, +just as +.B dstr_putf +does. +.PP +The function +.B dstr_putd +appends the contents of one dynamic string to another. A null +terminator is also appended. The macro +.B DPUTD +does the same thing. +.PP +The function +.B dstr_putm +puts an arbitrary block of memory, addressed by +.IR p , +with length +.I sz +bytes, at the end of a dynamic string. No terminating null is appended: +it's assumed that if you're playing with arbitrary chunks of memory then +you're probably not going to be using the resulting data as a normal +text string. The macro +.B DPUTM +works the same way. +.PP +The function +.B dstr_putline +reads a line from an input stream +.I fp +and appends it to a string. If an error occurs, or end-of-file is +encountered, before any characters have been read, then +.B dstr_putline +returns the value +.BR EOF. +Otherwise, it reads until it encounters a newline character, an error, +or end-of-file, and returns the number of characters read. If reading +was terminated by a newline character, the newline character is +.I not +inserted in the buffer. A terminating null is appended, as by +.BR dstr_putz . +.SH "OTHER FUNCTIONS" +The +.B dstr_write +function writes a string to an output stream +.IR fp . +It returns the number of characters written, or +.B 0 +if an error occurred before the first write. No newline character is +written to the stream, unless the string actually contains one already. +The macro +.B DWRITE +is equivalent. +.SH "SECURITY CONSIDERATIONS" +The implemenetation of the +.B dstr +functions is designed to do string handling in security-critical +programs. However, there may be bugs in the code somewhere. In +particular, the +.B dstr_putf +functions is quite complicated, and could do with some checking by +independent people who know what they're doing. +.SH AUTHOR +Mark Wooding, diff --git a/man/exc.3 b/man/exc.3 new file mode 100644 index 0000000..368a6f8 --- /dev/null +++ b/man/exc.3 @@ -0,0 +1,250 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.in +5n +.ft B +.nf +.. +.de VE +.ft R +.in -5n +.sp 1 +.fi +.. +.TH exc 3mLib "20 June 1999" mLib +.SH NAME +exc \- exception handling for C programs +.SH SYNOPSIS +.B "#include +.sp 1 +.B TRY +.I statement +.B CATCH +.I statement +.B END_TRY; +.br +.B EXIT_TRY; +.sp 1 +.BI "THROW(exc_extype " type +.RB [ , +.IR data ]\fB); +.br +.B RETHROW; +.sp 1 +.nf +.B "typedef void (*exc__uncaught)(exc_extype, exc_exval);" +.BI "exc__uncaught exc_uncaught(exc__uncaught " proc ); + +.BI "exc_extype EXC_PAIR(unsigned char " x ", unsigned char " y ); +.BI "exc_extype EXC_ALLOC(exc_extype " owner ", exc_extype " type ); +.BI "exc_extype EXC_ALLOCN(exc_extype " owner ", exc_extype " type ); +.BI "exc_extype EXC_ALLOCI(exc_extype " owner ", exc_extype " type ); +.BI "exc_extype EXC_ALLOCP(exc_extype " owner ", exc_extype " type ); +.BI "exc_extype EXC_ALLOCS(exc_extype " owner ", exc_extype " type ); +.fi +.SH DESCRIPTION +The header file +.B +introduces some new syntax and definitions to support exception handling +in C. The marriage is not particularly successful, although it works +well enough in practice. +.PP +The syntax introduced consists of new +.B TRY +and +.B EXIT_TRY +statements and a pair of new expression types +.B THROW +and +.BR RETHROW . +It's unfortunately important to remember that the syntax is provided +using macro expansion and standard C facilities; some of the +restrictions of these features show through. +.SS "The TRY statement" +The +.B TRY +statement associates an exception handler with a piece of code. The +second statement is an +.IR "exception handler" . +Its +.I "dynamic scope" +is the duration of the first statement's execution, together with the +duration of any functions called within the dynamic scope. (Note in +particular that an exception handler is not within its own dynamic +scope.) A thrown exception causes the exception handler with +dynamically innermost scope to be executed. +.PP +Two special variables are provided to the exception handler: +.TP +.B exc_type +The +.I type +of the exception caught. This is value of type +.B exc_extype +(described below). +.TP +.B exc_val +The +.I value +of the exception. This has a union type, with members +.BR "int i", +.B "void *p" +and +.BR "char *s" . +Only one of the members is valid; you should be able to work out which +from the exception type. There are abbreviations +.BR "exc_i", +.B exc_p +and +.B exc_s +which refer to members of +.B exc_val +directly. +.SS "The EXIT_TRY statement" +It is not safe to leave the dynamic scope of an exception handler early +(e.g., by a +.B goto +statement). You can force a safe exit from a dynamic scope using the +.B EXIT_TRY +statement from within the +.I lexical +scope of the +.B TRY +statement. +.SS "The THROW and RETHROW statements" +The +.B THROW +expression throws an exception. The first argument is the type of +exception; the second is some data to attach to the exception. The type +of data, integer, string or pointer, is determined from the exception +type. +.PP +Control is immediately passed to the exception handler with the +innermost enclosing dynamic scope. +.PP +The +.B RETHROW +expression is only valid within an exception handler. It rethrows the +last exception caught by the handler. +.PP +Neither +.B THROW +nor +.B RETHROW +yields any value. +.SS "Exception type allocation" +Exception types are 32-bit values. The top 16 bits are an +.IR "owner identifier" . +The idea is that each library can have an owner identifier, and it can +then allocate exceptions for its own use from the remaining space. Two +special owner codes are defined: +.TP +.B "EXC_GLOBAL (0x0000)" +The global space defined for everyone's benefit. Don't define your own +exceptions in this space. +.TP +.B "EXC_SHARED (0xffff)" +A shared space. You can use this for any exceptions which won't be seen +by anyone else. +.PP +Other owner codes may be allocated by choosing two characters (probably +letters) which best suit your application and applying the +.B EXC_PAIR +macro to them. For example, the owner code for +.B mLib +would probably be +.BR "EXC_PAIR('m', 'L')" , +if +.B mLib +defined any exceptions other then the global ones. +.PP +The bottom 16 bits are the actual exception type, and the data type +which gets passed around with the exception. The data type is +(bizarrely) in bits 6 and 7 of the type word. The data type code is one +of the following: +.TP +.B EXC_NOVAL +There is no data associated with this exception. +.TP +.B EXC_INTVAL +The data is an integer, with type +.BR int . +.TP +.B EXC_PTRVAL +The data is a pointer to some data structure, with type +.BR "void *" . +Note that you probably have to do an explicit cast to +.B "void *" +in the +.B THROW +expression. +.TP +.B EXC_STRVAL +The data is a pointer to a string of characters, of type +.BR "char *" . +.PP +If the data to be thrown is a pointer, make sure that the object pointed +to has a long enough lifetime for it to actually get to its exception +handler intact. In particular, don't pass pointers to automatic +variables unless you're +.I sure +they were allocated outside the handler's dynamic scope. +.PP +Individual exceptions are allocated by the macros +.BI EXC_ALLOC t\fR, +where +.I t +is one of: +.TP +.B N +The exception has no data +.TP +.B I +The exception data is an integer. +.TP +.B P +The exception data is a pointer. +.TP +.B S +The exception data is a character string. +.PP +The +.BI EXC_ALLOC t +macros take two arguments: the owner code (usually allocated with +.B EXC_PAIR +as described above), and the type code. The data type is encoded into +the exception type by the allocation macro. +.SS "Predefined exceptions" +The following exceptions are predefined: +.TP +.B EXC_NOMEM +No data. Signals an out-of-memory condition. +.TP +.B EXC_ERRNO +Integer data. Signals an operating system error. The data is the value +of +.B errno +associated with the error. +.TP +.B EXC_OSERROR +Pointer data. Signals a RISC\ OS error. The data is a pointer to the +RISC\ OS error block. (Non RISC\ OS programmers don't need to worry +about this.) +.TP +.B EXC_SIGNAL +Integer data. Signals a raised operating system signal. The data is +the signal number. +.TP +.B EXC_FAIL +String data. Signals a miscellaneous failure. The data is a pointer to +an explanatory string. +.SH BUGS +The call to an exception handler is acheived using +.BR longjmp (3). +Therefore all the caveats about +.B longjmp +and automatic data apply. Also, note that status such as the signal +mask is not reset, so you might have to do that manually in order to +recover from a signal. +.SH AUTHOR +Mark Wooding, diff --git a/man/lock.3 b/man/lock.3 new file mode 100644 index 0000000..68aebea --- /dev/null +++ b/man/lock.3 @@ -0,0 +1,57 @@ +.\" -*-nroff-*- +.TH lock 3mLib "23 May 1999" mLib +.SH NAME +lock \- oversimplified file locking interface +.SH SYNOPSIS +.nf +.B "#include + +.BI "int lock_file(int " fd ", unsigned " how ); +.fi +.SH DESCRIPTION +The +.B lock_file +function provides an extremely simplistic interface to POSIX +.BR fcntl (2) +locking. It locks only entire files, not sections of files. It doesn't +have a nonblocking `is this file locked?' function. +.PP +On entry, +.I fd +should be a file descriptor on an open file, and +.I how +is a constant which describes how the file is to be locked. The +possible values of +.I how +are: +.TP +.B LOCK_EXCL +Lock the file exclusively. Attempts to lock the file exclusively or +nonexclusively will fail until the file is unlocked. +.TP +.B LOCK_NONEXCL +Lock the file nonexclusively. Until the file is unlocked, attempts to +lock it exclusively will fail, but other nonexclusive locks will +succeed. +.TP +.B LOCK_UNLOCK +Unlocks a locked file. Any locks afterwards can succeed. +.PP +The +.B lock_file +function will block if it can't obtain a lock immediately. It will time +itself out after a short while (10 seconds in the current +implementation) if the lock doesn't become available. +.PP +If the call succeeds, +.B lock_file +returns zero. On failure, \-1 is returned, and +.B errno +is set to an appropriate value. Most of the error returns are from +.BR fcntl (2) +(q.v.). If the lock operation times out, +.B errno +is set to +.BR EINTR . +.SH AUTHOR +Mark Wooding, diff --git a/man/quis.3 b/man/quis.3 new file mode 100644 index 0000000..3b2cce8 --- /dev/null +++ b/man/quis.3 @@ -0,0 +1,40 @@ +.\" -*-nroff-*- +.TH quis 3mLib "22 May 1999" mLib +.SH NAME +quis \- remember the program's name for use in messages +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void ego(const char *" p ); +.B "const char *quis(void);" +.B "const char *QUIS;" +.fi +.SH DESCRIPTION +The +.B ego +function should be called early in your program's initialization +sequence, with the value of +.B argv[0] +as its argument. It will strip away leading path components, and a +leading `\-' character (in case the program was called as a login +shell), and keep the resulting short name for later. +.PP +The +.B quis +function returns the stored program name. There is also a macro +.B QUIS +which expands to the name of a global variable whose value is the string +returned by +.BR quis() . +.PP +Don't ask why it's done this way. There are raisins, but they're mostly +hysterical. +.PP +The program name is used in the messages produced by the +.BR die (3mLib) +and +.BR moan (3mLib) +functions. +.SH AUTHOR +Mark Wooding, diff --git a/man/report.3 b/man/report.3 new file mode 100644 index 0000000..e9dc226 --- /dev/null +++ b/man/report.3 @@ -0,0 +1,41 @@ +.\" -*-nroff-*- +.TH report 3mLib "20 June 1999" mLib +.SH NAME +report \- report errors +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void moan(const char *" f ", ...);" +.BI "void die(int " status ", const char *" f ", ...);" +.fi +.SH DESCRIPTION +The +.B moan +function emits a message to the standard error stream consisting of the +program's name (as read by the +.B quis +function; see +.BR quis (3mLib) for details), +a colon, a space, and the +.BR printf -style +formatted string +.I f +followed by a newline. This is a handy way to report nonfatal errors in +a program. +.PP +The +.B die +function emits a message to the standard error stream, just as for +.B moan +above, and then calls the +.B exit +function with argument +.I status +to halt the program. This is a handy way to report fatal errors in a +program. +.SH SEE ALSO +.BR exit (3), +.BR quis (3mLib). +.SH AUTHOR +Mark Wooding, diff --git a/man/sel.3 b/man/sel.3 new file mode 100644 index 0000000..c3e81ae --- /dev/null +++ b/man/sel.3 @@ -0,0 +1,258 @@ +.\" -*-nroff-*- +.TH sel 3mLib "22 May 1999" mLib +.SH NAME +sel \- low level interface for waiting for I/O +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void sel_init(sel_state *" s ); + +.BI "void sel_initfile(sel_state *" s ", sel_file *" f , +.BI " int " fd ", unsigned " mode , +.BI " void (*" func ")(int " fd ", unsigned " mode ", void *" p ), +.BI " void *" p ); +.BI "void sel_addfile(sel_file *" f ); +.BI "void sel_rmfile(sel_file *" f ); + +.BI "void sel_addtimer(sel_state *" s ", sel_timer *" t , +.BI " struct timeval *" tv , +.BI " void (*" func ")(struct timeval *" tv ", void *" p ), +.BI "void sel_rmtimer(sel_timer *" t ); + +.BI "int sel_select(sel_state *" s ); +.fi +.SH "OVERVIEW" +The +.B sel +subsystem provides a structured way of handling I/O in a non-blocking +event-driven sort of a way, for single-threaded programs. (Although +there's no reason at all why multithreaded programs shouldn't use +.BR sel , +it's much less useful.) +.PP +The +.B sel +subsystem does no memory allocation, and has no static state. All +of its data is stored in structures allocated by the caller. I'll +explain how this fits in nicely with typical calling sequences below. +.PP +Although all the data structures are exposed in the header file, you +should consider +.BR sel 's +data structures to be opaque except where described here, and not fiddle +around inside them. Some things may become more sophisticated later. +.SH "IMPORTANT CONCEPTS" +The system is based around two concepts: +.I multiplexors +and +.IR selectors . +.PP +A +.I selector +is interested in some sort of I/O event, which might be something like +`my socket has become readable', or `the time is now half past three on +the third of June 2013'. It has a handler function attached to it, +which is called when the appropriate event occurs. Some events happen +once only ever; some events happen over and over again. For example, a +socket might become readable many times, but it's only half-past three +on the third of June 2013 once. +.PP +When a selector is initialized, the caller describes the event the +selector is interested in, and specifies which function should handle +the event. Also, it must specify an arbitrary pointer which is passed +to the handler function when the event occurs. This is typically some +sort of pointer to instance data of some kind, providing more +information about the event (`it's +.I this +socket that's become readable'), or what to do about it. +.PP +A multiplexor gathers information about who's interested in what. It +maintains lists of selectors. Selectors must be added to a +mulitplexor before the events they're interested in are actually watched +for. Selectors can be removed again when their events aren't +interesting any more. Apart from adding and removing selectors, you can +.I select +on a multiplexor. This waits for something interesting to happen and +then fires off all the selectors which have events waiting for them. +.PP +You can have lots of multiplexors in your program if you like. You can +only ask for events from one of them at a time, though. +.PP +There are currently two types of selector understood by the low-level +.B sel +system: file selectors and timer selectors. These two types of +selectors react to corresponding different types of events. A file +event indicates that a file is now ready for reading or writing. A +timer event indicates that a particular time has now passed (useful for +implementing timeouts). More sophisticated selectors can be constructed +using +.BR sel 's +interface. For examples, see +.BR selbuf (3mLib) +and +.BR conn (3mLib). +.SH "PROGRAMMING INTERFACE" +A multiplexor is represented using the type +.B sel_state +defined in the +.B +header file. Before use, a +.B sel_state +must be initialized, by passing it to the +.B sel_init +function. The header file talks about `state blocks' a lot \- that's +because it was written before I thought the word `multiplexor' was +nicer. +.PP +File selectors are represented by the type +.BR sel_file . +The interface provides three operations on file selectors: +initialization, addition to multiplexor, and removal from a +multiplexor. It's convenient to separate addition and removal from +initialization because file selectors often get added and removed many +times over during their lifetimes. +.PP +A file selector is initialized by the +.B sel_initfile +function. This requires a large number of arguments: +.TP +.I s +A pointer to the multiplexor with which the file selector will be +associated. This is stored in the selector so that the multiplexor +argument can be omitted from later calls. +.TP +.I f +Pointer to the file selector object to be initialized. +.TP +.I fd +The file descriptor which the selector is meant to watch. +.TP +.I mode +A constant describing which condition the selector is interested in. +This must be one of the +.B SEL_ +constants described below. +.TP +.I func +The handler function which is called when the appropriate condition +occurs on the file. This function's interface is described in more +detail below. +.TP +.I p +An arbitrary pointer argument passed to +.I func +when it's called. Beyond this, no meaning is attached to the value of +the pointer. If you don't care about it, just leave it as null. +.PP +The mode argument is one of the following constants: +.TP +.B SEL_READ +Raise an event when the file is ready to be read from. +.TP +.B SEL_WRITE +Raise an event when the file is ready to be written to. +.TP +.B SEL_EXC +Raise an event when the file has an `exceptional condition'. +.PP +The constant +.B SEL_MODES +contains the number of possible file modes. This is useful internally +for allocating arrays of the right size. +.PP +The functions +.B sel_addfile +and +.B sel_rmfile +perform the addition and removal operations on file selectors. They are +passed only the actual selector object, since the selector already knows +which multiplexor it's associated with. A newly initialized file +selector is not added to its multiplexor: this must be done explicitly. +.PP +The handler function for a file multiplexor is passed three arguments: +the file descriptor for the file, a mode argument which descibes the +file's new condition, and the pointer argument set up at initialization +time. +.PP +The member +.B fd +of the +.B sel_file +structure is exported. It contains the file descriptor in which the +selector is interested. You may not modify this value, but it's useful +to be able to read it out \- it saves having to keep a copy. +.PP +Timer selectors are simpler. There are only two operations provided on +timer selectors: addition and removal. Initialization is performed as +part of the addition operation. +.PP +A timer selector is represented by an object of time +.BR sel_timer . +.PP +The function +.B sel_addtimer +requires lots of arguments: +.TP +.I s +Pointer to the multiplexor to which the selector is to be added. +.TP +.I t +Pointer to the timer selector object being initialized and added. +.TP +.I tv +A +.B "struct timeval" +object describing when the selector should raise its event. This is an +.I absolute +time, not a relative time as required by the traditional +.BR select (2) +and +.BR poll (2) +system calls. +.TP +.I func +A handler function to be called when the event occurs. The function is +passed the +.I current +time, and the arbitrary pointer passed to +.B sel_addtimer +as the +.I p +argument. +.TP +.I p +A pointer passed to +.I func +when the timer event occurs. Beyond this, the value of the pointer is +not inspected. +.PP +The function +.B sel_rmtimer +removes a timer selector. It is passed only the selector object. +.PP +Note that timer events are a one-shot thing. Once they've happened, the +timer selector is removed and the event can't happen again. This is +normally what you want. Removing a timer is only useful (or safe!) +before the timer event has been sent. +.PP +Finally, the function +.B sel_select +is passed a multiplexor object. It waits for something interesting to +happen, informs the appropriate selector handlers, and returns. If +everything went according to plan, +.B sel_select +returns zero. Otherwise it returns -1, and the global variable +.B errno +is set appropriately. +.SH "OTHER NOTES" +Although the naming seems to suggest that this is all +based around the BSD-ish +.BR select (2) +system call (and indeed it is), the interface is actually a good deal +more general than that. An implementation which worked off System V-ish +.BR poll (2) +instead would be fairly trivial to make, and would look just the same +from the outside. +.SH AUTHOR +Mark Wooding, diff --git a/man/selbuf.3 b/man/selbuf.3 new file mode 100644 index 0000000..386a718 --- /dev/null +++ b/man/selbuf.3 @@ -0,0 +1,89 @@ +.\" -*-nroff-*- +.TH selbuf 3mLib "23 May 1999" mLib +.SH NAME +selbuf \- line-buffering input selector +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void selbuf_enable(selbuf *" b ); +.BI "void selbuf_disable(selbuf *" b ); +.BI "void selbuf_init(selbuf *" b , +.BI " sel_state *" s , +.BI " int " fd , +.BI " void (*" func ")(char *" s ", void *" p ), +.BI " void *" p ); +.fi +.SH DESCRIPTION +The +.B selbuf +subsystem is a selector which integrates with the +.BR sel (3mLib) +system for I/O multiplexing. It reads entire text lines from a file +descriptor and passes them to a caller-defined function. It uses the +line buffer described in +.BR lbuf (3mLib) +to do its work: you should read about it in order to understand exactly +what gets considered to be a line of text and what doesn't, and the +exact rules about what your line handling function should and shouldn't +do. +.PP +All the data for a +.B selbuf +selector is stored in an object of type +.BR selbuf . +This object must be allocated by the caller, and initialized using the +.B selbuf_init +function. This requires a fair few arguments: +.TP +.I b +Pointer to the +.B selbuf +object to initialize. +.TP +.I s +Pointer to a multiplexor object (type +.BR sel_state ) +to which this selector should be attached. See +.BR sel (3mLib) +for more details about multiplexors, and how this whole system works. +.TP +.I fd +The file descriptor of the stream the selector should read from. +.TP +.I func +The +.I "line handler" +function. It is passed a pointer to each line read from the file (or +null to indicate end-of-file) and an arbitrary pointer (the +.I p +argument to +.B selbuf_init +described below). +.TP +.I p +A pointer argument passed to +.I func +for each line read from the file. Apart from this, the pointer is not +used at all. +.PP +The +.B selbuf +selector is immediately active. Subsequent calls to +.B sel_select +on the same multiplexor will cause any complete lines read from the file +to be passed to your handling function. This function can at any time +call +.B selbuf_disable +to stop itself from being called any more. The selector is then +disengaged from the I/O multiplexor and won't do anything until +.B selbuf_enable +is called. Note that +.B selbuf_enable +may well immediately start emitting complete lines of text which were +queued up from the last I/O operation: it doesn't necessarily wait for +the next +.B sel_select +call. +.SH AUTHOR +Mark Wooding, diff --git a/man/str.3 b/man/str.3 new file mode 100644 index 0000000..16e859d --- /dev/null +++ b/man/str.3 @@ -0,0 +1,136 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.in +5n +.ft B +.nf +.. +.de VE +.ft R +.in -5n +.sp 1 +.fi +.. +.TH str 3mLib "20 June 1999" mLib +.SH NAME +str \- small string utilities +.SH SYNOPSIS +.nf +.B "#include " + +.BI "char *str_getword(char **" pp ); +.BI "size_t str_split(char *" p ", char *" v "[], size_t " c ", char **" rest ); +.BI "void str_sanitize(char *" d ", const char *" p ", size_t " sz ); +.fi +.SH DESCRIPTION +The header file +.B +contains a few small utility functions for manipulating null-terminated +strings. +.PP +The function +.B str_getword +extracts the next whitespace-delimited word from a string. The +function's argument, +.IR pp , +is the address of a pointer into the string: this pointer is updated by +.B str_getword +so that it can extract the following word on the next call and so on. +The return value is the address of the next word, appropriately null +terminated. A null pointer is returned if the entire remainder of the +string is whitespace. Note that +.B str_getword +modifies the string as it goes, to null-terminate the individual words. +.PP +The function +.B str_split +divides a string into whitespace-separated words. The arguments are as +follows: +.TP +.I p +The address of the string to split. The string is modified by having +null terminators written after each word extracted. +.TP +.I v +The address of an array of pointers to characters. This array will be +filled in by +.BR str_split : +the first entry will point to the first word extracted from the string, +and so on. If there aren't enough words in the string, the remaining +array elements are filled with null pointers. +.TP +.I c +The maxmimum number of words to extract; also, the number of elements in +the array +.IR v . +.TP +.I rest +The address of a pointer in which to store the address of the remainder +of the string. Leading whitespace is removed from the remainder before +storing. If the remainder string is empty, a null pointer is stored +instead. If +.I rest +is null, the remainder pointer is discarded. +.PP +The return value of +.B str_split +is the number of words extracted from the input string. +.PP +The function +.B str_sanitize +copies at most +.I sz \- 1 +characters from the string +.I p +to +.IR d . +The result string is null terminated. Any nonprinting characters in +.I p +are replaced by an underscore +.RB ` _ ' +when written to +.IR d . +.SH EXAMPLES +Given the code +.VS +char p[] = " alpha beta gamma delta "; +char *v[3]; +size_t n; +char *q; + +n = str_split(p, v, 3, &q); +.VE +following the call to +.BR str_split , +.B n +will have the value 3, +.B v[0] +will point to +.RB ` alpha ', +.B v[1] +will point to +.RB ` beta ', +.B v[2] +will point to +.RB ` gamma ' +and +.B rest +will point to +.RB ` delta\ ' +(note the trailing space). +.PP +Similarly, given the string +.B """\ alpha\ \ beta\ """ +instead, +.B n +will be assigned the value 2, +.B v[0] +and +.B v[1] +will have the same values as last time, and +.B v[2] +and +.B rest +will be null. +.SH AUTHOR +Mark Wooding, diff --git a/man/sub.3 b/man/sub.3 new file mode 100644 index 0000000..b460506 --- /dev/null +++ b/man/sub.3 @@ -0,0 +1,100 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS 5 +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.TH sub 3mLib "8 May 1999" mLib +.SH NAME +sub \- efficient allocation and freeing of small blocks +.SH SYNOPSIS +.nf +.B "#include " + +.B "void sub_init(void);" +.BI "void *sub_alloc(size_t " sz ); +.BI "void sub_free(void *" p ", size_t " sz ); + +.BI "void *CREATE(" type ); +.BI "void DESTROY(" type " *" p ); +.fi +.SH DESCRIPTION +The +.B sub +collection of functions and macros implement an efficient allocator for +small blocks of known sizes. Free blocks of the same size are linked +together in list, making freeing and allocation fast. The `free' +operation requires the block size as an argument, so there's no data +overhead for an allocated block. The system takes advantage of this by +allocating big chunks from the underlying system (actually via +.BR xmalloc (3mLib), +q.v.) and splitting the chunks into smaller blocks of the right size, so +the space and time overhead from the underlying allocator is divided +over many blocks. +.PP +Calling +.B sub_alloc +allocates a block of +.I sz +bytes. If there isn't enough memory to allocate the block, the +exception +.B EXC_NOMEM +is raised. +.PP +The +.B sub_free +function frees a block allocated by +.BR sub_alloc . +You must know the size of the block in advance. Note that +.B sub_free +never gives memory back to the underlying allocator. Free sub-blocks +are just made available to later calls of +.BR sub_alloc . +.PP +Don't try to pass blocks allocated by +.B sub_alloc +to +.BR free (3); +similarly, don't try to pass blocks allocated by +.BR xmalloc (3mLib) +or +.BR malloc (3) +to +.BR sub_free . +If you do, you'll get what you deserve. +.PP +The pair of macros +.B CREATE +and +.B DESTROY +are intended to provide a slightly more natural interface to +allocation. The call +.VS +mystruct *p = sub_alloc(sizeof(mystruct)); +.VE +can be replaced by +.VS +mystruct p = CREATE(mystruct); +.VE +Similarly, the block can be freed by saying +.VS +DESTROY(p) +.VE +rather than the more cubersome +.VS +sub_free(p, sizeof(*p)); +.VE +The function +.B sub_init +must be called before any of the other +.B sub +functions or macros. +.SH AUTHOR +Mark Wooding, diff --git a/man/sym.3 b/man/sym.3 new file mode 100644 index 0000000..10b8084 --- /dev/null +++ b/man/sym.3 @@ -0,0 +1,214 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.RS 5 +.nf +.ft B +.. +.de VE +.ft R +.fi +.RE +.sp 1 +.. +.TH sym 3mLib "8 May 1999" mLib +.SH NAME +sym \- symbol table manager +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void sym_create(sym_table *" t ); +.BI "void sym_destroy(sym_table *" t ); + +.BI "char *SYM_NAME(void *" p ); +.BI "void *sym_find(sym_table *" t , +.BI " const char *" n ", long " l , +.BI " size_t " sz ", unsigned *" f ); +.BI "void sym_remove(sym_table *" t ", void *" b ); + +.BI "void sym_mkiter(sym_iter *" i ", sym_table *" t ); +.BI "void *sym_next(sym_iter *" i ); +.fi +.SH "HOW IT WORKS" +The +.B sym +functions implement a data structure often described as a dictionary, a +finite map, an associative array, or a symbol table. It associates +.I values +with +.I keys +such that the value corresponding to a given key can be found quickly. +Additionally, all stored associations can be enumerated. +.PP +The interface provides an +.I intrusive +symbol table. The data objects stored in the table must include a small +header used by the symbol table manager. This reduces the amount of +pointer fiddling that needs to be done, and in practice doesn't seem to +be much of a problem. It's also fairly easy to construct a +non-intrusive interface if you really want one. +.PP +There are three main data structures involved in the interface: +.TP +.B sym_table +Keeps track of the information associated with a particular table. +.TP +.B sym_base +The header which must be attached to the front of all the value +objects. +.TP +.B sym_iter +An iterator object, used for enumerating all of the associations stored +in a symbol table. +.PP +All of the above data structures should be considered +.IR opaque : +don't try looking inside. Representations have changed in the past, and +they may change again in the future. +.SH "CREATION AND DESTRUCTION" +The +.B sym_table +object itself needs to be allocated by the caller. It is initialized by +passing it to the function +.BR sym_create . +After initialization, the table contains no entries. +.PP +Initializing a symbol table involves allocating some memory. If this +allocation failes, an +.B EXC_NOMEM +exception is raised. +.PP +When a symbol table is no longer needed, the memory occupied by the +values and other maintenance structures can be reclaimed by calling +.BR sym_destroy . +Any bits of user data attached to the symbol table values should have +previously been destroyed. +.SH "ADDING, SEARCHING AND REMOVING" +Most of the actual work is done by the function +.BR sym_find . +It does both lookup and creation, depending on its arguments. To do its +job, it needs to know the following bits of information: +.TP +.I t +A pointer to a symbol table to manipulate. +.TP +.I n +The address of the +.I key +to look up or create. Usually this will be a simple text string, +although it can actually be any arbitrary binary data. +.TP +.I l +The length of the key. If this is \-1, +.B sym_find +assumes that the key is a null-terminated string, and calculates its +length itself. +.TP +.I sz +The size of the value block to allocate if the key could not be found. +If this is zero, no value is allocated, and a null pointer is returned +to indicate an unsuccessful lookup. +.TP +.I f +The address of a `found' flag to set. This is an output parameter. On +exit, +.B sym_find +will set the value of +.BI * f +to zero if the key could not be found, or nonzero if it was found. This +can be used to tell whether the value returned has been newly allocated, +or whether it was already in the table. +.PP +The macro +.B SYM_NAME +will return the key associated with a given value block. You must use +this macro rather than fiddling inside the +.B sym_base +structure. +.PP +A symbol can be removed from the table by calling +.BR sym_remove , +passing the symbol table itself, and the value block that needs +removing. +.SH ENUMERATION +Enumerating the values in a symbol table is fairly simple. Allocate a +.B sym_iter +object from somewhere. Attach it to a symbol table by calling +.BR sym_mkiter , +and passing in the addresses of the iterator and the symbol table. +Then, each call to +.B sym_next +will return a different value from the symbol table, until all of them +have been enumerated, at which point, +.B sym_next +returns a null pointer. +.PP +It's safe to remove the symbol you've just been returned by +.BR sym_next . +However, it's not safe to remove any other symbol. So don't do that. +.PP +When you've finished with an iterator, it's safe to just throw it away. +You don't need to call any functions beforehand. +.SH "USE IN PRACTICE" +In normal use, the keys are simple strings (usually identifiers from +some language), and the values are nontrivial structures providing +information about types and values. +.PP +In this case, you'd define something like the following structure for +your values: +.VS +typedef struct val { + sym_base _base; /* Symbol header */ + unsigned type; /* Type of this symbol */ + int dispoff; /* Which display variable is in */ + size_t frameoff; /* Offset of variable in frame */ +} val; +.VE +Given a pointer +.I v +to a +.BR val , +you can find the variable's name by calling +.BI SYM_NAME( v )\fR. +.PP +You can look up a name in the table by saying something like: +.VS +val *v = sym_find(t, name, -1, 0, 0); +if (!v) + error("unknown variable `%s'", name); +.VE +You can add in a new variable by saying something like +.VS +unsigned f; +val *v = sym_find(t, name, -1, sizeof(val), &f); +if (f) + error("variable `%s' already exists", name); +/* fill in v */ +.VE +You can examine all the variables in your symbol table by saying +something like: +.VS +sym_iter i; +val *v; + +for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) { + /* ... */ +} +.VE +That ought to be enough examples to be getting on with. +.SH CAVEATS +The symbol table manager requires the suballocator (see +.BR sub (3mLib) +for details). You must ensure that +.B sub_init +has been called before using any symbol tables in your program. +.SH IMPLEMENTATION +The symbol table is an extensible hashtable, using a 32-bit CRC as the +hash function. The hash chains are kept very short (probably too short, +actually). Every time a symbol is found, its block is promoted to the +front of its bin chain so it gets found faster next time. +.SH SEE ALSO +.BR sub (3mLib). +.SH AUTHOR +Mark Wooding, diff --git a/man/tv.3 b/man/tv.3 new file mode 100644 index 0000000..71f33f4 --- /dev/null +++ b/man/tv.3 @@ -0,0 +1,160 @@ +.\" -*-nroff-*- +.TH tv 3mLib "22 May 1999" mLib +.SH NAME +tv \- arithmetic on \fBstruct timeval\fR objects +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void tv_add(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " const struct timeval *" b ); +.BI "void tv_addl(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " time_t " sec ", unsigned long " usec ); +.BI "void tv_sub(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " const struct timeval *" b ); +.BI "void tv_subl(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " time_t " sec ", unsigned long " usec ); +.BI "int tv_cmp(const struct timeval *" a , +.BI " const struct timeval *" b ); + +.B "int MILLION;" +.BI "void TV_ADD(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " const struct timeval *" b ); +.BI "void TV_ADDL(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " time_t " sec ", unsigned long " usec ); +.BI "void TV_SUB(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " const struct timeval *" b ); +.BI "void TV_SUBL(struct timeval *" dst , +.BI " const struct timeval *" a , +.BI " time_t " sec ", unsigned long " usec ); +.BI "int TV_CMP(const struct timeval *" a ", " op , +.BI " const struct timeval *" b ); +.fi +.SH DESCRIPTION +The +.B +header file provides functions and macros which perform simple +arithmetic on objects of type +.BR "struct timeval" , +which is capable of representing times to microsecond precision. +See your +.BR gettimeofday (2) +or +.BR select (2) +manpages for details of this structure. +.PP +The macros are the recommended interface to +.BR tv 's +facilities. The function interface is provided for compatibility +reasons, and for bizarre cases when macros won't do the job. +.PP +The main arithmetic functions are in three-address form: they accept two +source arguments and a separate destination argument (which may be the +same as one or even both of the source arguments). The destination is +written before the sources. All the arguments are pointers to the +actual structures. +.PP +.BI TV_ADD( d ", " x ", " y ) +adds +.BR timeval s +.I x +and +.I y +storing the result in +.IR d . +Similarly, +.BI TV_SUB( d ", " x ", " y ) +subtracts +.I y +from +.I x +storing the result in +.IR d . +.PP +The macros +.B TV_ADDL +and +.B TV_SUBL +work the same way as +.B TV_ADD +and +.B TV_SUB +respectively, except their second source operand is expressed +immediately as two integers arguments expressing a time in seconds and +microseconds respectively. Hence, +.BI TV_ADDL( d ", " s ", 3, 250000)" +adds 3.25 seconds to +.I s +and puts the result in +.IR d . +Similarly, +.BI TV_SUBL( d ", " s ", 3, 250000)" +subtracts 3.25 seconds from +.I s +and puts the answer in +.IR d . +.PP +The function equivalents for the above arithmetic macros work in exactly +the same way (and indeed have trivial implementations in terms of the +macros). The name of the function corresponding to a macro is simply +the macro name in lower-case. +.PP +Two +.B timeval +objects can be compared using the +.B TV_CMP +macro. If +.I op +is a relational operator (e.g., +.RB ` == ', +.RB ` < ', +or +.RB ` >= ') +then +.BI TV_CMP( x ", " op ", " y ) +is one when +.I "x op y" +is true and zero otherwise. +.PP +The function +.B tv_cmp +works differently. Given two arguments +.I x +and +.IR y , +it returns -1 if +.IR x " < " y , +zero if +.IR x " == " y , +or 1 if +.IR x " > " y . +Hence, the result can be compared against zero in a relatively intuitive +way (as for +.BR strcmp (3), +except that because the results are defined more tightly, it's possible +to use a +.B switch +on the result). +.SH ACKNOWLEDGEMENT +The idea of passing a relational operator to +.B TV_CMP +is stolen from the +.B timercmp +macro in the GNU C library. I don't know whether this macro is a GNU +original, but it certainly doesn't seem to be portable. The +.B timercmp +macro had a warning attached to it that it wouldn't work for operators +like +.RB ` <= ', +although I can't see why there'd be a problem. (If there is one, then +my implementation has it too, because they're the same. I don't +document the restriction because I don't think it exists.) +.SH AUTHOR +Mark Wooding, diff --git a/man/url.3 b/man/url.3 new file mode 100644 index 0000000..f343962 --- /dev/null +++ b/man/url.3 @@ -0,0 +1,121 @@ +.\" -*-nroff-*- +.de VS +.sp 1 +.in +5n +.ft B +.nf +.. +.de VE +.ft R +.in -5n +.sp 1 +.fi +.. +.TH url 3mLib "20 June 1999" mLib +.SH NAME +url \- manipulation of form-urlencoded strings +.SH SYNOPSIS +.nf +.B "#include + +.BI "void url_initenc(url_ectx *" ctx ); +.BI "void url_enc(url_ectx *" ctx ", dstr *" d , +.BI " const char *" name ", const char *" value ); + +.BI "void url_initdec(url_dctx *" ctx ", const char *" p ); +.BI "int url_dec(url_dctx *" ctx ", dstr *" n ", dstr *" v ); +.fi +.SH DESCRIPTION +The functions in +.B +read and write `form-urlencoded' data, as specified in RFC1866. The +encoding represents a sequence of name/value pairs where both the name +and value are arbitrary binary strings (although the format is optimized +for textual data). An encoded string contains no nonprintable +characters or whitespace. This interface is capable of decoding any +urlencoded string; however, it can currently only +.I encode +names and values which do not contain null bytes, because the encoding +interface uses standard C strings. +.PP +Encoding a sequence of name/value pairs is achieved using the +.B url_enc +function. It requires as input an +.IR "encoding context" , +represented as an object of type +.BR url_ectx . +This must be initialized before use by passing it to the function +.BR url_initenc . +Each call to +.B url_enc +encodes one name/value pair, appending the encoded output to a dynamic +string (see +.BR dstr (3mLib) +for details). +.PP +Decoding a sequence of name/value pairs is performed using the +.B url_dec +function. It requires as input a +.IR "decoding context" , +represented as an object of type +.BR url_dctx . +This must be initialized before use by passing it to the function +.BR url_initdec , +along with the address of the urlencoded string to decode. The string +is not modified during decoding. Each call to +.B url_dec +extracts a name/value pair. The name and value are written to the +dynamic strings +.I n +and +.IR v , +so you probably want to reset them before each call. If there are no +more name/value pairs to read, +.B url_dec +returns zero; otherwise it returns a nonzero value. +.SH EXAMPLE +The example code below demonstrates converting between a symbol table +and a urlencoded representation. The code is untested. +.VS +#include +#include +#include +#include +#include + +typedef struct { + sym_base _b; + char *v; +} val; + +void decode(sym_table *t, const char *p) +{ + url_dctx c; + dstr n = DSTR_INIT, v = DSTR_INIT; + + for (url_initdec(&c, p); url_dec(&c, &n, &v); ) { + unsigned f; + val *vv = sym_find(t, n.buf, -1, sizeof(*vv), &f); + if (f) + free(vv->v); + vv->v = xstrdup(v.buf); + DRESET(&n); + DRESET(&v); + } + dstr_destroy(&n); + dstr_destroy(&v); +} + +void encode(sym_table *t, dstr *d) +{ + sym_iter i; + url_ectx c; + val *v; + + url_initenc(&c); + for (sym_mkiter(&i, t); (v = sym_next(&i)) != 0; ) + url_enc(&c, d, SYM_NAME(v), v->v); +} +.VE +.SH AUTHOR +Mark Wooding, . -- [mdw]