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