xref: /openbsd-src/usr.bin/systat/engine.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /* $Id: engine.c,v 1.14 2011/04/05 07:35:32 mpf 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 CIRCLEQ_HEAD(view_list, view_ent) view_head =
49 				  CIRCLEQ_HEAD_INITIALIZER(view_head);
50 struct view_ent {
51 	field_view *view;
52 	CIRCLEQ_ENTRY(view_ent) entries;
53 };
54 
55 useconds_t udelay = 5000000;
56 int dispstart = 0;
57 int interactive = 1;
58 int maxprint = 0;
59 int paused = 0;
60 int rawmode = 0;
61 int rawwidth = DEFAULT_WIDTH;
62 int sortdir = 1;
63 int columns, lines;
64 u_int32_t num_disp = 0;
65 int max_disp = -1;
66 
67 volatile sig_atomic_t gotsig_close = 0;
68 volatile sig_atomic_t gotsig_resize = 0;
69 volatile sig_atomic_t gotsig_alarm = 0;
70 int need_update = 0;
71 int need_sort = 0;
72 int separate_thousands = 0;
73 
74 SCREEN *screen;
75 
76 field_view *curr_view = NULL;
77 struct view_ent *curr_view_ent = NULL;
78 struct view_manager *curr_mgr = NULL;
79 
80 int curr_line = 0;
81 int home_line = 0;
82 
83 /* line buffer for raw mode */
84 char linebuf[MAX_LINE_BUF];
85 int linepos = 0;
86 
87 /* temp storage for state printing */
88 char tmp_buf[MAX_LINE_BUF];
89 
90 char cmdbuf[MAX_LINE_BUF];
91 int cmd_len = -1;
92 struct command *curr_cmd = NULL;
93 char *curr_message = NULL;
94 
95 void print_cmdline(void);
96 
97 
98 /* screen output functions */
99 
100 char * tb_ptr = NULL;
101 int tb_len = 0;
102 
103 void
104 tb_start(void)
105 {
106 	tb_ptr = tmp_buf;
107 	tb_len = sizeof(tmp_buf);
108 	tb_ptr[0] = '\0';
109 }
110 
111 void
112 tb_end(void)
113 {
114 	tb_ptr = NULL;
115 	tb_len = 0;
116 }
117 
118 int
119 tbprintf(char *format, ...)
120 	GCC_PRINTFLIKE(1,2)       /* defined in curses.h */
121 {
122 	int len;
123 	va_list arg;
124 
125 	if (tb_ptr == NULL || tb_len <= 0)
126 		return 0;
127 
128 	va_start(arg, format);
129 	len=vsnprintf(tb_ptr, tb_len, format, arg);
130 	va_end(arg);
131 
132 	if (len > tb_len)
133 		tb_end();
134 	else if (len > 0) {
135 		tb_ptr += len;
136 		tb_len -= len;
137 	}
138 
139 	return len;
140 }
141 
142 int
143 tbprintft(char *format, ...)
144 	GCC_PRINTFLIKE(1,2)       /* defined in curses.h */
145 {
146 	int len;
147 	va_list arg;
148 	char buf[MAX_LINE_BUF];
149 
150 	if (tb_ptr == NULL || tb_len <= 0)
151 		return 0;
152 
153 	va_start(arg, format);
154 	len = vsnprintf(buf, tb_len, format, arg);
155 	va_end(arg);
156 
157 	if (len > tb_len)
158 		tb_end();
159 	else if (len > 0) {
160 		int d, s;
161 		int digits, curdigit;
162 
163 		if (!separate_thousands) {
164 			strlcpy(tb_ptr, buf, tb_len);
165 			return len;
166 		}
167 
168 		/* count until we hit a non digit. (e.g. the prefix) */
169 		for (digits = 0; digits < len; digits++)
170 			if (!isdigit(buf[digits]))
171 				break;
172 
173 		curdigit = digits;
174 		d = s = 0;
175 		/* insert thousands separators while copying */
176 		while (curdigit && d < tb_len) {
177 			if (curdigit < digits && curdigit % 3 == 0)
178 				tb_ptr[d++] = ',';
179 			tb_ptr[d++] = buf[s++];
180 			curdigit--;
181 		}
182 		/* copy the remaining non-digits */
183 		while (len > digits && d < tb_len) {
184 			tb_ptr[d++] = buf[s++];
185 			digits++;
186 		}
187 		tb_ptr[d] = '\0';
188 		tb_ptr += d;
189 		tb_len -= d;
190 		len = d;
191 	}
192 	return len;
193 }
194 
195 void
196 move_horiz(int offset)
197 {
198 	if (rawmode) {
199 		if (offset <= 0)
200 			linepos = 0;
201 		else if (offset >= MAX_LINE_BUF)
202 			linepos = MAX_LINE_BUF - 1;
203 		else
204 			linepos = offset;
205 	} else {
206 		move(curr_line, offset);
207 	}
208 }
209 
210 void
211 print_str(int len, const char *str)
212 {
213 	if (len <= 0)
214 		return;
215 
216 	if (rawmode) {
217 		int length = MIN(len, MAX_LINE_BUF - linepos);
218 		if (length <= 0)
219 			return;
220 		bcopy(str, &linebuf[linepos], length);
221 		linepos += length;
222 	} else
223 		addnstr(str, len);
224 }
225 
226 void
227 clear_linebuf(void)
228 {
229 	memset(linebuf, ' ', MAX_LINE_BUF);
230 }
231 
232 void
233 end_line(void)
234 {
235 	if (rawmode) {
236 		linebuf[rawwidth] = '\0';
237 		printf("%s\n", linebuf);
238 		clear_linebuf();
239 	}
240 	curr_line++;
241 }
242 
243 void
244 end_page(void)
245 {
246 	if (rawmode) {
247 		linepos = 0;
248 		clear_linebuf();
249 	} else {
250 		move(home_line, 0);
251 		print_cmdline();
252 		refresh();
253 	}
254 	curr_line = 0;
255 }
256 
257 /* field output functions */
258 
259 void
260 print_fld_str(field_def *fld, const char *str)
261 {
262 	int len, offset;
263 	char *cpos;
264 
265 	if (str == NULL || fld == NULL)
266 		return;
267 
268 	if (fld->start < 0)
269 		return;
270 
271 	len = strlen(str);
272 
273 	if (len >= fld->width) {
274 		move_horiz(fld->start);
275 		print_str(fld->width, str);
276 	} else {
277 		switch (fld->align) {
278 		case FLD_ALIGN_RIGHT:
279 			move_horiz(fld->start + (fld->width - len));
280 			break;
281 		case FLD_ALIGN_CENTER:
282 			move_horiz(fld->start + (fld->width - len) / 2);
283 			break;
284 		case FLD_ALIGN_COLUMN:
285 			if ((cpos = strchr(str, ':')) == NULL) {
286 				offset = (fld->width - len) / 2;
287 			} else {
288 				offset = (fld->width / 2) - (cpos - str);
289 				if (offset < 0)
290 					offset = 0;
291 				else if (offset > (fld->width - len))
292 					offset = fld->width - len;
293 			}
294 			move_horiz(fld->start + offset);
295 			break;
296 		default:
297 			move_horiz(fld->start);
298 			break;
299 		}
300 		print_str(len, str);
301 	}
302 }
303 
304 void
305 print_bar_title(field_def *fld)
306 {
307 	char buf[16];
308 	int len, i, d, tr, tw, val, pos, cur;
309 
310 	int divs[] = {20, 10, 5, 4, 3, 2, 1, 0};
311 
312 	if (fld->width < 1)
313 		return;
314 
315 	len = snprintf(buf, sizeof(buf), " %d\\", fld->arg);
316 	if (len >= sizeof(buf))
317 		return;
318 
319 	for (i = 0; divs[i]; i++)
320 		if (divs[i] * len <= fld->width)
321 			break;
322 
323 	if (divs[i] == 0) {
324 		print_fld_str(fld, "*****");
325 		return;
326 	}
327 
328 	d = divs[i];
329 
330 	val = 0;
331 	pos = 0;
332 	tr = fld->arg % d;
333 	tw = fld->width % d;
334 
335 	tb_start();
336 	cur = 0;
337 	for(i = 0; i < d; i++) {
338 		tw += fld->width;
339 		tr += fld->arg;
340 
341 		while (tr >= d) {
342 			val++;
343 			tr -= d;
344 		}
345 		while (tw >= d) {
346 			pos++;
347 			tw -= d;
348 		}
349 
350 		len = snprintf(buf, sizeof(buf), "%d\\", val);
351 		while (cur < pos - len) {
352 			tbprintf(" ");
353 			cur++;
354 		}
355 		tbprintf("%s", buf);
356 		cur += len;
357 	}
358 
359 	print_fld_tb(fld);
360 }
361 
362 void
363 print_fld_bar(field_def *fld, int value)
364 {
365 	int i, tw, val, cur;
366 
367 	if (fld->width < 1)
368 		return;
369 
370 	val = 0;
371 	tw = fld->arg / 2;
372 
373 	tb_start();
374 	cur = 0;
375 	for(i = 0; i < fld->width; i++) {
376 		tw += fld->arg;
377 
378 		while (tw >= fld->width) {
379 			val++;
380 			tw -= fld->width;
381 		}
382 		if (val > value)
383 			break;
384 		tbprintf("#");
385 	}
386 
387 	print_fld_tb(fld);
388 }
389 
390 void
391 print_fld_tb(field_def *fld)
392 {
393 	print_fld_str(fld, tmp_buf);
394 	tb_end();
395 }
396 
397 void
398 print_title(void)
399 {
400 	field_def **fp;
401 
402 	if (curr_view != NULL && curr_view->view != NULL) {
403 		for (fp = curr_view->view; *fp != NULL; fp++) {
404 			switch((*fp)->align) {
405 			case FLD_ALIGN_LEFT:
406 			case FLD_ALIGN_RIGHT:
407 			case FLD_ALIGN_CENTER:
408 			case FLD_ALIGN_COLUMN:
409 				print_fld_str(*fp, (*fp)->title);
410 				break;
411 			case FLD_ALIGN_BAR:
412 				print_bar_title(*fp);
413 				break;
414 			}
415 		}
416 	}
417 	end_line();
418 }
419 
420 /* view related functions */
421 void
422 hide_field(field_def *fld)
423 {
424 	if (fld == NULL)
425 		return;
426 
427 	fld->flags |= FLD_FLAG_HIDDEN;
428 }
429 
430 void
431 show_field(field_def *fld)
432 {
433 	if (fld == NULL)
434 		return;
435 
436 	fld->flags &= ~((unsigned int) FLD_FLAG_HIDDEN);
437 }
438 
439 void
440 reset_fields(void)
441 {
442 	field_def **fp;
443 	field_def *fld;
444 
445 	if (curr_view == NULL)
446 		return;
447 
448 	if (curr_view->view == NULL)
449 		return;
450 
451 	for (fp = curr_view->view; *fp != NULL; fp++) {
452 		fld = *fp;
453 		fld->start = -1;
454 		fld->width = fld->norm_width;
455 	}
456 }
457 
458 void
459 field_setup(void)
460 {
461 	field_def **fp;
462 	field_def *fld;
463 	int st, fwid, change;
464 	int width = columns;
465 
466 	reset_fields();
467 
468 	dispstart = 0;
469 	st = 0;
470 
471 	for (fp = curr_view->view; *fp != NULL; fp++) {
472 		fld = *fp;
473 		if (fld->flags & FLD_FLAG_HIDDEN)
474 			continue;
475 
476 		if (width <= 1)
477 			break;
478 
479 		if (st != 1)
480 			width--;
481 
482 		fld->start = 1;
483 		fwid = fld->width;
484 		st++;
485 		if (fwid >= width) {
486 			fld->width = width;
487 			width = 0;
488 		} else
489 			width -= fwid;
490 	}
491 
492 	change = 0;
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 	CIRCLEQ_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 	CIRCLEQ_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 	CIRCLEQ_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 	CIRCLEQ_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 (CIRCLEQ_EMPTY(&view_head) || curr_view_ent == NULL)
636 		return;
637 
638 	ve = CIRCLEQ_NEXT(curr_view_ent, entries);
639 	if (ve == CIRCLEQ_END(&view_head))
640 		ve = CIRCLEQ_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 (CIRCLEQ_EMPTY(&view_head) || curr_view_ent == NULL)
651 		return;
652 
653 	ve = CIRCLEQ_PREV(curr_view_ent, entries);
654 	if (ve == CIRCLEQ_END(&view_head))
655 		ve = CIRCLEQ_LAST(&view_head);
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 			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