1 /* $OpenBSD: engine.c,v 1.30 2023/10/10 09:30:06 tb 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 */
47 TAILQ_HEAD(view_list, view_ent) view_head =
48 TAILQ_HEAD_INITIALIZER(view_head);
49 struct view_ent {
50 field_view *view;
51 TAILQ_ENTRY(view_ent) entries;
52 };
53
54 static struct timespec ts_delay = { 5, 0 };
55 static struct itimerval it_delay = { { 0, 0 }, { 5, 0 } };
56
57 int dispstart = 0;
58 int humanreadable = 0;
59 int interactive = 1;
60 int averageonly = 0;
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;
75 int separate_thousands = 0;
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;
97 enum message_mode message_mode = MESSAGE_NONE;
98 int message_cont = 1;
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
tb_start(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
tb_end(void)117 tb_end(void)
118 {
119 tb_ptr = NULL;
120 tb_len = 0;
121 }
122
123 int
tbprintf(char * format,...)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);
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
146 int
tbprintft(char * format,...)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++)
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
198 void
move_horiz(int offset)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
print_str(int len,const char * str)214 print_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
229 void
clear_linebuf(void)230 clear_linebuf(void)
231 {
232 memset(linebuf, ' ', MAX_LINE_BUF);
233 }
234
235 void
end_line(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
end_page(void)247 end_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
263 void
print_fld_str(field_def * fld,const char * str)264 print_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
308 void
print_bar_title(field_def * fld)309 print_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
368 void
print_fld_bar(field_def * fld,int value)369 print_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
396 void
print_fld_tb(field_def * fld)397 print_fld_tb(field_def *fld)
398 {
399 print_fld_str(fld, tmp_buf);
400 tb_end();
401 }
402
403 void
print_title(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
hide_field(field_def * fld)428 hide_field(field_def *fld)
429 {
430 if (fld == NULL)
431 return;
432
433 fld->flags |= FLD_FLAG_HIDDEN;
434 }
435
436 void
show_field(field_def * fld)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
reset_fields(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
field_setup(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
set_curr_view(struct view_ent * ve)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;
544
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
add_view(field_view * fv)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;
574 TAILQ_INSERT_TAIL(&view_head, ent, entries);
575
576 if (curr_view == NULL)
577 set_curr_view(ent);
578 }
579
580 int
set_view(const char * opt)581 set_view(const char *opt)
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
590 TAILQ_FOREACH(ve, &view_head, entries) {
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
foreach_view(void (* callback)(field_view *))608 foreach_view(void (*callback)(field_view *))
609 {
610 struct view_ent *ve;
611
612 TAILQ_FOREACH(ve, &view_head, entries) {
613 callback(ve->view);
614 }
615 }
616
617 int
set_view_hotkey(int ch)618 set_view_hotkey(int ch)
619 {
620 struct view_ent *ve;
621 field_view *v;
622 int key = tolower(ch);
623
624 TAILQ_FOREACH(ve, &view_head, entries) {
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
next_view(void)636 next_view(void)
637 {
638 struct view_ent *ve;
639
640 if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
641 return;
642
643 ve = TAILQ_NEXT(curr_view_ent, entries);
644 if (ve == NULL)
645 ve = TAILQ_FIRST(&view_head);
646
647 set_curr_view(ve);
648 }
649
650 void
prev_view(void)651 prev_view(void)
652 {
653 struct view_ent *ve;
654
655 if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
656 return;
657
658 ve = TAILQ_PREV(curr_view_ent, view_list, entries);
659 if (ve == NULL)
660 ve = TAILQ_LAST(&view_head, view_list);
661
662 set_curr_view(ve);
663 }
664
665 /* generic field printing */
666
667 void
print_fld_age(field_def * fld,unsigned int age)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;
688
689 tb_start();
690 if (tbprintf("%u", age) <= len)
691 goto ok;
692
693 tb_start();
694 age /= 60;
695 if (tbprintf("%um", age) <= len)
696 goto ok;
697 if (age == 0)
698 goto err;
699
700 tb_start();
701 age /= 60;
702 if (tbprintf("%uh", age) <= len)
703 goto ok;
704 if (age == 0)
705 goto err;
706
707 tb_start();
708 age /= 24;
709 if (tbprintf("%ud", age) <= len)
710 goto ok;
711
712 err:
713 print_fld_str(fld, "*");
714 tb_end();
715 return;
716
717 ok:
718 print_fld_tb(fld);
719 }
720
721 void
print_fld_sdiv(field_def * fld,u_int64_t size,int d)722 print_fld_sdiv(field_def *fld, u_int64_t size, int d)
723 {
724 int len;
725 char *mult = "KMGTPE";
726 int i = -1;
727
728 if (fld == NULL)
729 return;
730
731 len = fld->width;
732 if (len < 1)
733 return;
734
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;
743 goto err;
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:
753 tb_start();
754 print_fld_str(fld, "*");
755 tb_end();
756 return;
757
758 ok:
759 print_fld_tb(fld);
760 }
761
762 void
print_fld_size(field_def * fld,u_int64_t size)763 print_fld_size(field_def *fld, u_int64_t size)
764 {
765 print_fld_sdiv(fld, size, 1024);
766 }
767
768 void
print_fld_ssdiv(field_def * fld,int64_t size,int d)769 print_fld_ssdiv(field_def *fld, int64_t size, int d)
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();
781 if (tbprintft("%lld", size) <= len)
782 goto ok;
783
784 tb_start();
785 size /= d;
786 if (tbprintft("%lldK", size) <= len)
787 goto ok;
788 if (size == 0)
789 goto err;
790
791 tb_start();
792 size /= d;
793 if (tbprintft("%lldM", size) <= len)
794 goto ok;
795 if (size == 0)
796 goto err;
797
798 tb_start();
799 size /= d;
800 if (tbprintft("%lldG", size) <= len)
801 goto ok;
802 if (size == 0)
803 goto err;
804
805 tb_start();
806 size /= d;
807 if (tbprintft("%lldT", size) <= len)
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
print_fld_ssize(field_def * fld,int64_t size)820 print_fld_ssize(field_def *fld, int64_t size)
821 {
822 print_fld_ssdiv(fld, size, 1024);
823 }
824
825 void
print_fld_rate(field_def * fld,double rate)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
print_fld_bw(field_def * fld,double bw)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
print_fld_uint(field_def * fld,unsigned int size)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();
858 if (tbprintft("%u", size) > len)
859 print_fld_str(fld, "*");
860 else
861 print_fld_tb(fld);
862 tb_end();
863 }
864
865 void
print_fld_float(field_def * fld,double f,int prec)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)
879 print_fld_str(fld, "*");
880 else
881 print_fld_tb(fld);
882 tb_end();
883 }
884
885
886 /* ordering */
887
888 int
foreach_order(void (* callback)(order_type *))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 }
902
903 void
set_order(const char * opt)904 set_order(const char *opt)
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
set_order_hotkey(int ch)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
next_order(void)958 next_order(void)
959 {
960 order_type *o, *oc;
961
962 if (curr_view->mgr->order_list == NULL)
963 return;
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
read_view(void)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
disp_update(void)1000 disp_update(void)
1001 {
1002 int li;
1003
1004 if (maxprint < 0)
1005 dispstart = 0;
1006 else if (dispstart + maxprint > num_disp)
1007 dispstart = num_disp - maxprint;
1008
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) {
1019 li = curr_mgr->header_fn();
1020 if (li < 0)
1021 return (1);
1022 curr_line = ++li;
1023 home_line = li + maxprint + 1;
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
sort_view(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
sig_close(int sig)1044 sig_close(int sig)
1045 {
1046 gotsig_close = 1;
1047 }
1048
1049 void
sig_resize(int sig)1050 sig_resize(int sig)
1051 {
1052 gotsig_resize = 1;
1053 }
1054
1055 void
sig_alarm(int sig)1056 sig_alarm(int sig)
1057 {
1058 gotsig_alarm = 1;
1059 }
1060
1061 void
setup_term(int dmax)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
1102 void
do_resize_term(void)1103 do_resize_term(void)
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
1128 struct command *
command_set(struct command * cmd,const char * init)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 }
1145 message_set(NULL);
1146 curr_cmd = cmd;
1147 need_update = 1;
1148 return prev;
1149 }
1150
1151 void
message_toggle(enum message_mode mode)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
1159 const char *
message_set(const char * msg)1160 message_set(const char *msg)
1161 {
1162 free(curr_message);
1163
1164 if (msg) {
1165 curr_message = strdup(msg);
1166 message_cont = 0;
1167 } else {
1168 curr_message = NULL;
1169 message_cont = 1;
1170 }
1171 return NULL;
1172 }
1173
1174 void
print_cmdline(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
cmd_keyboard(int ch)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 }
1202
1203 switch (ch) {
1204 case KEY_ENTER:
1205 case 0x0a:
1206 case 0x0d:
1207 {
1208 struct command * c = command_set(NULL, NULL);
1209 c->exec(cmdbuf);
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
keyboard(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) {
1253 message_set(NULL);
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
engine_initialize(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
engine_loop(int countmax)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;
1359 setitimer(ITIMER_REAL, &it_delay, NULL);
1360 }
1361
1362 if (need_sort) {
1363 sort_view();
1364 need_sort = 0;
1365 need_update = 1;
1366
1367 /* XXX if sort took too long */
1368 if (gotsig_alarm) {
1369 gotsig_alarm = 0;
1370 setitimer(ITIMER_REAL, &it_delay, NULL);
1371 }
1372 }
1373
1374 if (need_update) {
1375 erase();
1376 if (!averageonly ||
1377 (averageonly && count == countmax - 1))
1378 disp_update();
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 }
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) {
1404 do_resize_term();
1405 gotsig_resize = 0;
1406 need_update = 1;
1407 }
1408
1409 if (interactive && need_update == 0)
1410 keyboard();
1411 else if (interactive == 0)
1412 nanosleep(&ts_delay, NULL);
1413 }
1414
1415 if (rawmode == 0)
1416 endwin();
1417 }
1418
1419 int
check_termcap(void)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
1440 warnx("no termcap entry for a `%s' terminal",
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);
1460 }
1461
1462 void
refresh_delay(double delay)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);
1473 }
1474