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