+#if defined(USE_TERMINFO)
+static const struct progress_ttyinfo *curtty = 0;
+static int putty(int ch) { return (putc(ch, curtty->fp)); }
+static void put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines)
+ { if (p) { curtty = tty; tputs(p, nlines, putty); } }
+static void set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
+static void set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { 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)); }
+static void put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines)
+ { if (p) { curtty = tty; tputs(p, nlines, putty); } }
+static void set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
+static void set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { put_sequence(tty, tgoto(tty->cap.ab, -1, colour), 1); }
+#else
+static void put_sequence(const struct progress_ttyinfo *tty,
+ const char *p, unsigned nlines) { ; }
+static void set_fgcolour(const struct progress_ttyinfo *tty, int colour)
+ { ; }
+static void set_bgcolour(const struct progress_ttyinfo *tty, int colour)
+ { ; }
+#endif
+
+#define CLRF_ALL 1u
+static int clear_progress(struct progress_state *progress,
+ struct progress_render_state *render, unsigned f)
+{
+ const struct progress_ttyinfo *tty = &progress->tty;
+ unsigned ndel, nleave;
+ unsigned i;
+
+ if (!tty->fp) return (-1);
+
+ put_sequence(tty, tty->cap.cr, 1);
+ if (progress->last_lines) {
+ 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;
+ }
+ if (!ndel)
+ for (i = 0; i < nleave - 1; i++) put_sequence(tty, tty->cap.up, 1);
+ else {
+ for (i = 0; i < ndel - 1; i++) put_sequence(tty, tty->cap.up, 1);
+ put_sequence(tty, tty->cap.cd, ndel);
+ for (i = 0; i < nleave; i++) put_sequence(tty, tty->cap.up, 1);
+ }
+ }
+ progress->last_lines = 0;
+ if (ferror(tty->fp)) return (-1);
+ return (0);
+}
+
+int progress_clear(struct progress_state *progress)
+{
+ struct progress_render_state render;
+
+ if (!progress->tty.fp) return (-1);
+ if (setup_render_state(progress, &render)) return (-1);
+ 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);
+ if (setup_render_state(progress, &render)) return (-1);
+ clear_progress(progress, &render, 0);
+
+ for (item = progress->items; item; item = item->next) {
+ if (f&f_any) 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) put_sequence(tty, tty->cap.cr, 1);
+ free_render_state(&render);
+ return (0);
+}
+