Commit | Line | Data |
---|---|---|
dc53ebfa MW |
1 | #define _XOPEN_SOURCE |
2 | ||
3 | #include <limits.h> | |
4 | #include <stdarg.h> | |
5 | #include <stdlib.h> | |
6 | #include <string.h> | |
7 | #include <wchar.h> | |
8 | ||
9 | #include <unistd.h> | |
10 | #include <termios.h> | |
11 | #include <sys/ioctl.h> | |
12 | ||
13 | #if defined(USE_TERMINFO) | |
14 | # include <curses.h> | |
15 | # include <term.h> | |
16 | #elif defined(USE_TERMCAP) | |
17 | # include <termcap.h> | |
18 | #endif | |
19 | ||
20 | #include "multiprogress.h" | |
21 | ||
22 | static FILE *dup_stream(int fd) | |
23 | { | |
24 | FILE *fp; | |
25 | int newfd; | |
26 | ||
27 | newfd = dup(fd); if (newfd < 0) return (0); | |
28 | fp = fdopen(newfd, "r+"); if (!fp) return (0); | |
29 | return (fp); | |
30 | } | |
31 | ||
32 | int progress_init(struct progress_state *progress) | |
33 | { | |
34 | #ifdef USE_TERMCAP | |
35 | char *term, *capcur; | |
36 | #endif | |
37 | #ifdef USE_TERMINFO | |
38 | int err; | |
39 | #endif | |
40 | struct progress_ttyinfo *tty; | |
41 | const char *t; | |
42 | int n; | |
43 | ||
44 | tty = &progress->tty; | |
45 | tty->fp = 0; | |
46 | tty->termbuf = tty->capbuf = 0; | |
47 | tty->cap.f = 0; | |
48 | tty->cap.cr = tty->cap.up = tty->cap.ce = tty->cap.cd = | |
49 | tty->cap.mr = tty->cap.md = tty->cap.me = | |
50 | tty->cap.af = tty->cap.ab = tty->cap.op = 0; | |
51 | ||
52 | progress->items = progress->end_item = 0; | |
53 | progress->nitems = 0; progress->last_lines = 0; | |
54 | progress->tv_update.tv_sec = 0; progress->tv_update.tv_usec = 0; | |
55 | ||
56 | if (isatty(1)) tty->fp = dup_stream(1); | |
57 | else if (isatty(2)) tty->fp = dup_stream(2); | |
58 | else tty->fp = fopen("/dev/tty", "r+"); | |
59 | if (!tty->fp) return (-1); | |
60 | ||
61 | #define SETDIM(dim, var, getcap, dflt) do { \ | |
62 | t = getenv(var); if (t) { n = atoi(t); if (n) { tty->dim = n; break; } } \ | |
63 | n = getcap; if (n > 0) { tty->dim = n; break; } \ | |
64 | tty->dim = dflt; \ | |
65 | } while (0) | |
66 | ||
67 | #if defined(USE_TERMINFO) | |
68 | ||
69 | if (setupterm(0, fileno(tty->fp), &err) != OK || err < 1) return (-1); | |
70 | ||
71 | tty->cap.cr = tigetstr("cr"); | |
e7b7bd3b | 72 | tty->cap.nw = tigetstr("nel"); |
dc53ebfa MW |
73 | tty->cap.up = tigetstr("cuu1"); |
74 | tty->cap.ce = tigetstr("el"); | |
75 | tty->cap.cd = tigetstr("ed"); | |
76 | ||
77 | if (tigetnum("xmc") < 1) { | |
78 | tty->cap.mr = tigetstr("rev"); | |
79 | tty->cap.md = tigetstr("bold"); | |
80 | tty->cap.me = tigetstr("sgr0"); | |
81 | ||
82 | tty->cap.af = tigetstr("setaf"); | |
83 | tty->cap.ab = tigetstr("setab"); | |
84 | tty->cap.op = tigetstr("op"); | |
85 | } | |
86 | ||
87 | if (tigetflag("bce") > 0) tty->cap.f |= TCF_BCE; | |
88 | ||
89 | SETDIM(defwd, "COLUMNS", tigetnum("co"), 80); | |
90 | SETDIM(defht, "LINES", tigetnum("li"), 25); | |
91 | ||
92 | #elif defined(USE_TERMCAP) | |
93 | ||
dc53ebfa MW |
94 | tty->termbuf = malloc(4096); if (!tty->termbuf) return (-1); |
95 | tty->capbuf = malloc(4096); if (!tty->capbuf) return (-1); | |
96 | ||
847b25bd MW |
97 | term = getenv("TERM"); if (!term) return (-1); |
98 | if (tgetent(tty->termbuf, term) < 1) return (-1); | |
99 | ||
dc53ebfa MW |
100 | capcur = tty->capbuf; |
101 | tty->cap.cr = tgetstr("cr", &capcur); | |
e7b7bd3b | 102 | tty->cap.nw = tgetstr("nw", &capcur); |
dc53ebfa MW |
103 | tty->cap.up = tgetstr("up", &capcur); |
104 | tty->cap.ce = tgetstr("ce", &capcur); | |
105 | tty->cap.cd = tgetstr("cd", &capcur); | |
106 | ||
107 | if (tgetnum("sg") < 1) { | |
108 | tty->cap.mr = tgetstr("mr", &capcur); | |
109 | tty->cap.md = tgetstr("md", &capcur); | |
110 | tty->cap.me = tgetstr("me", &capcur); | |
111 | ||
112 | tty->cap.af = tgetstr("AF", &capcur); | |
113 | tty->cap.ab = tgetstr("AB", &capcur); | |
114 | tty->cap.op = tgetstr("op", &capcur); | |
115 | } | |
116 | ||
117 | if (tgetflag("ut") > 0) tty->cap.f |= TCF_BCE; | |
118 | ||
35951074 | 119 | t = tgetstr("pc", &capcur); tty->cap.pc = t ? *t : 0; |
dc53ebfa MW |
120 | |
121 | SETDIM(defwd, "COLUMNS", tgetnum("co"), 80); | |
122 | SETDIM(defht, "LINES", tgetnum("li"), 25); | |
123 | ||
124 | #else | |
125 | ||
126 | SETDIM(defwd, "COLUMNS", -1, 80); | |
127 | SETDIM(defht, "LINES", -1, 25); | |
128 | ||
129 | #endif | |
130 | ||
131 | #undef SETDIM | |
132 | ||
b03fb933 | 133 | if (!tty->cap.cr) tty->cap.cr = "\r"; |
e7b7bd3b | 134 | if (!tty->cap.nw) tty->cap.nw = "\r\n"; |
b03fb933 | 135 | if (!tty->cap.up || !tty->cap.ce || !tty->cap.cd) |
dc53ebfa MW |
136 | { fclose(tty->fp); tty->fp = 0; return (-1); } |
137 | if (!tty->cap.af || !tty->cap.ab || !tty->cap.op) tty->cap.op = 0; | |
138 | if (!tty->cap.me) tty->cap.mr = tty->cap.md = 0; | |
139 | return (0); | |
140 | } | |
141 | ||
142 | void progress_free(struct progress_state *progress) | |
143 | { | |
144 | struct progress_ttyinfo *tty = &progress->tty; | |
145 | ||
146 | if (tty->fp) { fclose(tty->fp); tty->fp = 0; } | |
147 | free(tty->termbuf); free(tty->capbuf); tty->termbuf = tty->capbuf = 0; | |
148 | } | |
149 | ||
150 | #if defined(USE_TERMINFO) | |
151 | static const struct progress_ttyinfo *curtty = 0; | |
152 | static int putty(int ch) { return (putc(ch, curtty->fp)); } | |
153 | static void put_sequence(const struct progress_ttyinfo *tty, | |
154 | const char *p, unsigned nlines) | |
155 | { if (p) { curtty = tty; tputs(p, nlines, putty); } } | |
156 | static void set_fgcolour(const struct progress_ttyinfo *tty, int colour) | |
157 | { put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); } | |
158 | static void set_bgcolour(const struct progress_ttyinfo *tty, int colour) | |
159 | { put_sequence(tty, tgoto(tty->cap.ab, -1, colour), 1); } | |
160 | #elif defined(USE_TERMCAP) | |
161 | static const struct progress_ttyinfo *curtty = 0; | |
162 | static int putty(int ch) { return (putc(ch, curtty->fp)); } | |
163 | static void put_sequence(const struct progress_ttyinfo *tty, | |
164 | const char *p, unsigned nlines) | |
165 | { if (p) { curtty = tty; tputs(p, nlines, putty); } } | |
166 | static void set_fgcolour(const struct progress_ttyinfo *tty, int colour) | |
167 | { put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); } | |
168 | static void set_bgcolour(const struct progress_ttyinfo *tty, int colour) | |
169 | { put_sequence(tty, tgoto(tty->cap.ab, -1, colour), 1); } | |
170 | #else | |
171 | static void put_sequence(const struct progress_ttyinfo *tty, | |
172 | const char *p, unsigned nlines) { ; } | |
173 | static void set_fgcolour(const struct progress_ttyinfo *tty, int colour) | |
174 | { ; } | |
175 | static void set_bgcolour(const struct progress_ttyinfo *tty, int colour) | |
176 | { ; } | |
177 | #endif | |
178 | ||
179 | #define CLRF_ALL 1u | |
180 | static int clear_progress(struct progress_state *progress, | |
181 | struct progress_render_state *render, unsigned f) | |
182 | { | |
183 | const struct progress_ttyinfo *tty = &progress->tty; | |
184 | unsigned ndel, nleave; | |
185 | unsigned i; | |
186 | ||
187 | if (!tty->fp) return (-1); | |
188 | ||
189 | put_sequence(tty, tty->cap.cr, 1); | |
190 | if (progress->last_lines) { | |
191 | if (f&CLRF_ALL) | |
192 | { ndel = progress->last_lines; nleave = 0; } | |
193 | else { | |
194 | if (progress->nitems >= progress->last_lines) ndel = 0; | |
195 | else ndel = progress->last_lines - progress->nitems; | |
196 | nleave = progress->last_lines - ndel; | |
197 | } | |
198 | if (!ndel) | |
199 | for (i = 0; i < nleave - 1; i++) put_sequence(tty, tty->cap.up, 1); | |
200 | else { | |
201 | for (i = 0; i < ndel - 1; i++) put_sequence(tty, tty->cap.up, 1); | |
202 | put_sequence(tty, tty->cap.cd, ndel); | |
203 | for (i = 0; i < nleave; i++) put_sequence(tty, tty->cap.up, 1); | |
204 | } | |
205 | } | |
206 | progress->last_lines = 0; | |
207 | if (ferror(tty->fp)) return (-1); | |
208 | return (0); | |
209 | } | |
210 | ||
211 | static int grow_linebuf(struct progress_render_state *render, size_t want) | |
212 | { | |
213 | char *newbuf; size_t newsz; | |
214 | ||
215 | if (want <= render->linesz) return (0); | |
216 | if (!render->linesz) newsz = 4*render->width + 1; | |
217 | else newsz = render->linesz; | |
218 | while (newsz < want) newsz *= 2; | |
219 | newbuf = malloc(newsz + 1); if (!newbuf) return (-1); | |
220 | newbuf[newsz] = 0; | |
221 | if (render->leftsz) | |
222 | memcpy(newbuf, render->linebuf, render->leftsz); | |
223 | if (render->rightsz) | |
224 | memcpy(newbuf + newsz - render->rightsz, | |
225 | render->linebuf + render->linesz - render->rightsz, | |
226 | render->rightsz); | |
227 | free(render->linebuf); render->linebuf = newbuf; render->linesz = newsz; | |
228 | return (0); | |
229 | } | |
230 | ||
231 | static int grow_tempbuf(struct progress_render_state *render, size_t want) | |
232 | { | |
233 | char *newbuf; size_t newsz; | |
234 | ||
235 | if (want <= render->tempsz) return (0); | |
236 | if (!render->tempsz) newsz = 4*render->width + 1; | |
237 | else newsz = render->tempsz; | |
238 | while (newsz < want) newsz *= 2; | |
239 | newbuf = malloc(newsz + 1); if (!newbuf) return (-1); | |
240 | newbuf[newsz] = 0; | |
241 | if (render->tempsz) memcpy(newbuf, render->tempbuf, render->tempsz); | |
242 | free(render->tempbuf); render->tempbuf = newbuf; render->tempsz = newsz; | |
243 | return (0); | |
244 | } | |
245 | ||
246 | static int setup_render_state(struct progress_state *progress, | |
247 | struct progress_render_state *render) | |
248 | { | |
249 | const struct progress_ttyinfo *tty = &progress->tty; | |
250 | struct winsize wsz; | |
251 | int rc = 0; | |
252 | ||
253 | render->tty = tty; | |
254 | render->linebuf = 0; render->linesz = 0; | |
255 | render->tempbuf = 0; render->tempsz = 0; | |
256 | ||
257 | #ifdef USE_TERMCAP | |
258 | render->old_bc = BC; BC = 0; | |
259 | render->old_up = UP; UP = 0; | |
35951074 | 260 | render->old_pc = PC; PC = tty->cap.pc; |
dc53ebfa MW |
261 | #endif |
262 | ||
263 | if (!ioctl(fileno(tty->fp), TIOCGWINSZ, &wsz)) | |
264 | { render->width = wsz.ws_col; render->height = wsz.ws_row; } | |
265 | else | |
266 | { render->width = tty->defwd; render->height = tty->defht; rc = -1; } | |
267 | ||
268 | if (render->width && !tty->cap.op && !tty->cap.mr) render->width--; | |
269 | ||
270 | return (rc); | |
271 | } | |
272 | ||
273 | static void free_render_state(struct progress_render_state *render) | |
274 | { | |
275 | fflush(render->tty->fp); | |
276 | free(render->linebuf); render->linebuf = 0; render->linesz = 0; | |
277 | free(render->tempbuf); render->tempbuf = 0; render->tempsz = 0; | |
278 | #ifdef USE_TERMCAP | |
279 | UP = render->old_up; | |
280 | BC = render->old_bc; | |
35951074 | 281 | PC = render->old_pc; |
dc53ebfa MW |
282 | #endif |
283 | } | |
284 | ||
285 | #define CONV_MORE ((size_t)-2) | |
286 | #define CONV_BAD ((size_t)-1) | |
287 | ||
288 | struct measure { | |
289 | mbstate_t ps; | |
290 | const char *p; size_t i, sz; | |
291 | unsigned wd; | |
292 | }; | |
293 | ||
294 | static void init_measure(struct measure *m, const char *p, size_t sz) | |
295 | { | |
296 | m->p = p; m->sz = sz; m->i = 0; m->wd = 0; | |
297 | memset(&m->ps, 0, sizeof(m->ps)); | |
298 | } | |
299 | ||
300 | static int advance_measure(struct measure *m) | |
301 | { | |
302 | wchar_t wch; | |
303 | unsigned chwd; | |
304 | size_t n; | |
305 | ||
306 | n = mbrtowc(&wch, m->p + m->i, m->sz - m->i, &m->ps); | |
307 | if (!n) { chwd = 0; n = m->sz - m->i; } | |
308 | else if (n == CONV_MORE) { chwd = 2; n = m->sz - m->i; } | |
309 | else if (n == CONV_BAD) { chwd = 2; n = 1; } | |
310 | else chwd = wcwidth(wch); | |
311 | ||
312 | m->i += n; m->wd += chwd; | |
313 | return (m->i < m->sz); | |
314 | } | |
315 | ||
316 | static unsigned string_width(const char *p, size_t sz) | |
317 | { | |
318 | struct measure m; | |
319 | ||
320 | init_measure(&m, p, sz); | |
321 | while (advance_measure(&m)); | |
322 | return (m.wd); | |
323 | } | |
324 | ||
325 | static size_t split_string(const char *p, size_t sz, | |
326 | unsigned *wd_out, unsigned maxwd) | |
327 | { | |
328 | struct measure m; | |
329 | size_t lasti; unsigned lastwd; | |
330 | int more; | |
331 | ||
332 | init_measure(&m, p, sz); | |
333 | for (;;) { | |
334 | lasti = m.i; lastwd = m.wd; | |
335 | more = advance_measure(&m); | |
336 | if (m.wd > maxwd) { *wd_out = lastwd; return (lasti); } | |
337 | else if (!more) { *wd_out = m.wd; return (sz); } | |
338 | } | |
339 | } | |
340 | ||
341 | enum { LEFT, RIGHT }; | |
342 | static int putstr(struct progress_render_state *render, unsigned side, | |
343 | const char *p, size_t n) | |
344 | { | |
345 | unsigned newwd = string_width(p, n); | |
346 | size_t want; | |
347 | ||
348 | if (newwd >= render->width - render->leftwd - render->rightwd) return (-1); | |
349 | want = render->leftsz + render->rightsz + n; | |
350 | if (want > render->linesz && grow_linebuf(render, want)) return (-1); | |
351 | switch (side) { | |
352 | case LEFT: | |
353 | memcpy(render->linebuf + render->leftsz, p, n); | |
354 | render->leftsz += n; render->leftwd += newwd; | |
355 | break; | |
356 | case RIGHT: | |
357 | memcpy(render->linebuf + render->linesz - render->rightsz - n, p, n); | |
358 | render->rightsz += n; render->rightwd += newwd; | |
359 | break; | |
360 | default: | |
361 | return (-1); | |
362 | } | |
363 | return (0); | |
364 | } | |
365 | ||
366 | static int vputf(struct progress_render_state *render, unsigned side, | |
367 | const char *fmt, va_list ap) | |
368 | { | |
369 | va_list bp; | |
370 | int rc; | |
371 | ||
372 | if (!render->tempsz && grow_tempbuf(render, 2*strlen(fmt))) return (-1); | |
373 | for (;;) { | |
374 | va_copy(bp, ap); | |
375 | rc = vsnprintf(render->tempbuf, render->tempsz, fmt, bp); | |
376 | va_end(bp); | |
377 | if (rc < 0) return (-1); | |
378 | if (rc <= render->tempsz) break; | |
379 | if (grow_tempbuf(render, 2*(rc + 1))) return (-1); | |
380 | } | |
381 | if (putstr(render, side, render->tempbuf, rc)) return (-1); | |
382 | return (0); | |
383 | } | |
384 | ||
385 | int progress_vputleft(struct progress_render_state *render, | |
386 | const char *fmt, va_list ap) | |
387 | { return (vputf(render, LEFT, fmt, ap)); } | |
388 | ||
389 | int progress_vputright(struct progress_render_state *render, | |
390 | const char *fmt, va_list ap) | |
391 | { return (vputf(render, RIGHT, fmt, ap)); } | |
392 | ||
393 | int progress_putleft(struct progress_render_state *render, | |
394 | const char *fmt, ...) | |
395 | { | |
396 | va_list ap; | |
397 | int rc; | |
398 | ||
399 | va_start(ap, fmt); rc = vputf(render, LEFT, fmt, ap); va_end(ap); | |
400 | return (rc); | |
401 | } | |
402 | ||
403 | int progress_putright(struct progress_render_state *render, | |
404 | const char *fmt, ...) | |
405 | { | |
406 | va_list ap; | |
407 | int rc; | |
408 | ||
409 | va_start(ap, fmt); rc = vputf(render, RIGHT, fmt, ap); va_end(ap); | |
410 | return (rc); | |
411 | } | |
412 | ||
413 | enum { | |
414 | LEFT_COLOUR, | |
415 | LEFT_MONO, | |
416 | LEFT_SIMPLE, | |
417 | RIGHT_ANY, | |
418 | STOP | |
419 | }; | |
420 | ||
421 | struct bar_state { | |
422 | const struct progress_render_state *render; | |
423 | unsigned pos, nextpos, state; | |
424 | }; | |
425 | ||
426 | static void advance_bar_state(struct bar_state *bar) | |
427 | { | |
428 | const struct progress_render_state *render = bar->render; | |
429 | const struct progress_ttyinfo *tty = render->tty; | |
430 | size_t here = bar->nextpos; | |
431 | ||
432 | while (bar->nextpos == here) { | |
433 | switch (bar->state) { | |
434 | case LEFT_COLOUR: set_bgcolour(tty, 3); goto right; | |
435 | case LEFT_MONO: put_sequence(tty, tty->cap.me, 1); goto right; | |
436 | case LEFT_SIMPLE: putc('|', tty->fp); goto right; | |
437 | right: bar->state = RIGHT_ANY; bar->nextpos = render->width; break; | |
438 | case RIGHT_ANY: bar->state = STOP; bar->nextpos = UINT_MAX; break; | |
439 | } | |
440 | } | |
441 | } | |
442 | ||
443 | static void put_str(FILE *fp, const char *p, size_t sz) | |
444 | { while (sz--) putc(*p++, fp); } | |
445 | static void put_spc(FILE *fp, unsigned n) | |
446 | { while (n--) putc(' ', fp); } | |
447 | ||
448 | static void put_barstr(struct bar_state *bar, const char *p, size_t sz) | |
449 | { | |
450 | unsigned wd; | |
451 | size_t n; | |
452 | ||
453 | for (;;) { | |
454 | n = split_string(p, sz, &wd, bar->nextpos - bar->pos); | |
455 | if (n == sz && wd < bar->nextpos - bar->pos) break; | |
456 | put_str(bar->render->tty->fp, p, n); bar->pos += wd; | |
457 | advance_bar_state(bar); | |
458 | p += n; sz -= n; | |
459 | } | |
460 | put_str(bar->render->tty->fp, p, sz); bar->pos += wd; | |
461 | } | |
462 | ||
463 | static void put_barspc(struct bar_state *bar, unsigned n) | |
464 | { | |
465 | unsigned step; | |
466 | ||
467 | for (;;) { | |
468 | step = bar->nextpos - bar->pos; | |
469 | if (n < step) break; | |
470 | put_spc(bar->render->tty->fp, step); bar->pos += step; | |
471 | advance_bar_state(bar); | |
472 | n -= step; | |
473 | } | |
474 | put_spc(bar->render->tty->fp, n); bar->pos += n; | |
475 | } | |
476 | ||
477 | int progress_showbar(struct progress_render_state *render, double frac) | |
478 | { | |
479 | const struct progress_ttyinfo *tty = render->tty; | |
480 | struct bar_state bar; | |
481 | ||
482 | if (!tty->fp) return (-1); | |
483 | ||
484 | bar.render = render; bar.pos = 0; bar.nextpos = frac*render->width + 0.5; | |
485 | ||
486 | if (tty->cap.op) { | |
487 | set_fgcolour(tty, 0); bar.state = LEFT_COLOUR; | |
488 | if (bar.nextpos) set_bgcolour(tty, 2); | |
489 | else advance_bar_state(&bar); | |
490 | } else if (tty->cap.mr) { | |
491 | if (bar.nextpos) | |
492 | { bar.state = LEFT_MONO; put_sequence(tty, tty->cap.mr, 1); } | |
493 | else | |
494 | { bar.state = RIGHT; bar.nextpos = render->width; } | |
495 | } else | |
496 | bar.state = LEFT_SIMPLE; | |
497 | ||
498 | put_barstr(&bar, render->linebuf, render->leftsz); | |
499 | put_barspc(&bar, render->width - render->leftwd - render->rightwd); | |
500 | put_barstr(&bar, | |
501 | render->linebuf + render->linesz - render->rightsz, | |
502 | render->rightsz); | |
503 | ||
504 | put_sequence(tty, tty->cap.me, 1); | |
505 | put_sequence(tty, tty->cap.op, 1); | |
506 | ||
507 | return (0); | |
508 | } | |
509 | ||
510 | int progress_shownotice(struct progress_render_state *render, int bg, int fg) | |
511 | { | |
512 | const struct progress_ttyinfo *tty = render->tty; | |
513 | ||
514 | if (!tty->fp) return (-1); | |
515 | ||
516 | if (tty->cap.op) { set_fgcolour(tty, fg); set_bgcolour(tty, bg); } | |
517 | else if (tty->cap.mr) put_sequence(tty, tty->cap.mr, 1); | |
518 | if (tty->cap.md) put_sequence(tty, tty->cap.md, 1); | |
519 | ||
520 | put_str(tty->fp, render->linebuf, render->leftsz); | |
521 | if (!render->rightsz && (tty->cap.f&TCF_BCE) && tty->cap.ce) | |
522 | put_sequence(tty, tty->cap.ce, 1); | |
523 | else { | |
524 | put_spc(tty->fp, render->width - render->leftwd - render->rightwd); | |
525 | put_str(tty->fp, | |
526 | render->linebuf + render->linesz - render->rightsz, | |
527 | render->rightsz); | |
528 | } | |
529 | ||
530 | put_sequence(tty, tty->cap.me, 1); | |
531 | put_sequence(tty, tty->cap.op, 1); | |
532 | ||
533 | return (0); | |
534 | } | |
535 | ||
536 | int progress_additem(struct progress_state *progress, | |
537 | struct progress_item *item) | |
538 | { | |
539 | if (item->parent) return (-1); | |
540 | item->prev = progress->end_item; item->next = 0; | |
541 | if (progress->end_item) progress->end_item->next = item; | |
542 | else progress->items = item; | |
543 | progress->end_item = item; item->parent = progress; | |
544 | progress->nitems++; | |
545 | ||
546 | return (0); | |
547 | } | |
548 | ||
549 | int progress_clear(struct progress_state *progress) | |
550 | { | |
551 | struct progress_render_state render; | |
552 | ||
553 | if (!progress->tty.fp) return (-1); | |
554 | if (setup_render_state(progress, &render)) return (-1); | |
555 | clear_progress(progress, &render, CLRF_ALL); | |
556 | free_render_state(&render); | |
557 | return (0); | |
558 | } | |
559 | ||
560 | int progress_update(struct progress_state *progress) | |
561 | { | |
562 | struct progress_render_state render; | |
e7b7bd3b | 563 | const struct progress_ttyinfo *tty = &progress->tty; |
dc53ebfa MW |
564 | struct progress_item *item; |
565 | unsigned f = 0; | |
566 | #define f_any 1u | |
567 | ||
568 | if (!progress->tty.fp) return (-1); | |
569 | if (setup_render_state(progress, &render)) return (-1); | |
570 | clear_progress(progress, &render, 0); | |
571 | ||
572 | for (item = progress->items; item; item = item->next) { | |
e7b7bd3b | 573 | if (f&f_any) put_sequence(tty, tty->cap.nw, 1); |
dc53ebfa MW |
574 | render.leftsz = render.rightsz = 0; |
575 | render.leftwd = render.rightwd = 0; | |
576 | item->render(item, &render); progress->last_lines++; f |= f_any; | |
577 | if (progress->last_lines > render.height) break; | |
578 | } | |
ce512975 | 579 | if (f&f_any) put_sequence(tty, tty->cap.cr, 1); |
dc53ebfa MW |
580 | free_render_state(&render); |
581 | return (0); | |
582 | } | |
583 | ||
584 | int progress_removeitem(struct progress_state *progress, | |
585 | struct progress_item *item) | |
586 | { | |
587 | if (!item->parent) return (-1); | |
588 | if (item->next) item->next->prev = item->prev; | |
589 | else (progress->end_item) = item->prev; | |
590 | if (item->prev) item->prev->next = item->next; | |
591 | else (progress->items) = item->next; | |
592 | progress->nitems--; item->parent = 0; | |
593 | ||
594 | return (0); | |
595 | } |