Annotation of src/usr.bin/systat/engine.c, Revision 1.30
1.30 ! tb 1: /* $OpenBSD: engine.c,v 1.29 2021/07/02 15:34:16 millert Exp $ */
1.1 canacar 2: /*
3: * Copyright (c) 2001, 2007 Can Erkin Acar <canacar@openbsd.org>
4: *
5: * Permission to use, copy, modify, and distribute this software for any
6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
8: *
9: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16: */
17:
18:
1.10 chl 19: #include <sys/ioctl.h>
1.1 canacar 20: #include <sys/types.h>
21: #include <sys/queue.h>
22:
23: #include <ctype.h>
24: #include <curses.h>
25: #include <signal.h>
26: #include <stdlib.h>
27: #include <string.h>
1.8 canacar 28: #include <term.h>
1.1 canacar 29: #include <unistd.h>
1.29 millert 30: #include <math.h>
1.12 lum 31: #include <err.h>
1.1 canacar 32:
1.8 canacar 33: /* XXX These are defined in term.h and conflict with our variable names */
34: #ifdef columns
35: #undef columns
36: #endif
37:
38: #ifdef lines
39: #undef lines
40: #endif
41:
1.1 canacar 42: #include "engine.h"
43:
1.18 deraadt 44: #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
1.1 canacar 45:
46: /* circular linked list of views */
1.17 krw 47: TAILQ_HEAD(view_list, view_ent) view_head =
48: TAILQ_HEAD_INITIALIZER(view_head);
1.1 canacar 49: struct view_ent {
50: field_view *view;
1.17 krw 51: TAILQ_ENTRY(view_ent) entries;
1.1 canacar 52: };
53:
1.29 millert 54: static struct timespec ts_delay = { 5, 0 };
55: static struct itimerval it_delay = { { 0, 0 }, { 5, 0 } };
56:
1.1 canacar 57: int dispstart = 0;
1.25 martijn 58: int humanreadable = 0;
1.1 canacar 59: int interactive = 1;
1.15 reyk 60: int averageonly = 0;
1.1 canacar 61: int maxprint = 0;
62: int paused = 0;
63: int rawmode = 0;
64: int rawwidth = DEFAULT_WIDTH;
65: int sortdir = 1;
66: int columns, lines;
67: u_int32_t num_disp = 0;
68: int max_disp = -1;
69:
70: volatile sig_atomic_t gotsig_close = 0;
71: volatile sig_atomic_t gotsig_resize = 0;
72: volatile sig_atomic_t gotsig_alarm = 0;
73: int need_update = 0;
74: int need_sort = 0;
1.14 mpf 75: int separate_thousands = 0;
1.1 canacar 76:
77: SCREEN *screen;
78:
79: field_view *curr_view = NULL;
80: struct view_ent *curr_view_ent = NULL;
81: struct view_manager *curr_mgr = NULL;
82:
83: int curr_line = 0;
84: int home_line = 0;
85:
86: /* line buffer for raw mode */
87: char linebuf[MAX_LINE_BUF];
88: int linepos = 0;
89:
90: /* temp storage for state printing */
91: char tmp_buf[MAX_LINE_BUF];
92:
93: char cmdbuf[MAX_LINE_BUF];
94: int cmd_len = -1;
95: struct command *curr_cmd = NULL;
96: char *curr_message = NULL;
1.28 martijn 97: enum message_mode message_mode = MESSAGE_NONE;
98: int message_cont = 1;
1.1 canacar 99:
100: void print_cmdline(void);
101:
102:
103: /* screen output functions */
104:
105: char * tb_ptr = NULL;
106: int tb_len = 0;
107:
108: void
109: tb_start(void)
110: {
111: tb_ptr = tmp_buf;
112: tb_len = sizeof(tmp_buf);
113: tb_ptr[0] = '\0';
114: }
115:
116: void
117: tb_end(void)
118: {
119: tb_ptr = NULL;
120: tb_len = 0;
121: }
122:
123: int
124: tbprintf(char *format, ...)
125: {
126: int len;
127: va_list arg;
128:
129: if (tb_ptr == NULL || tb_len <= 0)
130: return 0;
131:
132: va_start(arg, format);
1.20 deraadt 133: len = vsnprintf(tb_ptr, tb_len, format, arg);
1.1 canacar 134: va_end(arg);
1.26 jasper 135:
1.1 canacar 136: if (len > tb_len)
137: tb_end();
138: else if (len > 0) {
139: tb_ptr += len;
140: tb_len -= len;
141: }
1.14 mpf 142:
143: return len;
144: }
145:
146: int
147: tbprintft(char *format, ...)
148: {
149: int len;
150: va_list arg;
151: char buf[MAX_LINE_BUF];
152:
153: if (tb_ptr == NULL || tb_len <= 0)
154: return 0;
155:
156: va_start(arg, format);
157: len = vsnprintf(buf, tb_len, format, arg);
158: va_end(arg);
159:
160: if (len > tb_len)
161: tb_end();
162: else if (len > 0) {
163: int d, s;
164: int digits, curdigit;
165:
166: if (!separate_thousands) {
167: strlcpy(tb_ptr, buf, tb_len);
168: return len;
169: }
170:
171: /* count until we hit a non digit. (e.g. the prefix) */
172: for (digits = 0; digits < len; digits++)
1.16 deraadt 173: if (!isdigit((unsigned char)buf[digits]))
1.14 mpf 174: break;
175:
176: curdigit = digits;
177: d = s = 0;
178: /* insert thousands separators while copying */
179: while (curdigit && d < tb_len) {
180: if (curdigit < digits && curdigit % 3 == 0)
181: tb_ptr[d++] = ',';
182: tb_ptr[d++] = buf[s++];
183: curdigit--;
184: }
185: /* copy the remaining non-digits */
186: while (len > digits && d < tb_len) {
187: tb_ptr[d++] = buf[s++];
188: digits++;
189: }
190: tb_ptr[d] = '\0';
191: tb_ptr += d;
192: tb_len -= d;
193: len = d;
194: }
1.1 canacar 195: return len;
196: }
197:
198: void
199: move_horiz(int offset)
200: {
201: if (rawmode) {
202: if (offset <= 0)
203: linepos = 0;
204: else if (offset >= MAX_LINE_BUF)
205: linepos = MAX_LINE_BUF - 1;
206: else
207: linepos = offset;
208: } else {
209: move(curr_line, offset);
210: }
211: }
212:
213: void
214: print_str(int len, const char *str)
215: {
216: if (len <= 0)
217: return;
218:
219: if (rawmode) {
1.18 deraadt 220: int length = MINIMUM(len, MAX_LINE_BUF - linepos);
1.1 canacar 221: if (length <= 0)
222: return;
223: bcopy(str, &linebuf[linepos], length);
224: linepos += length;
225: } else
226: addnstr(str, len);
227: }
228:
229: void
230: clear_linebuf(void)
231: {
232: memset(linebuf, ' ', MAX_LINE_BUF);
233: }
234:
235: void
236: end_line(void)
237: {
238: if (rawmode) {
239: linebuf[rawwidth] = '\0';
240: printf("%s\n", linebuf);
241: clear_linebuf();
242: }
243: curr_line++;
244: }
245:
246: void
247: end_page(void)
248: {
249: if (rawmode) {
250: linepos = 0;
251: clear_linebuf();
1.24 solene 252: fflush(stdout);
1.1 canacar 253: } else {
254: move(home_line, 0);
255: print_cmdline();
256: refresh();
257: }
258: curr_line = 0;
259: }
260:
261: /* field output functions */
262:
263: void
264: print_fld_str(field_def *fld, const char *str)
265: {
1.7 canacar 266: int len, offset;
1.1 canacar 267: char *cpos;
268:
269: if (str == NULL || fld == NULL)
270: return;
271:
272: if (fld->start < 0)
273: return;
274:
275: len = strlen(str);
276:
277: if (len >= fld->width) {
278: move_horiz(fld->start);
279: print_str(fld->width, str);
280: } else {
281: switch (fld->align) {
282: case FLD_ALIGN_RIGHT:
283: move_horiz(fld->start + (fld->width - len));
284: break;
285: case FLD_ALIGN_CENTER:
286: move_horiz(fld->start + (fld->width - len) / 2);
287: break;
288: case FLD_ALIGN_COLUMN:
289: if ((cpos = strchr(str, ':')) == NULL) {
1.7 canacar 290: offset = (fld->width - len) / 2;
1.1 canacar 291: } else {
1.7 canacar 292: offset = (fld->width / 2) - (cpos - str);
293: if (offset < 0)
294: offset = 0;
295: else if (offset > (fld->width - len))
296: offset = fld->width - len;
1.1 canacar 297: }
1.7 canacar 298: move_horiz(fld->start + offset);
1.1 canacar 299: break;
300: default:
301: move_horiz(fld->start);
302: break;
303: }
304: print_str(len, str);
305: }
306: }
307:
308: void
309: print_bar_title(field_def *fld)
310: {
311: char buf[16];
1.7 canacar 312: int len, i, d, tr, tw, val, pos, cur;
1.1 canacar 313:
314: int divs[] = {20, 10, 5, 4, 3, 2, 1, 0};
315:
316: if (fld->width < 1)
317: return;
318:
319: len = snprintf(buf, sizeof(buf), " %d\\", fld->arg);
320: if (len >= sizeof(buf))
321: return;
322:
323: for (i = 0; divs[i]; i++)
324: if (divs[i] * len <= fld->width)
325: break;
326:
327: if (divs[i] == 0) {
328: print_fld_str(fld, "*****");
329: return;
330: }
331:
1.7 canacar 332: d = divs[i];
1.1 canacar 333:
334: val = 0;
335: pos = 0;
1.7 canacar 336: tr = fld->arg % d;
337: tw = fld->width % d;
1.1 canacar 338:
339: tb_start();
340: cur = 0;
1.7 canacar 341: for(i = 0; i < d; i++) {
1.1 canacar 342: tw += fld->width;
343: tr += fld->arg;
344:
1.7 canacar 345: while (tr >= d) {
1.1 canacar 346: val++;
1.7 canacar 347: tr -= d;
1.1 canacar 348: }
1.7 canacar 349: while (tw >= d) {
1.1 canacar 350: pos++;
1.7 canacar 351: tw -= d;
1.1 canacar 352: }
353:
354: len = snprintf(buf, sizeof(buf), "%d\\", val);
1.21 deraadt 355: if (len >= sizeof(buf))
356: len = strlen(buf);
1.1 canacar 357: while (cur < pos - len) {
358: tbprintf(" ");
359: cur++;
360: }
361: tbprintf("%s", buf);
362: cur += len;
363: }
364:
365: print_fld_tb(fld);
366: }
367:
368: void
369: print_fld_bar(field_def *fld, int value)
370: {
1.19 benno 371: int i, tw, val;
1.1 canacar 372:
373: if (fld->width < 1)
374: return;
375:
376: val = 0;
377: tw = fld->arg / 2;
378:
379: tb_start();
1.19 benno 380:
1.1 canacar 381: for(i = 0; i < fld->width; i++) {
382: tw += fld->arg;
383:
384: while (tw >= fld->width) {
385: val++;
386: tw -= fld->width;
387: }
388: if (val > value)
389: break;
390: tbprintf("#");
391: }
392:
393: print_fld_tb(fld);
394: }
395:
396: void
397: print_fld_tb(field_def *fld)
398: {
399: print_fld_str(fld, tmp_buf);
400: tb_end();
401: }
402:
403: void
404: print_title(void)
405: {
406: field_def **fp;
407:
408: if (curr_view != NULL && curr_view->view != NULL) {
409: for (fp = curr_view->view; *fp != NULL; fp++) {
410: switch((*fp)->align) {
411: case FLD_ALIGN_LEFT:
412: case FLD_ALIGN_RIGHT:
413: case FLD_ALIGN_CENTER:
414: case FLD_ALIGN_COLUMN:
415: print_fld_str(*fp, (*fp)->title);
416: break;
417: case FLD_ALIGN_BAR:
418: print_bar_title(*fp);
419: break;
420: }
421: }
422: }
423: end_line();
424: }
425:
426: /* view related functions */
427: void
428: hide_field(field_def *fld)
429: {
430: if (fld == NULL)
431: return;
432:
433: fld->flags |= FLD_FLAG_HIDDEN;
434: }
435:
436: void
437: show_field(field_def *fld)
438: {
439: if (fld == NULL)
440: return;
441:
442: fld->flags &= ~((unsigned int) FLD_FLAG_HIDDEN);
443: }
444:
445: void
446: reset_fields(void)
447: {
448: field_def **fp;
449: field_def *fld;
450:
451: if (curr_view == NULL)
452: return;
453:
454: if (curr_view->view == NULL)
455: return;
456:
457: for (fp = curr_view->view; *fp != NULL; fp++) {
458: fld = *fp;
459: fld->start = -1;
460: fld->width = fld->norm_width;
461: }
462: }
463:
464: void
465: field_setup(void)
466: {
467: field_def **fp;
468: field_def *fld;
469: int st, fwid, change;
470: int width = columns;
471:
472: reset_fields();
473:
474: dispstart = 0;
475: st = 0;
476:
477: for (fp = curr_view->view; *fp != NULL; fp++) {
478: fld = *fp;
479: if (fld->flags & FLD_FLAG_HIDDEN)
480: continue;
481:
482: if (width <= 1)
483: break;
484:
485: if (st != 1)
486: width--;
487:
488: fld->start = 1;
489: fwid = fld->width;
490: st++;
491: if (fwid >= width) {
492: fld->width = width;
493: width = 0;
494: } else
495: width -= fwid;
496: }
497:
498: while (width > 0) {
499: change = 0;
500: for (fp = curr_view->view; *fp != NULL; fp++) {
501: fld = *fp;
502: if (fld->flags & FLD_FLAG_HIDDEN)
503: continue;
504: if ((fld->width < fld->max_width) &&
505: (fld->increment <= width)) {
506: int w = fld->width + fld->increment;
507: if (w > fld->max_width)
508: w = fld->max_width;
509: width += fld->width - w;
510: fld->width = w;
511: change = 1;
512: }
513: if (width <= 0) break;
514: }
515: if (change == 0) break;
516: }
517:
518: st = 0;
519: for (fp = curr_view->view; *fp != NULL; fp++) {
520: fld = *fp;
521: if (fld->flags & FLD_FLAG_HIDDEN)
522: continue;
523: if (fld->start < 0) break;
524: fld->start = st;
525: st += fld->width + 1;
526: }
527: }
528:
529: void
530: set_curr_view(struct view_ent *ve)
531: {
532: field_view *v;
533:
534: reset_fields();
535:
536: if (ve == NULL) {
537: curr_view_ent = NULL;
538: curr_view = NULL;
539: curr_mgr = NULL;
540: return;
541: }
542:
543: v = ve->view;
1.26 jasper 544:
1.1 canacar 545: if ((curr_view != NULL) && (curr_mgr != v->mgr)) {
546: gotsig_alarm = 1;
547: if (v->mgr != NULL && v->mgr->select_fn != NULL)
548: v->mgr->select_fn();
549: }
550:
551: curr_view_ent = ve;
552: curr_view = v;
553: curr_mgr = v->mgr;
554: field_setup();
555: need_update = 1;
556: }
557:
558: void
559: add_view(field_view *fv)
560: {
561: struct view_ent *ent;
562:
563: if (fv == NULL)
564: return;
565:
566: if (fv->view == NULL || fv->name == NULL || fv->mgr == NULL)
567: return;
568:
569: ent = malloc(sizeof(struct view_ent));
570: if (ent == NULL)
571: return;
572:
573: ent->view = fv;
1.17 krw 574: TAILQ_INSERT_TAIL(&view_head, ent, entries);
1.1 canacar 575:
576: if (curr_view == NULL)
577: set_curr_view(ent);
578: }
579:
580: int
1.5 canacar 581: set_view(const char *opt)
1.1 canacar 582: {
583: struct view_ent *ve, *vm = NULL;
584: field_view *v;
585: int len;
586:
587: if (opt == NULL || (len = strlen(opt)) == 0)
588: return 1;
589:
1.17 krw 590: TAILQ_FOREACH(ve, &view_head, entries) {
1.1 canacar 591: v = ve->view;
592: if (strncasecmp(opt, v->name, len) == 0) {
593: if (vm)
594: return 1;
595: vm = ve;
596: }
597: }
598:
599: if (vm) {
600: set_curr_view(vm);
601: return 0;
602: }
603:
604: return 1;
605: }
606:
607: void
608: foreach_view(void (*callback)(field_view *))
609: {
610: struct view_ent *ve;
611:
1.17 krw 612: TAILQ_FOREACH(ve, &view_head, entries) {
1.1 canacar 613: callback(ve->view);
614: }
615: }
616:
617: int
618: set_view_hotkey(int ch)
619: {
620: struct view_ent *ve;
621: field_view *v;
622: int key = tolower(ch);
623:
1.17 krw 624: TAILQ_FOREACH(ve, &view_head, entries) {
1.1 canacar 625: v = ve->view;
626: if (key == v->hotkey) {
627: set_curr_view(ve);
628: return 1;
629: }
630: }
631:
632: return 0;
633: }
634:
635: void
636: next_view(void)
637: {
638: struct view_ent *ve;
639:
1.17 krw 640: if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
1.1 canacar 641: return;
642:
1.17 krw 643: ve = TAILQ_NEXT(curr_view_ent, entries);
644: if (ve == NULL)
645: ve = TAILQ_FIRST(&view_head);
1.1 canacar 646:
647: set_curr_view(ve);
648: }
649:
650: void
651: prev_view(void)
652: {
653: struct view_ent *ve;
654:
1.17 krw 655: if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
1.1 canacar 656: return;
657:
1.17 krw 658: ve = TAILQ_PREV(curr_view_ent, view_list, entries);
659: if (ve == NULL)
660: ve = TAILQ_LAST(&view_head, view_list);
1.1 canacar 661:
662: set_curr_view(ve);
663: }
664:
665: /* generic field printing */
666:
667: void
668: print_fld_age(field_def *fld, unsigned int age)
669: {
670: int len;
671: unsigned int h, m, s;
672:
673: if (fld == NULL)
674: return;
675: len = fld->width;
676:
677: if (len < 1)
678: return;
679:
680: s = age % 60;
681: m = age / 60;
682: h = m / 60;
683: m %= 60;
684:
685: tb_start();
686: if (tbprintf("%02u:%02u:%02u", h, m, s) <= len)
687: goto ok;
1.26 jasper 688:
1.3 canacar 689: tb_start();
1.1 canacar 690: if (tbprintf("%u", age) <= len)
691: goto ok;
692:
1.3 canacar 693: tb_start();
1.1 canacar 694: age /= 60;
695: if (tbprintf("%um", age) <= len)
696: goto ok;
697: if (age == 0)
698: goto err;
1.26 jasper 699:
1.3 canacar 700: tb_start();
1.1 canacar 701: age /= 60;
702: if (tbprintf("%uh", age) <= len)
703: goto ok;
704: if (age == 0)
705: goto err;
1.26 jasper 706:
1.3 canacar 707: tb_start();
1.1 canacar 708: age /= 24;
709: if (tbprintf("%ud", age) <= len)
710: goto ok;
1.26 jasper 711:
1.9 jasper 712: err:
1.1 canacar 713: print_fld_str(fld, "*");
714: tb_end();
715: return;
1.26 jasper 716:
1.9 jasper 717: ok:
1.1 canacar 718: print_fld_tb(fld);
719: }
720:
721: void
1.7 canacar 722: print_fld_sdiv(field_def *fld, u_int64_t size, int d)
1.1 canacar 723: {
724: int len;
1.25 martijn 725: char *mult = "KMGTPE";
726: int i = -1;
1.1 canacar 727:
728: if (fld == NULL)
729: return;
730:
731: len = fld->width;
732: if (len < 1)
733: return;
734:
1.25 martijn 735: if (humanreadable) {
736: while (size >= 10000 && sizeof(mult) >= i + 1) {
737: i++;
738: size /= d;
739: }
740: tb_start();
741: if (tbprintft("%llu%.1s", size, i == -1 ? "" : mult + i) <= len)
742: goto ok;
1.1 canacar 743: goto err;
1.25 martijn 744: }
745: do {
746: tb_start();
747: if (tbprintft("%llu%.1s", size, i == -1 ? "" : mult + i) <= len)
748: goto ok;
749: i++;
750: size /= d;
751: } while (size != 0 && sizeof(mult) >= i);
752: err:
1.3 canacar 753: tb_start();
1.1 canacar 754: print_fld_str(fld, "*");
755: tb_end();
756: return;
757:
758: ok:
759: print_fld_tb(fld);
760: }
761:
762: void
763: print_fld_size(field_def *fld, u_int64_t size)
764: {
765: print_fld_sdiv(fld, size, 1024);
1.4 canacar 766: }
767:
768: void
1.7 canacar 769: print_fld_ssdiv(field_def *fld, int64_t size, int d)
1.4 canacar 770: {
771: int len;
772:
773: if (fld == NULL)
774: return;
775:
776: len = fld->width;
777: if (len < 1)
778: return;
779:
780: tb_start();
1.14 mpf 781: if (tbprintft("%lld", size) <= len)
1.4 canacar 782: goto ok;
783:
784: tb_start();
1.7 canacar 785: size /= d;
1.14 mpf 786: if (tbprintft("%lldK", size) <= len)
1.4 canacar 787: goto ok;
788: if (size == 0)
789: goto err;
790:
791: tb_start();
1.7 canacar 792: size /= d;
1.14 mpf 793: if (tbprintft("%lldM", size) <= len)
1.4 canacar 794: goto ok;
795: if (size == 0)
796: goto err;
797:
798: tb_start();
1.7 canacar 799: size /= d;
1.14 mpf 800: if (tbprintft("%lldG", size) <= len)
1.4 canacar 801: goto ok;
802: if (size == 0)
803: goto err;
804:
805: tb_start();
1.7 canacar 806: size /= d;
1.14 mpf 807: if (tbprintft("%lldT", size) <= len)
1.4 canacar 808: goto ok;
809:
810: err:
811: print_fld_str(fld, "*");
812: tb_end();
813: return;
814:
815: ok:
816: print_fld_tb(fld);
817: }
818:
819: void
820: print_fld_ssize(field_def *fld, int64_t size)
821: {
822: print_fld_ssdiv(fld, size, 1024);
1.1 canacar 823: }
824:
825: void
826: print_fld_rate(field_def *fld, double rate)
827: {
828: if (rate < 0) {
829: print_fld_str(fld, "*");
830: } else {
831: print_fld_size(fld, rate);
832: }
833: }
834:
835: void
836: print_fld_bw(field_def *fld, double bw)
837: {
838: if (bw < 0) {
839: print_fld_str(fld, "*");
840: } else {
841: print_fld_sdiv(fld, bw, 1000);
842: }
843: }
844:
845: void
846: print_fld_uint(field_def *fld, unsigned int size)
847: {
848: int len;
849:
850: if (fld == NULL)
851: return;
852:
853: len = fld->width;
854: if (len < 1)
855: return;
856:
857: tb_start();
1.14 mpf 858: if (tbprintft("%u", size) > len)
1.6 naddy 859: print_fld_str(fld, "*");
860: else
861: print_fld_tb(fld);
862: tb_end();
863: }
864:
865: void
866: print_fld_float(field_def *fld, double f, int prec)
867: {
868: int len;
869:
870: if (fld == NULL)
871: return;
872:
873: len = fld->width;
874: if (len < 1)
875: return;
876:
877: tb_start();
878: if (tbprintf("%*.*f", len, prec, f) > len)
1.1 canacar 879: print_fld_str(fld, "*");
880: else
881: print_fld_tb(fld);
882: tb_end();
883: }
884:
885:
886: /* ordering */
1.22 martijn 887:
888: int
889: foreach_order(void (*callback)(order_type *))
890: {
891: order_type *o;
892:
893: if (curr_view == NULL || curr_view->mgr == NULL ||
894: curr_view->mgr->order_list == NULL)
895: return -1;
896: o = curr_view->mgr->order_list;
897: do {
898: callback(o++);
899: } while (o->name != NULL);
900: return 0;
901: }
1.1 canacar 902:
903: void
1.5 canacar 904: set_order(const char *opt)
1.1 canacar 905: {
906: order_type *o;
907:
908: if (curr_view == NULL || curr_view->mgr == NULL)
909: return;
910:
911: curr_view->mgr->order_curr = curr_view->mgr->order_list;
912:
913: if (opt == NULL)
914: return;
915:
916: o = curr_view->mgr->order_list;
917:
918: if (o == NULL)
919: return;
920:
921: for (;o->name != NULL; o++) {
922: if (strcasecmp(opt, o->match) == 0) {
923: curr_view->mgr->order_curr = o;
924: return;
925: }
926: }
927: }
928:
929: int
930: set_order_hotkey(int ch)
931: {
932: order_type *o;
933: int key = ch;
934:
935: if (curr_view == NULL || curr_view->mgr == NULL)
936: return 0;
937:
938: o = curr_view->mgr->order_list;
939:
940: if (o == NULL)
941: return 0;
942:
943: for (;o->name != NULL; o++) {
944: if (key == o->hotkey) {
945: if (curr_view->mgr->order_curr == o) {
946: sortdir *= -1;
947: } else {
948: curr_view->mgr->order_curr = o;
949: }
950: return 1;
951: }
952: }
953:
954: return 0;
955: }
956:
957: void
958: next_order(void)
959: {
960: order_type *o, *oc;
1.2 canacar 961:
962: if (curr_view->mgr->order_list == NULL)
963: return;
1.1 canacar 964:
965: oc = curr_view->mgr->order_curr;
966:
967: for (o = curr_view->mgr->order_list; o->name != NULL; o++) {
968: if (oc == o) {
969: o++;
970: if (o->name == NULL)
971: break;
972: curr_view->mgr->order_curr = o;
973: return;
974: }
975: }
976:
977: curr_view->mgr->order_curr = curr_view->mgr->order_list;
978: }
979:
980:
981: /* main program functions */
982:
983: int
984: read_view(void)
985: {
986: if (curr_mgr == NULL)
987: return (0);
988:
989: if (paused)
990: return (0);
991:
992: if (curr_mgr->read_fn != NULL)
993: return (curr_mgr->read_fn());
994:
995: return (0);
996: }
997:
998:
999: int
1000: disp_update(void)
1001: {
1.7 canacar 1002: int li;
1.1 canacar 1003:
1004: if (maxprint < 0)
1005: dispstart = 0;
1006: else if (dispstart + maxprint > num_disp)
1007: dispstart = num_disp - maxprint;
1.26 jasper 1008:
1.1 canacar 1009: if (dispstart < 0)
1010: dispstart = 0;
1011:
1012: if (curr_view == NULL)
1013: return 0;
1014:
1015: if (curr_mgr != NULL) {
1016: curr_line = 0;
1017:
1018: if (curr_mgr->header_fn != NULL) {
1.7 canacar 1019: li = curr_mgr->header_fn();
1020: if (li < 0)
1.1 canacar 1021: return (1);
1.7 canacar 1022: curr_line = ++li;
1023: home_line = li + maxprint + 1;
1.1 canacar 1024: }
1025:
1026: print_title();
1027:
1028: if (curr_mgr->print_fn != NULL)
1029: curr_mgr->print_fn();
1030: }
1031:
1032: return (0);
1033: }
1034:
1035: void
1036: sort_view(void)
1037: {
1038: if (curr_mgr != NULL)
1039: if (curr_mgr->sort_fn != NULL)
1040: curr_mgr->sort_fn();
1041: }
1042:
1043: void
1.7 canacar 1044: sig_close(int sig)
1.1 canacar 1045: {
1046: gotsig_close = 1;
1047: }
1048:
1049: void
1.7 canacar 1050: sig_resize(int sig)
1.1 canacar 1051: {
1052: gotsig_resize = 1;
1053: }
1054:
1055: void
1.7 canacar 1056: sig_alarm(int sig)
1.1 canacar 1057: {
1058: gotsig_alarm = 1;
1059: }
1060:
1061: void
1062: setup_term(int dmax)
1063: {
1064: max_disp = dmax;
1065: maxprint = dmax;
1066:
1067: if (rawmode) {
1068: columns = rawwidth;
1069: lines = DEFAULT_HEIGHT;
1070: clear_linebuf();
1071: } else {
1072: if (dmax < 0)
1073: dmax = 0;
1074:
1075: screen = newterm(NULL, stdout, stdin);
1076: if (screen == NULL) {
1077: rawmode = 1;
1078: interactive = 0;
1079: setup_term(dmax);
1080: return;
1081: }
1082: columns = COLS;
1083: lines = LINES;
1084:
1085: if (maxprint > lines - HEADER_LINES)
1086: maxprint = lines - HEADER_LINES;
1087:
1088: nonl();
1089: keypad(stdscr, TRUE);
1090: intrflush(stdscr, FALSE);
1091:
1092: halfdelay(10);
1093: noecho();
1094: }
1095:
1096: if (dmax == 0)
1097: maxprint = lines - HEADER_LINES;
1098:
1099: field_setup();
1100: }
1101:
1.8 canacar 1102: void
1.11 nicm 1103: do_resize_term(void)
1.8 canacar 1104: {
1105: struct winsize ws;
1106:
1107: if (rawmode)
1108: return;
1109:
1110: if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
1111: return;
1112:
1113: resizeterm(ws.ws_row, ws.ws_col);
1114:
1115: columns = COLS;
1116: lines = LINES;
1117:
1118: maxprint = max_disp;
1119:
1120: if (maxprint == 0 || maxprint > lines - HEADER_LINES)
1121: maxprint = lines - HEADER_LINES;
1122:
1123: clear();
1124:
1125: field_setup();
1126: }
1127:
1.1 canacar 1128: struct command *
1129: command_set(struct command *cmd, const char *init)
1130: {
1131: struct command *prev = curr_cmd;
1132:
1133: if (cmd) {
1134: if (init) {
1135: cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf));
1136: if (cmd_len >= sizeof(cmdbuf)) {
1137: cmdbuf[0] = '\0';
1138: cmd_len = 0;
1139: }
1140: } else {
1141: cmd_len = 0;
1142: cmdbuf[0] = 0;
1143: }
1144: }
1.27 tb 1145: message_set(NULL);
1.1 canacar 1146: curr_cmd = cmd;
1147: need_update = 1;
1148: return prev;
1149: }
1150:
1.28 martijn 1151: void
1152: message_toggle(enum message_mode mode)
1153: {
1154: message_mode = message_mode != mode ? mode : MESSAGE_NONE;
1155: need_update = 1;
1156: message_cont = 1;
1157: }
1158:
1.1 canacar 1159: const char *
1.28 martijn 1160: message_set(const char *msg)
1161: {
1162: free(curr_message);
1163:
1164: if (msg) {
1.1 canacar 1165: curr_message = strdup(msg);
1.28 martijn 1166: message_cont = 0;
1167: } else {
1.1 canacar 1168: curr_message = NULL;
1.28 martijn 1169: message_cont = 1;
1170: }
1.1 canacar 1171: return NULL;
1172: }
1173:
1174: void
1175: print_cmdline(void)
1176: {
1177: if (curr_cmd) {
1178: attron(A_STANDOUT);
1179: mvprintw(home_line, 0, "%s: ", curr_cmd->prompt);
1180: attroff(A_STANDOUT);
1181: printw("%s", cmdbuf);
1182: } else if (curr_message) {
1183: mvprintw(home_line, 0, "> %s", curr_message);
1184: }
1185: clrtoeol();
1186: }
1187:
1188:
1189: void
1190: cmd_keyboard(int ch)
1191: {
1192: if (curr_cmd == NULL)
1193: return;
1194:
1195: if (ch > 0 && isprint(ch)) {
1196: if (cmd_len < sizeof(cmdbuf) - 1) {
1197: cmdbuf[cmd_len++] = ch;
1198: cmdbuf[cmd_len] = 0;
1199: } else
1200: beep();
1201: }
1.26 jasper 1202:
1.1 canacar 1203: switch (ch) {
1204: case KEY_ENTER:
1205: case 0x0a:
1206: case 0x0d:
1207: {
1208: struct command * c = command_set(NULL, NULL);
1.5 canacar 1209: c->exec(cmdbuf);
1.1 canacar 1210: break;
1211: }
1212: case KEY_BACKSPACE:
1213: case KEY_DC:
1214: case CTRL_H:
1215: if (cmd_len > 0) {
1216: cmdbuf[--cmd_len] = 0;
1217: } else
1218: beep();
1219: break;
1220: case 0x1b:
1221: case CTRL_G:
1222: if (cmd_len > 0) {
1223: cmdbuf[0] = '\0';
1224: cmd_len = 0;
1225: } else
1226: command_set(NULL, NULL);
1227: break;
1228: default:
1229: break;
1230: }
1231: }
1232:
1233: void
1234: keyboard(void)
1235: {
1236: int ch;
1237:
1238: ch = getch();
1239:
1240: if (curr_cmd) {
1241: cmd_keyboard(ch);
1242: print_cmdline();
1243: return;
1244: }
1245:
1246: if (curr_mgr != NULL)
1247: if (curr_mgr->key_fn != NULL)
1248: if (curr_mgr->key_fn(ch))
1249: return;
1250:
1251: if (curr_message != NULL) {
1252: if (ch > 0) {
1.27 tb 1253: message_set(NULL);
1.1 canacar 1254: need_update = 1;
1255: }
1256: }
1257:
1258: switch (ch) {
1259: case ' ':
1260: gotsig_alarm = 1;
1261: break;
1262: case 'o':
1263: next_order();
1264: need_sort = 1;
1265: break;
1266: case 'p':
1267: paused = !paused;
1268: gotsig_alarm = 1;
1269: break;
1270: case 'q':
1271: gotsig_close = 1;
1272: break;
1273: case 'r':
1274: sortdir *= -1;
1275: need_sort = 1;
1276: break;
1277: case 'v':
1278: /* FALLTHROUGH */
1279: case KEY_RIGHT:
1280: /* FALLTHROUGH */
1281: case CTRL_F:
1282: next_view();
1283: break;
1284: case KEY_LEFT:
1285: /* FALLTHROUGH */
1286: case CTRL_B:
1287: prev_view();
1288: break;
1289: case KEY_DOWN:
1290: /* FALLTHROUGH */
1291: case CTRL_N:
1292: dispstart++;
1293: need_update = 1;
1294: break;
1295: case KEY_UP:
1296: /* FALLTHROUGH */
1297: case CTRL_P:
1298: dispstart--;
1299: need_update = 1;
1300: break;
1301: case KEY_NPAGE:
1302: /* FALLTHROUGH */
1303: case CTRL_V:
1304: dispstart += maxprint;
1305: need_update = 1;
1306: break;
1307: case KEY_PPAGE:
1308: /* FALLTHROUGH */
1309: case META_V:
1310: dispstart -= maxprint;
1311: need_update = 1;
1312: break;
1313: case KEY_HOME:
1314: /* FALLTHROUGH */
1315: case CTRL_A:
1316: dispstart = 0;
1317: need_update = 1;
1318: break;
1319: case KEY_END:
1320: /* FALLTHROUGH */
1321: case CTRL_E:
1322: dispstart = num_disp;
1323: need_update = 1;
1324: break;
1325: case CTRL_L:
1326: clear();
1327: need_update = 1;
1328: break;
1329: default:
1330: break;
1331: }
1332:
1333: if (set_order_hotkey(ch))
1334: need_sort = 1;
1335: else
1336: set_view_hotkey(ch);
1337: }
1338:
1339: void
1340: engine_initialize(void)
1341: {
1342: signal(SIGTERM, sig_close);
1343: signal(SIGINT, sig_close);
1344: signal(SIGQUIT, sig_close);
1345: signal(SIGWINCH, sig_resize);
1346: signal(SIGALRM, sig_alarm);
1347: }
1348:
1349: void
1350: engine_loop(int countmax)
1351: {
1352: int count = 0;
1353:
1354: for (;;) {
1355: if (gotsig_alarm) {
1356: read_view();
1357: need_sort = 1;
1358: gotsig_alarm = 0;
1.29 millert 1359: setitimer(ITIMER_REAL, &it_delay, NULL);
1.1 canacar 1360: }
1361:
1362: if (need_sort) {
1363: sort_view();
1364: need_sort = 0;
1365: need_update = 1;
1.26 jasper 1366:
1.1 canacar 1367: /* XXX if sort took too long */
1368: if (gotsig_alarm) {
1369: gotsig_alarm = 0;
1.29 millert 1370: setitimer(ITIMER_REAL, &it_delay, NULL);
1.1 canacar 1371: }
1372: }
1373:
1374: if (need_update) {
1375: erase();
1.15 reyk 1376: if (!averageonly ||
1377: (averageonly && count == countmax - 1))
1378: disp_update();
1.28 martijn 1379: if (message_cont) {
1380: switch (message_mode) {
1381: case MESSAGE_NONE:
1382: message_set(NULL);
1383: break;
1384: case MESSAGE_HELP:
1385: show_help();
1386: break;
1387: case MESSAGE_VIEW:
1388: show_view();
1389: break;
1390: case MESSAGE_ORDER:
1391: show_order();
1392: break;
1393: }
1394: }
1.1 canacar 1395: end_page();
1396: need_update = 0;
1397: if (countmax && ++count >= countmax)
1398: break;
1399: }
1400:
1401: if (gotsig_close)
1402: break;
1403: if (gotsig_resize) {
1.11 nicm 1404: do_resize_term();
1.1 canacar 1405: gotsig_resize = 0;
1406: need_update = 1;
1407: }
1408:
1409: if (interactive && need_update == 0)
1410: keyboard();
1411: else if (interactive == 0)
1.29 millert 1412: nanosleep(&ts_delay, NULL);
1.1 canacar 1413: }
1414:
1415: if (rawmode == 0)
1416: endwin();
1.12 lum 1417: }
1418:
1419: int
1420: check_termcap(void)
1421: {
1422: char *term_name;
1423: int status;
1424: static struct termios screen_settings;
1425:
1426: if (!interactive)
1427: /* pretend we have a dumb terminal */
1428: return(1);
1429:
1430: /* get the terminal name */
1431: term_name = getenv("TERM");
1432: if (term_name == NULL)
1433: return(1);
1434:
1435: /* now get the termcap entry */
1436: if ((status = tgetent(NULL, term_name)) != 1) {
1437: if (status == -1)
1438: warnx("can't open termcap file");
1439: else
1.26 jasper 1440: warnx("no termcap entry for a `%s' terminal",
1.12 lum 1441: term_name);
1442:
1443: /* pretend it's dumb and proceed */
1444: return(1);
1445: }
1446:
1447: /* "hardcopy" immediately indicates a very stupid terminal */
1448: if (tgetflag("hc"))
1449: return(1);
1450:
1451: /* get necessary capabilities */
1452: if (tgetstr("cl", NULL) == NULL || tgetstr("cm", NULL) == NULL)
1453: return(1);
1454:
1455: /* if stdout is not a terminal, pretend we are a dumb terminal */
1456: if (tcgetattr(STDOUT_FILENO, &screen_settings) == -1)
1457: return(1);
1458:
1459: return(0);
1.29 millert 1460: }
1461:
1462: void
1463: refresh_delay(double delay)
1464: {
1465: double secs, frac;
1466:
1467: frac = modf(delay, &secs);
1468: ts_delay.tv_sec = secs;
1469: ts_delay.tv_nsec = frac * 1000000000.0;
1470: if (!timespecisset(&ts_delay))
1471: ts_delay.tv_nsec = 1000000000;
1472: TIMESPEC_TO_TIMEVAL(&it_delay.it_value, &ts_delay);
1.1 canacar 1473: }