--- /dev/null
+COPYING
+config.guess
+config.sub
+install-sh
+missing
+mkinstalldirs
--- /dev/null
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+ (append
+ '((author . "Straylight/Edgeware")
+ (full-title . "the mLib utilities library")
+ (program . "mLib"))
+ skel-alist))
--- /dev/null
+## 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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+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)
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+#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);
+}
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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