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