chiark / gitweb /
Initial revision
authormdw <mdw>
Wed, 17 Jun 1998 23:44:41 +0000 (23:44 +0000)
committermdw <mdw>
Wed, 17 Jun 1998 23:44:41 +0000 (23:44 +0000)
30 files changed:
.links [new file with mode: 0644]
.skelrc [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
alloc.c [new file with mode: 0644]
alloc.h [new file with mode: 0644]
configure.in [new file with mode: 0644]
crc32.c [new file with mode: 0644]
crc32.h [new file with mode: 0644]
dstr.c [new file with mode: 0644]
dstr.h [new file with mode: 0644]
dynarray.h [new file with mode: 0644]
exc.c [new file with mode: 0644]
exc.h [new file with mode: 0644]
exctest.c [new file with mode: 0644]
mdwopt.c [new file with mode: 0644]
mdwopt.h [new file with mode: 0644]
quis.c [new file with mode: 0644]
quis.h [new file with mode: 0644]
report.c [new file with mode: 0644]
report.h [new file with mode: 0644]
sub.c [new file with mode: 0644]
sub.h [new file with mode: 0644]
sym.c [new file with mode: 0644]
sym.h [new file with mode: 0644]
testrig.c [new file with mode: 0644]
testrig.h [new file with mode: 0644]
trace.c [new file with mode: 0644]
trace.h [new file with mode: 0644]
track.c [new file with mode: 0644]
track.h [new file with mode: 0644]

diff --git a/.links b/.links
new file mode 100644 (file)
index 0000000..e866fdd
--- /dev/null
+++ b/.links
@@ -0,0 +1,6 @@
+COPYING
+config.guess
+config.sub
+install-sh
+missing
+mkinstalldirs
diff --git a/.skelrc b/.skelrc
new file mode 100644 (file)
index 0000000..6d88ea1
--- /dev/null
+++ b/.skelrc
@@ -0,0 +1,8 @@
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+      (append
+       '((author . "Straylight/Edgeware")
+        (full-title . "the mLib utilities library")
+        (program . "mLib"))
+       skel-alist))
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..8997cf1
--- /dev/null
@@ -0,0 +1,57 @@
+## Process this file with Automake to generate `Makefile.in'
+## -*-Makefile-*-
+##
+## $Id: Makefile.am,v 1.1 1998/06/17 23:44:42 mdw Exp $
+##
+## Building the distribution
+##
+## (c) 1998 Straylight/Edgeware
+##
+
+##----- Licensing notice ----------------------------------------------------
+##
+## This file is part of the mLib utilities library.
+## 
+## mLib is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 2 of the License, or
+## (at your option) any later version.
+## 
+## mLib is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+## 
+## You should have received a copy of the GNU General Public License
+## along with mLib; if not, write to the Free Software Foundation,
+## Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.am,v $
+## Revision 1.1  1998/06/17 23:44:42  mdw
+## Initial revision
+##
+## Revision 1.5  1997/08/13 17:55:43  mdw
+## Add big GPL header.  General tidying up.
+##
+
+## --- Options ---
+
+AUTOMAKE_OPTIONS = foreign
+
+## --- What to build ---
+
+lib_LIBRARIES = libmLib.a
+
+include_HEADERS = \
+       alloc.h crc32.h dstr.h dynarray.h exc.h mdwopt.h \
+       quis.h report.h sub.h sym.h testrig.h trace.h track.h
+
+## --- Things to put in the library ---
+
+## libmLib_la_LDFLAGS = -version-info 1:0
+
+libmLib_a_SOURCES = \
+       alloc.c crc32.c dstr.c exc.c mdwopt.c quis.c \
+       report.c sub.c sym.c testrig.h trace.c track.c
diff --git a/alloc.c b/alloc.c
new file mode 100644 (file)
index 0000000..b42718a
--- /dev/null
+++ b/alloc.c
@@ -0,0 +1,110 @@
+/* -*-c-*-
+ *
+ * $Id: alloc.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Memory allocation functions
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: alloc.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "alloc.h"
+#include "exc.h"
+#include "track.h"
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments:  @size_t sz@ = size of block to allocate
+ *
+ * Returns:    Pointer to allocated block.
+ *
+ * Use:                Allocates memory.  If there's not enough memory, the
+ *             exception @EXC_NOMEM@ is thrown.
+ */
+
+void *xmalloc(size_t sz)
+{
+  void *p = malloc(sz);
+  if (!p)
+    THROW(EXC_NOMEM);
+  return (p);
+}
+
+/* --- @xstrdup@ --- *
+ *
+ * Arguments:  @const char *s@ = pointer to a string
+ *
+ * Returns:    Pointer to a copy of the string.
+ *
+ * Use:                Copies a string (like @strdup@ would, if it existed).  If
+ *             there's not enough memory, the exception @EXC_NOMEM@ is
+ *             thrown.
+ */
+
+char *xstrdup(const char *s)
+{
+  size_t sz = strlen(s) + 1;
+  char *p = xmalloc(sz);
+  memcpy(p, s, sz);
+  return (p);
+}
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to a block of memory
+ *             @size_t sz@ = new size desired for the block
+ *
+ * Returns:    Pointer to the resized memory block (which is almost
+ *             certainly not in the same place any more).
+ *
+ * Use:                Resizes a memory block.  If there's not enough memory, the
+ *             exception @EXC_NOMEM@ is thrown.
+ */
+
+void *xrealloc(void *p, size_t sz)
+{
+  p = realloc(p, sz);
+  if (!p)
+    THROW(EXC_NOMEM);
+  return (p);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/alloc.h b/alloc.h
new file mode 100644 (file)
index 0000000..6d9d58a
--- /dev/null
+++ b/alloc.h
@@ -0,0 +1,93 @@
+/* -*-c-*-
+ *
+ * $Id: alloc.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Memory allocation functions
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: alloc.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef ALLOC_H
+#define ALLOC_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <stddef.h>
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments:  @size_t sz@ = size of block to allocate
+ *
+ * Returns:    Pointer to allocated block.
+ *
+ * Use:                Allocates memory.  If there's not enough memory, the
+ *             exception @EXC_NOMEM@ is thrown.
+ */
+
+extern void *xmalloc(size_t /*sz*/);
+
+/* --- @xstrdup@ --- *
+ *
+ * Arguments:  @const char *s@ = pointer to a string
+ *
+ * Returns:    Pointer to a copy of the string.
+ *
+ * Use:                Copies a string (like @strdup@ would, if it existed).  If
+ *             there's not enough memory, the exception @EXC_NOMEM@ is
+ *             thrown.
+ */
+
+extern char *xstrdup(const char */*s*/);
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to a block of memory
+ *             @size_t sz@ = new size desired for the block
+ *
+ * Returns:    Pointer to the resized memory block (which is almost
+ *             certainly not in the same place any more).
+ *
+ * Use:                Resizes a memory block.  If there's not enough memory, the
+ *             exception @EXC_NOMEM@ is thrown.
+ */
+
+extern void *xrealloc(void */*p*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..3701e74
--- /dev/null
@@ -0,0 +1,55 @@
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 1998/06/17 23:44:42 mdw Exp $
+dnl
+dnl Configuration script for mLib
+dnl
+dnl (c) 1998 Straylight/Edgeware
+dnl
+
+dnl ----- Licensing notice --------------------------------------------------
+dnl
+dnl This file is part of the mLib utilities library.
+dnl
+dnl mLib is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl mLib is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with mLib; if not, write to the Free Software Foundation,
+dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+dnl ----- Revision history --------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1  1998/06/17 23:44:42  mdw
+dnl Initial revision
+dnl
+
+dnl --- Boring boilerplate ---
+
+AC_INIT(exc.c)
+AM_INIT_AUTOMAKE(mLib, 1.0)
+
+dnl --- Compiling and making libraries ---
+
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_CHECK_PROG(AR, ar, ar)
+mdw_GCC_FLAGS
+AC_PROG_RANLIB
+dnl AM_PROG_LIBTOOL
+
+dnl --- Useful options ---
+
+mdw_OPT_mLib_DEBUG(mLib)
+
+dnl --- Done ---
+
+AC_OUTPUT(Makefile)
diff --git a/crc32.c b/crc32.c
new file mode 100644 (file)
index 0000000..54e3f41
--- /dev/null
+++ b/crc32.c
@@ -0,0 +1,176 @@
+/* -*-c-*-
+ *
+ * $Id: crc32.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Calculating cyclic redundancy values (non-cryptographic!)
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: crc32.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "crc32.h"
+
+/*----- CRC table ---------------------------------------------------------*/
+
+/* --- Acknowledgements and information --- *
+ *
+ * This table was generated using a derivative of the Rocksoft Model CRC
+ * generator.  The parameters given are:
+ *
+ *   width = 32bits
+ *   poly = 0x04C11DB7
+ *   init = -1
+ *   xorout = -1
+ *   reverse = yes
+ *   check = crc(123456789) = 0xCBF43926
+ */
+
+unsigned long crc32_table[256] = {
+  0x00000000L, 0x77073096L, 0xEE0E612CL, 0x990951BAL,
+  0x076DC419L, 0x706AF48FL, 0xE963A535L, 0x9E6495A3L,
+  0x0EDB8832L, 0x79DCB8A4L, 0xE0D5E91EL, 0x97D2D988L,
+  0x09B64C2BL, 0x7EB17CBDL, 0xE7B82D07L, 0x90BF1D91L,
+  0x1DB71064L, 0x6AB020F2L, 0xF3B97148L, 0x84BE41DEL,
+  0x1ADAD47DL, 0x6DDDE4EBL, 0xF4D4B551L, 0x83D385C7L,
+  0x136C9856L, 0x646BA8C0L, 0xFD62F97AL, 0x8A65C9ECL,
+  0x14015C4FL, 0x63066CD9L, 0xFA0F3D63L, 0x8D080DF5L,
+  0x3B6E20C8L, 0x4C69105EL, 0xD56041E4L, 0xA2677172L,
+  0x3C03E4D1L, 0x4B04D447L, 0xD20D85FDL, 0xA50AB56BL,
+  0x35B5A8FAL, 0x42B2986CL, 0xDBBBC9D6L, 0xACBCF940L,
+  0x32D86CE3L, 0x45DF5C75L, 0xDCD60DCFL, 0xABD13D59L,
+  0x26D930ACL, 0x51DE003AL, 0xC8D75180L, 0xBFD06116L,
+  0x21B4F4B5L, 0x56B3C423L, 0xCFBA9599L, 0xB8BDA50FL,
+  0x2802B89EL, 0x5F058808L, 0xC60CD9B2L, 0xB10BE924L,
+  0x2F6F7C87L, 0x58684C11L, 0xC1611DABL, 0xB6662D3DL,
+  0x76DC4190L, 0x01DB7106L, 0x98D220BCL, 0xEFD5102AL,
+  0x71B18589L, 0x06B6B51FL, 0x9FBFE4A5L, 0xE8B8D433L,
+  0x7807C9A2L, 0x0F00F934L, 0x9609A88EL, 0xE10E9818L,
+  0x7F6A0DBBL, 0x086D3D2DL, 0x91646C97L, 0xE6635C01L,
+  0x6B6B51F4L, 0x1C6C6162L, 0x856530D8L, 0xF262004EL,
+  0x6C0695EDL, 0x1B01A57BL, 0x8208F4C1L, 0xF50FC457L,
+  0x65B0D9C6L, 0x12B7E950L, 0x8BBEB8EAL, 0xFCB9887CL,
+  0x62DD1DDFL, 0x15DA2D49L, 0x8CD37CF3L, 0xFBD44C65L,
+  0x4DB26158L, 0x3AB551CEL, 0xA3BC0074L, 0xD4BB30E2L,
+  0x4ADFA541L, 0x3DD895D7L, 0xA4D1C46DL, 0xD3D6F4FBL,
+  0x4369E96AL, 0x346ED9FCL, 0xAD678846L, 0xDA60B8D0L,
+  0x44042D73L, 0x33031DE5L, 0xAA0A4C5FL, 0xDD0D7CC9L,
+  0x5005713CL, 0x270241AAL, 0xBE0B1010L, 0xC90C2086L,
+  0x5768B525L, 0x206F85B3L, 0xB966D409L, 0xCE61E49FL,
+  0x5EDEF90EL, 0x29D9C998L, 0xB0D09822L, 0xC7D7A8B4L,
+  0x59B33D17L, 0x2EB40D81L, 0xB7BD5C3BL, 0xC0BA6CADL,
+  0xEDB88320L, 0x9ABFB3B6L, 0x03B6E20CL, 0x74B1D29AL,
+  0xEAD54739L, 0x9DD277AFL, 0x04DB2615L, 0x73DC1683L,
+  0xE3630B12L, 0x94643B84L, 0x0D6D6A3EL, 0x7A6A5AA8L,
+  0xE40ECF0BL, 0x9309FF9DL, 0x0A00AE27L, 0x7D079EB1L,
+  0xF00F9344L, 0x8708A3D2L, 0x1E01F268L, 0x6906C2FEL,
+  0xF762575DL, 0x806567CBL, 0x196C3671L, 0x6E6B06E7L,
+  0xFED41B76L, 0x89D32BE0L, 0x10DA7A5AL, 0x67DD4ACCL,
+  0xF9B9DF6FL, 0x8EBEEFF9L, 0x17B7BE43L, 0x60B08ED5L,
+  0xD6D6A3E8L, 0xA1D1937EL, 0x38D8C2C4L, 0x4FDFF252L,
+  0xD1BB67F1L, 0xA6BC5767L, 0x3FB506DDL, 0x48B2364BL,
+  0xD80D2BDAL, 0xAF0A1B4CL, 0x36034AF6L, 0x41047A60L,
+  0xDF60EFC3L, 0xA867DF55L, 0x316E8EEFL, 0x4669BE79L,
+  0xCB61B38CL, 0xBC66831AL, 0x256FD2A0L, 0x5268E236L,
+  0xCC0C7795L, 0xBB0B4703L, 0x220216B9L, 0x5505262FL,
+  0xC5BA3BBEL, 0xB2BD0B28L, 0x2BB45A92L, 0x5CB36A04L,
+  0xC2D7FFA7L, 0xB5D0CF31L, 0x2CD99E8BL, 0x5BDEAE1DL,
+  0x9B64C2B0L, 0xEC63F226L, 0x756AA39CL, 0x026D930AL,
+  0x9C0906A9L, 0xEB0E363FL, 0x72076785L, 0x05005713L,
+  0x95BF4A82L, 0xE2B87A14L, 0x7BB12BAEL, 0x0CB61B38L,
+  0x92D28E9BL, 0xE5D5BE0DL, 0x7CDCEFB7L, 0x0BDBDF21L,
+  0x86D3D2D4L, 0xF1D4E242L, 0x68DDB3F8L, 0x1FDA836EL,
+  0x81BE16CDL, 0xF6B9265BL, 0x6FB077E1L, 0x18B74777L,
+  0x88085AE6L, 0xFF0F6A70L, 0x66063BCAL, 0x11010B5CL,
+  0x8F659EFFL, 0xF862AE69L, 0x616BFFD3L, 0x166CCF45L,
+  0xA00AE278L, 0xD70DD2EEL, 0x4E048354L, 0x3903B3C2L,
+  0xA7672661L, 0xD06016F7L, 0x4969474DL, 0x3E6E77DBL,
+  0xAED16A4AL, 0xD9D65ADCL, 0x40DF0B66L, 0x37D83BF0L,
+  0xA9BCAE53L, 0xDEBB9EC5L, 0x47B2CF7FL, 0x30B5FFE9L,
+  0xBDBDF21CL, 0xCABAC28AL, 0x53B39330L, 0x24B4A3A6L,
+  0xBAD03605L, 0xCDD70693L, 0x54DE5729L, 0x23D967BFL,
+  0xB3667A2EL, 0xC4614AB8L, 0x5D681B02L, 0x2A6F2B94L,
+  0xB40BBE37L, 0xC30C8EA1L, 0x5A05DF1BL, 0x2D02EF8DL,
+};
+
+/*----- Functionc provided ------------------------------------------------*/
+
+/* --- @crc32@ --- *
+ *
+ * Arguments:  @unsigned long crc@ = carryover from previous call, or zero
+ *             @const void *buf@ = pointer to buffer to check
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    The CRC updated by the new buffer.
+ *
+ * Use:                A restartable CRC calculator.  This is just a function
+ *             wrapper for the macro version.
+ */
+
+unsigned long crc32(unsigned long crc, const void *buf, size_t sz)
+{
+  unsigned long c;
+  CRC32(c, crc, buf, sz);
+  return (c);
+}
+
+/*----- Test driver -------------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <stdio.h>
+
+int main(void)
+{
+  unsigned long crc = 0;
+  char buf[BUFSIZ];
+  int r;
+
+  do {
+    r = fread(buf, 1, sizeof(buf), stdin);
+    if (r > 0)
+      crc = crc32(crc, buf, r);
+  } while (r == sizeof(buf));
+
+  printf("crc32(stdin) = %08lx\n", crc);
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/crc32.h b/crc32.h
new file mode 100644 (file)
index 0000000..5d4b2fa
--- /dev/null
+++ b/crc32.h
@@ -0,0 +1,93 @@
+/* -*-c-*-
+ *
+ * $Id: crc32.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Calculating cyclic redundancy values (non-cryptographic!)
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: crc32.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef CRC32_H
+#define CRC32_h
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- External values ---------------------------------------------------*/
+
+extern unsigned long crc32_table[256];
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- @CRC32@ --- *
+ *
+ * Arguments:  @unsigned long result@ = where to put the result
+ *             @unsigned long crc@ = carryover from previous call, or zero
+ *             @void *buf@ = pointer to buffer to check
+ *             @size_t sz@ = size of the buffer
+ *
+ * Use:                A restartable CRC calculator wrapped up in a macro.
+ */
+
+#define CRC32(result, crc, buf, sz) do {                               \
+  const unsigned char *_p = (const unsigned char *)(buf);              \
+  const unsigned char *_l = _p + (sz);                                 \
+  unsigned long _crc = ~(crc) & 0xffffffffu;                           \
+                                                                       \
+  while (_p < _l)                                                      \
+    _crc = (_crc >> 8) ^ crc32_table[(*_p++ ^ _crc) & 0xffu];          \
+  (result) = ~_crc & 0xffffffffu;                                      \
+} while (0)
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @crc32@ --- *
+ *
+ * Arguments:  @unsigned long crc@ = carryover from previous call, or zero
+ *             @const void *buf@ = pointer to buffer to check
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    The CRC updated by the new buffer.
+ *
+ * Use:                A restartable CRC calculator.  This is just a function
+ *             wrapper for the macro version.
+ */
+
+extern unsigned long crc32(unsigned long /*crc*/,
+                          const void */*buf*/, size_t /*sz*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/dstr.c b/dstr.c
new file mode 100644 (file)
index 0000000..7299bf6
--- /dev/null
+++ b/dstr.c
@@ -0,0 +1,308 @@
+/* -*-c-*-
+ *
+ * $Id: dstr.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Handle dynamically growing strings
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: dstr.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "alloc.h"
+#include "dstr.h"
+
+/*----- Tunable constants -------------------------------------------------*/
+
+#define DSTR_INITSZ 256                        /* Initial buffer size */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @dstr_create@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialises a dynamic string.
+ */
+
+void dstr_create(dstr *d)
+{
+  d->sz = 0;
+  d->len = 0;
+  d->buf = 0;
+}
+
+/* --- @dstr_destroy@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Reclaims the space used by a dynamic string.
+ */
+
+void dstr_destroy(dstr *d)
+{
+  if (d->buf)
+    free(d->buf);
+  d->buf = 0;
+  d->len = 0;
+  d->sz = 0;
+}
+
+/* --- @dstr_reset@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynaimc string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Resets a string so that new data gets put at the beginning.
+ */
+
+void dstr_reset(dstr *d)
+{
+  d->len = 0;
+}
+
+/* --- @dstr_ensure@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @size_t sz@ = amount of free space to ensure
+ *
+ * Returns:    ---
+ *
+ * Use:                Ensures that at least @sz@ bytes are available in the
+ *             given string.
+ */
+
+void dstr_ensure(dstr *d, size_t sz)
+{
+  size_t rq = d->len + sz;
+  size_t nsz;
+
+  /* --- If we have enough space, just leave it --- */
+
+  if (rq <= d->sz)
+    return;
+
+  /* --- Grow the buffer --- *
+   *
+   * For small buffers, just double the size.  For big buffers, make them
+   * a multiple of some suitably large chunk size.
+   */
+
+  nsz = d->sz;
+
+  do {
+    if (nsz == 0)
+      nsz = DSTR_INITSZ;
+    else if (d->sz < 0x1000)
+      nsz <<= 1;
+    else
+      nsz = (rq + 0x0fff) & ~0x0fff;
+  } while (rq > nsz);
+
+  if (d->buf)
+    d->buf = xrealloc(d->buf, nsz);
+  else
+    d->buf = xmalloc(nsz);
+  d->sz = nsz;
+}
+
+/* --- @dstr_putc@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @char ch@ = character to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a character to a string.
+ */
+
+void dstr_putc(dstr *d, char ch)
+{
+  DPUTC(d, ch);
+}
+
+/* --- @dstr_putz@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a null byte to a string.  The null byte does not
+ *             contribute to the string's length, and will be overwritten
+ *             by subsequent `put' operations.
+ */
+
+void dstr_putz(dstr *d)
+{
+  DPUTZ(d);
+}
+
+/* --- @dstr_puts@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const char *s@ = pointer to string to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a character string to a string.  A trailing null
+ *             byte is added, as for @dstr_putz@.
+ */
+
+void dstr_puts(dstr *d, const char *s)
+{
+  DPUTS(d, s);
+}
+
+/* --- @dstr_putd@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const dstr *s@ = pointer to a dynamic string to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a dynamic string to a string.  A trailing null
+ *             byte is added, as for @dstr_putz@.
+ */
+
+void dstr_putd(dstr *d, const dstr *s)
+{
+  DPUTD(d, s);
+}
+
+/* --- @dstr_putm@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const void *p@ = pointer to a block to append
+ *             @size_t sz@ = size of the block
+ *
+ * Returns:    Appends an arbitrary data block to a string.  No trailing
+ *             null is appended.
+ */
+
+void dstr_putm(dstr *d, const void *p, size_t sz)
+{
+  DPUTM(d, p, sz);
+}
+
+/* --- @dstr_tidy@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Reduces the amount of memory used by a string.  A trailing
+ *             null byte is added, as for @dstr_putz@.
+ */
+
+void dstr_tidy(dstr *d)
+{
+  dstr_putz(d);
+  d->buf = xrealloc(d->buf, d->len + 1);
+  d->sz = d->len + 1;
+}
+
+/* --- @dstr_putline@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @FILE *fp@ = a stream to read from
+ *
+ * Returns:    The number of characters read into the buffer, or @EOF@ if
+ *             end-of-file was reached before any characters were read.
+ *
+ * Use:                Appends the next line from the given input stream to the
+ *             string.  A trailing newline is not added; a trailing null
+ *             byte is appended, as for @dstr_putz@.
+ */
+
+int dstr_putline(dstr *d, FILE *fp)
+{
+  size_t left = d->sz - d->len;
+  size_t off = d->len;
+  int rd = 0;
+  int ch;
+
+  for (;;) {
+
+    /* --- Make sure there's some buffer space --- */
+
+    if (!left) {
+      dstr_ensure(d, 1);
+      left = d->sz - off;
+    }
+
+    /* --- Read the next byte --- */
+
+    ch = getc(fp);
+
+    /* --- End-of-file when no characters read is special --- */
+
+    if (ch == EOF && !rd)
+      return (EOF);
+
+    /* --- End-of-file or newline ends the loop --- */
+
+    if (ch == EOF || ch == '\n') {
+      d->buf[off] = 0;
+      d->len = off;
+      return rd;
+    }
+
+    /* --- Append the character and continue --- */
+
+    d->buf[off++] = ch;
+    left--; rd++;
+  }
+}
+
+/* --- @dstr_write@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @FILE *fp@ = a stream to write on
+ *
+ * Returns:    The number of bytes written (as for @fwrite@).
+ *
+ * Use:                Writes a dynamic string to a file.
+ */
+
+size_t dstr_write(dstr *d, FILE *fp)
+{
+  return (fwrite(d->buf, 1, d->len, fp));
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/dstr.h b/dstr.h
new file mode 100644 (file)
index 0000000..b7240fe
--- /dev/null
+++ b/dstr.h
@@ -0,0 +1,256 @@
+/* -*-c-*-
+ *
+ * $Id: dstr.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Handle dynamically growing strings
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: dstr.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef DSTR_H
+#define DSTR_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Rationale ---------------------------------------------------------*
+ *
+ * This file declares what is hopefully a fairly useful collection of
+ * primitive string handling functions.  The idea is that the strings
+ * allocate memory for themselves as required.  The @dstr@ routines don't
+ * assume any sort of terminator character, so arbitrary binary data can
+ * be stored in a dynamic string.  With luck, this should put a stop to
+ * any buffer overflow problems.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdio.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct dstr {
+  char *buf;                           /* Pointer to string buffer */
+  size_t sz;                           /* Size of the buffer */
+  size_t len;                          /* Length of the string */
+} dstr;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @dstr_create@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialises a dynamic string.
+ */
+
+extern void dstr_create(dstr */*d*/);
+
+/* --- @dstr_destroy@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Reclaims the space used by a dynamic string.
+ */
+
+extern void dstr_destroy(dstr */*d*/);
+
+/* --- @dstr_reset@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynaimc string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Resets a string so that new data gets put at the beginning.
+ */
+
+extern void dstr_reset(dstr */*d*/);
+
+/* --- @dstr_ensure@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @size_t sz@ = amount of free space to ensure
+ *
+ * Returns:    ---
+ *
+ * Use:                Ensures that at least @sz@ bytes are available in the
+ *             given string.
+ */
+
+extern void dstr_ensure(dstr */*d*/, size_t /*sz*/);
+
+#define DENSURE(d, rq) do {                                            \
+  if ((d)->len + (rq) > (d)->sz) dstr_ensure((d), (rq));               \
+} while (0)
+
+/* --- @dstr_putc@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @char ch@ = character to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a character to a string.
+ */
+
+extern void dstr_putc(dstr */*d*/, char /*ch*/);
+
+#define DPUTC(d, ch) do {                                              \
+  DENSURE((d), 1);                                                     \
+  (d)->buf[(d)->len++] = (ch);                                         \
+} while (0)
+
+/* --- @dstr_putz@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a null byte to a string.  The null byte does not
+ *             contribute to the string's length, and will be overwritten
+ *             by subsequent `put' operations.
+ */
+
+extern void dstr_putz(dstr */*d*/);
+
+#define DPUTZ(d) do {                                                  \
+  DENSURE((d), 1);                                                     \
+  (d)->buf[(d)->len] = 0;                                              \
+} while (0)
+
+/* --- @dstr_puts@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const char *s@ = pointer to string to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a character string to a string.  A trailing null
+ *             byte is added, as for @dstr_putz@.
+ */
+
+extern void dstr_puts(dstr */*d*/, const char */*s*/);
+
+#define DPUTS(d, s) do {                                               \
+  size_t sz = strlen(s);                                               \
+  DENSURE((d), sz + 1);                                                        \
+  memcpy((d)->buf + (d)->len, (s), sz + 1);                            \
+  (d)->len += sz;                                                      \
+} while (0)
+
+/* --- @dstr_putd@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const dstr *s@ = pointer to a dynamic string to append
+ *
+ * Returns:    ---
+ *
+ * Use:                Appends a dynamic string to a string.  A trailing null
+ *             byte is added, as for @dstr_putz@.
+ */
+
+extern void dstr_putd(dstr */*d*/, const dstr */*s*/);
+
+#define DPUTD(d, s) do {                                               \
+  DENSURE((d), (s)->len + 1);                                          \
+  memcpy((d)->buf + (d)->len, (s)->buf, (s)->len);                     \
+  (d)->len += (s)->len;                                                        \
+  (d)->buf[(d)->len] = 0;                                              \
+} while (0)
+
+/* --- @dstr_putm@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @const void *p@ = pointer to a block to append
+ *             @size_t sz@ = size of the block
+ *
+ * Returns:    Appends an arbitrary data block to a string.  No trailing
+ *             null is appended.
+ */
+
+extern void dstr_putm(dstr */*d*/, const void */*p*/, size_t /*sz*/);
+
+#define DPUTM(d, p, sz) do {                                           \
+  DENSURE((d), (sz));                                                  \
+  memcpy((d)->buf + (d)->len, (p), (sz));                              \
+  (d)->len += (sz);                                                    \
+} while (0)
+
+/* --- @dstr_tidy@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns:    ---
+ *
+ * Use:                Reduces the amount of memory used by a string.  A trailing
+ *             null byte is added, as for @dstr_putz@.
+ */
+
+extern void dstr_tidy(dstr */*d*/);
+
+/* --- @dstr_putline@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @FILE *fp@ = a stream to read from
+ *
+ * Returns:    The number of characters read into the buffer, or @EOF@ if
+ *             end-of-file was reached before any characters were read.
+ *
+ * Use:                Appends the next line from the given input stream to the
+ *             string.  A trailing newline is not added; a trailing null
+ *             byte is appended, as for @dstr_putz@.
+ */
+
+extern int dstr_putline(dstr */*d*/, FILE */*fp*/);
+
+/* --- @dstr_write@ --- *
+ *
+ * Arguments:  @dstr *d@ = pointer to a dynamic string block
+ *             @FILE *fp@ = a stream to write on
+ *
+ * Returns:    The number of bytes written (as for @fwrite@).
+ *
+ * Use:                Writes a dynamic string to a file.
+ */
+
+extern size_t dstr_write(dstr */*d*/, FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/dynarray.h b/dynarray.h
new file mode 100644 (file)
index 0000000..32a7939
--- /dev/null
@@ -0,0 +1,279 @@
+/* -*-c-*-
+ *
+ * $Id: dynarray.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Dynamic arrays implementation
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: dynarray.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef DYNARRAY_H
+#define DYNARRAY_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Required header files ---------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifndef SUB_H
+#  include "sub.h"
+#endif
+
+#ifndef TRACK_H
+#  include "track.h"
+#endif
+
+/*----- Horrific hacking -------------------------------------------------*
+ *
+ * Several bits of this code need extending arrays (in particular, the array
+ * handler and the label allocator), although the sizes of the objects being
+ * arrayed differs.
+ *
+ * This file contains (horrors!) a sort of C++ template-a-like which
+ * implements such growing arrays.  Read on, if your constitution can stand
+ * it...
+ */
+
+/* --- Macro: @DYNDECLS@ --- *
+ *
+ * Arguments:  @prefix@ = prefix string which begins all external
+ *                     identifiers
+ *             @obtype@ = the element type of the array
+ *             @chunksize@ = the number of items in a chunk
+ *
+ * Use:                Provides declarations suitable for use in a header file which
+ *             define functions for manipulating the specific dynamic array
+ *             described in the arguments.
+ */
+
+#define DYNDECLS(prefix, obtype, chunksize) /* ... */                  \
+                                                                       \
+/* --- Define some constants --- */                                    \
+                                                                       \
+enum {                                                                 \
+  prefix ## __mask = chunksize - 1,                                    \
+  prefix ## __size = chunksize                                         \
+};                                                                     \
+                                                                       \
+/* --- Type definitions --- */                                         \
+                                                                       \
+typedef obtype prefix ## __object;                                     \
+                                                                       \
+typedef struct prefix ## _chunk {                                      \
+  struct prefix ## _chunk *next;                                       \
+  size_t base;                                                         \
+  prefix ## __object o[prefix ## __size];                              \
+} prefix ## _chunk;                                                    \
+                                                                       \
+/* --- External routines --- */                                                \
+                                                                       \
+/* --- @PREFIX_find@ --- *                                             \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *             @size_t index@ = index into array which we want         \
+ *                                                                     \
+ * Returns:    Pointer to the object at appropriate index, or null     \
+ *                                                                     \
+ * Use:                Indexes an item without creating it if it's not there   \
+ *             already.                                                \
+ */                                                                    \
+                                                                       \
+extern prefix ## __object *prefix ## _find(prefix ## _chunk **base,    \
+                                          size_t index);               \
+                                                                       \
+/* --- @PREFIX_new@ --- *                                              \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *             @size_t index@ = index into array which we want         \
+ *                                                                     \
+ * Returns:    Pointer to the object at appropriate index              \
+ *                                                                     \
+ * Use:                Indexes an item, creating it if necessary.              \
+ */                                                                    \
+                                                                       \
+extern prefix ## __object *prefix ## _new(prefix ## _chunk **base,     \
+                                         size_t index);                \
+                                                                       \
+/* --- @PREFIX_free@ --- *                                             \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Releases all the memory directly attached to an array.  \
+ */                                                                    \
+                                                                       \
+extern void prefix ## _free(prefix ## _chunk **base);
+
+/* --- Macro: @DYNARRAY@ --- *
+ *
+ * Arguments:  @prefix@ = prefix string for uniquification
+ *             @init@ = how to initialise a chunk
+ *             @kill@ = how to free a chunk
+ *
+ * Use:                Builds template routines for dynamically growing arrays.
+ *             The two arguments @init@ and @kill@ must use the macros
+ *             described below.
+ */
+
+#define DYNARRAY(prefix, init, kill) /* ... */                         \
+                                                                       \
+/* --- @PREFIX_find@ --- *                                             \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *             @size_t index@ = index into array which we want         \
+ *                                                                     \
+ * Returns:    Pointer to the object at appropriate index, or null     \
+ *                                                                     \
+ * Use:                Indexes an item without creating it if it's not there   \
+ *             already.                                                \
+ */                                                                    \
+                                                                       \
+prefix ## __object *prefix ## _find(prefix ## _chunk **base,           \
+                                   size_t index)                       \
+{                                                                      \
+  size_t chunkid = index & ~prefix ## __mask;                          \
+  prefix ## _chunk *p = *base;                                         \
+  index &= prefix ## __mask;                                           \
+                                                                       \
+  for (;;) {                                                           \
+    if (!p || p->base > chunkid)                                       \
+      return (0);                                                      \
+    if (p->base == chunkid)                                            \
+      break;                                                           \
+    p = p->next;                                                       \
+  }                                                                    \
+                                                                       \
+  return (p->o + index);                                               \
+}                                                                      \
+                                                                       \
+/* --- @PREFIX_new@ --- *                                              \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *             @size_t index@ = index into array which we want         \
+ *                                                                     \
+ * Returns:    Pointer to the object at appropriate index              \
+ *                                                                     \
+ * Use:                Indexes an item, creating it if necessary.              \
+ */                                                                    \
+                                                                       \
+prefix ## __object *prefix ## _new(prefix ## _chunk **base,            \
+                                  size_t index)                        \
+{                                                                      \
+  size_t chunkid = index & ~prefix ## __mask;                          \
+  prefix ## _chunk *p = (prefix ## _chunk *)base;                      \
+  index &= prefix ## __mask;                                           \
+                                                                       \
+  while (p->next && p->next->base < chunkid)                           \
+    p = p->next;                                                       \
+                                                                       \
+  if (!p->next || p->next->base != chunkid) {                          \
+    prefix ## _chunk *q = CREATE(prefix ## _chunk);                    \
+    q->next = p->next;                                                 \
+    p->next = q;                                                       \
+    q->base = chunkid;                                                 \
+                                                                       \
+    DYN__GRABARGS(init, (q, prefix));                                  \
+  }                                                                    \
+                                                                       \
+  return (p->next->o + index);                                         \
+}                                                                      \
+                                                                       \
+/* --- @PREFIX_free@ --- *                                             \
+ *                                                                     \
+ * Arguments:  @PREFIX_chunk **base@ = anchor address of chunk list    \
+ *                                                                     \
+ * Returns:    ---                                                     \
+ *                                                                     \
+ * Use:                Releases all the memory directly attached to an array.  \
+ */                                                                    \
+                                                                       \
+void prefix ## _free(prefix ## _chunk **base)                          \
+{                                                                      \
+  prefix ## _chunk *p = *base, *q;                                     \
+                                                                       \
+  while (p) {                                                          \
+    DYN__GRABARGS(kill, (p, prefix));                                  \
+    q = p;                                                             \
+    p = p->next;                                                       \
+    DESTROY(q);                                                                \
+  }                                                                    \
+                                                                       \
+  *base = 0;                                                           \
+}
+
+/* --- A vile bit of hacking --- *
+ *
+ * All of this yukkiness is a perfectly legitimate consequence of the (daft)
+ * rules about when macro arguments get expanded.
+ */
+
+#define DYN__ID(x) x
+#define DYN__ID2(x, y) x, y
+#define DYN__CONTORT(mac, args) mac args
+#define DYN__GRABARGS(mac, args, more)                                 \
+    DYN__CONTORT(mac, (DYN__ID args, DYN__ID2 more))
+
+/* --- Macro: @DYNITER@ --- *
+ *
+ * Arguments:  @what@ = what to do for each item -- a macro or function
+ *                     which is passed the address of an item
+ *
+ * Use:                Does something for each item.
+ */
+
+#define DYNITER DYN__ITER ,
+#define DYN__ITER(what, chunk, prefix) do {                            \
+  int i;                                                               \
+  for (i = 0; i < prefix ## __size; i++)                               \
+    what(chunk->o + i);                                                        \
+} while (0)
+
+/* --- Macro: @DYNNOP@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Use:                Does nothing.
+ */
+
+#define DYNNOP DYN__NOP, (dummy)
+#define DYN__NOP(dummy, chunk, prefix) /* nop */
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/exc.c b/exc.c
new file mode 100644 (file)
index 0000000..1c6468d
--- /dev/null
+++ b/exc.c
@@ -0,0 +1,145 @@
+/* -*-c-*-
+ *
+ * $Id: exc.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Structured exception handling in C
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: exc.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "exc.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+__exc_hnd *__exc_list = 0;
+
+/*----- Functions ---------------------------------------------------------*/
+
+/* --- @exc__duff@ --- *
+ *
+ * Arguments:  @exc_extype type@ = type of duff exception
+ *             @exc_exval val@ = extra data supplied
+ *
+ * Returns:    Doesn't
+ *
+ * Use:                Default handler when everything goes wrong.
+ */
+
+static void exc__duff(exc_extype type, exc_exval val)
+{
+  fprintf(stderr, "fatal error: uncaught exception (type = %lu)\n", type);
+  abort();
+}
+
+/* --- @exc__duffproc@ --- *
+ *
+ * Current handler when there are no more exceptions left.
+ */
+
+static exc__uncaught exc__duffproc = exc__duff;
+
+/* --- @exc_uncaught@ --- *
+ *
+ * Arguments:  @void (*proc)(exc_extype type, exc_exval val) = new handler
+ *
+ * Returns:    Pointer to the old handler value.
+ *
+ * Use:                Sets the handler for uncaught exceptions.
+ */
+
+exc__uncaught exc_uncaught(exc__uncaught proc)
+{
+  exc__uncaught p = exc__duffproc;
+  if (proc)
+    exc__duffproc = proc;
+  return (p);
+}
+
+/* --- @__exc_throw@ --- *
+ *
+ * Arguments:  @exc_extype type@ = type of exception to throw
+ *
+ * Returns:    Doesn't
+ *
+ * Use:                NOT FOR USER CONSUMPTION.  Reads an appropriate exception
+ *             value and throws an exception.
+ */
+
+void __exc_throw(exc_extype type, ...)
+{
+  va_list ap;
+  exc_exval v;
+
+  va_start(ap, type);
+  switch (type & 0xC0) {
+    case EXC_NOVAL:
+      v.i = 0;
+      break;
+    case EXC_INTVAL:
+      v.i = va_arg(ap, int);
+      break;
+    case EXC_PTRVAL:
+      v.p = va_arg(ap, void *);
+      break;
+    case EXC_STRVAL:
+      v.s = va_arg(ap, char *);
+      break;
+  }
+  va_end(ap);
+  __exc_rethrow(type, v);
+}
+
+/* --- @__exc_rethrow@ --- *
+ *
+ * Arguments:  @exc_extype type@ = type of exception to throw
+ *             @exc_exval val@ = value of exception to throw
+ *
+ * Returns:    Doesn't
+ *
+ * Use:                NOT FOR USER CONSUMPTION.  Does the donkey-work of raising
+ *             an exception.
+ */
+
+void __exc_rethrow(exc_extype type, exc_exval val)
+{
+  __exc_hnd *p = __exc_list;
+  if (!p)
+    exc__duffproc(type, val);
+  p->type = type;
+  p->val = val;
+  __exc_list = p->next;
+  longjmp(p->buf, type);
+}  
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/exc.h b/exc.h
new file mode 100644 (file)
index 0000000..a2e004e
--- /dev/null
+++ b/exc.h
@@ -0,0 +1,341 @@
+/* -*-c-*-
+ *
+ * $Id: exc.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Structured exception handling in C
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: exc.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef EXC_H
+#define EXC_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <setjmp.h>
+
+/*----- Quick documentation -----------------------------------------------*
+ *
+ * This header file provides some exception handling facilities in C
+ * programs.  It modifies the syntax of the language slightly, using the
+ * preprocessor.
+ *
+ * The `throw' expression returns no value.  It has the syntax:
+ *
+ *   THROW ( expr , expr )
+ *
+ * The first expression must have type compatible with unsigned integer; it
+ * identifies an `exception type'.  The second must have type compatible
+ * with pointer to void; it contains the `exception data'.  Control is
+ * passed to the current exception handler.
+ *
+ * The `RETHROW' expression, valid only within an exception handler, causes
+ * the current exception to be thrown again.
+ *
+ * A `try' statement has the syntax:
+ *
+ *   TRY stat CATCH stat END_TRY;
+ *
+ * The first statement is called the `test'; the second is the `handler'.
+ * During execution of the test, the handler is added to a stack of
+ * active exception handlers; the topmost handler on this stack is called
+ * the `current' handler.  When execution of the test completes, the
+ * corresponding handler is removed from the stack.
+ *
+ * The test statement may complete in one of these ways:
+ *
+ *   * Normal completion -- control reaches the end of the statement
+ *     normally.
+ *
+ *   * Throwing an exception -- an exception is thrown when the handler is
+ *     the current exception handler.
+ *
+ *   * By executing a `break' statement.
+ *
+ *   * By executing the expression `EXIT_TRY' and transferring control to
+ *     a point outside the entire `try' statement (e.g., executing a `goto'
+ *     or `return' statement).
+ *
+ * Any other attempt to leave the test causes undefined behaviour.
+ *
+ * If an exception is thrown while the handler is the current exception
+ * handler, it is given control.  The variables `exc_type' and `exc_val'
+ * denote the exception type and value respectively -- they are passed
+ * unchanged from the `throw' expression which caused the exception.
+ * A handler is deactivated before it is invoked; if it causes an
+ * exception to be thrown (and does not contain a nested `try' statement)
+ * control will be passed to an earlier active handler.
+ *
+ * Control is passed to handlers using the `longjmp' function.
+ *
+ * Example:
+ *
+ *   TRY {
+ *     ... something dangerous ...
+ *   } CATCH switch (exc_type) {
+ *     case EXC_INTERESTING:
+ *      ... handle exception ...
+ *      break;
+ *     default:
+ *      ... do tidying up ...
+ *      RETHROW;
+ *   } END_TRY;
+ */
+
+/*----- Exception type allocation -----------------------------------------*
+ *
+ * Nobody allocates exception types, so we'll just have to try to get along
+ * without too many collisions.  An exception type is an unsigned long,
+ * which gives us four bytes.  The top two bytes identify the library which
+ * `owns' the exception, with special values zero meaning `defined as part
+ * of the system' and 0xFFFF providing a shared space of types which can
+ * be used by anyone as long as they don't get seen by anyone else.
+ *
+ * The lower byte pair encodes a type number, and a value which defines
+ * the type of the value field (see below).
+ */
+
+/* --- Type type of an exception --- */
+
+typedef unsigned long exc_extype;
+
+/* --- Build a byte pair from two characters --- *
+ *
+ * Note the icky casting to handle signed chars.
+ */
+
+#define EXC_PAIR(x, y) (((unsigned long)(unsigned char)(x) << 8) |     \
+                       (unsigned long)(unsigned char)(y))
+
+/* --- Allocate an exception number --- */
+
+#define EXC_ALLOC(owner, type) (((unsigned long)(owner) << 16) |       \
+                               (unsigned long)(type))
+
+/* --- Special owner codes --- */
+
+#define EXC_GLOBAL 0u                  /* The global space defined here */
+#define EXC_SHARED 0xFFFFu             /* The shared space for everyone */
+
+/*----- Exception values --------------------------------------------------*
+ *
+ * Exception values can have several different types.  This is a mess, and
+ * C doesn't handle it too well, but we can try.  I'll encode the value type
+ * as part of the exception type, in the top bits of the bottom byte.  Messy?
+ * You betcha.
+ */
+
+/* --- Encoding a value type in an extype --- */
+
+#define EXC_TYPECODE(t, w) (((w) & ~0xC0u) | ((t) & 0xC0u))
+
+/* --- The various value types --- */
+
+#define EXC_NOVAL 0x00u                        /* No interesting value */
+#define EXC_INTVAL 0x40u               /* Integer value */
+#define EXC_PTRVAL 0x80u               /* Arbitrary pointer value */
+#define EXC_STRVAL 0xC0u               /* Pointer to character string */
+
+/* --- Allocating exceptions with appropriate types --- */
+
+#define EXC_ALLOCN(o, t) EXC_TYPECODE(EXC_NOVAL,  EXC_ALLOC(o, t))
+#define EXC_ALLOCI(o, t) EXC_TYPECODE(EXC_INTVAL, EXC_ALLOC(o, t))
+#define EXC_ALLOCP(o, t) EXC_TYPECODE(EXC_PTRVAL, EXC_ALLOC(o, t))
+#define EXC_ALLOCS(o, t) EXC_TYPECODE(EXC_STRVAL, EXC_ALLOC(o, t))
+
+/* --- A union representing the type --- */
+
+typedef union exc_exval {
+  int i;
+  void *p;
+  char *s;
+} exc_exval;
+
+/*----- Predefined exceptions ---------------------------------------------*/
+
+/* --- @EXC_NOMEM@ --- *
+ *
+ * Value:      ---
+ *
+ * Meaning:    An attempt to allocate memory failed.
+ */
+
+#define EXC_NOMEM EXC_ALLOCN(EXC_GLOBAL, 0u)
+
+/* --- @EXC_ERRNO@ --- *
+ *
+ * Value:      @int errno@ = the error raised
+ *
+ * Meaning:    Some kind of OS error occurred.
+ */
+
+#define EXC_ERRNO EXC_ALLOCI(EXC_GLOBAL, 1u)
+
+/* --- @EXC_OSERROR@ --- *
+ *
+ * Value:      @os_error *e@ = pointer to error block
+ *
+ * Meaning:    For RISC OS programmers only: alternative way of propagating
+ *             errors.
+ */
+
+#define EXC_OSERROR EXC_ALLOCP(EXC_GLOBAL, 1u)
+
+/* --- @EXC_SIGNAL@ --- *
+ *
+ * Value:      @int sig@ = signal number
+ *
+ * Meaning:    Report the raising of a signal.
+ */
+
+#define EXC_SIGNAL EXC_ALLOCI(EXC_GLOBAL, 2u)
+
+/* --- @EXC_FAIL@ --- *
+ *
+ * Value:      @const char *p@ = pointer to expanatory string
+ *
+ * Meaning:    Miscellaneous error.
+ */
+
+#define EXC_FAIL EXC_ALLOCS(EXC_GLOBAL, 0xFFu)
+
+/*----- An exception handler block ----------------------------------------*/
+
+/* --- Try to think of this as being opaque --- */
+
+typedef struct __exc_hnd {
+  struct __exc_hnd *next;              /* Pointer to next record down */
+  exc_extype type;                     /* Type of this exception */
+  exc_exval val;                       /* Value of this exception */
+  jmp_buf buf;                         /* Jump buffer when exceptions hit */
+} __exc_hnd;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern __exc_hnd *__exc_list;          /* List of active handlers */
+
+/*----- Macros ------------------------------------------------------------*/
+
+/* --- References to current exception type and value --- */
+
+#define exc_type (__exc_ec.type)
+#define exc_val (__exc_ec.val)
+#define exc_i (__exc_ec.val.i)
+#define exc_p (__exc_ec.val.p)
+#define exc_s (__exc_ec.val.s)
+
+/* --- How it actually works --- *
+ *
+ * A `try' block is contained within a block which provides an exception
+ * handler buffer in automatic storage.  This block is a loop, to allow
+ * `break' to escape from it.  It adds the handler buffer to the top of a
+ * list, and does a `setjmp' to allow a return here following an exception.
+ * The `setjmp' returns zero for the `try' section, and nonzero if there's
+ * an exception to `catch'.  It looks a little like this:
+ *
+ *   do {
+ *     __exc_hnd h;
+ *     add_handler(&h);
+ *     if (!setjmp(h.buf)) {
+ *      do <try code> while (0);
+ *      remove_handler(&h);
+ *     } else
+ *      <catch code>
+ *   } while (0)
+ *
+ * Everything else is ugly hacking to make things work.
+ */
+
+/* --- Trying things which may cause exceptions --- */
+
+#define TRY do {                                                       \
+  volatile __exc_hnd __exc_ec;                                         \
+  __exc_ec.next = __exc_list;                                          \
+  __exc_list = (__exc_hnd *)&__exc_ec;                                 \
+  if (!setjmp(*(jmp_buf *)&__exc_ec.buf /* very nasty! */ )) { do
+
+#define EXIT_TRY do __exc_list = __exc_ec.next; while (0)
+#define CATCH while (0); EXIT_TRY; } else
+
+#define END_TRY } while (0)
+
+/* --- Raising exceptions --- */
+
+#define THROW __exc_throw
+#define RETHROW __exc_rethrow(__exc_ec.type, __exc_ec.val)
+
+/*----- Functions ---------------------------------------------------------*/
+
+/* --- @exc_uncaught@ --- *
+ *
+ * Arguments:  @void (*proc)(exc_extype type, exc_exval val) = new handler
+ *
+ * Returns:    Pointer to the old handler value.
+ *
+ * Use:                Sets the handler for uncaught exceptions.
+ */
+
+typedef void (*exc__uncaught)(exc_extype /*type*/, exc_exval /*val*/);
+extern exc__uncaught exc_uncaught(exc__uncaught /*proc*/);
+
+/* --- @__exc_throw@ --- *
+ *
+ * Arguments:  @exc_extype type@ = type of exception to throw
+ *
+ * Returns:    Doesn't
+ *
+ * Use:                NOT FOR USER CONSUMPTION.  Reads an appropriate exception
+ *             value and throws an exception.
+ */
+
+extern void __exc_throw(exc_extype /*type*/, ...);
+
+/* --- @__exc_rethrow@ --- *
+ *
+ * Arguments:  @exc_extype type@ = type of exception to throw
+ *             @exc_exval val@ = value of exception to throw
+ *
+ * Returns:    Doesn't
+ *
+ * Use:                NOT FOR USER CONSUMPTION.  Does the donkey-work of raising
+ *             an exception.
+ */
+
+extern void __exc_rethrow(exc_extype /*type*/, exc_exval /*val*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/exctest.c b/exctest.c
new file mode 100644 (file)
index 0000000..f9a9463
--- /dev/null
+++ b/exctest.c
@@ -0,0 +1,41 @@
+#include <stdio.h>
+#include "exc.h"
+
+void func(void)
+{
+  printf("cabbage\n");
+  TRY {
+    printf("dibble\n");
+    THROW(EXC_FAIL, "excession");
+    printf("can't see me\n");
+  } CATCH switch (exc_type) {
+    case 1:
+      printf("exc type 1 (not possible)\n");
+      break;
+    case EXC_FAIL:
+      printf("exc type 2 (%s)\n", exc_s);
+      break;
+    default:
+      RETHROW;
+  } END_TRY;
+
+  printf("fennel\n");
+  THROW(EXC_ERRNO, 53);
+}
+
+int main(void)
+{
+  printf("apple\n");
+  TRY {
+    printf("banana\n");
+    func();
+    printf("can't see me\n");
+  } CATCH switch (exc_type) {
+    default:
+      printf("%lu exception (val = %i)\n", exc_type, exc_i);
+      break;
+  } END_TRY;
+  printf("hello! __exc_list = %p\n", __exc_list);
+
+  return (0);
+}
diff --git a/mdwopt.c b/mdwopt.c
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/mdwopt.h b/mdwopt.h
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/quis.c b/quis.c
new file mode 100644 (file)
index 0000000..34b0f1f
--- /dev/null
+++ b/quis.c
@@ -0,0 +1,92 @@
+/* -*-c-*-
+ *
+ * $Id: quis.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Setting the program name
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: quis.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "quis.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+const char *pn__name = "<UNNAMED>";    /* Program name */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @quis@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Pointer to the program name.
+ *
+ * Use:                Returns the program name.
+ */
+
+const char *quis(void) { return (QUIS); }
+
+/* --- @ego@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to program name
+ *
+ * Returns:    ---
+ *
+ * Use:                Tells mLib what the program's name is.
+ */
+
+#ifndef PATHSEP
+#  if defined(__riscos)
+#    define PATHSEP '.'
+#  elif defined(__unix) || defined(unix)
+#    define PATHSEP '/'
+#  else
+#    define PATHSEP '\\'
+#  endif
+#endif
+
+void ego(const char *p)
+{
+  const char *q = p;
+  while (*q) {
+    if (*q++ == PATHSEP)
+      p = q;
+  }
+  pn__name = p;
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/quis.h b/quis.h
new file mode 100644 (file)
index 0000000..b60084c
--- /dev/null
+++ b/quis.h
@@ -0,0 +1,79 @@
+/* -*-c-*-
+ *
+ * $Id: quis.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Setting the program name
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: quis.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef PROGNAME_H
+#define PROGNAME_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Global variables --------------------------------------------------*/
+
+extern const char *pn__name;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @quis@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Pointer to the program name.
+ *
+ * Use:                Returns the program name.
+ */
+
+extern const char *quis(void);
+#define QUIS (pn__name)
+
+/* --- @ego@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to program name
+ *
+ * Returns:    ---
+ *
+ * Use:                Tells mLib what the program's name is.
+ */
+
+extern void ego(const char */*p*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/report.c b/report.c
new file mode 100644 (file)
index 0000000..7a7aacf
--- /dev/null
+++ b/report.c
@@ -0,0 +1,92 @@
+/* -*-c-*-
+ *
+ * $Id: report.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Reporting errors and things
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: report.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "quis.h"
+#include "report.h"
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @moan@ --- *
+ *
+ * Arguments:  @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports an error.
+ */
+
+void moan(const char *f, ...)
+{
+  va_list ap;
+  va_start(ap, f);
+  fprintf(stderr, "%s: ", QUIS);
+  vfprintf(stderr, f, ap);
+  va_end(ap);
+  putc('\n', stderr);
+}
+
+/* --- @die@ --- *
+ *
+ * Arguments:  @int status@ = exit status to return
+ *             @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    Never.
+ *
+ * Use:                Reports an error and exits.  Like @moan@ above, only more
+ *             permanent.
+ */
+
+void die(int status, const char *f, ...)
+{
+  va_list ap;
+  va_start(ap, f);
+  fprintf(stderr, "%s: ", QUIS);
+  vfprintf(stderr, f, ap);
+  va_end(ap);
+  putc('\n', stderr);
+  exit(status);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/report.h b/report.h
new file mode 100644 (file)
index 0000000..16b4ab7
--- /dev/null
+++ b/report.h
@@ -0,0 +1,78 @@
+/* -*-c-*-
+ *
+ * $Id: report.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Reporting errors and things
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: report.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef REPORT_H
+#define REPORT_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @moan@ --- *
+ *
+ * Arguments:  @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports an error.
+ */
+
+extern void moan(const char *f, ...);
+
+/* --- @die@ --- *
+ *
+ * Arguments:  @int status@ = exit status to return
+ *             @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    Never.
+ *
+ * Use:                Reports an error and exits.  Like @moan@ above, only more
+ *             permanent.
+ */
+
+extern void die(int status, const char *f, ...);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/sub.c b/sub.c
new file mode 100644 (file)
index 0000000..2f0fed3
--- /dev/null
+++ b/sub.c
@@ -0,0 +1,260 @@
+/* -*-c-*-
+ *
+ * $Id: sub.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Allocation of known-size blocks
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sub.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- The big idea ------------------------------------------------------*
+ *
+ * This file provides an extra layer over @malloc@.  It provides fast
+ * turnover for small blocks, and tries to minimise the per-block overhead.
+ *
+ * To do its job, @alloc@ must place an extra restriction on you: you must
+ * know the size of a block when you free it.  Usually you'll have this
+ * information encoded in some way either in the block or in the thing that
+ * referenced it, so this isn't a hardship.
+ *
+ * It works fairly simply.  If a request for a big block (as defined by the
+ * constants below) comes in, it gets sent on to @malloc@ unmolested.  For
+ * small blocks, it goes straight to a `bin' -- a list containing free blocks
+ * of exactly that size, or the nearest bigger size we can manage.  If the
+ * bin is empty, a `chunk' is allocated from @malloc@: this has enough room
+ * for lots of blocks of the requested size, so it ets split up and each
+ * individual small block is added to the bin list.  The first block in the
+ * bin list is then removed and given to the caller.  In this way, @malloc@
+ * only stores its information once for lots of little blocks, so we save
+ * memory.  Because I know where the correct bin is just from the block size,
+ * and I don't need to do any searching at all in the usual case (because the
+ * list isn't empty) I can get a speed advantage too.
+ *
+ * This code is almost certainly not ANSI conformant, although I'm not
+ * actually sure.  If some kind soul would let me know how seriously I've
+ * violated the standard, and whether this is easily fixable, I'd be
+ * grateful.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#undef TRACK_ENABLE                    /* Can't track suballoc routines */
+#include "alloc.h"
+
+/*----- Configuration and tuning ------------------------------------------*/
+
+/* --- The largest block I'll handle here --- *
+ *
+ * Anything larger will be handed on to @malloc@.
+ */
+
+#define SUB_MAXBIN 256
+
+/* --- Preferred chunk size --- *
+ *
+ * When a bin is empty, I'll allocate a large chunk of approximately this
+ * size and divvy it up into small bin-sized blocks.
+ */
+
+#define SUB_CHUNK 4096
+
+/*----- Other useful macros -----------------------------------------------*/
+
+/* --- The granularity of bin buffers --- *
+ *
+ * All blocks allocated by the binner are a multiple of this size.  I've
+ * chosen @void *@ because I need to store @void *@ things in here.
+ */
+
+#define SUB_GRANULE sizeof(void *)
+
+/* --- Finding the right bin for a given size --- *
+ *
+ * This chooses the correct bin for an allocation.  Input is the size of
+ * block wanted; result is the bin index.
+ */
+
+#define SUB_BIN(x) (((x) + SUB_GRANULE - 1) / SUB_GRANULE)
+
+/* --- Convert a bin back to the block size --- *
+ *
+ * This gives the size of block contained in a given bin.
+ */
+
+#define SUB_BINSZ(x) ((x) * SUB_GRANULE)
+
+/* --- Number of bins required --- */
+
+#define SUB_BINS (SUB_MAXBIN / SUB_GRANULE + 1)
+
+/*----- Static variables --------------------------------------------------*/
+
+static void *sub__bins[SUB_BINS];
+static size_t sub__sizes[SUB_BINS];
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sub_alloc@ --- *
+ *
+ * Arguments:   @size_t s@ = size of chunk wanted
+ *
+ * Returns:     Pointer to a block at least as large as the one wanted.
+ *
+ * Use:         Allocates a small block of memory.  If there is no more
+ *             memory left, the exception @EXC_NOMEM@ is raised.
+ */
+
+void *sub_alloc(size_t s)
+{
+  int bin = SUB_BIN(s);
+  void *p;
+
+  /* --- Handle oversize blocks --- */
+
+  if (bin >= SUB_BINS)
+    return (xmalloc(s));
+
+  /* --- If the bin is empty, find some memory --- */
+
+  if (!sub__bins[bin]) {
+    char *p, *q;
+
+    p = xmalloc(sub__sizes[bin]);
+    q = p + sub__sizes[bin];
+
+    s = SUB_BINSZ(bin);
+
+    q -= s;
+    *(void **)q = 0;
+
+    while (q > p) {
+      q -= s;
+      *(void **)q = q + s;
+    }
+
+    sub__bins[bin] = p;
+  }
+
+  /* --- Extract the first block in the list --- */
+
+  p = sub__bins[bin];
+  sub__bins[bin] = *(void **)p;
+  return (p);
+}
+
+/* --- @sub_free@ --- *
+ *
+ * Arguments:   @void *p@ = address of block to free
+ *              @size_t s@ = size of block
+ *
+ * Returns:     ---
+ *
+ * Use:         Frees a block allocated by @sub_alloc@.
+ */
+
+void sub_free(void *p, size_t s)
+{
+  int bin = SUB_BIN(s);
+
+  if (bin >= SUB_BINS)
+    free(p);
+  else {
+    *(void **)p = sub__bins[bin];
+    sub__bins[bin] = p;
+  }
+}
+
+/* --- @sub_init@ --- *
+ *
+ * Arguments:   ---
+ *
+ * Returns:     ---
+ *
+ * Use:         Initialises the magic allocator.
+ */
+
+void sub_init(void)
+{
+  int i;
+
+  /* --- Initialise the sizes bins --- */
+
+  for (i = 1; i < SUB_BINS; i++) {
+    sub__sizes[i] = ((SUB_CHUNK + SUB_BINSZ(i) - 1) /
+                    SUB_BINSZ(i) * SUB_BINSZ(i));
+  }
+}
+
+/*----- Debugging code ----------------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#define BLOCKS 1024
+#define SIZE_MAX 2048
+#define ITERATIONS 500000
+
+int main(void)
+{
+  static void *block[BLOCKS];
+  static size_t size[BLOCKS];
+  size_t allocced = 0;
+  int i;
+  long count;
+
+  sub_init();
+
+  for (count = 0; count < ITERATIONS; count++) {
+    i = rand() % BLOCKS;
+    if (block[i]) {
+      sub_free(block[i], size[i]);
+      block[i] = 0;
+      allocced -= size[i];
+    } else {
+      block[i] = sub_alloc(size[i] =
+                          rand() % (SUB_MAXBIN - 128) + 128);
+      allocced += size[i];
+      memset(block[i], 0, size[i]);    /* trample allocated storage */
+    }
+  }
+
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/sub.h b/sub.h
new file mode 100644 (file)
index 0000000..e7bd633
--- /dev/null
+++ b/sub.h
@@ -0,0 +1,127 @@
+/* -*-c-*-
+ *
+ * $Id: sub.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Allocation of known-size blocks
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sub.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SUB_H
+#define SUB_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Required header files ---------------------------------------------*/
+
+#include <stdlib.h>
+
+#ifndef ALLOC_H
+#  include "alloc.h"
+#endif
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @sub_alloc@ --- *
+ *
+ * Arguments:   @size_t s@ = size of chunk wanted
+ *
+ * Returns:     Pointer to a block at least as large as the one wanted.
+ *
+ * Use:         Allocates a small block of memory.  If there is no more
+ *             memory left, the exception @EXC_NOMEM@ is raised.
+ */
+
+#ifdef TRACK_ENABLE
+#  define sub_alloc(s) xmalloc(s)
+#else
+void *sub_alloc(size_t s);
+#endif
+
+/* --- @sub_free@ --- *
+ *
+ * Arguments:   @void *p@ = address of block to free
+ *              @size_t s@ = size of block
+ *
+ * Returns:     ---
+ *
+ * Use:         Frees a block allocated by @sub_alloc@.
+ */
+
+#ifdef TRACK_ENABLE
+#  define sub_free(p, s) free(p)
+#else
+void sub_free(void *p, size_t s);
+#endif
+
+/* --- @CREATE@ --- *
+ *
+ * Arguments:  @type@ = type of object required; must be passable to
+ *                     @sizeof@
+ *
+ * Returns:    Pointer to a block sufficiently big to hold an object of the
+ *             named type.
+ *
+ * Use:                Allocates a block of the required type.
+ */
+
+#define CREATE(type) sub_alloc(sizeof(type))
+
+/* --- @DESTROY@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to an object
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees the thing pointed to by @p@.
+ */
+
+#define DESTROY(p) sub_free(p, sizeof(*p))
+
+/* --- @sub_init@ --- *
+ *
+ * Arguments:   ---
+ *
+ * Returns:     ---
+ *
+ * Use:         Initialises the magic allocator.
+ */
+
+void sub_init(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/sym.c b/sym.c
new file mode 100644 (file)
index 0000000..2cdbc24
--- /dev/null
+++ b/sym.c
@@ -0,0 +1,624 @@
+/* -*-c-*-
+ *
+ * $Id: sym.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Symbol table management
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sym.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "alloc.h"
+#include "crc32.h"
+#include "exc.h"
+#include "sub.h"
+#include "sym.h"
+#include "track.h"
+
+/*----- Tuning parameters -------------------------------------------------*/
+
+/* --- Initial hash table size --- *
+ *
+ * This is the initial @mask@ value.  It must be of the form %$2^n - 1$%,
+ * so that it can be used to mask of the bottom bits of a hash value.
+ */
+
+#define SYM_INITSZ 255                 /* Size of a new hash table */
+
+/* --- Maximum load factor --- *
+ *
+ * This parameter controls how much the table has to be loaded before the
+ * table is extended.  The number of elements %$n$%, the number of bins %$b$%
+ * and the limit %$l$% satisfy the relation %$n < bl$%; if a new item is
+ * added to the table and this relation is found to be false, the table is
+ * doubled in size.
+ *
+ * The current function gives %$l = {3n \over 4}$%, which appears to be
+ * reasonable on the face of things.
+ */
+
+#define SYM_LIMIT(n) (((n) * 3) >> 2)  /* Load factor for growing table */
+
+/*----- Useful macros -----------------------------------------------------*/
+
+#define SYM_NAME(sy)                                                   \
+  ((sy)->len > SYM_BUFSZ ? (sy)->name.p : (sy)->name.b)
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @sym_createTable@ --- *
+ *
+ * Arguments:  @sym_table *t@ = symbol table to initialise
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialises the given symbol table.  Raises @EXC_NOMEM@ if
+ *             there isn't enough memory.
+ */
+
+void sym_createTable(sym_table *t)
+{
+  size_t i;
+
+  TRACK_CTX("symbol table creation");
+  TRACK_PUSH;
+
+  t->mask = SYM_INITSZ;
+  t->c = SYM_LIMIT(SYM_INITSZ);
+  t->a = xmalloc((t->mask + 1) * sizeof(sym_base *));
+
+  for (i = 0; i < SYM_INITSZ + 1; i++)
+    t->a[i] = 0;
+
+  TRACK_POP;
+}
+
+/* --- @sym_destroyTable@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table in question
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a symbol table, freeing all the memory it used to
+ *             occupy.
+ */
+
+void sym_destroyTable(sym_table *t)
+{
+  size_t i;
+  sym_base *p, *q;
+
+  TRACK_CTX("symbol table deletion");
+  TRACK_PUSH;
+
+  for (i = 0; i <= t->mask; i++) {
+    p = t->a[i];
+    while (p) {
+      q = p->next;
+      if (p->len > SYM_BUFSZ)
+       sub_free(p->name.p, p->len);
+      free(p);
+      p = q;
+    }
+  }
+  free(t->a);
+
+  TRACK_POP;
+}
+
+/* --- @sym_find@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table in question
+ *             @const char *n@ = pointer to symbol table to look up
+ *             @long l@ = length of the name string or negative to measure
+ *             @size_t sz@ = size of desired symbol object, or zero
+ *             @unsigned *f@ = pointer to a flag, or null.
+ *
+ * Returns:    The address of a @sym_base@ structure, or null if not found
+ *             and @sz@ is zero.
+ *
+ * Use:                Looks up a symbol in a given symbol table.  The name is
+ *             passed by the address of its first character.  The length
+ *             may be given, in which case the name may contain arbitrary
+ *             binary data, or it may be given as a negative number, in
+ *             which case the length of the name is calculated as
+ *             @strlen(n) + 1@.
+ *
+ *             The return value is the address of a pointer to a @sym_base@
+ *             block (which may have other things on the end, as above).  If
+ *             the symbol could be found, the return value points to the
+ *             symbol block.  If the symbol wasn't there, then if @sz@ is
+ *             nonzero, a new symbol is created and its address is returned;
+ *             otherwise a null pointer is returned.  The exception
+ *             @EXC_NOMEM@ is raised if the block can't be allocated.
+ *
+ *             The value of @*f@ indicates whether a new symbol entry was
+ *             created: a nonzero value indicates that an old value was
+ *             found.
+ */
+
+void *sym_find(sym_table *t, const char *n, long l, size_t sz, unsigned *f)
+{
+  unsigned long hash;                  /* Hash value for user's name */
+  size_t len = l < 0 ? strlen(n) + 1 : l; /* Find length of user's name */
+  sym_base *bin;                       /* Bin containing our item */
+  sym_base *p, *q;                     /* Pointer wandering through list */
+
+  /* --- Find the correct bin --- */
+
+  CRC32(hash, 0, n, len);              /* Find hash value for this name */
+  bin = p = (sym_base *)(t->a + (hash & t->mask));
+
+  /* --- Search the bin list --- */
+
+  while (p->next) {
+    if (hash == p->next->hash &&       /* Check full hash values first */
+       len == p->next->len &&          /* Then compare string lengths */
+       !memcmp(n, SYM_NAME(p->next), len)) /* And finally compare names */
+    {
+      /* --- Found a match --- *
+       *
+       * As a minor, and probably pointless, tweak, move the item to the
+       * front of its bin list.
+       */
+
+      q = p->next;                     /* Find the actual symbol block */
+      p->next = q->next;               /* Extract block from bin list */
+      q->next = bin->next;             /* Set up symbol's next pointer */
+      bin->next = q;                   /* And reinsert the block */
+
+      /* --- Return the block --- */
+
+      if (f) *f = 1;                   /* Didn't fail to find the item */
+      return (q);                      /* And return the block */
+    }
+
+    p = p->next;                       /* Move onto the next item */
+  }
+
+  /* --- Couldn't find the item there --- */
+
+  if (f) *f = 0;                       /* Failed to find the block */
+  if (!sz) return (0);                 /* Return zero if not creating */
+
+  /* --- Create a new symbol block and initialise it --- */
+
+  {
+    TRACK_CTX("new symbol creation");
+    TRACK_PUSH;
+
+    p = xmalloc(sz);                   /* Create a new symbol block */
+    p->next = bin->next;               /* Set up the next pointer */
+    p->hash = hash;                    /* Set up the hash value too */
+    p->len = len;                      /* And set up the string length */
+    if (len <= SYM_BUFSZ)
+      memcpy(p->name.b, n, len);       /* And copy the string over */
+    else {
+      TRY {
+       p->name.p = sub_alloc(len);     /* Allocate a block for the name */
+       memcpy(p->name.p, n, len);      /* And copy the string over */
+      } CATCH {
+       free(p);
+       TRACK_POP;
+       RETHROW;
+      } END_TRY;
+    }
+
+    TRACK_POP;
+  }
+
+  bin->next = p;                       /* Put the pointer into the bin */
+
+  /* --- Consider growing the array --- */
+
+  if (!--t->c) {
+    unsigned long m = t->mask + 1;     /* Next set bit in has word */
+    sym_base *p, *q, *r;               /* More useful pointers */
+    size_t i, lim;                     /* Loop counter and limit */
+
+    TRACK_CTX("symbol table extension");
+    TRACK_PUSH;
+
+    /* --- Update values in the anchor block --- */
+
+    TRY {
+      t->a = xrealloc(t->a, (t->mask + 1) * 2 * sizeof(sym_base *));
+    } CATCH switch (exc_type) {
+      case EXC_NOMEM:
+       TRACK_POP;
+       return (p);
+      default:
+       TRACK_POP;
+       RETHROW;
+    } END_TRY;
+
+    t->c = SYM_LIMIT(t->mask + 1);     /* Set load value */
+    t->mask = (t->mask + 1) * 2 - 1;   /* Set the new mask value */
+
+    /* --- Now wander through the table rehashing things --- *
+     *
+     * This loop is very careful to avoid problems with aliasing.  The items
+     * are dealt with from the end backwards to avoid overwriting bins before
+     * they've been processed.
+     */
+
+    lim = (t->mask + 1) >> 1;
+    for (i = 0; i < lim; i++) {
+
+      /* --- Some initialisation --- */
+
+      r = t->a[i];                     /* Find the list we're dissecting */
+      p = (sym_base *)(t->a + i);      /* Find bit-clear list */
+      q = (sym_base *)(t->a + i + lim);        /* And the bit-set lsit */
+
+      /* --- Now go through the @r@ list --- */
+
+      while (r) {
+       if (r->hash & m)                /* Is the next bit set? */
+         q = q->next = r;              /* Yes, so fit up previous link */
+       else
+         p = p->next = r;              /* No, so fit up previous link */
+       r = r->next;                    /* Move onto the next item */
+      }
+      p->next = q->next = 0;           /* Null terminate the new lists */
+    }
+
+    TRACK_POP;
+  }
+
+  /* --- Finished that, so return the new symbol block --- */
+
+  return (p);
+}
+
+/* --- @sym_remove@ --- *
+ *
+ * Arguments:  @sym_table *i@ = pointer to a symbol table object
+ *             @void *b@ = pointer to symbol table entry
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes the object from the symbol table.  The space occupied
+ *             by the object and its name is freed; anything else attached
+ *             to the entry should already be gone by this point.
+ */
+
+void sym_remove(sym_table *t, void *b)
+{
+  /* --- A quick comment --- *
+   *
+   * Since the @sym_base@ block contains the hash, finding the element in the
+   * bin list is really quick -- it's not worth bothering with things like
+   * doubly linked lists.
+   */
+
+  sym_base *p = b;
+  sym_base *bin = (sym_base *)(t->a + (p->hash & t->mask));
+
+  /* --- Find the item in the bin list --- */
+
+  while (bin->next) {
+    if (bin->next == p)
+      break;
+    bin = bin->next;
+  }
+  if (!bin->next)
+    return;
+
+  /* --- Now just remove the item from the list and free it --- *
+   *
+   * Oh, and bump the load counter.
+   */
+
+  bin->next = p->next;
+  if (p->len > SYM_BUFSZ)
+    sub_free(p->name.p, p->len);
+  free(p);
+  t->c++;
+}
+
+/* --- @sym_createIter@ --- *
+ *
+ * Arguments:  @sym_iter *i@ = pointer to an iterator object
+ *             @sym_table *t@ = pointer to a symbol table object
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates a new symbol table iterator which may be used to
+ *             iterate through a symbol table.
+ */
+
+void sym_createIter(sym_iter *i, sym_table *t)
+{
+  i->t = t;
+  i->i = 0;
+  i->n = 0;
+}
+
+/* --- @sym_next@ --- *
+ *
+ * Arguments:  @sym_iter *i@ = pointer to iterator object
+ *
+ * Returns:    Pointer to the next symbol found, or null when finished.
+ *
+ * Use:                Returns the next symbol from the table.  Symbols are not
+ *             returned in any particular order.
+ */
+
+void *sym_next(sym_iter *i)
+{
+  sym_base *p;
+
+  /* --- Find the next item --- */
+
+  while (!i->n) {
+    if (i->i > i->t->mask)
+      return (0);
+    i->n = i->t->a[i->i++];
+  }
+
+  /* --- Update the iterator block --- */
+
+  p = i->n;
+  i->n = p->next;
+
+  /* --- Done --- */
+
+  return (p);
+}
+
+/*----- Symbol table test code --------------------------------------------*/
+
+#ifdef TEST_RIG
+
+#include <errno.h>
+#include <time.h>
+
+typedef struct sym_word {
+  sym_base base;
+  size_t i;
+} sym_word;
+
+
+/* --- What it does --- *
+ *
+ * Reads the file /usr/dict/words (change to some other file full of
+ * interesting and appropriate bits of text to taste) into a big buffer and
+ * picks apart into lines.  Then picks lines at random and enters them into
+ * the symbol table.
+ */
+
+int main(void)
+{
+  char *buff, *p, *lim;
+  size_t sz, done;
+  FILE *fp;
+  int i;
+  char **line;
+  sym_word **flag;
+  sym_table tbl;
+  int entries;
+
+  /* --- Initialise for reading the file --- */
+
+  sz = BUFSIZ;
+  buff = xmalloc(sz + 1);
+  done = 0;
+  sub_init();
+
+  if ((fp = fopen("/usr/dict/words", "r")) == 0)
+    fprintf(stderr, "buggered ;-( (%s)\n", strerror(errno));
+
+  /* --- Read buffers of text --- *
+   *
+   * Read a buffer.  If more to come, double the buffer size and try again.
+   * This is the method I recommended to comp.lang.c, so I may as well try
+   * it.
+   */
+
+  for (;;) {
+    i = fread(buff + done, 1, sz - done, fp);
+    done += i;
+    if (done != sz)
+      break;
+    sz <<= 1;
+    buff = xrealloc(buff, sz + 1);
+  }
+
+  /* --- Count the lines --- */
+
+  lim = buff + done;
+
+  sz = 1;
+  for (p = buff; p < lim; p++)
+    if (*p == '\n') sz++;
+
+  /* --- Build a table of line starts --- */
+
+  line = xmalloc(sz * sizeof(char *));
+  i = 0;
+  line[i++] = buff;
+  for (p = buff; p < lim; p++)
+    if (*p == '\n') *p = 0, line[i++] = p + 1;
+  *lim = 0;
+
+  /* --- Build a table of lines --- *
+   *
+   * This reverses the mapping which the symbol table performs, so that its
+   * accuracy can be tested.
+   */
+
+  flag = xmalloc(sz * sizeof(sym_word *));
+  for (i = 0; i < sz; i++)
+    flag[i] = 0;
+  entries = 0;
+
+  sym_createTable(&tbl);
+
+  for (;;) {
+    i = (unsigned)rand() % sz;
+
+    switch (rand() % 5)
+    {
+      case 0: {
+       sym_word *w;
+
+       printf("find `%s'\n", line[i]);
+       if ((rand() & 1023) == 0) {
+         putchar('.'); fflush(stdout);
+       }
+
+       w = sym_find(&tbl, line[i], -1, 0, 0);
+       if (w != flag[i])
+         printf("*** error: find `%s' gave %p not %p\n",
+                line[i], (void *)w, (void *)flag[i]);
+       else if (w && w->i != i)
+         printf("*** error: find sym for `%s' gives index %i not %i\n",
+                line[i], w->i, i);     
+      } break;
+
+      case 1: {
+       unsigned f;
+       sym_word *w;
+
+       printf("create `%s'\n", line[i]);
+       if ((rand() & 1023) == 0) {
+         putchar('+'); fflush(stdout);
+       }
+
+       w = sym_find(&tbl, line[i], -1, sizeof(sym_word), &f);
+       if (f)
+       {
+         if (w != flag[i])
+           printf("*** error: create `%s' gave %p not %p\n",
+                  line[i], (void *)w, (void *)flag[i]);
+         else if (w && w->i != i)
+           printf("*** error: create sym for `%s' gives index %i not %i\n",
+                  line[i], w->i, i);
+       }
+       else
+       {
+         if (flag[i])
+           printf("*** error: create `%s' gave new block, should be %p\n",
+                  line[i], (void *)flag[i]);
+         else {
+           flag[i] = w;
+           w->i = i;
+           entries++;
+         }
+       }
+      } break;
+
+      case 2: {
+       sym_iter it;
+       sym_word *w, **ntbl;
+       int v;
+
+       if (!entries)
+         break;
+       v = (rand() % entries) == 0;
+       if (!v)
+         break;
+       printf("\niterated %i entries\n", entries);
+       break;
+
+       printf("iterate\n");
+
+       ntbl = xmalloc(sz * sizeof(sym_word *));
+       memcpy(ntbl, flag, sz * sizeof(sym_word *));
+       sym_createIter(&it, &tbl);
+
+       while ((w = sym_next(&it)) != 0) {
+         if (ntbl[w->i] == 0)
+           printf("*** error: iterate returned duff item %i\n", w->i);
+         else
+           ntbl[w->i] = 0;
+       }
+
+       for (i = 0; i < sz; i++)
+         if (ntbl[i]) printf("*** error: iterate didn't return item %i\n",
+                             i);
+       free(ntbl);
+      } break;
+
+      case 3: {
+       sym_base *b;
+       int v = rand() & 255 ? 0 : 1;
+       break;
+
+       printf("dump\n");
+
+       for (i = 0; i <= tbl.mask; i++) {
+         if (!tbl.a[i]) continue;
+         if (v) printf("  %i: ", i);
+         b = tbl.a[i];
+         while (b) {
+           if ((b->hash & tbl.mask) != i)
+             printf("*** error: bad hash value found");
+           if (v) printf("`%s'(%08lx:%lu) ",
+                         line[((sym_word *)b)->i],
+                         b->hash,
+                         b->hash & tbl.mask);
+           b = b->next;
+         }
+         if (v) putchar('\n');
+       }
+      } break;
+
+      case 4: {
+       if (flag[i]) {
+         printf("remove `%s'\n", SYM_NAME(&flag[i]->base));
+         if ((rand() & 1023) == 0) {
+           putchar('-'); fflush(stdout);
+         }
+         sym_remove(&tbl, flag[i]);
+         flag[i] = 0;
+         entries--;
+       }
+      } break;
+    }
+
+  }
+
+  return (0);
+}
+
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/sym.h b/sym.h
new file mode 100644 (file)
index 0000000..35a3fa6
--- /dev/null
+++ b/sym.h
@@ -0,0 +1,197 @@
+/* -*-c-*-
+ *
+ * $Id: sym.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Symbol table management
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: sym.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef SYM_H
+#define SYM_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Required headers --------------------------------------------------*/
+
+#include <stddef.h>
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- Symbol table --- *
+ *
+ * A @sym_table@ contains the information needed to manage a symbol table.
+ * Users shouldn't fiddle with this information directly, but it needs to be
+ * here so that objects of the correct type can be created.
+ */
+
+typedef struct sym_table {
+  unsigned long mask;                  /* Bit mask for hashing purposes */
+  size_t c;                            /* Down counter for growing table */
+  struct sym_base **a;                 /* Array of hash bins */
+} sym_table;
+
+/* --- A symbol table entry --- *
+ *
+ * I don't care what actually gets stored in symbol entries because I don't
+ * create them: that's the responsibility of my client.  All I care about
+ * here is that whatever gets passed to me is a structure whose first member
+ * is a @sym_base@.  The ANSI guarantees about structure layout are
+ * sufficient to allow me to manipulate such objects.
+ */
+
+#define SYM_BUFSZ 16                   /* Size of local string buffer */
+
+typedef struct sym_base {
+  struct sym_base *next;               /* Next symbol in hash bin */
+  unsigned long hash;                  /* Hash value for symbol's name */
+  union {
+    char *p;                           /* Pointer to name string */
+    char b[SYM_BUFSZ];                 /* Buffer containing a short name */
+  } name;                              /* Name of this symbol */
+  size_t len;                          /* Length of the symbol's name */
+} sym_base;
+
+/* --- An iterator block --- */
+
+typedef struct sym_iter {
+  sym_table *t;                                /* Symbol table being iterated */
+  sym_base *n;                         /* Address of next item to return */
+  size_t i;                            /* Index of next hash bin to use */
+} sym_iter;
+
+/*----- External functions ------------------------------------------------*/
+
+/* --- @sym_createTable@ --- *
+ *
+ * Arguments:  @sym_table *t@ = symbol table to initialise
+ *
+ * Returns:    ---
+ *
+ * Use:                Initialises the given symbol table.  Raises @EXC_NOMEM@ if
+ *             there isn't enough memory.
+ */
+
+extern void sym_createTable(sym_table */*t*/);
+
+/* --- @sym_destroyTable@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table in question
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a symbol table, freeing all the memory it used to
+ *             occupy.
+ */
+
+extern void sym_destroyTable(sym_table */*t*/);
+
+/* --- @sym_find@ --- *
+ *
+ * Arguments:  @sym_table *t@ = pointer to symbol table in question
+ *             @const char *n@ = pointer to symbol table to look up
+ *             @long l@ = length of the name string or negative to measure
+ *             @size_t sz@ = size of desired symbol object, or zero
+ *             @unsigned *f@ = pointer to a flag, or null.
+ *
+ * Returns:    The address of a @sym_base@ structure, or null if not found
+ *             and @sz@ is zero.
+ *
+ * Use:                Looks up a symbol in a given symbol table.  The name is
+ *             passed by the address of its first character.  The length
+ *             may be given, in which case the name may contain arbitrary
+ *             binary data, or it may be given as a negative number, in
+ *             which case the length of the name is calculated as
+ *             @strlen(n)@.
+ *
+ *             The return value is the address of a pointer to a @sym_base@
+ *             block (which may have other things on the end, as above).  If
+ *             the symbol could be found, the return value points to the
+ *             symbol block.  If the symbol wasn't there, then if @sz@ is
+ *             nonzero, a new symbol is created and its address is returned;
+ *             otherwise a null pointer is returned.  The exception
+ *             @EXC_NOMEM@ is raised if the block can't be allocated.
+ *
+ *             The value of @*f@ indicates whether a new symbol entry was
+ *             created: a nonzero value indicates that an old value was
+ *             found.
+ */
+
+extern void *sym_find(sym_table */*t*/, const char */*n*/, long /*l*/,
+                     size_t /*sz*/, unsigned */*f*/);
+
+/* --- @sym_remove@ --- *
+ *
+ * Arguments:  @sym_table *i@ = pointer to a symbol table object
+ *             @void *b@ = pointer to symbol table entry
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes the object from the symbol table.  The space occupied
+ *             by the object and its name is freed; anything else attached
+ *             to the entry should already be gone by this point.
+ */
+
+extern void sym_remove(sym_table */*t*/, void */*b*/);
+
+/* --- @sym_createIter@ --- *
+ *
+ * Arguments:  @sym_iter *i@ = pointer to an iterator object
+ *             @sym_table *t@ = pointer to a symbol table object
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates a new symbol table iterator which may be used to
+ *             iterate through a symbol table.
+ */
+
+extern void sym_createIter(sym_iter */*i*/, sym_table */*t*/);
+
+/* --- @sym_next@ --- *
+ *
+ * Arguments:  @sym_iter *i@ = pointer to iterator object
+ *
+ * Returns:    Pointer to the next symbol found, or null when finished.
+ *
+ * Use:                Returns the next symbol from the table.  Symbols are not
+ *             returned in any particular order.
+ */
+
+extern void *sym_next(sym_iter */*i*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/testrig.c b/testrig.c
new file mode 100644 (file)
index 0000000..fc8d21d
--- /dev/null
+++ b/testrig.c
@@ -0,0 +1,401 @@
+/* -*-c-*-
+ *
+ * $Id: testrig.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Generic test driver
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: testrig.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dstr.h"
+#include "report.h"
+#include "quis.h"
+#include "testrig.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static dstr test__tok;
+
+enum {
+  tok_eof = 0x100,
+  tok_word
+};
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @test__decode@ --- *
+ *
+ * Arguments:  @int tok@ = token type to decode
+ *
+ * Returns:    Pointer to a textual representation of the token.
+ *
+ * Use:                Produces a readable representation of a token.
+ */
+
+static const char *test__decode(int tok)
+{
+  static char buf[4];
+
+  switch (tok) {
+    case tok_eof:
+      return ("<eof>");
+    case tok_word:
+      return (test__tok.buf);
+    default:
+      buf[0] = tok;
+      buf[1] = 0;
+      return (buf);
+  }
+  return ("<buggy-program>");
+}
+
+/* --- @test__gettok@ --- *
+ *
+ * Arguments:  @FILE *fp@ = file handle to read from
+ *
+ * Returns:    Type of token read.
+ *
+ * Use:                Reads a token from the input stream.
+ */
+
+static int test__gettok(FILE *fp)
+{
+  int ch;
+
+  /* --- Clear the token accumulator --- */
+
+  dstr_reset(&test__tok);
+
+  /* --- Prime the lookahead character --- */
+
+again:
+  ch = getc(fp);
+
+  /* --- Skip leading whitespace --- */
+
+  while (isspace((unsigned char)ch))
+    ch = getc(fp);
+
+  /* --- Trap some special characters --- */
+
+  switch (ch) {
+
+    /* --- Comments --- */
+
+    case '#':
+      do ch = getc(fp); while (ch != EOF && ch != '\n');
+      goto again;
+
+    /* --- End of file --- */
+      
+    case EOF:
+      return (tok_eof);
+
+    /* --- Quote characters --- */
+
+    case '`':
+      ch = '\'';
+    case '\'':
+    case '\"': {
+      int quote = ch;
+
+      for (;;) {
+       ch = getc(fp);
+       if (ch == EOF || ch == quote)
+         break;
+       if (ch == '\\') {
+         ch = getc(fp);
+         if (ch == EOF)
+           ch = '\\';
+       }
+       DPUTC(&test__tok, ch);
+      }
+      DPUTZ(&test__tok);
+      return (tok_word);
+    }
+
+    /* --- Anything else is either a word or self-delimiting --- */
+
+    default:
+      if (isalnum((unsigned char)ch)) {
+       for (;;) {
+         DPUTC(&test__tok, ch);
+         ch = getc(fp);
+         if (ch == EOF ||
+             ch == ';' ||
+             ch == '\"' || ch == '\'' || ch == '`' ||
+             isspace((unsigned char)ch))
+           break;
+         if (ch == '\\') {
+           ch = getc(fp);
+           if (ch == EOF)
+             ch = '\\';
+         }
+       }
+       ungetc(ch, fp);
+       DPUTZ(&test__tok);
+       return (tok_word);
+      } else
+       return (ch);
+  }
+}
+
+/* --- @type_hex@ --- */
+
+static void cvt_hex(const char *s, dstr *d)
+{
+  while (s[0] && s[1]) {
+    int x = s[0], y = s[1];
+    if ('0' <= x && x <= '9') x -= '0';
+    else if ('A' <= x && x <= 'F') x -= 'A' - 10;
+    else if ('a' <= x && x <= 'f') x -= 'a' - 10;
+    else x = 0;
+    if ('0' <= y && y <= '9') y -= '0';
+    else if ('A' <= y && y <= 'F') y -= 'A' - 10;
+    else if ('a' <= y && y <= 'f') y -= 'a' - 10;
+    else y = 0;
+    DPUTC(d, (x << 4) + y);
+    s += 2;
+  }
+}
+
+static void dump_hex(dstr *d, FILE *fp)
+{
+  const char *p, *q;
+  for (p = d->buf, q = p + d->len; p < q; p++)
+    fprintf(fp, "%02x", *(unsigned char *)p);
+}
+
+test_type type_hex = { cvt_hex, dump_hex };
+
+/* --- @type_string@ --- */
+
+static void cvt_string(const char *s, dstr *d)
+{
+  DPUTS(d, s);
+}
+
+static void dump_string(dstr *d, FILE *fp)
+{
+  dstr_write(d, fp);
+}
+
+test_type type_string = { cvt_string, dump_string };
+
+/* --- @type_int@ --- */
+
+static void cvt_int(const char *s, dstr *d)
+{
+  DENSURE(d, sizeof(long));
+  sscanf(s, "%i", (int *)d->buf + d->len);
+}
+
+static void dump_int(dstr *d, FILE *fp)
+{
+  fprintf(fp, "%i", *(int *)(d->buf));
+}
+
+test_type type_int = { cvt_int, dump_int };
+
+/* --- @test_run@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = pointer to command line arguments
+ *             @const test_chunk chunk[]@ = pointer to chunk definitions
+ *             @const char *vec@ = name of default test vector file
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Runs a set of test vectors to ensure that a component is
+ *             working properly.
+ */
+
+void test_run(int argc, char *argv[],
+             const test_chunk chunk[],
+             const char *vec)
+{
+  FILE *fp;
+  int i;
+  const test_chunk *cch;
+  dstr dv[TEST_FIELDMAX];
+  int fail = 0, ok = 1;
+  int sofar = 0;
+
+  /* --- Silly bits of initialisation --- */
+
+  ego(argv[0]);
+
+  for (i = 0; i < TEST_FIELDMAX; i++)
+    dstr_create(&dv[i]);
+
+  /* --- Parse command line arguments --- */
+
+  {
+    const char *p = 0;
+
+    i = 0;
+    for (;;) {
+      if (!p || !*p) {
+       if (i >= argc - 1)
+         break;
+       p = argv[++i];
+       if (strcmp(p, "--") == 0) {
+         i++;
+         break;
+       }
+       if (p[0] != '-' || p[1] == 0)
+         break;
+       p++;
+      }
+      switch (*p++) {
+       case 'h':
+         printf("%s test driver\n"
+                "Usage: %s [-f FILENAME]\n", QUIS, QUIS);
+         exit(0);
+       case 'f':
+         if (!*p) {
+           if (i >= argc - 1)
+             die(1, "option `-f' expects an argument");
+           p = argv[++i];
+         }
+         vec = p;
+         p = 0;
+         break;
+       default:
+         die(1, "option `-%c' unknown", p[-1]);
+         break;
+      }
+    }
+  }
+
+  /* --- Start parsing from the file --- */
+
+  if ((fp = fopen(vec, "r")) == 0)
+    die(1, "couldn't open test vector file `%s': %s", vec, strerror(errno));
+
+  for (;;) {
+    int tok = test__gettok(fp);
+
+    /* --- This is a reasonable place to stop --- */
+
+    if (tok == tok_eof)
+      break;
+
+    /* --- Pick out the chunk name --- */
+
+    if (tok != tok_word)
+      die(1, "expected <word>; found `%s'", test__decode(tok));
+
+    /* --- Find the right chunk block --- */
+
+    for (cch = chunk; ; cch++) {
+      if (!cch->name)
+       goto skip_chunk;
+      if (strcmp(test__tok.buf, cch->name) == 0)
+       break;
+    }
+
+    /* --- Past the open brace to the first chunk --- */
+
+    if ((tok = test__gettok(fp)) != '{')
+      die(1, "expected '{'; found `%s'", test__decode(tok));
+
+    /* --- Start on the test data now --- */
+
+    printf("%s: ", cch->name);
+    fflush(stdout);
+    sofar = 0;
+    ok = 1;
+
+    for (;;) {
+      tok = test__gettok(fp);
+
+      /* --- Accept a close brace --- */
+
+      if (tok == '}')
+       break;
+
+      /* --- Otherwise I expect a list of words --- */
+
+      for (i = 0; cch->f[i]; i++) {
+       dstr_reset(&dv[i]);
+       if (tok != tok_word)
+         die(1, "expected <word>; found `%s'", test__decode(tok));
+       cch->f[i]->cvt(test__tok.buf, &dv[i]);
+       tok = test__gettok(fp);
+      }
+
+      /* --- And a terminating semicolon --- */
+
+      if (tok != ';')
+       die(1, "expected `;'; found `%s'", test__decode(tok));
+
+      /* --- Run the test code --- */
+
+      if (!cch->test(dv)) {
+       printf("%s: ", cch->name);
+       for (i = 0; i < sofar; i++) putchar('.');
+       fail = 1; ok = 0;
+      }
+      sofar++;
+      putchar('.');
+      fflush(stdout);
+    }
+
+    puts(ok ? " ok" : " failed");
+    fflush(stdout);
+    continue;
+
+  skip_chunk:
+    if ((tok = test__gettok(fp)) != '{')
+      die(1, "expected '{'; found `%s'", test__decode(tok));
+    for (;;) {
+      tok = test__gettok(fp);
+      if (tok == '}')
+       break;
+      while (tok == tok_word)
+       tok = test__gettok(fp);
+      if (tok != ';')
+       die(1, "expected `;'; found `%s'", test__decode(tok));
+    }      
+  }
+
+  exit(fail);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/testrig.h b/testrig.h
new file mode 100644 (file)
index 0000000..c8fa10b
--- /dev/null
+++ b/testrig.h
@@ -0,0 +1,102 @@
+/* -*-c-*-
+ *
+ * $Id: testrig.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Generic test driver
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------* 
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ * 
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: testrig.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef TESTER_H
+#define TESTER_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <stddef.h>
+
+#include "dstr.h"
+
+/*----- Magical numbers ---------------------------------------------------*/
+
+#define TEST_FIELDMAX 16               /* Maximum fields in a line */
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Test field definition --- */
+
+typedef struct test_type {
+  void (*cvt)(const char *buf, dstr *d); /* Conversion function */
+  void (*dump)(dstr *d, FILE *fp);     /* Dump function */
+} test_type;
+
+/* --- Test chunk definition --- */
+
+typedef struct test_chunk {
+  const char *name;                    /* Name of this chunk */
+  int (*test)(dstr dv[]);              /* Test verification function */
+  test_type *f[TEST_FIELDMAX];         /* Field definitions */
+} test_chunk;
+
+/*----- Predefined data types ---------------------------------------------*/
+
+extern test_type type_hex;
+extern test_type type_string;
+extern test_type type_int;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @test_run@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = pointer to command line arguments
+ *             @const test_chunk chunk[]@ = pointer to chunk definitions
+ *             @const char *def@ = name of default test vector file
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Runs a set of test vectors to ensure that a component is
+ *             working properly.
+ */
+
+extern void test_run(int /*argc*/, char */*argv*/[],
+                    const test_chunk /*chunk*/[],
+                    const char */*def*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/trace.c b/trace.c
new file mode 100644 (file)
index 0000000..c99c682
--- /dev/null
+++ b/trace.c
@@ -0,0 +1,178 @@
+/* -*-c-*-
+ *
+ * $Id: trace.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Tracing functions for debugging
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: trace.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "quis.h"
+#include "trace.h"
+
+/*----- Private state information -----------------------------------------*/
+
+static FILE *trace__fp = 0;            /* Where does debugging go? */
+static unsigned int trace__lvl = 0;    /* How much tracing gets done? */
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @trace@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level for output
+ *             @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports a message to the trace output.
+ */
+
+void trace(unsigned int l, const char *f, ...)
+{
+  va_list ap;
+  if ((l & tracing()) == 0)
+    return;
+  va_start(ap, f);
+  fprintf(trace__fp, "*** %s: ", QUIS);
+  vfprintf(trace__fp, f, ap);
+  va_end(ap);
+  putc('\n', trace__fp);
+  fflush(trace__fp);
+}
+
+/* --- @trace_block@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level for output
+ *             @const char *s@ = some header string to write
+ *             @const void *b@ = pointer to a block of memory to dump
+ *             @size_t sz@ = size of the block of memory
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps the contents of a block to the trace output.
+ */
+
+void trace_block(unsigned int l, const char *s, const void *b, size_t sz)
+{
+  const unsigned char *p = b;
+  size_t i;
+  unsigned long o = 0;
+  size_t c;
+
+  /* --- Skip if the trace level is too high --- */
+
+  if ((l & tracing()) == 0)
+    return;
+
+  /* --- Now start work --- */
+
+  fprintf(trace__fp, "*** %s: %s\n", QUIS, s);
+
+  while (sz) {
+    fprintf(trace__fp, "*** %s:   %08lx : ", QUIS, o);
+    for (i = 0; i < 8; i++) {
+      if (i < sz)
+       fprintf(trace__fp, "%02x ", p[i]);
+      else
+       fputs("** ", trace__fp);
+    }
+    fputs(": ", trace__fp);
+    for (i = 0; i < 8; i++) {
+      if (i < sz)
+       fputc(isprint(p[i]) ? p[i] : '.', trace__fp);
+      else
+       fputc('*', trace__fp);
+    }
+    fputc('\n', trace__fp);
+    c = (sz >= 8) ? 8 : sz;
+    sz -= c, p += c, o += c;
+  }
+  fflush(trace__fp);
+}
+
+/* --- @trace_on@ --- *
+ *
+ * Arguments:  @FILE *fp@ = a file to trace on
+ *             @unsigned int l@ = trace level to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Enables tracing to a file.
+ */
+
+void trace_on(FILE *fp, unsigned int l)
+{
+  trace__fp = fp;
+  if (!trace__lvl)
+    trace__lvl = l;
+}
+
+/* --- @trace_setLevel@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the tracing level.
+ */
+
+void trace_setLevel(unsigned int l)
+{
+  trace__lvl = l;
+}
+
+/* --- @tracing@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if not tracing, tracing level if tracing.
+ *
+ * Use:                Informs the caller whether tracing is enabled.
+ */
+
+unsigned int tracing(void)
+{
+  return (trace__fp ? trace__lvl : 0u);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/trace.h b/trace.h
new file mode 100644 (file)
index 0000000..a6ffabe
--- /dev/null
+++ b/trace.h
@@ -0,0 +1,126 @@
+/* -*-c-*-
+ *
+ * $Id: trace.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Tracing functions for debugging
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: trace.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <stdio.h>
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @trace@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level for output
+ *             @const char *f@ = a @printf@-style format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Reports a message to the trace output.
+ */
+
+extern void trace(unsigned int /*l*/, const char */*f*/, ...);
+
+/* --- @trace_block@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level for output
+ *             @const char *s@ = some header string to write
+ *             @const void *b@ = pointer to a block of memory to dump
+ *             @size_t sz@ = size of the block of memory
+ *
+ * Returns:    ---
+ *
+ * Use:                Dumps the contents of a block to the trace output.
+ */
+
+extern void trace_block(unsigned int /*l*/, const char */*s*/,
+                       const void */*b*/, size_t /*sz*/);
+
+/* --- @trace_on@ --- *
+ *
+ * Arguments:  @FILE *fp@ = a file to trace on
+ *             @unsigned int l@ = trace level to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Enables tracing to a file.
+ */
+
+extern void trace_on(FILE */*fp*/, unsigned int /*l*/);
+
+/* --- @trace_setLevel@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level to set
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the tracing level.
+ */
+
+extern void trace_setLevel(unsigned int /*l*/);
+
+/* --- @tracing@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if not tracing, tracing level if tracing.
+ *
+ * Use:                Informs the caller whether tracing is enabled.
+ */
+
+extern unsigned int tracing(void);
+
+/*----- Tracing macros ----------------------------------------------------*/
+
+#ifndef NTRACE
+#  define T(x) x
+#  define IF_TRACING(l, x) if ((l) & tracing()) x
+#else
+#  define T(x)
+#  define IF_TRACING(l, x)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/track.c b/track.c
new file mode 100644 (file)
index 0000000..d11f956
--- /dev/null
+++ b/track.c
@@ -0,0 +1,288 @@
+/* -*-c-*-
+ *
+ * $Id: track.c,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Tracing functions for debugging
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: track.c,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+/* --- ANSI headers --- */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* --- Local headers --- */
+
+#include "trace.h"
+#include "track.h"
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- A track block --- *
+ *
+ * This gets prefixed to every block I manage.
+ */
+
+typedef union track__block {
+  struct {
+    union track__block *next;          /* Link to previous block */
+    union track__block *prev;          /* Link to next block */
+    size_t sz;                         /* Size of the block */
+    const char *ctx;                   /* Pointer to allocating context */
+  } x;                                 /* Main data area */
+  long double _ld;                     /* Long double for alignment */
+  void *_p;                            /* Void pointer for alignment */
+} track__block;
+
+/*----- Private state -----------------------------------------------------*/
+
+/* --- Tracking memory usage --- */
+
+static unsigned int track__used = 0;   /* Count of bytes occupied */
+static track__block *track__list;      /* List of allocated blocks */
+
+/* --- Trace level for verbose messages --- */
+
+static unsigned int track__vLevel = 0;
+
+/* --- Context tracking --- */
+
+static track_ctx track__baseContext = {
+  0, "[unknown context]"
+};
+
+static track_ctx *track__context = &track__baseContext;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @track_setLevel@ --- *
+ *
+ * Arguments:  @unsigned int l@ = tracing level for allocation messages
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the trace level for allocation messages.
+ */
+
+void track_setLevel(unsigned int l)
+{
+  track__vLevel = l;
+}
+
+/* --- @track_pushContext@ --- *
+ *
+ * Arguments:  @track_ctx *ctx@ = context holder to push
+ *
+ * Returns:    ---
+ *
+ * Use:                Pushes the given context block onto the stack.
+ */
+
+void track_pushContext(track_ctx *ctx)
+{
+  ctx->next = track__context;
+  track__context = ctx;
+}
+
+/* --- @track_popContext@ --- *
+ *
+ * Arguments:  @track_ctx *ctx@ = context holder to pop
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes the given context block from the stack.
+ */
+
+void track_popContext(track_ctx *ctx)
+{
+  track__context = ctx->next;
+}
+
+/* --- @track_malloc@ --- *
+ *
+ * Arguments:  @size_t sz@ = size requested
+ *
+ * Returns:    Pointer to allocated space, or null
+ *
+ * Use:                Allocates memory, and tracks how much is allocated.
+ */
+
+void *track_malloc(size_t sz)
+{
+  track__block *q = (malloc)(sz + sizeof(track__block));
+  if (q) {
+    track__used += sz;
+    if (track__vLevel) {
+      trace(track__vLevel, "(track) allocated %lu at %p in %s",
+           (unsigned long)sz, (void *)(q + 1), track__context->s);
+    }
+    q->x.sz = sz;
+    q->x.next = track__list;
+    q->x.prev = 0;
+    q->x.ctx = track__context->s;
+    if (q->x.next)
+      q->x.next->x.prev = q;
+    track__list = q;
+    return (q + 1);
+  }
+  return (0);
+}
+
+/* --- @track_free@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to an allocated block
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees memory, and tracks how much is still allocated.
+ */
+
+void track_free(void *p)
+{
+  track__block *q;
+
+  if (!p)
+    return;
+  q = (track__block *)p - 1;
+  if (track__vLevel) {
+    trace(track__vLevel, "(track) freed %lu at %p for %s in %s",
+         (unsigned long)q->x.sz, (void *)(q + 1),
+         q->x.ctx, track__context->s);
+  }
+  if (q->x.next)
+    q->x.next->x.prev = q->x.prev;
+  if (q->x.prev)
+    q->x.prev->x.next = q->x.next;
+  else
+    track__list = q->x.next;
+  track__used -= q->x.sz;
+  (free)(q);
+}
+
+/* --- @track_realloc@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to an allocated block
+ *             @size_t sz@ = how big it wants to be
+ *
+ * Returns:    Pointer to the new block.
+ *
+ * Use:                Reallocates a block, tracking how much memory is still
+ *             available.
+ */
+
+void *track_realloc(void *p, size_t sz)
+{
+  size_t osz;
+  track__block *q, *qq;
+  if (p) {
+    q = (track__block *)p - 1;
+    osz = q->x.sz;
+    if (q->x.next)
+      q->x.next->x.prev = q->x.prev;
+    if (q->x.prev)
+      q->x.prev->x.next = q->x.next;
+    else
+      track__list = q->x.next;
+  } else {
+    q = 0;
+    osz = 0;
+  }
+  qq = (realloc)(q, sz + sizeof(track__block));
+  if (qq) {
+    if (track__vLevel) {
+      trace(track__vLevel,
+           "(track) reallocated %lu at %p to %lu for %s in %s",
+           (unsigned long)osz, (void *)(q + 1),
+           (unsigned long)sz,  (void *)(qq + 1),
+           qq->x.ctx, track__context->s);
+    }
+    qq->x.sz = sz;
+    qq->x.next = track__list;
+    qq->x.prev = 0;
+    if (qq->x.next)
+      qq->x.next->x.prev = qq;
+    track__list = qq;
+    track__used += sz - osz;
+    qq->x.sz = sz;
+    return (qq + 1);
+  }
+  return (0);
+}
+
+/* --- @track_used@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A count of how much memory is used currently.
+ *
+ * Use:                Returns the amount of memory which the @track_@-functions
+ *             above have counted as being currently allocated.
+ */
+
+unsigned long track_used(void)
+{
+  return (track__used);
+}
+
+/* --- @track_list@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level to use
+ *
+ * Returns:    ---
+ *
+ * Use:                Traces a dump of the currently known blocks.  Combined with
+ *             a verbose dump of allocations and deallocations, and a
+ *             good idea of which blocks were allocated where, this can
+ *             be useful for locating memory leaks.  It's not exactly a
+ *             picnic, though.
+ */
+
+void track_list(unsigned int l)
+{
+  track__block *q = track__list;
+
+  if (!(tracing() & l))
+    return;
+
+  trace(l, "(track dump) Dumping all blocks.  Stand well back...");
+  while (q) {
+    trace(l, "(track dump) %p: %lu in %s",
+         (void *)(q + 1), (unsigned long)q->x.sz, q->x.ctx);
+    q = q->x.next;
+  }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/track.h b/track.h
new file mode 100644 (file)
index 0000000..fa50269
--- /dev/null
+++ b/track.h
@@ -0,0 +1,217 @@
+/* -*-c-*-
+ *
+ * $Id: track.h,v 1.1 1998/06/17 23:44:42 mdw Exp $
+ *
+ * Tracing functions for debugging
+ *
+ * (c) 1998 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of the mLib utilities library.
+ *
+ * mLib is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mLib is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with mLib; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: track.h,v $
+ * Revision 1.1  1998/06/17 23:44:42  mdw
+ * Initial revision
+ *
+ */
+
+#ifndef TRACK_H
+#define TRACK_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <stdlib.h>
+
+/*----- Options and conventions -------------------------------------------*
+ *
+ * The following macros affect the tracking system:
+ *
+ * @TRACK_ENABLE@:     Enable tracking of memory allocations
+ * @TRACK_BLAME@:      Register my context blocks in allocations
+ *
+ * The reason there are two switches is simple.  It's often the case that a
+ * library routine allocates memory for its client.  Therefore, whether we
+ * want to record the library or the client depends on how much we trust
+ * the two pieces of software.  Setting @TRACK_ENABLE@ and @TRACK_BLAME@
+ * suggests that the current source file might leak memory, so we want its
+ * context markers in the list.  Setting @TRACK_ENABLE@ but not
+ * @TRACK_BLAME@ suggests that we trust this code, but not the code which
+ * calls it, so we want to preserve the caller's context markers.
+ *
+ * Got it?  Good.
+ */
+
+/*----- Type definitions --------------------------------------------------*/
+
+/* --- A context buffer --- */
+
+typedef struct track_ctx {
+  struct track_ctx *next;
+  const char *s;
+} track_ctx;
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @track_setLevel@ --- *
+ *
+ * Arguments:  @unsigned int l@ = tracing level for allocation messages
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the trace level for allocation messages.
+ */
+
+extern void track_setLevel(unsigned int /*l*/);
+
+/* --- @track_pushContext@ --- *
+ *
+ * Arguments:  @track_ctx *ctx@ = context holder to push
+ *
+ * Returns:    ---
+ *
+ * Use:                Pushes the given context block onto the stack.
+ */
+
+extern void track_pushContext(track_ctx */*ctx*/);
+
+/* --- @track_popContext@ --- *
+ *
+ * Arguments:  @track_ctx *ctx@ = context holder to pop
+ *
+ * Returns:    ---
+ *
+ * Use:                Removes the given context block from the stack.
+ */
+
+extern void track_popContext(track_ctx */*ctx*/);
+
+/* --- @track_malloc@ --- *
+ *
+ * Arguments:  @size_t sz@ = size requested
+ *
+ * Returns:    Pointer to allocated space, or null
+ *
+ * Use:                Allocates memory, and tracks how much is allocated.
+ */
+
+extern void *track_malloc(size_t /*sz*/);
+
+/* --- @track_free@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to an allocated block
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees memory, and tracks how much is still allocated.
+ */
+
+extern void track_free(void */*p*/);
+
+/* --- @track_realloc@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to an allocated block
+ *             @size_t sz@ = how big it wants to be
+ *
+ * Returns:    Pointer to the new block.
+ *
+ * Use:                Reallocates a block, tracking how much memory is still
+ *             available.
+ */
+
+extern void *track_realloc(void */*p*/, size_t /*sz*/);
+
+/* --- @track_used@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    A count of how much memory is used currently.
+ *
+ * Use:                Returns the amount of memory which the @track_@-functions
+ *             above have counted as being currently allocated.
+ */
+
+extern unsigned long track_used(void);
+
+/* --- @track_list@ --- *
+ *
+ * Arguments:  @unsigned int l@ = trace level to use
+ *
+ * Returns:    ---
+ *
+ * Use:                Traces a dump of the currently known blocks.  Combined with
+ *             a verbose dump of allocations and deallocations, and a
+ *             good idea of which blocks were allocated where, this can
+ *             be useful for locating memory leaks.  It's not exactly a
+ *             picnic, though.
+ */
+
+extern void track_list(unsigned int l);
+
+/*----- Macro wrappers ----------------------------------------------------*/
+
+
+/* --- If tracking is to be done, set it up --- */
+
+#ifdef TRACK_ENABLE
+#  undef malloc
+#  define malloc(sz) track_malloc(sz)
+#  undef free
+#  define free(p) track_free(p)
+#  undef realloc
+#  define realloc(p, sz) track_realloc(p, sz)
+#endif
+
+/* --- Provide a context for doing track-related things --- */
+
+#ifdef TRACK_ENABLE
+#  define TRACK(x) x
+#else
+#  define TRACK(x)
+#endif
+
+/* --- Handle contexts --- */
+
+#if defined(TRACK_ENABLE) && defined(TRACK_BLAME)
+#  define TRACK_NCTX(name, string) track__context name = { 0, string }
+#  define TRACK_NPUSH(name) track_pushContext(name)
+#  define TRACK_NPOP(name) track_popContext(name)
+#  define TRACK_CTX(string) TRACK_NCTX(track__localContext, string)
+#  define TRACK_PUSH TRACK_NPUSH(track__localContext)
+#  define TRACK_POP TRACK_NPOP(track__localContext)
+#else
+#  define TRACK_NCTX(name, string)
+#  define TRACK_NPUSH(name) ((void)0)
+#  define TRACK_NPOP(name) ((void)0)
+#  define TRACK_CTX(string)
+#  define TRACK_PUSH ((void)0)
+#  define TRACK_POP ((void)0)
+#endif
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif