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