multiprogress.c (progress_shownotice): Remove pointless guard.
[dvdrip] / multiprogress.c
CommitLineData
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
22static 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
32int 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
143void 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
152int 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
165int 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
178static 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
202static 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
217struct measure {
218 mbstate_t ps;
219 const char *p; size_t i, sz;
220 unsigned wd;
221};
222
223static 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
229static 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
245static 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
254static 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
276static 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
296static 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
310enum { LEFT, RIGHT };
311static 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
335static 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
354int progress_vputleft(struct progress_render_state *render,
355 const char *fmt, va_list ap)
356 { return (vputf(render, LEFT, fmt, ap)); }
357
358int progress_vputright(struct progress_render_state *render,
359 const char *fmt, va_list ap)
360 { return (vputf(render, RIGHT, fmt, ap)); }
361
362int 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
372int 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)
383static const struct progress_ttyinfo *curtty = 0;
384static int putty(int ch) { return (putc(ch, curtty->fp)); }
c45831b0
MW
385void 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
388void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
389 { progress_put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
390void 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)
393static const struct progress_ttyinfo *curtty = 0;
394static int putty(int ch) { return (putc(ch, curtty->fp)); }
c45831b0
MW
395void 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
398void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
399 { progress_put_sequence(tty, tgoto(tty->cap.af, -1, colour), 1); }
400void 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
403void progress_put_sequence(const struct progress_ttyinfo *tty,
404 const char *p, unsigned nlines) { ; }
405void progress_set_fgcolour(const struct progress_ttyinfo *tty, int colour)
44d94f48 406 { ; }
c45831b0 407void progress_set_bgcolour(const struct progress_ttyinfo *tty, int colour)
44d94f48
MW
408 { ; }
409#endif
410
411#define CLRF_ALL 1u
0d5797b0
MW
412static 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
442int 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
453int 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
477enum {
478 LEFT_COLOUR,
479 LEFT_MONO,
480 LEFT_SIMPLE,
481 RIGHT_ANY,
482 STOP
483};
484
485struct bar_state {
486 const struct progress_render_state *render;
487 unsigned pos, nextpos, state;
488};
489
490static 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
507static void put_str(FILE *fp, const char *p, size_t sz)
508 { while (sz--) putc(*p++, fp); }
509static void put_spc(FILE *fp, unsigned n)
510 { while (n--) putc(' ', fp); }
511
512static 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
527static 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
541int 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
574int 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}