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