+ /* Like the corresponding `moan' functions, except that they also
+ * exit with status code 2.
+ */
+
+/*----- Resizing buffers --------------------------------------------------*/
+
+struct buf {
+ /* A buffer for a string which can grow automatically. */
+
+ char *p; /* pointer to the buffer */
+ size_t n, sz; /* string length, buffer size */
+};
+#define BUF_INIT { 0, 0, 0 }
+
+static inline void buf_rewind(struct buf *b) { b->n = 0; }
+ /* Throw away the current contents of B so that new stuff gets added
+ * to the beginning.
+ */
+
+static inline void buf_free(struct buf *b)
+ { free(b->p); b->p = 0; b->n = b->sz = 0; }
+ /* Release the memory allocated for B. The buffer can be reused
+ * immediately and/or freed again safely.
+ */
+
+extern void buf__grow(struct buf *b);
+ /* Make B's buffer larger, so that (at least) one extra byte can be
+ * written to it. (Internal to `buf_putc'.)
+ */
+
+static inline void buf_putc(struct buf *b, int ch)
+ { if (b->n >= b->sz) buf__grow(b); b->p[b->n++] = ch; }
+ /* Append the character CH to the buffer B. */
+
+static inline void buf_putz(struct buf *b)
+ { if (b->n >= b->sz) buf__grow(b); b->p[b->n] = 0; }
+ /* Append a zero byte to B without increasing the string length, so
+ * that a future `buf_putc' will overwrite it.
+ */
+
+/*----- Resizing vectors --------------------------------------------------*/
+
+#define DEFVEC(vtype, etype) \
+ typedef struct { etype *v; size_t n, sz; } vtype
+#define VEC_INIT { 0, 0, 0 }
+ /* Define VTYPE as a (structured) type for vectors holding elements
+ * of ETYPE.
+ *
+ * A vector V has `V.n' elements, addressed as `V.v[0]' up to
+ * `V.v[V.n - 1]'.
+ */
+
+#define VEC_FREE(vv) do { \
+ free((vv)->v); (vv)->v 0; (vv)->n = (vv)->sz = 0; \
+} while (0)
+ /* Free the vector VV. It's safe to free a vector multiple times. */
+
+extern void *vec__grow(void *p, size_t esz, size_t *sz_inout);
+ /* Extend the buffer P, which currently has space for *SZ_INOUT
+ * elements, each ESZ bytes in size, so that there's space for at
+ * least one one more; return the new buffer address, and update
+ * *SZ_INOUT with the new size.
+ */
+
+#define VEC_PUSH(p, vv) do { \
+ if ((vv)->n >= (vv)->sz) \
+ (vv)->v = vec__grow((vv)->v, sizeof(*(vv)->v), &(vv)->sz); \
+ (p) = &(vv)->v[(vv)->n++]; \
+} while (0)
+ /* Add an initialized element to the end of vector VV, storing its
+ * address in P.
+ */
+
+/*----- Parsing utilities -------------------------------------------------*/
+
+#define PNF_JUNK 1u
+extern double parse_float(const char **p_inout, unsigned f,
+ double min, double max, const char *what);
+extern long parse_int(const char **p_inout, unsigned f,
+ long min, long max, const char *what);
+ /* Parse a number starting at *P_IN OUT, advancing that pointer past
+ * it, and return the resulting value. If no number can be read from
+ * the string, or the resulting number is not between MIN and MAX
+ * inclusive, or the `PNF_JUNK' bit is clear in F and the number is
+ * followed by anything other than whitespace, then report a fatal
+ * error, quoting WHAT as having been expected.
+ */
+
+/*----- System utilities --------------------------------------------------*/
+
+extern double tvdiff(const struct timeval *tv_lo,
+ const struct timeval *tv_hi);
+ /* Return the (signed) difference from TV_LO to TV_HI, as a floating-
+ * point number of seconds.
+ */