xref: /openbsd/usr.bin/systat/engine.c (revision cecf84d4)
1 /* $Id: engine.c,v 1.18 2015/01/19 07:39:24 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 		while (cur < pos - len) {
351 			tbprintf(" ");
352 			cur++;
353 		}
354 		tbprintf("%s", buf);
355 		cur += len;
356 	}
357 
358 	print_fld_tb(fld);
359 }
360 
361 void
362 print_fld_bar(field_def *fld, int value)
363 {
364 	int i, tw, val, cur;
365 
366 	if (fld->width < 1)
367 		return;
368 
369 	val = 0;
370 	tw = fld->arg / 2;
371 
372 	tb_start();
373 	cur = 0;
374 	for(i = 0; i < fld->width; i++) {
375 		tw += fld->arg;
376 
377 		while (tw >= fld->width) {
378 			val++;
379 			tw -= fld->width;
380 		}
381 		if (val > value)
382 			break;
383 		tbprintf("#");
384 	}
385 
386 	print_fld_tb(fld);
387 }
388 
389 void
390 print_fld_tb(field_def *fld)
391 {
392 	print_fld_str(fld, tmp_buf);
393 	tb_end();
394 }
395 
396 void
397 print_title(void)
398 {
399 	field_def **fp;
400 
401 	if (curr_view != NULL && curr_view->view != NULL) {
402 		for (fp = curr_view->view; *fp != NULL; fp++) {
403 			switch((*fp)->align) {
404 			case FLD_ALIGN_LEFT:
405 			case FLD_ALIGN_RIGHT:
406 			case FLD_ALIGN_CENTER:
407 			case FLD_ALIGN_COLUMN:
408 				print_fld_str(*fp, (*fp)->title);
409 				break;
410 			case FLD_ALIGN_BAR:
411 				print_bar_title(*fp);
412 				break;
413 			}
414 		}
415 	}
416 	end_line();
417 }
418 
419 /* view related functions */
420 void
421 hide_field(field_def *fld)
422 {
423 	if (fld == NULL)
424 		return;
425 
426 	fld->flags |= FLD_FLAG_HIDDEN;
427 }
428 
429 void
430 show_field(field_def *fld)
431 {
432 	if (fld == NULL)
433 		return;
434 
435 	fld->flags &= ~((unsigned int) FLD_FLAG_HIDDEN);
436 }
437 
438 void
439 reset_fields(void)
440 {
441 	field_def **fp;
442 	field_def *fld;
443 
444 	if (curr_view == NULL)
445 		return;
446 
447 	if (curr_view->view == NULL)
448 		return;
449 
450 	for (fp = curr_view->view; *fp != NULL; fp++) {
451 		fld = *fp;
452 		fld->start = -1;
453 		fld->width = fld->norm_width;
454 	}
455 }
456 
457 void
458 field_setup(void)
459 {
460 	field_def **fp;
461 	field_def *fld;
462 	int st, fwid, change;
463 	int width = columns;
464 
465 	reset_fields();
466 
467 	dispstart = 0;
468 	st = 0;
469 
470 	for (fp = curr_view->view; *fp != NULL; fp++) {
471 		fld = *fp;
472 		if (fld->flags & FLD_FLAG_HIDDEN)
473 			continue;
474 
475 		if (width <= 1)
476 			break;
477 
478 		if (st != 1)
479 			width--;
480 
481 		fld->start = 1;
482 		fwid = fld->width;
483 		st++;
484 		if (fwid >= width) {
485 			fld->width = width;
486 			width = 0;
487 		} else
488 			width -= fwid;
489 	}
490 
491 	change = 0;
492 	while (width > 0) {
493 		change = 0;
494 		for (fp = curr_view->view; *fp != NULL; fp++) {
495 			fld = *fp;
496 			if (fld->flags & FLD_FLAG_HIDDEN)
497 				continue;
498 			if ((fld->width < fld->max_width) &&
499 			    (fld->increment <= width)) {
500 				int w = fld->width + fld->increment;
501 				if (w > fld->max_width)
502 					w = fld->max_width;
503 				width += fld->width - w;
504 				fld->width = w;
505 				change = 1;
506 			}
507 			if (width <= 0) break;
508 		}
509 		if (change == 0) break;
510 	}
511 
512 	st = 0;
513 	for (fp = curr_view->view; *fp != NULL; fp++) {
514 		fld = *fp;
515 		if (fld->flags & FLD_FLAG_HIDDEN)
516 			continue;
517 		if (fld->start < 0) break;
518 		fld->start = st;
519 		st += fld->width + 1;
520 	}
521 }
522 
523 void
524 set_curr_view(struct view_ent *ve)
525 {
526 	field_view *v;
527 
528 	reset_fields();
529 
530 	if (ve == NULL) {
531 		curr_view_ent = NULL;
532 		curr_view = NULL;
533 		curr_mgr = NULL;
534 		return;
535 	}
536 
537 	v = ve->view;
538 
539 	if ((curr_view != NULL) && (curr_mgr != v->mgr)) {
540 		gotsig_alarm = 1;
541 		if (v->mgr != NULL && v->mgr->select_fn != NULL)
542 			v->mgr->select_fn();
543 	}
544 
545 	curr_view_ent = ve;
546 	curr_view = v;
547 	curr_mgr = v->mgr;
548 	field_setup();
549 	need_update = 1;
550 }
551 
552 void
553 add_view(field_view *fv)
554 {
555 	struct view_ent *ent;
556 
557 	if (fv == NULL)
558 		return;
559 
560 	if (fv->view == NULL || fv->name == NULL || fv->mgr == NULL)
561 		return;
562 
563 	ent = malloc(sizeof(struct view_ent));
564 	if (ent == NULL)
565 		return;
566 
567 	ent->view = fv;
568 	TAILQ_INSERT_TAIL(&view_head, ent, entries);
569 
570 	if (curr_view == NULL)
571 		set_curr_view(ent);
572 }
573 
574 int
575 set_view(const char *opt)
576 {
577 	struct view_ent *ve, *vm = NULL;
578 	field_view *v;
579 	int len;
580 
581 	if (opt == NULL || (len = strlen(opt)) == 0)
582 		return 1;
583 
584 	TAILQ_FOREACH(ve, &view_head, entries) {
585 		v = ve->view;
586 		if (strncasecmp(opt, v->name, len) == 0) {
587 			if (vm)
588 				return 1;
589 			vm = ve;
590 		}
591 	}
592 
593 	if (vm) {
594 		set_curr_view(vm);
595 		return 0;
596 	}
597 
598 	return 1;
599 }
600 
601 void
602 foreach_view(void (*callback)(field_view *))
603 {
604 	struct view_ent *ve;
605 
606 	TAILQ_FOREACH(ve, &view_head, entries) {
607 		callback(ve->view);
608 	}
609 }
610 
611 int
612 set_view_hotkey(int ch)
613 {
614 	struct view_ent *ve;
615 	field_view *v;
616 	int key = tolower(ch);
617 
618 	TAILQ_FOREACH(ve, &view_head, entries) {
619 		v = ve->view;
620 		if (key == v->hotkey) {
621 			set_curr_view(ve);
622 			return 1;
623 		}
624 	}
625 
626 	return 0;
627 }
628 
629 void
630 next_view(void)
631 {
632 	struct view_ent *ve;
633 
634 	if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
635 		return;
636 
637 	ve = TAILQ_NEXT(curr_view_ent, entries);
638 	if (ve == NULL)
639 		ve = TAILQ_FIRST(&view_head);
640 
641 	set_curr_view(ve);
642 }
643 
644 void
645 prev_view(void)
646 {
647 	struct view_ent *ve;
648 
649 	if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL)
650 		return;
651 
652 	ve = TAILQ_PREV(curr_view_ent, view_list, entries);
653 	if (ve == NULL)
654 		ve = TAILQ_LAST(&view_head, view_list);
655 
656 	set_curr_view(ve);
657 }
658 
659 /* generic field printing */
660 
661 void
662 print_fld_age(field_def *fld, unsigned int age)
663 {
664 	int len;
665 	unsigned int h, m, s;
666 
667 	if (fld == NULL)
668 		return;
669 	len = fld->width;
670 
671 	if (len < 1)
672 		return;
673 
674 	s = age % 60;
675 	m = age / 60;
676 	h = m / 60;
677 	m %= 60;
678 
679 	tb_start();
680 	if (tbprintf("%02u:%02u:%02u", h, m, s) <= len)
681 		goto ok;
682 
683 	tb_start();
684 	if (tbprintf("%u", age) <= len)
685 		goto ok;
686 
687 	tb_start();
688 	age /= 60;
689 	if (tbprintf("%um", age) <= len)
690 		goto ok;
691 	if (age == 0)
692 		goto err;
693 
694 	tb_start();
695 	age /= 60;
696 	if (tbprintf("%uh", age) <= len)
697 		goto ok;
698 	if (age == 0)
699 		goto err;
700 
701 	tb_start();
702 	age /= 24;
703 	if (tbprintf("%ud", age) <= len)
704 		goto ok;
705 
706 err:
707 	print_fld_str(fld, "*");
708 	tb_end();
709 	return;
710 
711 ok:
712 	print_fld_tb(fld);
713 }
714 
715 void
716 print_fld_sdiv(field_def *fld, u_int64_t size, int d)
717 {
718 	int len;
719 
720 	if (fld == NULL)
721 		return;
722 
723 	len = fld->width;
724 	if (len < 1)
725 		return;
726 
727 	tb_start();
728 	if (tbprintft("%llu", size) <= len)
729 		goto ok;
730 
731 	tb_start();
732 	size /= d;
733 	if (tbprintft("%lluK", size) <= len)
734 		goto ok;
735 	if (size == 0)
736 		goto err;
737 
738 	tb_start();
739 	size /= d;
740 	if (tbprintft("%lluM", size) <= len)
741 		goto ok;
742 	if (size == 0)
743 		goto err;
744 
745 	tb_start();
746 	size /= d;
747 	if (tbprintft("%lluG", size) <= len)
748 		goto ok;
749 	if (size == 0)
750 		goto err;
751 
752 	tb_start();
753 	size /= d;
754 	if (tbprintft("%lluT", size) <= len)
755 		goto ok;
756 
757 err:
758 	print_fld_str(fld, "*");
759 	tb_end();
760 	return;
761 
762 ok:
763 	print_fld_tb(fld);
764 }
765 
766 void
767 print_fld_size(field_def *fld, u_int64_t size)
768 {
769 	print_fld_sdiv(fld, size, 1024);
770 }
771 
772 void
773 print_fld_ssdiv(field_def *fld, int64_t size, int d)
774 {
775 	int len;
776 
777 	if (fld == NULL)
778 		return;
779 
780 	len = fld->width;
781 	if (len < 1)
782 		return;
783 
784 	tb_start();
785 	if (tbprintft("%lld", size) <= len)
786 		goto ok;
787 
788 	tb_start();
789 	size /= d;
790 	if (tbprintft("%lldK", size) <= len)
791 		goto ok;
792 	if (size == 0)
793 		goto err;
794 
795 	tb_start();
796 	size /= d;
797 	if (tbprintft("%lldM", size) <= len)
798 		goto ok;
799 	if (size == 0)
800 		goto err;
801 
802 	tb_start();
803 	size /= d;
804 	if (tbprintft("%lldG", size) <= len)
805 		goto ok;
806 	if (size == 0)
807 		goto err;
808 
809 	tb_start();
810 	size /= d;
811 	if (tbprintft("%lldT", size) <= len)
812 		goto ok;
813 
814 err:
815 	print_fld_str(fld, "*");
816 	tb_end();
817 	return;
818 
819 ok:
820 	print_fld_tb(fld);
821 }
822 
823 void
824 print_fld_ssize(field_def *fld, int64_t size)
825 {
826 	print_fld_ssdiv(fld, size, 1024);
827 }
828 
829 void
830 print_fld_rate(field_def *fld, double rate)
831 {
832 	if (rate < 0) {
833 		print_fld_str(fld, "*");
834 	} else {
835 		print_fld_size(fld, rate);
836 	}
837 }
838 
839 void
840 print_fld_bw(field_def *fld, double bw)
841 {
842 	if (bw < 0) {
843 		print_fld_str(fld, "*");
844 	} else {
845 		print_fld_sdiv(fld, bw, 1000);
846 	}
847 }
848 
849 void
850 print_fld_uint(field_def *fld, unsigned int size)
851 {
852 	int len;
853 
854 	if (fld == NULL)
855 		return;
856 
857 	len = fld->width;
858 	if (len < 1)
859 		return;
860 
861 	tb_start();
862 	if (tbprintft("%u", size) > len)
863 		print_fld_str(fld, "*");
864 	else
865 		print_fld_tb(fld);
866 	tb_end();
867 }
868 
869 void
870 print_fld_float(field_def *fld, double f, int prec)
871 {
872 	int len;
873 
874 	if (fld == NULL)
875 		return;
876 
877 	len = fld->width;
878 	if (len < 1)
879 		return;
880 
881 	tb_start();
882 	if (tbprintf("%*.*f", len, prec, f) > len)
883 		print_fld_str(fld, "*");
884 	else
885 		print_fld_tb(fld);
886 	tb_end();
887 }
888 
889 
890 /* ordering */
891 
892 void
893 set_order(const char *opt)
894 {
895 	order_type *o;
896 
897 	if (curr_view == NULL || curr_view->mgr == NULL)
898 		return;
899 
900 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
901 
902 	if (opt == NULL)
903 		return;
904 
905 	o = curr_view->mgr->order_list;
906 
907 	if (o == NULL)
908 		return;
909 
910 	for (;o->name != NULL; o++) {
911 		if (strcasecmp(opt, o->match) == 0) {
912 			curr_view->mgr->order_curr = o;
913 			return;
914 		}
915 	}
916 }
917 
918 int
919 set_order_hotkey(int ch)
920 {
921 	order_type *o;
922 	int key = ch;
923 
924 	if (curr_view == NULL || curr_view->mgr == NULL)
925 		return 0;
926 
927 	o = curr_view->mgr->order_list;
928 
929 	if (o == NULL)
930 		return 0;
931 
932 	for (;o->name != NULL; o++) {
933 		if (key == o->hotkey) {
934 			if (curr_view->mgr->order_curr == o) {
935 				sortdir *= -1;
936 			} else {
937 				curr_view->mgr->order_curr = o;
938 			}
939 			return 1;
940 		}
941 	}
942 
943 	return 0;
944 }
945 
946 void
947 next_order(void)
948 {
949 	order_type *o, *oc;
950 
951 	if (curr_view->mgr->order_list == NULL)
952 		return;
953 
954 	oc = curr_view->mgr->order_curr;
955 
956 	for (o = curr_view->mgr->order_list; o->name != NULL; o++) {
957 		if (oc == o) {
958 			o++;
959 			if (o->name == NULL)
960 				break;
961 			curr_view->mgr->order_curr = o;
962 			return;
963 		}
964 	}
965 
966 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
967 }
968 
969 
970 /* main program functions */
971 
972 int
973 read_view(void)
974 {
975 	if (curr_mgr == NULL)
976 		return (0);
977 
978 	if (paused)
979 		return (0);
980 
981 	if (curr_mgr->read_fn != NULL)
982 		return (curr_mgr->read_fn());
983 
984 	return (0);
985 }
986 
987 
988 int
989 disp_update(void)
990 {
991 	int li;
992 
993 	if (maxprint < 0)
994 		dispstart = 0;
995 	else if (dispstart + maxprint > num_disp)
996 		dispstart = num_disp - maxprint;
997 
998 	if (dispstart < 0)
999 		dispstart = 0;
1000 
1001 	if (curr_view == NULL)
1002 		return 0;
1003 
1004 	if (curr_mgr != NULL) {
1005 		curr_line = 0;
1006 
1007 		if (curr_mgr->header_fn != NULL) {
1008 			li = curr_mgr->header_fn();
1009 			if (li < 0)
1010 				return (1);
1011 			curr_line = ++li;
1012 			home_line = li + maxprint + 1;
1013 		}
1014 
1015 		print_title();
1016 
1017 		if (curr_mgr->print_fn != NULL)
1018 			curr_mgr->print_fn();
1019 	}
1020 
1021 	return (0);
1022 }
1023 
1024 void
1025 sort_view(void)
1026 {
1027 	if (curr_mgr != NULL)
1028 		if (curr_mgr->sort_fn != NULL)
1029 			curr_mgr->sort_fn();
1030 }
1031 
1032 void
1033 sig_close(int sig)
1034 {
1035 	gotsig_close = 1;
1036 }
1037 
1038 void
1039 sig_resize(int sig)
1040 {
1041 	gotsig_resize = 1;
1042 }
1043 
1044 void
1045 sig_alarm(int sig)
1046 {
1047 	gotsig_alarm = 1;
1048 }
1049 
1050 void
1051 setup_term(int dmax)
1052 {
1053 	max_disp = dmax;
1054 	maxprint = dmax;
1055 
1056 	if (rawmode) {
1057 		columns = rawwidth;
1058 		lines = DEFAULT_HEIGHT;
1059 		clear_linebuf();
1060 	} else {
1061 		if (dmax < 0)
1062 			dmax = 0;
1063 
1064 		screen = newterm(NULL, stdout, stdin);
1065 		if (screen == NULL) {
1066 			rawmode = 1;
1067 			interactive = 0;
1068 			setup_term(dmax);
1069 			return;
1070 		}
1071 		columns = COLS;
1072 		lines = LINES;
1073 
1074 		if (maxprint > lines - HEADER_LINES)
1075 			maxprint = lines - HEADER_LINES;
1076 
1077 		nonl();
1078 		keypad(stdscr, TRUE);
1079 		intrflush(stdscr, FALSE);
1080 
1081 		halfdelay(10);
1082 		noecho();
1083 	}
1084 
1085 	if (dmax == 0)
1086 		maxprint = lines - HEADER_LINES;
1087 
1088 	field_setup();
1089 }
1090 
1091 void
1092 do_resize_term(void)
1093 {
1094 	struct winsize ws;
1095 
1096 	if (rawmode)
1097 		return;
1098 
1099 	if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1)
1100 		return;
1101 
1102 	resizeterm(ws.ws_row, ws.ws_col);
1103 
1104 	columns = COLS;
1105 	lines = LINES;
1106 
1107 	maxprint = max_disp;
1108 
1109 	if (maxprint == 0 || maxprint > lines - HEADER_LINES)
1110 		maxprint = lines - HEADER_LINES;
1111 
1112 	clear();
1113 
1114 	field_setup();
1115 }
1116 
1117 struct command *
1118 command_set(struct command *cmd, const char *init)
1119 {
1120 	struct command *prev = curr_cmd;
1121 
1122 	if (cmd) {
1123 		if (init) {
1124 			cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf));
1125 			if (cmd_len >= sizeof(cmdbuf)) {
1126 				cmdbuf[0] = '\0';
1127 				cmd_len = 0;
1128 			}
1129 		} else {
1130 			cmd_len = 0;
1131 			cmdbuf[0] = 0;
1132 		}
1133 	}
1134 	curr_message = NULL;
1135 	curr_cmd = cmd;
1136 	need_update = 1;
1137 	return prev;
1138 }
1139 
1140 const char *
1141 message_set(const char *msg) {
1142 	char *prev = curr_message;
1143 	if (msg)
1144 		curr_message = strdup(msg);
1145 	else
1146 		curr_message = NULL;
1147 	free(prev);
1148 	return NULL;
1149 }
1150 
1151 void
1152 print_cmdline(void)
1153 {
1154 	if (curr_cmd) {
1155 		attron(A_STANDOUT);
1156 		mvprintw(home_line, 0, "%s: ", curr_cmd->prompt);
1157 		attroff(A_STANDOUT);
1158 		printw("%s", cmdbuf);
1159 	} else if (curr_message) {
1160 		mvprintw(home_line, 0, "> %s", curr_message);
1161 	}
1162 	clrtoeol();
1163 }
1164 
1165 
1166 void
1167 cmd_keyboard(int ch)
1168 {
1169 	if (curr_cmd == NULL)
1170 		return;
1171 
1172 	if (ch > 0 && isprint(ch)) {
1173 		if (cmd_len < sizeof(cmdbuf) - 1) {
1174 			cmdbuf[cmd_len++] = ch;
1175 			cmdbuf[cmd_len] = 0;
1176 		} else
1177 			beep();
1178 	}
1179 
1180 	switch (ch) {
1181 	case KEY_ENTER:
1182 	case 0x0a:
1183 	case 0x0d:
1184 	{
1185 		struct command * c = command_set(NULL, NULL);
1186 		c->exec(cmdbuf);
1187 		break;
1188 	}
1189 	case KEY_BACKSPACE:
1190 	case KEY_DC:
1191 	case CTRL_H:
1192 		if (cmd_len > 0) {
1193 			cmdbuf[--cmd_len] = 0;
1194 		} else
1195 			beep();
1196 		break;
1197 	case 0x1b:
1198 	case CTRL_G:
1199 		if (cmd_len > 0) {
1200 			cmdbuf[0] = '\0';
1201 			cmd_len = 0;
1202 		} else
1203 			command_set(NULL, NULL);
1204 		break;
1205 	default:
1206 		break;
1207 	}
1208 }
1209 
1210 void
1211 keyboard(void)
1212 {
1213 	int ch;
1214 
1215 	ch = getch();
1216 
1217 	if (curr_cmd) {
1218 		cmd_keyboard(ch);
1219 		print_cmdline();
1220 		return;
1221 	}
1222 
1223 	if (curr_mgr != NULL)
1224 		if (curr_mgr->key_fn != NULL)
1225 			if (curr_mgr->key_fn(ch))
1226 				return;
1227 
1228 	if (curr_message != NULL) {
1229 		if (ch > 0) {
1230 			curr_message = NULL;
1231 			need_update = 1;
1232 		}
1233 	}
1234 
1235 	switch (ch) {
1236 	case ' ':
1237 		gotsig_alarm = 1;
1238 		break;
1239 	case 'o':
1240 		next_order();
1241 		need_sort = 1;
1242 		break;
1243 	case 'p':
1244 		paused = !paused;
1245 		gotsig_alarm = 1;
1246 		break;
1247 	case 'q':
1248 		gotsig_close = 1;
1249 		break;
1250 	case 'r':
1251 		sortdir *= -1;
1252 		need_sort = 1;
1253 		break;
1254 	case 'v':
1255 		/* FALLTHROUGH */
1256 	case KEY_RIGHT:
1257 		/* FALLTHROUGH */
1258 	case CTRL_F:
1259 		next_view();
1260 		break;
1261 	case KEY_LEFT:
1262 		/* FALLTHROUGH */
1263 	case CTRL_B:
1264 		prev_view();
1265 		break;
1266 	case KEY_DOWN:
1267 		/* FALLTHROUGH */
1268 	case CTRL_N:
1269 		dispstart++;
1270 		need_update = 1;
1271 		break;
1272 	case KEY_UP:
1273 		/* FALLTHROUGH */
1274 	case CTRL_P:
1275 		dispstart--;
1276 		need_update = 1;
1277 		break;
1278 	case KEY_NPAGE:
1279 		/* FALLTHROUGH */
1280 	case CTRL_V:
1281 		dispstart += maxprint;
1282 		need_update = 1;
1283 		break;
1284 	case KEY_PPAGE:
1285 		/* FALLTHROUGH */
1286 	case META_V:
1287 		dispstart -= maxprint;
1288 		need_update = 1;
1289 		break;
1290 	case KEY_HOME:
1291 		/* FALLTHROUGH */
1292 	case CTRL_A:
1293 		dispstart = 0;
1294 		need_update = 1;
1295 		break;
1296 	case KEY_END:
1297 		/* FALLTHROUGH */
1298 	case CTRL_E:
1299 		dispstart = num_disp;
1300 		need_update = 1;
1301 		break;
1302 	case CTRL_L:
1303 		clear();
1304 		need_update = 1;
1305 		break;
1306 	default:
1307 		break;
1308 	}
1309 
1310 	if (set_order_hotkey(ch))
1311 		need_sort = 1;
1312 	else
1313 		set_view_hotkey(ch);
1314 }
1315 
1316 void
1317 engine_initialize(void)
1318 {
1319 	signal(SIGTERM, sig_close);
1320 	signal(SIGINT, sig_close);
1321 	signal(SIGQUIT, sig_close);
1322 	signal(SIGWINCH, sig_resize);
1323 	signal(SIGALRM, sig_alarm);
1324 }
1325 
1326 void
1327 engine_loop(int countmax)
1328 {
1329 	int count = 0;
1330 
1331 	for (;;) {
1332 		if (gotsig_alarm) {
1333 			read_view();
1334 			need_sort = 1;
1335 			gotsig_alarm = 0;
1336 			ualarm(udelay, 0);
1337 		}
1338 
1339 		if (need_sort) {
1340 			sort_view();
1341 			need_sort = 0;
1342 			need_update = 1;
1343 
1344 			/* XXX if sort took too long */
1345 			if (gotsig_alarm) {
1346 				gotsig_alarm = 0;
1347 				ualarm(udelay, 0);
1348 			}
1349 		}
1350 
1351 		if (need_update) {
1352 			erase();
1353 			if (!averageonly ||
1354 			    (averageonly && count == countmax - 1))
1355 				disp_update();
1356 			end_page();
1357 			need_update = 0;
1358 			if (countmax && ++count >= countmax)
1359 				break;
1360 		}
1361 
1362 		if (gotsig_close)
1363 			break;
1364 		if (gotsig_resize) {
1365 			do_resize_term();
1366 			gotsig_resize = 0;
1367 			need_update = 1;
1368 		}
1369 
1370 		if (interactive && need_update == 0)
1371 			keyboard();
1372 		else if (interactive == 0)
1373 			usleep(udelay);
1374 	}
1375 
1376 	if (rawmode == 0)
1377 		endwin();
1378 }
1379 
1380 int
1381 check_termcap(void)
1382 {
1383 	char *term_name;
1384 	int status;
1385 	static struct termios screen_settings;
1386 
1387 	if (!interactive)
1388 		/* pretend we have a dumb terminal */
1389 		return(1);
1390 
1391 	/* get the terminal name */
1392 	term_name = getenv("TERM");
1393 	if (term_name == NULL)
1394 		return(1);
1395 
1396 	/* now get the termcap entry */
1397 	if ((status = tgetent(NULL, term_name)) != 1) {
1398 		if (status == -1)
1399 			warnx("can't open termcap file");
1400 		else
1401 			warnx("no termcap entry for a `%s' terminal",
1402 			    term_name);
1403 
1404 		/* pretend it's dumb and proceed */
1405 		return(1);
1406 	}
1407 
1408 	/* "hardcopy" immediately indicates a very stupid terminal */
1409 	if (tgetflag("hc"))
1410 		return(1);
1411 
1412         /* get necessary capabilities */
1413         if (tgetstr("cl", NULL) == NULL || tgetstr("cm", NULL) == NULL)
1414 		return(1);
1415 
1416 	/* if stdout is not a terminal, pretend we are a dumb terminal */
1417 	if (tcgetattr(STDOUT_FILENO, &screen_settings) == -1)
1418 		return(1);
1419 
1420 	return(0);
1421 }
1422