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