+/*----- Output formatting -------------------------------------------------*/
+
+/* We have two main jobs in output formatting: trimming trailing blanks; and
+ * adding a prefix to each line.
+ *
+ * This is somehow much more complicated than it ought to be.
+ */
+
+struct format {
+ FILE *fp; /* output file */
+ const char *prefix, *pfxtail, *pfxlim; /* prefix pointers */
+ dstr w; /* trailing whitespace */
+ unsigned f; /* flags */
+#define FMTF_NEWL 1u /* start of output line */
+};
+
+/* Support macros. These assume `fmt' is defined as a pointer to the `struct
+ * format' state.
+ */
+
+#define SPLIT_RANGE(tail, base, limit) do { \
+ /* Set TAIL to point just after the last nonspace character between \
+ * BASE and LIMIT. If there are no nonspace characters, then set \
+ * TAIL to equal BASE. \
+ */ \
+ \
+ for (tail = limit; tail > base && ISSPACE(tail[-1]); tail--); \
+} while (0)
+
+#define PUT_RANGE(base, limit) do { \
+ /* Write the range of characters between BASE and LIMIT to the output \
+ * file. Return immediately on error. \
+ */ \
+ \
+ size_t n = limit - base; \
+ if (fwrite(base, 1, n, fmt->fp) < n) return (-1); \
+} while (0)
+
+#define PUT_CHAR(ch) do { \
+ /* Write CH to the output. Return immediately on error. */ \
+ \
+ if (putc(ch, fmt->fp) == EOF) return (-1); \
+} while (0)
+
+#define PUT_PREFIX do { \
+ /* Output the prefix, if there is one. Return immediately on error. */ \
+ \
+ if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxlim); \
+} while (0)
+
+#define PUT_SAVED do { \
+ /* Output the saved trailing blank material in the buffer. */ \
+ \
+ size_t n = fmt->w.len; \
+ if (n && fwrite(fmt->w.buf, 1, n, fmt->fp) < n) return (-1); \
+} while (0)
+
+#define PUT_PFXINB do { \
+ /* Output the initial nonblank portion of the prefix, if there is \
+ * one. Return immediately on error. \
+ */ \
+ \
+ if (fmt->prefix) PUT_RANGE(fmt->prefix, fmt->pfxtail); \
+} while (0)
+
+#define SAVE_PFXTAIL do { \
+ /* Save the trailing blank portion of the prefix. */ \
+ \
+ if (fmt->prefix) \
+ DPUTM(&fmt->w, fmt->pfxtail, fmt->pfxlim - fmt->pfxtail); \
+} while (0)
+
+/* --- @init_fmt@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state to initialize
+ * @FILE *fp@ = output file
+ * @const char *prefix@ = prefix string (or null if empty)
+ *
+ * Returns: ---
+ *
+ * Use: Initialize a formatting state.
+ */
+
+static void init_fmt(struct format *fmt, FILE *fp, const char *prefix)
+{
+ const char *q, *l;
+
+ /* Basics. */
+ fmt->fp = fp;
+ fmt->f = FMTF_NEWL;
+ dstr_create(&fmt->w);
+
+ /* Prefix portions. */
+ if (!prefix || !*prefix)
+ fmt->prefix = fmt->pfxtail = fmt->pfxlim = 0;
+ else {
+ fmt->prefix = prefix;
+ l = fmt->pfxlim = prefix + strlen(prefix);
+ SPLIT_RANGE(q, prefix, l); fmt->pfxtail = q;
+ DPUTM(&fmt->w, q, l - q);
+ }
+}
+
+/* --- @destroy_fmt@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @unsigned f@ = flags (@DFF_...@)
+ *
+ * Returns: ---
+ *
+ * Use: Releases a formatting state and the resources it holds.
+ * Close the file if @DFF_CLOSE@ is set in @f@; otherwise leave
+ * it open (in case it's @stderr@ or something).
+ */
+
+#define DFF_CLOSE 1u
+static void destroy_fmt(struct format *fmt, unsigned f)
+{
+ if (f&DFF_CLOSE) fclose(fmt->fp);
+ dstr_destroy(&fmt->w);
+}
+
+/* --- @format_char@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @int ch@ = character to write
+ *
+ * Returns: Zero on success, @-1@ on failure.
+ *
+ * Use: Write a single character to the output.
+ */
+
+static int format_char(struct format *fmt, int ch)
+{
+ if (ch == '\n') {
+ if (fmt->f&FMTF_NEWL) PUT_PFXINB;
+ PUT_CHAR('\n'); fmt->f |= FMTF_NEWL; DRESET(&fmt->w);
+ } else if (isspace(ch))
+ DPUTC(&fmt->w, ch);
+ else {
+ if (fmt->f&FMTF_NEWL) { PUT_PFXINB; fmt->f &= ~FMTF_NEWL; }
+ PUT_SAVED; PUT_CHAR(ch); DRESET(&fmt->w);
+ }
+ return (0);
+}
+
+/* --- @format_string@ --- *
+ *
+ * Arguments: @struct format *fmt@ = formatting state
+ * @const char *p@ = string to write
+ * @size_t sz@ = length of string
+ *
+ * Returns: Zero on success, @-1@ on failure.
+ *
+ * Use: Write a string to the output.
+ */
+
+static int format_string(struct format *fmt, const char *p, size_t sz)
+{
+ const char *q, *r, *l = p + sz;
+
+ /* This is rather vexing. There are a small number of jobs to do, but the
+ * logic for deciding which to do when gets rather hairy if, as I've tried
+ * here, one aims to minimize the number of decisions being checked, so
+ * it's worth canning them into macros.
+ *
+ * Here, a `blank' is a whitespace character other than newline. The input
+ * buffer consists of one or more `segments', each of which consists of:
+ *
+ * * an initial portion, which is either empty or ends with a nonblank
+ * character;
+ *
+ * * a suffix which consists only of blanks; and
+ *
+ * * an optional newline.
+ *
+ * All segments except the last end with a newline.
+ */
+
+#define SPLIT_SEGMENT do { \
+ /* Determine the bounds of the current segment. If there is a final \
+ * newline, then q is non-null and points to this newline; otherwise, \
+ * q is null. The initial portion of the segment lies between p .. r \
+ * and the blank suffix lies between r .. q (or r .. l if q is null). \
+ * This sounds awkward, but the suffix is only relevant if there is \
+ * no newline. \
+ */ \
+ \
+ q = memchr(p, '\n', l - p); SPLIT_RANGE(r, p, q ? q : l); \
+} while (0)
+
+#define PUT_NONBLANK do { \
+ /* Output the initial portion of the segment. */ \
+ \
+ PUT_RANGE(p, r); \
+} while (0)
+
+#define PUT_NEWLINE do { \
+ /* Write a newline, and advance to the next segment. */ \
+ \
+ PUT_CHAR('\n'); p = q + 1; \
+} while (0)
+
+#define SAVE_TAIL do { \
+ /* Save the trailing blank portion of the segment in the buffer. \
+ * Assumes that there is no newline, since otherwise the suffix would \
+ * be omitted. \
+ */ \
+ \
+ DPUTM(&fmt->w, r, l - r); \
+} while (0)
+
+ /* Determine the bounds of the first segment. Handling this is the most
+ * complicated part of this function.
+ */
+ SPLIT_SEGMENT;
+
+ if (!q) {
+ /* This is the only segment. We'll handle the whole thing here.
+ *
+ * If there's an initial nonblank portion, then we need to write that
+ * out. Furthermore, if we're at the start of the line then we'll need
+ * to write the prefix, and if there's saved blank material then we'll
+ * need to write that. Otherwise, there's only blank stuff, which we
+ * accumulate in the buffer.
+ *
+ * If we're at the start of a line here, then
+ */
+
+ if (r > p) {
+ if (fmt->f&FMTF_NEWL) { PUT_PFXINB; fmt->f &= ~FMTF_NEWL; }
+ PUT_SAVED; PUT_NONBLANK; DRESET(&fmt->w);
+ }
+ SAVE_TAIL;
+ return (0);
+ }
+
+ /* There is at least one more segment, so we know that there'll be a line
+ * to output.
+ */
+ if (fmt->f&FMTF_NEWL) PUT_PFXINB;
+ if (r > p) { PUT_SAVED; PUT_NONBLANK; }
+ PUT_NEWLINE; DRESET(&fmt->w);
+ SPLIT_SEGMENT;
+
+ /* Main loop over whole segments with trailing newlines. For each one, we
+ * know that we're starting at the beginning of a line and there's a final
+ * newline, so we write the initial prefix and drop the trailing blanks.
+ */
+ while (q) {
+ PUT_PREFIX; PUT_NONBLANK; PUT_NEWLINE;
+ SPLIT_SEGMENT;
+ }
+
+ /* At the end, there's no final newline. If there's nonblank material,
+ * then we can write the prefix and the nonblank stuff. Otherwise, stash
+ * the blank stuff (including the trailing blanks of the prefix) and leave
+ * the newline flag set.
+ */
+ if (r > p) { PUT_PREFIX; PUT_NONBLANK; fmt->f &= ~FMTF_NEWL; }
+ else { fmt->f |= FMTF_NEWL; SAVE_PFXTAIL; }
+ SAVE_TAIL;
+
+#undef SPLIT_SEGMENT
+#undef PUT_NONBLANK
+#undef PUT_NEWLINE
+#undef SAVE_TAIL
+
+ return (0);
+}
+
+#undef SPLIT_RANGE
+#undef PUT_RANGE
+#undef PUT_PREFIX
+#undef PUT_PFXINB
+#undef PUT_SAVED
+#undef PUT_CHAR
+#undef SAVE_PFXTAIL
+