xref: /openbsd-src/usr.bin/systat/engine.c (revision c7e8ea31cd41a963f06f0a8ba93948b06aa6b4a4)
1 /* $Id: engine.c,v 1.21 2017/04/05 15:57:11 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 <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 void
894 set_order(const char *opt)
895 {
896 	order_type *o;
897 
898 	if (curr_view == NULL || curr_view->mgr == NULL)
899 		return;
900 
901 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
902 
903 	if (opt == NULL)
904 		return;
905 
906 	o = curr_view->mgr->order_list;
907 
908 	if (o == NULL)
909 		return;
910 
911 	for (;o->name != NULL; o++) {
912 		if (strcasecmp(opt, o->match) == 0) {
913 			curr_view->mgr->order_curr = o;
914 			return;
915 		}
916 	}
917 }
918 
919 int
920 set_order_hotkey(int ch)
921 {
922 	order_type *o;
923 	int key = ch;
924 
925 	if (curr_view == NULL || curr_view->mgr == NULL)
926 		return 0;
927 
928 	o = curr_view->mgr->order_list;
929 
930 	if (o == NULL)
931 		return 0;
932 
933 	for (;o->name != NULL; o++) {
934 		if (key == o->hotkey) {
935 			if (curr_view->mgr->order_curr == o) {
936 				sortdir *= -1;
937 			} else {
938 				curr_view->mgr->order_curr = o;
939 			}
940 			return 1;
941 		}
942 	}
943 
944 	return 0;
945 }
946 
947 void
948 next_order(void)
949 {
950 	order_type *o, *oc;
951 
952 	if (curr_view->mgr->order_list == NULL)
953 		return;
954 
955 	oc = curr_view->mgr->order_curr;
956 
957 	for (o = curr_view->mgr->order_list; o->name != NULL; o++) {
958 		if (oc == o) {
959 			o++;
960 			if (o->name == NULL)
961 				break;
962 			curr_view->mgr->order_curr = o;
963 			return;
964 		}
965 	}
966 
967 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
968 }
969 
970 
971 /* main program functions */
972 
973 int
974 read_view(void)
975 {
976 	if (curr_mgr == NULL)
977 		return (0);
978 
979 	if (paused)
980 		return (0);
981 
982 	if (curr_mgr->read_fn != NULL)
983 		return (curr_mgr->read_fn());
984 
985 	return (0);
986 }
987 
988 
989 int
990 disp_update(void)
991 {
992 	int li;
993 
994 	if (maxprint < 0)
995 		dispstart = 0;
996 	else if (dispstart + maxprint > num_disp)
997 		dispstart = num_disp - maxprint;
998 
999 	if (dispstart < 0)
1000 		dispstart = 0;
1001 
1002 	if (curr_view == NULL)
1003 		return 0;
1004 
1005 	if (curr_mgr != NULL) {
1006 		curr_line = 0;
1007 
1008 		if (curr_mgr->header_fn != NULL) {
1009 			li = curr_mgr->header_fn();
1010 			if (li < 0)
1011 				return (1);
1012 			curr_line = ++li;
1013 			home_line = li + maxprint + 1;
1014 		}
1015 
1016 		print_title();
1017 
1018 		if (curr_mgr->print_fn != NULL)
1019 			curr_mgr->print_fn();
1020 	}
1021 
1022 	return (0);
1023 }
1024 
1025 void
1026 sort_view(void)
1027 {
1028 	if (curr_mgr != NULL)
1029 		if (curr_mgr->sort_fn != NULL)
1030 			curr_mgr->sort_fn();
1031 }
1032 
1033 void
1034 sig_close(int sig)
1035 {
1036 	gotsig_close = 1;
1037 }
1038 
1039 void
1040 sig_resize(int sig)
1041 {
1042 	gotsig_resize = 1;
1043 }
1044 
1045 void
1046 sig_alarm(int sig)
1047 {
1048 	gotsig_alarm = 1;
1049 }
1050 
1051 void
1052 setup_term(int dmax)
1053 {
1054 	max_disp = dmax;
1055 	maxprint = dmax;
1056 
1057 	if (rawmode) {
1058 		columns = rawwidth;
1059 		lines = DEFAULT_HEIGHT;
1060 		clear_linebuf();
1061 	} else {
1062 		if (dmax < 0)
1063 			dmax = 0;
1064 
1065 		screen = newterm(NULL, stdout, stdin);
1066 		if (screen == NULL) {
1067 			rawmode = 1;
1068 			interactive = 0;
1069 			setup_term(dmax);
1070 			return;
1071 		}
1072 		columns = COLS;
1073 		lines = LINES;
1074 
1075 		if (maxprint > lines - HEADER_LINES)
1076 			maxprint = lines - HEADER_LINES;
1077 
1078 		nonl();
1079 		keypad(stdscr, TRUE);
1080 		intrflush(stdscr, FALSE);
1081 
1082 		halfdelay(10);
1083 		noecho();
1084 	}
1085 
1086 	if (dmax == 0)
1087 		maxprint = lines - HEADER_LINES;
1088 
1089 	field_setup();
1090 }
1091 
1092 void
1093 do_resize_term(void)
1094 {
1095 	struct winsize ws;
1096 
1097 	if (rawmode)
1098 		return;
1099 
1100 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
1101 		return;
1102 
1103 	resizeterm(ws.ws_row, ws.ws_col);
1104 
1105 	columns = COLS;
1106 	lines = LINES;
1107 
1108 	maxprint = max_disp;
1109 
1110 	if (maxprint == 0 || maxprint > lines - HEADER_LINES)
1111 		maxprint = lines - HEADER_LINES;
1112 
1113 	clear();
1114 
1115 	field_setup();
1116 }
1117 
1118 struct command *
1119 command_set(struct command *cmd, const char *init)
1120 {
1121 	struct command *prev = curr_cmd;
1122 
1123 	if (cmd) {
1124 		if (init) {
1125 			cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf));
1126 			if (cmd_len >= sizeof(cmdbuf)) {
1127 				cmdbuf[0] = '\0';
1128 				cmd_len = 0;
1129 			}
1130 		} else {
1131 			cmd_len = 0;
1132 			cmdbuf[0] = 0;
1133 		}
1134 	}
1135 	curr_message = NULL;
1136 	curr_cmd = cmd;
1137 	need_update = 1;
1138 	return prev;
1139 }
1140 
1141 const char *
1142 message_set(const char *msg) {
1143 	char *prev = curr_message;
1144 	if (msg)
1145 		curr_message = strdup(msg);
1146 	else
1147 		curr_message = NULL;
1148 	free(prev);
1149 	return NULL;
1150 }
1151 
1152 void
1153 print_cmdline(void)
1154 {
1155 	if (curr_cmd) {
1156 		attron(A_STANDOUT);
1157 		mvprintw(home_line, 0, "%s: ", curr_cmd->prompt);
1158 		attroff(A_STANDOUT);
1159 		printw("%s", cmdbuf);
1160 	} else if (curr_message) {
1161 		mvprintw(home_line, 0, "> %s", curr_message);
1162 	}
1163 	clrtoeol();
1164 }
1165 
1166 
1167 void
1168 cmd_keyboard(int ch)
1169 {
1170 	if (curr_cmd == NULL)
1171 		return;
1172 
1173 	if (ch > 0 && isprint(ch)) {
1174 		if (cmd_len < sizeof(cmdbuf) - 1) {
1175 			cmdbuf[cmd_len++] = ch;
1176 			cmdbuf[cmd_len] = 0;
1177 		} else
1178 			beep();
1179 	}
1180 
1181 	switch (ch) {
1182 	case KEY_ENTER:
1183 	case 0x0a:
1184 	case 0x0d:
1185 	{
1186 		struct command * c = command_set(NULL, NULL);
1187 		c->exec(cmdbuf);
1188 		break;
1189 	}
1190 	case KEY_BACKSPACE:
1191 	case KEY_DC:
1192 	case CTRL_H:
1193 		if (cmd_len > 0) {
1194 			cmdbuf[--cmd_len] = 0;
1195 		} else
1196 			beep();
1197 		break;
1198 	case 0x1b:
1199 	case CTRL_G:
1200 		if (cmd_len > 0) {
1201 			cmdbuf[0] = '\0';
1202 			cmd_len = 0;
1203 		} else
1204 			command_set(NULL, NULL);
1205 		break;
1206 	default:
1207 		break;
1208 	}
1209 }
1210 
1211 void
1212 keyboard(void)
1213 {
1214 	int ch;
1215 
1216 	ch = getch();
1217 
1218 	if (curr_cmd) {
1219 		cmd_keyboard(ch);
1220 		print_cmdline();
1221 		return;
1222 	}
1223 
1224 	if (curr_mgr != NULL)
1225 		if (curr_mgr->key_fn != NULL)
1226 			if (curr_mgr->key_fn(ch))
1227 				return;
1228 
1229 	if (curr_message != NULL) {
1230 		if (ch > 0) {
1231 			curr_message = NULL;
1232 			need_update = 1;
1233 		}
1234 	}
1235 
1236 	switch (ch) {
1237 	case ' ':
1238 		gotsig_alarm = 1;
1239 		break;
1240 	case 'o':
1241 		next_order();
1242 		need_sort = 1;
1243 		break;
1244 	case 'p':
1245 		paused = !paused;
1246 		gotsig_alarm = 1;
1247 		break;
1248 	case 'q':
1249 		gotsig_close = 1;
1250 		break;
1251 	case 'r':
1252 		sortdir *= -1;
1253 		need_sort = 1;
1254 		break;
1255 	case 'v':
1256 		/* FALLTHROUGH */
1257 	case KEY_RIGHT:
1258 		/* FALLTHROUGH */
1259 	case CTRL_F:
1260 		next_view();
1261 		break;
1262 	case KEY_LEFT:
1263 		/* FALLTHROUGH */
1264 	case CTRL_B:
1265 		prev_view();
1266 		break;
1267 	case KEY_DOWN:
1268 		/* FALLTHROUGH */
1269 	case CTRL_N:
1270 		dispstart++;
1271 		need_update = 1;
1272 		break;
1273 	case KEY_UP:
1274 		/* FALLTHROUGH */
1275 	case CTRL_P:
1276 		dispstart--;
1277 		need_update = 1;
1278 		break;
1279 	case KEY_NPAGE:
1280 		/* FALLTHROUGH */
1281 	case CTRL_V:
1282 		dispstart += maxprint;
1283 		need_update = 1;
1284 		break;
1285 	case KEY_PPAGE:
1286 		/* FALLTHROUGH */
1287 	case META_V:
1288 		dispstart -= maxprint;
1289 		need_update = 1;
1290 		break;
1291 	case KEY_HOME:
1292 		/* FALLTHROUGH */
1293 	case CTRL_A:
1294 		dispstart = 0;
1295 		need_update = 1;
1296 		break;
1297 	case KEY_END:
1298 		/* FALLTHROUGH */
1299 	case CTRL_E:
1300 		dispstart = num_disp;
1301 		need_update = 1;
1302 		break;
1303 	case CTRL_L:
1304 		clear();
1305 		need_update = 1;
1306 		break;
1307 	default:
1308 		break;
1309 	}
1310 
1311 	if (set_order_hotkey(ch))
1312 		need_sort = 1;
1313 	else
1314 		set_view_hotkey(ch);
1315 }
1316 
1317 void
1318 engine_initialize(void)
1319 {
1320 	signal(SIGTERM, sig_close);
1321 	signal(SIGINT, sig_close);
1322 	signal(SIGQUIT, sig_close);
1323 	signal(SIGWINCH, sig_resize);
1324 	signal(SIGALRM, sig_alarm);
1325 }
1326 
1327 void
1328 engine_loop(int countmax)
1329 {
1330 	int count = 0;
1331 
1332 	for (;;) {
1333 		if (gotsig_alarm) {
1334 			read_view();
1335 			need_sort = 1;
1336 			gotsig_alarm = 0;
1337 			ualarm(udelay, 0);
1338 		}
1339 
1340 		if (need_sort) {
1341 			sort_view();
1342 			need_sort = 0;
1343 			need_update = 1;
1344 
1345 			/* XXX if sort took too long */
1346 			if (gotsig_alarm) {
1347 				gotsig_alarm = 0;
1348 				ualarm(udelay, 0);
1349 			}
1350 		}
1351 
1352 		if (need_update) {
1353 			erase();
1354 			if (!averageonly ||
1355 			    (averageonly && count == countmax - 1))
1356 				disp_update();
1357 			end_page();
1358 			need_update = 0;
1359 			if (countmax && ++count >= countmax)
1360 				break;
1361 		}
1362 
1363 		if (gotsig_close)
1364 			break;
1365 		if (gotsig_resize) {
1366 			do_resize_term();
1367 			gotsig_resize = 0;
1368 			need_update = 1;
1369 		}
1370 
1371 		if (interactive && need_update == 0)
1372 			keyboard();
1373 		else if (interactive == 0)
1374 			usleep(udelay);
1375 	}
1376 
1377 	if (rawmode == 0)
1378 		endwin();
1379 }
1380 
1381 int
1382 check_termcap(void)
1383 {
1384 	char *term_name;
1385 	int status;
1386 	static struct termios screen_settings;
1387 
1388 	if (!interactive)
1389 		/* pretend we have a dumb terminal */
1390 		return(1);
1391 
1392 	/* get the terminal name */
1393 	term_name = getenv("TERM");
1394 	if (term_name == NULL)
1395 		return(1);
1396 
1397 	/* now get the termcap entry */
1398 	if ((status = tgetent(NULL, term_name)) != 1) {
1399 		if (status == -1)
1400 			warnx("can't open termcap file");
1401 		else
1402 			warnx("no termcap entry for a `%s' terminal",
1403 			    term_name);
1404 
1405 		/* pretend it's dumb and proceed */
1406 		return(1);
1407 	}
1408 
1409 	/* "hardcopy" immediately indicates a very stupid terminal */
1410 	if (tgetflag("hc"))
1411 		return(1);
1412 
1413         /* get necessary capabilities */
1414         if (tgetstr("cl", NULL) == NULL || tgetstr("cm", NULL) == NULL)
1415 		return(1);
1416 
1417 	/* if stdout is not a terminal, pretend we are a dumb terminal */
1418 	if (tcgetattr(STDOUT_FILENO, &screen_settings) == -1)
1419 		return(1);
1420 
1421 	return(0);
1422 }
1423