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