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