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