xref: /openbsd-src/usr.bin/systat/engine.c (revision 850e275390052b330d93020bf619a739a3c277ac)
1 /* $Id: engine.c,v 1.4 2008/07/22 03:00:23 canacar 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 
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 <unistd.h>
29 
30 #include "engine.h"
31 
32 #ifndef MIN
33 #define MIN(a,b) (((a)<(b))?(a):(b))
34 #endif
35 
36 /* circular linked list of views */
37 CIRCLEQ_HEAD(view_list, view_ent) view_head =
38 				  CIRCLEQ_HEAD_INITIALIZER(view_head);
39 struct view_ent {
40 	field_view *view;
41 	CIRCLEQ_ENTRY(view_ent) entries;
42 };
43 
44 useconds_t udelay = 5000000;
45 int dispstart = 0;
46 int interactive = 1;
47 int maxprint = 0;
48 int paused = 0;
49 int rawmode = 0;
50 int rawwidth = DEFAULT_WIDTH;
51 int sortdir = 1;
52 int columns, lines;
53 u_int32_t num_disp = 0;
54 int max_disp = -1;
55 
56 volatile sig_atomic_t gotsig_close = 0;
57 volatile sig_atomic_t gotsig_resize = 0;
58 volatile sig_atomic_t gotsig_alarm = 0;
59 int need_update = 0;
60 int need_sort = 0;
61 
62 SCREEN *screen;
63 
64 field_view *curr_view = NULL;
65 struct view_ent *curr_view_ent = NULL;
66 struct view_manager *curr_mgr = NULL;
67 
68 int curr_line = 0;
69 int home_line = 0;
70 
71 /* line buffer for raw mode */
72 char linebuf[MAX_LINE_BUF];
73 int linepos = 0;
74 
75 /* temp storage for state printing */
76 char tmp_buf[MAX_LINE_BUF];
77 
78 char cmdbuf[MAX_LINE_BUF];
79 int cmd_len = -1;
80 struct command *curr_cmd = NULL;
81 char *curr_message = NULL;
82 
83 void print_cmdline(void);
84 
85 
86 /* screen output functions */
87 
88 char * tb_ptr = NULL;
89 int tb_len = 0;
90 
91 void
92 tb_start(void)
93 {
94 	tb_ptr = tmp_buf;
95 	tb_len = sizeof(tmp_buf);
96 	tb_ptr[0] = '\0';
97 }
98 
99 void
100 tb_end(void)
101 {
102 	tb_ptr = NULL;
103 	tb_len = 0;
104 }
105 
106 int
107 tbprintf(char *format, ...)
108 	GCC_PRINTFLIKE(1,2)       /* defined in curses.h */
109 {
110 	int len;
111 	va_list arg;
112 
113 	if (tb_ptr == NULL || tb_len <= 0)
114 		return 0;
115 
116 	va_start(arg, format);
117 	len=vsnprintf(tb_ptr, tb_len, format, arg);
118 	va_end(arg);
119 
120 	if (len > tb_len)
121 		tb_end();
122 	else if (len > 0) {
123 		tb_ptr += len;
124 		tb_len -= len;
125 	}
126 
127 	return len;
128 }
129 
130 void
131 move_horiz(int offset)
132 {
133 	if (rawmode) {
134 		if (offset <= 0)
135 			linepos = 0;
136 		else if (offset >= MAX_LINE_BUF)
137 			linepos = MAX_LINE_BUF - 1;
138 		else
139 			linepos = offset;
140 	} else {
141 		move(curr_line, offset);
142 	}
143 }
144 
145 void
146 print_str(int len, const char *str)
147 {
148 	if (len <= 0)
149 		return;
150 
151 	if (rawmode) {
152 		int length = MIN(len, MAX_LINE_BUF - linepos);
153 		if (length <= 0)
154 			return;
155 		bcopy(str, &linebuf[linepos], length);
156 		linepos += length;
157 	} else
158 		addnstr(str, len);
159 }
160 
161 void
162 clear_linebuf(void)
163 {
164 	memset(linebuf, ' ', MAX_LINE_BUF);
165 }
166 
167 void
168 end_line(void)
169 {
170 	if (rawmode) {
171 		linebuf[rawwidth] = '\0';
172 		printf("%s\n", linebuf);
173 		clear_linebuf();
174 	}
175 	curr_line++;
176 }
177 
178 void
179 end_page(void)
180 {
181 	if (rawmode) {
182 		linepos = 0;
183 		clear_linebuf();
184 	} else {
185 		move(home_line, 0);
186 		print_cmdline();
187 		refresh();
188 	}
189 	curr_line = 0;
190 }
191 
192 void
193 rawaddstr(char *s)
194 {
195 	if (rawmode)
196 		printf("%s", s);
197 	else
198 		addstr(s);
199 }
200 
201 /* field output functions */
202 
203 void
204 print_fld_str(field_def *fld, const char *str)
205 {
206 	int len, move;
207 	char *cpos;
208 
209 	if (str == NULL || fld == NULL)
210 		return;
211 
212 	if (fld->start < 0)
213 		return;
214 
215 	len = strlen(str);
216 
217 	if (len >= fld->width) {
218 		move_horiz(fld->start);
219 		print_str(fld->width, str);
220 	} else {
221 		switch (fld->align) {
222 		case FLD_ALIGN_RIGHT:
223 			move_horiz(fld->start + (fld->width - len));
224 			break;
225 		case FLD_ALIGN_CENTER:
226 			move_horiz(fld->start + (fld->width - len) / 2);
227 			break;
228 		case FLD_ALIGN_COLUMN:
229 			if ((cpos = strchr(str, ':')) == NULL) {
230 				move = (fld->width - len) / 2;
231 			} else {
232 				move = (fld->width / 2) - (cpos - str);
233 				if (move < 0)
234 					move = 0;
235 				else if (move > (fld->width - len))
236 					move = fld->width - len;
237 			}
238 			move_horiz(fld->start + move);
239 			break;
240 		default:
241 			move_horiz(fld->start);
242 			break;
243 		}
244 		print_str(len, str);
245 	}
246 }
247 
248 void
249 print_bar_title(field_def *fld)
250 {
251 	char buf[16];
252 	int len, div, i, tr, tw, val, pos, cur;
253 
254 	int divs[] = {20, 10, 5, 4, 3, 2, 1, 0};
255 
256 	if (fld->width < 1)
257 		return;
258 
259 	len = snprintf(buf, sizeof(buf), " %d\\", fld->arg);
260 	if (len >= sizeof(buf))
261 		return;
262 
263 	for (i = 0; divs[i]; i++)
264 		if (divs[i] * len <= fld->width)
265 			break;
266 
267 	if (divs[i] == 0) {
268 		print_fld_str(fld, "*****");
269 		return;
270 	}
271 
272 	div = divs[i];
273 
274 	val = 0;
275 	pos = 0;
276 	tr = fld->arg % div;
277 	tw = fld->width % div;
278 
279 	tb_start();
280 	cur = 0;
281 	for(i = 0; i < div; i++) {
282 		tw += fld->width;
283 		tr += fld->arg;
284 
285 		while (tr >= div) {
286 			val++;
287 			tr -= div;
288 		}
289 		while (tw >= div) {
290 			pos++;
291 			tw -= div;
292 		}
293 
294 		len = snprintf(buf, sizeof(buf), "%d\\", val);
295 		while (cur < pos - len) {
296 			tbprintf(" ");
297 			cur++;
298 		}
299 		tbprintf("%s", buf);
300 		cur += len;
301 	}
302 
303 	print_fld_tb(fld);
304 }
305 
306 void
307 print_fld_bar(field_def *fld, int value)
308 {
309 	int i, tw, val, cur;
310 
311 	if (fld->width < 1)
312 		return;
313 
314 	val = 0;
315 	tw = fld->arg / 2;
316 
317 	tb_start();
318 	cur = 0;
319 	for(i = 0; i < fld->width; i++) {
320 		tw += fld->arg;
321 
322 		while (tw >= fld->width) {
323 			val++;
324 			tw -= fld->width;
325 		}
326 		if (val > value)
327 			break;
328 		tbprintf("#");
329 	}
330 
331 	print_fld_tb(fld);
332 }
333 
334 void
335 print_fld_tb(field_def *fld)
336 {
337 	print_fld_str(fld, tmp_buf);
338 	tb_end();
339 }
340 
341 void
342 print_title(void)
343 {
344 	field_def **fp;
345 
346 	if (curr_view != NULL && curr_view->view != NULL) {
347 		for (fp = curr_view->view; *fp != NULL; fp++) {
348 			switch((*fp)->align) {
349 			case FLD_ALIGN_LEFT:
350 			case FLD_ALIGN_RIGHT:
351 			case FLD_ALIGN_CENTER:
352 			case FLD_ALIGN_COLUMN:
353 				print_fld_str(*fp, (*fp)->title);
354 				break;
355 			case FLD_ALIGN_BAR:
356 				print_bar_title(*fp);
357 				break;
358 			}
359 		}
360 	}
361 	end_line();
362 }
363 
364 /* view related functions */
365 void
366 hide_field(field_def *fld)
367 {
368 	if (fld == NULL)
369 		return;
370 
371 	fld->flags |= FLD_FLAG_HIDDEN;
372 }
373 
374 void
375 show_field(field_def *fld)
376 {
377 	if (fld == NULL)
378 		return;
379 
380 	fld->flags &= ~((unsigned int) FLD_FLAG_HIDDEN);
381 }
382 
383 void
384 reset_fields(void)
385 {
386 	field_def **fp;
387 	field_def *fld;
388 
389 	if (curr_view == NULL)
390 		return;
391 
392 	if (curr_view->view == NULL)
393 		return;
394 
395 	for (fp = curr_view->view; *fp != NULL; fp++) {
396 		fld = *fp;
397 		fld->start = -1;
398 		fld->width = fld->norm_width;
399 	}
400 }
401 
402 void
403 field_setup(void)
404 {
405 	field_def **fp;
406 	field_def *fld;
407 	int st, fwid, change;
408 	int width = columns;
409 
410 	reset_fields();
411 
412 	dispstart = 0;
413 	st = 0;
414 
415 	for (fp = curr_view->view; *fp != NULL; fp++) {
416 		fld = *fp;
417 		if (fld->flags & FLD_FLAG_HIDDEN)
418 			continue;
419 
420 		if (width <= 1)
421 			break;
422 
423 		if (st != 1)
424 			width--;
425 
426 		fld->start = 1;
427 		fwid = fld->width;
428 		st++;
429 		if (fwid >= width) {
430 			fld->width = width;
431 			width = 0;
432 		} else
433 			width -= fwid;
434 	}
435 
436 	change = 0;
437 	while (width > 0) {
438 		change = 0;
439 		for (fp = curr_view->view; *fp != NULL; fp++) {
440 			fld = *fp;
441 			if (fld->flags & FLD_FLAG_HIDDEN)
442 				continue;
443 			if ((fld->width < fld->max_width) &&
444 			    (fld->increment <= width)) {
445 				int w = fld->width + fld->increment;
446 				if (w > fld->max_width)
447 					w = fld->max_width;
448 				width += fld->width - w;
449 				fld->width = w;
450 				change = 1;
451 			}
452 			if (width <= 0) break;
453 		}
454 		if (change == 0) break;
455 	}
456 
457 	st = 0;
458 	for (fp = curr_view->view; *fp != NULL; fp++) {
459 		fld = *fp;
460 		if (fld->flags & FLD_FLAG_HIDDEN)
461 			continue;
462 		if (fld->start < 0) break;
463 		fld->start = st;
464 		st += fld->width + 1;
465 	}
466 }
467 
468 void
469 set_curr_view(struct view_ent *ve)
470 {
471 	field_view *v;
472 
473 	reset_fields();
474 
475 	if (ve == NULL) {
476 		curr_view_ent = NULL;
477 		curr_view = NULL;
478 		curr_mgr = NULL;
479 		return;
480 	}
481 
482 	v = ve->view;
483 
484 	if ((curr_view != NULL) && (curr_mgr != v->mgr)) {
485 		gotsig_alarm = 1;
486 		if (v->mgr != NULL && v->mgr->select_fn != NULL)
487 			v->mgr->select_fn();
488 	}
489 
490 	curr_view_ent = ve;
491 	curr_view = v;
492 	curr_mgr = v->mgr;
493 	field_setup();
494 	need_update = 1;
495 }
496 
497 void
498 add_view(field_view *fv)
499 {
500 	struct view_ent *ent;
501 
502 	if (fv == NULL)
503 		return;
504 
505 	if (fv->view == NULL || fv->name == NULL || fv->mgr == NULL)
506 		return;
507 
508 	ent = malloc(sizeof(struct view_ent));
509 	if (ent == NULL)
510 		return;
511 
512 	ent->view = fv;
513 	CIRCLEQ_INSERT_TAIL(&view_head, ent, entries);
514 
515 	if (curr_view == NULL)
516 		set_curr_view(ent);
517 }
518 
519 int
520 set_view(char *opt)
521 {
522 	struct view_ent *ve, *vm = NULL;
523 	field_view *v;
524 	int len;
525 
526 	if (opt == NULL || (len = strlen(opt)) == 0)
527 		return 1;
528 
529 	CIRCLEQ_FOREACH(ve, &view_head, entries) {
530 		v = ve->view;
531 		if (strncasecmp(opt, v->name, len) == 0) {
532 			if (vm)
533 				return 1;
534 			vm = ve;
535 		}
536 	}
537 
538 	if (vm) {
539 		set_curr_view(vm);
540 		return 0;
541 	}
542 
543 	return 1;
544 }
545 
546 void
547 foreach_view(void (*callback)(field_view *))
548 {
549 	struct view_ent *ve;
550 
551 	CIRCLEQ_FOREACH(ve, &view_head, entries) {
552 		callback(ve->view);
553 	}
554 }
555 
556 int
557 set_view_hotkey(int ch)
558 {
559 	struct view_ent *ve;
560 	field_view *v;
561 	int key = tolower(ch);
562 
563 	CIRCLEQ_FOREACH(ve, &view_head, entries) {
564 		v = ve->view;
565 		if (key == v->hotkey) {
566 			set_curr_view(ve);
567 			return 1;
568 		}
569 	}
570 
571 	return 0;
572 }
573 
574 void
575 next_view(void)
576 {
577 	struct view_ent *ve;
578 
579 	if (CIRCLEQ_EMPTY(&view_head) || curr_view_ent == NULL)
580 		return;
581 
582 	ve = CIRCLEQ_NEXT(curr_view_ent, entries);
583 	if (ve == CIRCLEQ_END(&view_head))
584 		ve = CIRCLEQ_FIRST(&view_head);
585 
586 	set_curr_view(ve);
587 }
588 
589 void
590 prev_view(void)
591 {
592 	struct view_ent *ve;
593 
594 	if (CIRCLEQ_EMPTY(&view_head) || curr_view_ent == NULL)
595 		return;
596 
597 	ve = CIRCLEQ_PREV(curr_view_ent, entries);
598 	if (ve == CIRCLEQ_END(&view_head))
599 		ve = CIRCLEQ_LAST(&view_head);
600 
601 	set_curr_view(ve);
602 }
603 
604 /* generic field printing */
605 
606 void
607 print_fld_age(field_def *fld, unsigned int age)
608 {
609 	int len;
610 	unsigned int h, m, s;
611 
612 	if (fld == NULL)
613 		return;
614 	len = fld->width;
615 
616 	if (len < 1)
617 		return;
618 
619 	s = age % 60;
620 	m = age / 60;
621 	h = m / 60;
622 	m %= 60;
623 
624 	tb_start();
625 	if (tbprintf("%02u:%02u:%02u", h, m, s) <= len)
626 		goto ok;
627 
628 	tb_start();
629 	if (tbprintf("%u", age) <= len)
630 		goto ok;
631 
632 	tb_start();
633 	age /= 60;
634 	if (tbprintf("%um", age) <= len)
635 		goto ok;
636 	if (age == 0)
637 		goto err;
638 
639 	tb_start();
640 	age /= 60;
641 	if (tbprintf("%uh", age) <= len)
642 		goto ok;
643 	if (age == 0)
644 		goto err;
645 
646 	tb_start();
647 	age /= 24;
648 	if (tbprintf("%ud", age) <= len)
649 		goto ok;
650 
651  err:
652 	print_fld_str(fld, "*");
653 	tb_end();
654 	return;
655 
656  ok:
657 	print_fld_tb(fld);
658 }
659 
660 void
661 print_fld_sdiv(field_def *fld, u_int64_t size, int div)
662 {
663 	int len;
664 
665 	if (fld == NULL)
666 		return;
667 
668 	len = fld->width;
669 	if (len < 1)
670 		return;
671 
672 	tb_start();
673 	if (tbprintf("%llu", size) <= len)
674 		goto ok;
675 
676 	tb_start();
677 	size /= div;
678 	if (tbprintf("%lluK", size) <= len)
679 		goto ok;
680 	if (size == 0)
681 		goto err;
682 
683 	tb_start();
684 	size /= div;
685 	if (tbprintf("%lluM", size) <= len)
686 		goto ok;
687 	if (size == 0)
688 		goto err;
689 
690 	tb_start();
691 	size /= div;
692 	if (tbprintf("%lluG", size) <= len)
693 		goto ok;
694 	if (size == 0)
695 		goto err;
696 
697 	tb_start();
698 	size /= div;
699 	if (tbprintf("%lluT", size) <= len)
700 		goto ok;
701 
702 err:
703 	print_fld_str(fld, "*");
704 	tb_end();
705 	return;
706 
707 ok:
708 	print_fld_tb(fld);
709 }
710 
711 void
712 print_fld_size(field_def *fld, u_int64_t size)
713 {
714 	print_fld_sdiv(fld, size, 1024);
715 }
716 
717 void
718 print_fld_ssdiv(field_def *fld, int64_t size, int div)
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 (tbprintf("%lld", size) <= len)
731 		goto ok;
732 
733 	tb_start();
734 	size /= div;
735 	if (tbprintf("%lldK", size) <= len)
736 		goto ok;
737 	if (size == 0)
738 		goto err;
739 
740 	tb_start();
741 	size /= div;
742 	if (tbprintf("%lldM", size) <= len)
743 		goto ok;
744 	if (size == 0)
745 		goto err;
746 
747 	tb_start();
748 	size /= div;
749 	if (tbprintf("%lldG", size) <= len)
750 		goto ok;
751 	if (size == 0)
752 		goto err;
753 
754 	tb_start();
755 	size /= div;
756 	if (tbprintf("%lldT", 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_ssize(field_def *fld, int64_t size)
770 {
771 	print_fld_ssdiv(fld, size, 1024);
772 }
773 
774 void
775 print_fld_rate(field_def *fld, double rate)
776 {
777 	if (rate < 0) {
778 		print_fld_str(fld, "*");
779 	} else {
780 		print_fld_size(fld, rate);
781 	}
782 }
783 
784 void
785 print_fld_bw(field_def *fld, double bw)
786 {
787 	if (bw < 0) {
788 		print_fld_str(fld, "*");
789 	} else {
790 		print_fld_sdiv(fld, bw, 1000);
791 	}
792 }
793 
794 void
795 print_fld_uint(field_def *fld, unsigned int size)
796 {
797 	int len;
798 
799 	if (fld == NULL)
800 		return;
801 
802 	len = fld->width;
803 	if (len < 1)
804 		return;
805 
806 	tb_start();
807 	if (tbprintf("%u", size) > len)
808 		print_fld_str(fld, "*");
809 	else
810 		print_fld_tb(fld);
811 	tb_end();
812 }
813 
814 
815 /* ordering */
816 
817 void
818 set_order(char *opt)
819 {
820 	order_type *o;
821 
822 	if (curr_view == NULL || curr_view->mgr == NULL)
823 		return;
824 
825 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
826 
827 	if (opt == NULL)
828 		return;
829 
830 	o = curr_view->mgr->order_list;
831 
832 	if (o == NULL)
833 		return;
834 
835 	for (;o->name != NULL; o++) {
836 		if (strcasecmp(opt, o->match) == 0) {
837 			curr_view->mgr->order_curr = o;
838 			return;
839 		}
840 	}
841 }
842 
843 int
844 set_order_hotkey(int ch)
845 {
846 	order_type *o;
847 	int key = ch;
848 
849 	if (curr_view == NULL || curr_view->mgr == NULL)
850 		return 0;
851 
852 	o = curr_view->mgr->order_list;
853 
854 	if (o == NULL)
855 		return 0;
856 
857 	for (;o->name != NULL; o++) {
858 		if (key == o->hotkey) {
859 			if (curr_view->mgr->order_curr == o) {
860 				sortdir *= -1;
861 			} else {
862 				curr_view->mgr->order_curr = o;
863 			}
864 			return 1;
865 		}
866 	}
867 
868 	return 0;
869 }
870 
871 void
872 next_order(void)
873 {
874 	order_type *o, *oc;
875 
876 	if (curr_view->mgr->order_list == NULL)
877 		return;
878 
879 	oc = curr_view->mgr->order_curr;
880 
881 	for (o = curr_view->mgr->order_list; o->name != NULL; o++) {
882 		if (oc == o) {
883 			o++;
884 			if (o->name == NULL)
885 				break;
886 			curr_view->mgr->order_curr = o;
887 			return;
888 		}
889 	}
890 
891 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
892 }
893 
894 
895 /* main program functions */
896 
897 int
898 read_view(void)
899 {
900 	if (curr_mgr == NULL)
901 		return (0);
902 
903 	if (paused)
904 		return (0);
905 
906 	if (curr_mgr->read_fn != NULL)
907 		return (curr_mgr->read_fn());
908 
909 	return (0);
910 }
911 
912 
913 int
914 disp_update(void)
915 {
916 	int lines;
917 
918 	if (maxprint < 0)
919 		dispstart = 0;
920 	else if (dispstart + maxprint > num_disp)
921 		dispstart = num_disp - maxprint;
922 
923 	if (dispstart < 0)
924 		dispstart = 0;
925 
926 	if (curr_view == NULL)
927 		return 0;
928 
929 	if (curr_mgr != NULL) {
930 		curr_line = 0;
931 
932 		if (curr_mgr->header_fn != NULL) {
933 			lines = curr_mgr->header_fn();
934 			if (lines < 0)
935 				return (1);
936 //			home_line = lines++;
937 			curr_line = ++lines;
938 			home_line = lines + maxprint + 1;
939 		}
940 
941 		print_title();
942 
943 		if (curr_mgr->print_fn != NULL)
944 			curr_mgr->print_fn();
945 	}
946 
947 	return (0);
948 }
949 
950 void
951 sort_view(void)
952 {
953 	if (curr_mgr != NULL)
954 		if (curr_mgr->sort_fn != NULL)
955 			curr_mgr->sort_fn();
956 }
957 
958 void
959 sig_close(int signal)
960 {
961 	gotsig_close = 1;
962 }
963 
964 void
965 sig_resize(int signal)
966 {
967 	gotsig_resize = 1;
968 }
969 
970 void
971 sig_alarm(int signal)
972 {
973 	gotsig_alarm = 1;
974 }
975 
976 void
977 setup_term(int dmax)
978 {
979 	max_disp = dmax;
980 	maxprint = dmax;
981 
982 	if (rawmode) {
983 		columns = rawwidth;
984 		lines = DEFAULT_HEIGHT;
985 		clear_linebuf();
986 	} else {
987 		if (dmax < 0)
988 			dmax = 0;
989 
990 		screen = newterm(NULL, stdout, stdin);
991 		if (screen == NULL) {
992 			rawmode = 1;
993 			interactive = 0;
994 			setup_term(dmax);
995 			return;
996 		}
997 		columns = COLS;
998 		lines = LINES;
999 
1000 		if (maxprint > lines - HEADER_LINES)
1001 			maxprint = lines - HEADER_LINES;
1002 
1003 		nonl();
1004 		keypad(stdscr, TRUE);
1005 		intrflush(stdscr, FALSE);
1006 
1007 		halfdelay(10);
1008 		noecho();
1009 	}
1010 
1011 	if (dmax == 0)
1012 		maxprint = lines - HEADER_LINES;
1013 
1014 	field_setup();
1015 }
1016 
1017 struct command *
1018 command_set(struct command *cmd, const char *init)
1019 {
1020 	struct command *prev = curr_cmd;
1021 
1022 	if (cmd) {
1023 		if (init) {
1024 			cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf));
1025 			if (cmd_len >= sizeof(cmdbuf)) {
1026 				cmdbuf[0] = '\0';
1027 				cmd_len = 0;
1028 			}
1029 		} else {
1030 			cmd_len = 0;
1031 			cmdbuf[0] = 0;
1032 		}
1033 	}
1034 	curr_message = NULL;
1035 	curr_cmd = cmd;
1036 	need_update = 1;
1037 	return prev;
1038 }
1039 
1040 const char *
1041 message_set(const char *msg) {
1042 	char *prev = curr_message;
1043 	if (msg)
1044 		curr_message = strdup(msg);
1045 	else
1046 		curr_message = NULL;
1047 	free(prev);
1048 	return NULL;
1049 }
1050 
1051 void
1052 print_cmdline(void)
1053 {
1054 	if (curr_cmd) {
1055 		attron(A_STANDOUT);
1056 		mvprintw(home_line, 0, "%s: ", curr_cmd->prompt);
1057 		attroff(A_STANDOUT);
1058 		printw("%s", cmdbuf);
1059 	} else if (curr_message) {
1060 		mvprintw(home_line, 0, "> %s", curr_message);
1061 	}
1062 	clrtoeol();
1063 }
1064 
1065 
1066 void
1067 cmd_keyboard(int ch)
1068 {
1069 	if (curr_cmd == NULL)
1070 		return;
1071 
1072 	if (ch > 0 && isprint(ch)) {
1073 		if (cmd_len < sizeof(cmdbuf) - 1) {
1074 			cmdbuf[cmd_len++] = ch;
1075 			cmdbuf[cmd_len] = 0;
1076 		} else
1077 			beep();
1078 	}
1079 
1080 	switch (ch) {
1081 	case KEY_ENTER:
1082 	case 0x0a:
1083 	case 0x0d:
1084 	{
1085 		struct command * c = command_set(NULL, NULL);
1086 		c->exec();
1087 		break;
1088 	}
1089 	case KEY_BACKSPACE:
1090 	case KEY_DC:
1091 	case CTRL_H:
1092 		if (cmd_len > 0) {
1093 			cmdbuf[--cmd_len] = 0;
1094 		} else
1095 			beep();
1096 		break;
1097 	case 0x1b:
1098 	case CTRL_G:
1099 		if (cmd_len > 0) {
1100 			cmdbuf[0] = '\0';
1101 			cmd_len = 0;
1102 		} else
1103 			command_set(NULL, NULL);
1104 		break;
1105 	default:
1106 		break;
1107 	}
1108 }
1109 
1110 void
1111 keyboard(void)
1112 {
1113 	int ch;
1114 
1115 	ch = getch();
1116 
1117 	if (curr_cmd) {
1118 		cmd_keyboard(ch);
1119 		print_cmdline();
1120 		return;
1121 	}
1122 
1123 	if (curr_mgr != NULL)
1124 		if (curr_mgr->key_fn != NULL)
1125 			if (curr_mgr->key_fn(ch))
1126 				return;
1127 
1128 	if (curr_message != NULL) {
1129 		if (ch > 0) {
1130 			curr_message = NULL;
1131 			need_update = 1;
1132 		}
1133 	}
1134 
1135 	switch (ch) {
1136 	case ' ':
1137 		gotsig_alarm = 1;
1138 		break;
1139 	case 'o':
1140 		next_order();
1141 		need_sort = 1;
1142 		break;
1143 	case 'p':
1144 		paused = !paused;
1145 		gotsig_alarm = 1;
1146 		break;
1147 	case 'q':
1148 		gotsig_close = 1;
1149 		break;
1150 	case 'r':
1151 		sortdir *= -1;
1152 		need_sort = 1;
1153 		break;
1154 	case 'v':
1155 		/* FALLTHROUGH */
1156 	case KEY_RIGHT:
1157 		/* FALLTHROUGH */
1158 	case CTRL_F:
1159 		next_view();
1160 		break;
1161 	case KEY_LEFT:
1162 		/* FALLTHROUGH */
1163 	case CTRL_B:
1164 		prev_view();
1165 		break;
1166 	case KEY_DOWN:
1167 		/* FALLTHROUGH */
1168 	case CTRL_N:
1169 		dispstart++;
1170 		need_update = 1;
1171 		break;
1172 	case KEY_UP:
1173 		/* FALLTHROUGH */
1174 	case CTRL_P:
1175 		dispstart--;
1176 		need_update = 1;
1177 		break;
1178 	case KEY_NPAGE:
1179 		/* FALLTHROUGH */
1180 	case CTRL_V:
1181 		dispstart += maxprint;
1182 		need_update = 1;
1183 		break;
1184 	case KEY_PPAGE:
1185 		/* FALLTHROUGH */
1186 	case META_V:
1187 		dispstart -= maxprint;
1188 		need_update = 1;
1189 		break;
1190 	case KEY_HOME:
1191 		/* FALLTHROUGH */
1192 	case CTRL_A:
1193 		dispstart = 0;
1194 		need_update = 1;
1195 		break;
1196 	case KEY_END:
1197 		/* FALLTHROUGH */
1198 	case CTRL_E:
1199 		dispstart = num_disp;
1200 		need_update = 1;
1201 		break;
1202 	case CTRL_L:
1203 		clear();
1204 		need_update = 1;
1205 		break;
1206 	default:
1207 		break;
1208 	}
1209 
1210 	if (set_order_hotkey(ch))
1211 		need_sort = 1;
1212 	else
1213 		set_view_hotkey(ch);
1214 }
1215 
1216 void
1217 engine_initialize(void)
1218 {
1219 	signal(SIGTERM, sig_close);
1220 	signal(SIGINT, sig_close);
1221 	signal(SIGQUIT, sig_close);
1222 	signal(SIGWINCH, sig_resize);
1223 	signal(SIGALRM, sig_alarm);
1224 }
1225 
1226 void
1227 engine_loop(int countmax)
1228 {
1229 	int count = 0;
1230 
1231 	for (;;) {
1232 		if (gotsig_alarm) {
1233 			read_view();
1234 			need_sort = 1;
1235 			gotsig_alarm = 0;
1236 			ualarm(udelay, 0);
1237 		}
1238 
1239 		if (need_sort) {
1240 			sort_view();
1241 			need_sort = 0;
1242 			need_update = 1;
1243 
1244 			/* XXX if sort took too long */
1245 			if (gotsig_alarm) {
1246 				gotsig_alarm = 0;
1247 				ualarm(udelay, 0);
1248 			}
1249 		}
1250 
1251 		if (need_update) {
1252 			erase();
1253 			disp_update();
1254 			end_page();
1255 			need_update = 0;
1256 			if (countmax && ++count >= countmax)
1257 				break;
1258 		}
1259 
1260 		if (gotsig_close)
1261 			break;
1262 		if (gotsig_resize) {
1263 			if (rawmode == 0)
1264 				endwin();
1265 			setup_term(max_disp);
1266 			gotsig_resize = 0;
1267 			need_update = 1;
1268 		}
1269 
1270 		if (interactive && need_update == 0)
1271 			keyboard();
1272 		else if (interactive == 0)
1273 			usleep(udelay);
1274 	}
1275 
1276 	if (rawmode == 0)
1277 		endwin();
1278 }
1279