+/*----- Terminal output ---------------------------------------------------*/
+
+#if defined(USE_TERMINFO)
+
+static const struct progress_ttyinfo *curtty = 0;
+static int putty(int ch) { return (putc(ch, curtty->fp)); }
+void progress_put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines)
+ { if (p) { curtty = tty; tputs(p, nlines, putty); } }
+void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { progress_put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
+void progress_set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { progress_put_sequence(tty, tgoto(tty->cap.ab, -1, colour), 1); }
+
+#elif defined(USE_TERMCAP)
+
+static const struct progress_ttyinfo *curtty = 0;
+static int putty(int ch) { return (putc(ch, curtty->fp)); }
+void progress_put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines)
+ { if (p) { curtty = tty; tputs(p, nlines, putty); } }
+void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { progress_put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
+void progress_set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { progress_put_sequence(tty, tgoto(tty->cap.ab, -1, colour), 1); }
+
+#else
+
+void progress_put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines) { ; }
+void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { ; }
+void progress_set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { ; }
+
+#endif
+
+/*----- Maintaining the progress display ----------------------------------*/
+
+#define CLRF_ALL 1u /* clear everything */
+static void clear_progress(struct progress_state *progress,
+ struct progress_render_state *render, unsigned f)
+ /* Clear the current progress display maintained by PROGRESS,
+ * assisted by the RENDER state.
+ *
+ * If `CLRF_ALL' is set in F, then clear the entire display.
+ * Otherwise, clear the bottom few lines if there are now fewer
+ * progress items than there were last time we rendered the display,
+ * and leave the cursor at the start of the top line ready to
+ * overwrite it.
+ */
+{
+ const struct progress_ttyinfo *tty = &progress->tty;
+ unsigned ndel, nleave;
+ unsigned i;
+
+ progress_put_sequence(tty, tty->cap.cr, 1);
+ if (progress->last_lines) {
+
+ /* Decide how many lines to delete. Set `ndel' to the number of lines
+ * that will be entirely erased, and `nleave' to the number that we'll
+ * leave.
+ */
+ if (f&CLRF_ALL)
+ { ndel = progress->last_lines; nleave = 0; }
+ else {
+ if (progress->nitems >= progress->last_lines) ndel = 0;
+ else ndel = progress->last_lines - progress->nitems;
+ nleave = progress->last_lines - ndel;
+ }
+
+ /* Now actually do the clearing. Remember that the cursor is still on
+ * the last line.
+ */
+ if (!ndel)
+ for (i = 1; i < nleave; i++)
+ progress_put_sequence(tty, tty->cap.up, 1);
+ else {
+ for (i = 1; i < ndel; i++)
+ progress_put_sequence(tty, tty->cap.up, 1);
+ progress_put_sequence(tty, tty->cap.cd, ndel);
+ for (i = 0; i < nleave; i++)
+ progress_put_sequence(tty, tty->cap.up, 1);
+ }
+ }
+
+ /* Remember that we're now at the top of the display. */
+ progress->last_lines = 0;
+}
+
+int progress_clear(struct progress_state *progress)
+{
+ struct progress_render_state render;
+
+ if (!progress->tty.fp) return (-1);
+ setup_render_state(progress, &render);
+ clear_progress(progress, &render, CLRF_ALL);
+ free_render_state(&render);
+ return (0);
+}
+
+int progress_update(struct progress_state *progress)
+{
+ struct progress_render_state render;
+ const struct progress_ttyinfo *tty = &progress->tty;
+ struct progress_item *item;
+ unsigned f = 0;
+#define f_any 1u
+
+ if (!progress->tty.fp) return (-1);
+
+ setup_render_state(progress, &render);
+ clear_progress(progress, &render, 0);
+
+ for (item = progress->items; item; item = item->next) {
+ if (f&f_any) progress_put_sequence(tty, tty->cap.nw, 1);
+ render.leftsz = render.rightsz = 0;
+ render.leftwd = render.rightwd = 0;
+ item->render(item, &render); progress->last_lines++; f |= f_any;
+ if (progress->last_lines > render.height) break;
+ }
+ if (f&f_any) progress_put_sequence(tty, tty->cap.cr, 1);
+ free_render_state(&render);
+ return (0);
+}
+
+/*----- Rendering progress bars -------------------------------------------*/
+
+/* The basic problem here is to render text, formed of several pieces, to the
+ * terminal, placing some marker in the middle of it to indicate how much
+ * progress has been made. This marker might be a colour change, switching
+ * off reverse-video mode, or a `|' character.
+ */
+