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 | ||
94 | term = getenv("TERM"); if (!term) return (-1); | |
95 | if (tgetent(tty->termbuf, term) < 1) return (-1); | |
96 | ||
97 | tty->termbuf = malloc(4096); if (!tty->termbuf) return (-1); | |
98 | tty->capbuf = malloc(4096); if (!tty->capbuf) return (-1); | |
99 | ||
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 | ||
119 | t = tgetstr("pc", &capcur); PC = t ? *t : 0; | |
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; | |
260 | #endif | |
261 | ||
262 | if (!ioctl(fileno(tty->fp), TIOCGWINSZ, &wsz)) | |
263 | { render->width = wsz.ws_col; render->height = wsz.ws_row; } | |
264 | else | |
265 | { render->width = tty->defwd; render->height = tty->defht; rc = -1; } | |
266 | ||
267 | if (render->width && !tty->cap.op && !tty->cap.mr) render->width--; | |
268 | ||
269 | return (rc); | |
270 | } | |
271 | ||
272 | static void free_render_state(struct progress_render_state *render) | |
273 | { | |
274 | fflush(render->tty->fp); | |
275 | free(render->linebuf); render->linebuf = 0; render->linesz = 0; | |
276 | free(render->tempbuf); render->tempbuf = 0; render->tempsz = 0; | |
277 | #ifdef USE_TERMCAP | |
278 | UP = render->old_up; | |
279 | BC = render->old_bc; | |
280 | #endif | |
281 | } | |
282 | ||
283 | #define CONV_MORE ((size_t)-2) | |
284 | #define CONV_BAD ((size_t)-1) | |
285 | ||
286 | struct measure { | |
287 | mbstate_t ps; | |
288 | const char *p; size_t i, sz; | |
289 | unsigned wd; | |
290 | }; | |
291 | ||
292 | static void init_measure(struct measure *m, const char *p, size_t sz) | |
293 | { | |
294 | m->p = p; m->sz = sz; m->i = 0; m->wd = 0; | |
295 | memset(&m->ps, 0, sizeof(m->ps)); | |
296 | } | |
297 | ||
298 | static int advance_measure(struct measure *m) | |
299 | { | |
300 | wchar_t wch; | |
301 | unsigned chwd; | |
302 | size_t n; | |
303 | ||
304 | n = mbrtowc(&wch, m->p + m->i, m->sz - m->i, &m->ps); | |
305 | if (!n) { chwd = 0; n = m->sz - m->i; } | |
306 | else if (n == CONV_MORE) { chwd = 2; n = m->sz - m->i; } | |
307 | else if (n == CONV_BAD) { chwd = 2; n = 1; } | |
308 | else chwd = wcwidth(wch); | |
309 | ||
310 | m->i += n; m->wd += chwd; | |
311 | return (m->i < m->sz); | |
312 | } | |
313 | ||
314 | static unsigned string_width(const char *p, size_t sz) | |
315 | { | |
316 | struct measure m; | |
317 | ||
318 | init_measure(&m, p, sz); | |
319 | while (advance_measure(&m)); | |
320 | return (m.wd); | |
321 | } | |
322 | ||
323 | static size_t split_string(const char *p, size_t sz, | |
324 | unsigned *wd_out, unsigned maxwd) | |
325 | { | |
326 | struct measure m; | |
327 | size_t lasti; unsigned lastwd; | |
328 | int more; | |
329 | ||
330 | init_measure(&m, p, sz); | |
331 | for (;;) { | |
332 | lasti = m.i; lastwd = m.wd; | |
333 | more = advance_measure(&m); | |
334 | if (m.wd > maxwd) { *wd_out = lastwd; return (lasti); } | |
335 | else if (!more) { *wd_out = m.wd; return (sz); } | |
336 | } | |
337 | } | |
338 | ||
339 | enum { LEFT, RIGHT }; | |
340 | static int putstr(struct progress_render_state *render, unsigned side, | |
341 | const char *p, size_t n) | |
342 | { | |
343 | unsigned newwd = string_width(p, n); | |
344 | size_t want; | |
345 | ||
346 | if (newwd >= render->width - render->leftwd - render->rightwd) return (-1); | |
347 | want = render->leftsz + render->rightsz + n; | |
348 | if (want > render->linesz && grow_linebuf(render, want)) return (-1); | |
349 | switch (side) { | |
350 | case LEFT: | |
351 | memcpy(render->linebuf + render->leftsz, p, n); | |
352 | render->leftsz += n; render->leftwd += newwd; | |
353 | break; | |
354 | case RIGHT: | |
355 | memcpy(render->linebuf + render->linesz - render->rightsz - n, p, n); | |
356 | render->rightsz += n; render->rightwd += newwd; | |
357 | break; | |
358 | default: | |
359 | return (-1); | |
360 | } | |
361 | return (0); | |
362 | } | |
363 | ||
364 | static int vputf(struct progress_render_state *render, unsigned side, | |
365 | const char *fmt, va_list ap) | |
366 | { | |
367 | va_list bp; | |
368 | int rc; | |
369 | ||
370 | if (!render->tempsz && grow_tempbuf(render, 2*strlen(fmt))) return (-1); | |
371 | for (;;) { | |
372 | va_copy(bp, ap); | |
373 | rc = vsnprintf(render->tempbuf, render->tempsz, fmt, bp); | |
374 | va_end(bp); | |
375 | if (rc < 0) return (-1); | |
376 | if (rc <= render->tempsz) break; | |
377 | if (grow_tempbuf(render, 2*(rc + 1))) return (-1); | |
378 | } | |
379 | if (putstr(render, side, render->tempbuf, rc)) return (-1); | |
380 | return (0); | |
381 | } | |
382 | ||
383 | int progress_vputleft(struct progress_render_state *render, | |
384 | const char *fmt, va_list ap) | |
385 | { return (vputf(render, LEFT, fmt, ap)); } | |
386 | ||
387 | int progress_vputright(struct progress_render_state *render, | |
388 | const char *fmt, va_list ap) | |
389 | { return (vputf(render, RIGHT, fmt, ap)); } | |
390 | ||
391 | int progress_putleft(struct progress_render_state *render, | |
392 | const char *fmt, ...) | |
393 | { | |
394 | va_list ap; | |
395 | int rc; | |
396 | ||
397 | va_start(ap, fmt); rc = vputf(render, LEFT, fmt, ap); va_end(ap); | |
398 | return (rc); | |
399 | } | |
400 | ||
401 | int progress_putright(struct progress_render_state *render, | |
402 | const char *fmt, ...) | |
403 | { | |
404 | va_list ap; | |
405 | int rc; | |
406 | ||
407 | va_start(ap, fmt); rc = vputf(render, RIGHT, fmt, ap); va_end(ap); | |
408 | return (rc); | |
409 | } | |
410 | ||
411 | enum { | |
412 | LEFT_COLOUR, | |
413 | LEFT_MONO, | |
414 | LEFT_SIMPLE, | |
415 | RIGHT_ANY, | |
416 | STOP | |
417 | }; | |
418 | ||
419 | struct bar_state { | |
420 | const struct progress_render_state *render; | |
421 | unsigned pos, nextpos, state; | |
422 | }; | |
423 | ||
424 | static void advance_bar_state(struct bar_state *bar) | |
425 | { | |
426 | const struct progress_render_state *render = bar->render; | |
427 | const struct progress_ttyinfo *tty = render->tty; | |
428 | size_t here = bar->nextpos; | |
429 | ||
430 | while (bar->nextpos == here) { | |
431 | switch (bar->state) { | |
432 | case LEFT_COLOUR: set_bgcolour(tty, 3); goto right; | |
433 | case LEFT_MONO: put_sequence(tty, tty->cap.me, 1); goto right; | |
434 | case LEFT_SIMPLE: putc('|', tty->fp); goto right; | |
435 | right: bar->state = RIGHT_ANY; bar->nextpos = render->width; break; | |
436 | case RIGHT_ANY: bar->state = STOP; bar->nextpos = UINT_MAX; break; | |
437 | } | |
438 | } | |
439 | } | |
440 | ||
441 | static void put_str(FILE *fp, const char *p, size_t sz) | |
442 | { while (sz--) putc(*p++, fp); } | |
443 | static void put_spc(FILE *fp, unsigned n) | |
444 | { while (n--) putc(' ', fp); } | |
445 | ||
446 | static void put_barstr(struct bar_state *bar, const char *p, size_t sz) | |
447 | { | |
448 | unsigned wd; | |
449 | size_t n; | |
450 | ||
451 | for (;;) { | |
452 | n = split_string(p, sz, &wd, bar->nextpos - bar->pos); | |
453 | if (n == sz && wd < bar->nextpos - bar->pos) break; | |
454 | put_str(bar->render->tty->fp, p, n); bar->pos += wd; | |
455 | advance_bar_state(bar); | |
456 | p += n; sz -= n; | |
457 | } | |
458 | put_str(bar->render->tty->fp, p, sz); bar->pos += wd; | |
459 | } | |
460 | ||
461 | static void put_barspc(struct bar_state *bar, unsigned n) | |
462 | { | |
463 | unsigned step; | |
464 | ||
465 | for (;;) { | |
466 | step = bar->nextpos - bar->pos; | |
467 | if (n < step) break; | |
468 | put_spc(bar->render->tty->fp, step); bar->pos += step; | |
469 | advance_bar_state(bar); | |
470 | n -= step; | |
471 | } | |
472 | put_spc(bar->render->tty->fp, n); bar->pos += n; | |
473 | } | |
474 | ||
475 | int progress_showbar(struct progress_render_state *render, double frac) | |
476 | { | |
477 | const struct progress_ttyinfo *tty = render->tty; | |
478 | struct bar_state bar; | |
479 | ||
480 | if (!tty->fp) return (-1); | |
481 | ||
482 | bar.render = render; bar.pos = 0; bar.nextpos = frac*render->width + 0.5; | |
483 | ||
484 | if (tty->cap.op) { | |
485 | set_fgcolour(tty, 0); bar.state = LEFT_COLOUR; | |
486 | if (bar.nextpos) set_bgcolour(tty, 2); | |
487 | else advance_bar_state(&bar); | |
488 | } else if (tty->cap.mr) { | |
489 | if (bar.nextpos) | |
490 | { bar.state = LEFT_MONO; put_sequence(tty, tty->cap.mr, 1); } | |
491 | else | |
492 | { bar.state = RIGHT; bar.nextpos = render->width; } | |
493 | } else | |
494 | bar.state = LEFT_SIMPLE; | |
495 | ||
496 | put_barstr(&bar, render->linebuf, render->leftsz); | |
497 | put_barspc(&bar, render->width - render->leftwd - render->rightwd); | |
498 | put_barstr(&bar, | |
499 | render->linebuf + render->linesz - render->rightsz, | |
500 | render->rightsz); | |
501 | ||
502 | put_sequence(tty, tty->cap.me, 1); | |
503 | put_sequence(tty, tty->cap.op, 1); | |
504 | ||
505 | return (0); | |
506 | } | |
507 | ||
508 | int progress_shownotice(struct progress_render_state *render, int bg, int fg) | |
509 | { | |
510 | const struct progress_ttyinfo *tty = render->tty; | |
511 | ||
512 | if (!tty->fp) return (-1); | |
513 | ||
514 | if (tty->cap.op) { set_fgcolour(tty, fg); set_bgcolour(tty, bg); } | |
515 | else if (tty->cap.mr) put_sequence(tty, tty->cap.mr, 1); | |
516 | if (tty->cap.md) put_sequence(tty, tty->cap.md, 1); | |
517 | ||
518 | put_str(tty->fp, render->linebuf, render->leftsz); | |
519 | if (!render->rightsz && (tty->cap.f&TCF_BCE) && tty->cap.ce) | |
520 | put_sequence(tty, tty->cap.ce, 1); | |
521 | else { | |
522 | put_spc(tty->fp, render->width - render->leftwd - render->rightwd); | |
523 | put_str(tty->fp, | |
524 | render->linebuf + render->linesz - render->rightsz, | |
525 | render->rightsz); | |
526 | } | |
527 | ||
528 | put_sequence(tty, tty->cap.me, 1); | |
529 | put_sequence(tty, tty->cap.op, 1); | |
530 | ||
531 | return (0); | |
532 | } | |
533 | ||
534 | int progress_additem(struct progress_state *progress, | |
535 | struct progress_item *item) | |
536 | { | |
537 | if (item->parent) return (-1); | |
538 | item->prev = progress->end_item; item->next = 0; | |
539 | if (progress->end_item) progress->end_item->next = item; | |
540 | else progress->items = item; | |
541 | progress->end_item = item; item->parent = progress; | |
542 | progress->nitems++; | |
543 | ||
544 | return (0); | |
545 | } | |
546 | ||
547 | int progress_clear(struct progress_state *progress) | |
548 | { | |
549 | struct progress_render_state render; | |
550 | ||
551 | if (!progress->tty.fp) return (-1); | |
552 | if (setup_render_state(progress, &render)) return (-1); | |
553 | clear_progress(progress, &render, CLRF_ALL); | |
554 | free_render_state(&render); | |
555 | return (0); | |
556 | } | |
557 | ||
558 | int progress_update(struct progress_state *progress) | |
559 | { | |
560 | struct progress_render_state render; | |
e7b7bd3b | 561 | const struct progress_ttyinfo *tty = &progress->tty; |
dc53ebfa MW |
562 | struct progress_item *item; |
563 | unsigned f = 0; | |
564 | #define f_any 1u | |
565 | ||
566 | if (!progress->tty.fp) return (-1); | |
567 | if (setup_render_state(progress, &render)) return (-1); | |
568 | clear_progress(progress, &render, 0); | |
569 | ||
570 | for (item = progress->items; item; item = item->next) { | |
e7b7bd3b | 571 | if (f&f_any) put_sequence(tty, tty->cap.nw, 1); |
dc53ebfa MW |
572 | render.leftsz = render.rightsz = 0; |
573 | render.leftwd = render.rightwd = 0; | |
574 | item->render(item, &render); progress->last_lines++; f |= f_any; | |
575 | if (progress->last_lines > render.height) break; | |
576 | } | |
ce512975 | 577 | if (f&f_any) put_sequence(tty, tty->cap.cr, 1); |
dc53ebfa MW |
578 | free_render_state(&render); |
579 | return (0); | |
580 | } | |
581 | ||
582 | int progress_removeitem(struct progress_state *progress, | |
583 | struct progress_item *item) | |
584 | { | |
585 | if (!item->parent) return (-1); | |
586 | if (item->next) item->next->prev = item->prev; | |
587 | else (progress->end_item) = item->prev; | |
588 | if (item->prev) item->prev->next = item->next; | |
589 | else (progress->items) = item->next; | |
590 | progress->nitems--; item->parent = 0; | |
591 | ||
592 | return (0); | |
593 | } |