xref: /openbsd-src/usr.bin/systat/engine.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /* $Id: engine.c,v 1.7 2008/12/07 02:56:06 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, offset;
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 				offset = (fld->width - len) / 2;
231 			} else {
232 				offset = (fld->width / 2) - (cpos - str);
233 				if (offset < 0)
234 					offset = 0;
235 				else if (offset > (fld->width - len))
236 					offset = fld->width - len;
237 			}
238 			move_horiz(fld->start + offset);
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, i, d, 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 	d = divs[i];
273 
274 	val = 0;
275 	pos = 0;
276 	tr = fld->arg % d;
277 	tw = fld->width % d;
278 
279 	tb_start();
280 	cur = 0;
281 	for(i = 0; i < d; i++) {
282 		tw += fld->width;
283 		tr += fld->arg;
284 
285 		while (tr >= d) {
286 			val++;
287 			tr -= d;
288 		}
289 		while (tw >= d) {
290 			pos++;
291 			tw -= d;
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(const 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 d)
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 /= d;
678 	if (tbprintf("%lluK", size) <= len)
679 		goto ok;
680 	if (size == 0)
681 		goto err;
682 
683 	tb_start();
684 	size /= d;
685 	if (tbprintf("%lluM", size) <= len)
686 		goto ok;
687 	if (size == 0)
688 		goto err;
689 
690 	tb_start();
691 	size /= d;
692 	if (tbprintf("%lluG", size) <= len)
693 		goto ok;
694 	if (size == 0)
695 		goto err;
696 
697 	tb_start();
698 	size /= d;
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 d)
719 {
720 	int len;
721 
722 	if (fld == NULL)
723 		return;
724 
725 	len = fld->width;
726 	if (len < 1)
727 		return;
728 
729 	tb_start();
730 	if (tbprintf("%lld", size) <= len)
731 		goto ok;
732 
733 	tb_start();
734 	size /= d;
735 	if (tbprintf("%lldK", size) <= len)
736 		goto ok;
737 	if (size == 0)
738 		goto err;
739 
740 	tb_start();
741 	size /= d;
742 	if (tbprintf("%lldM", size) <= len)
743 		goto ok;
744 	if (size == 0)
745 		goto err;
746 
747 	tb_start();
748 	size /= d;
749 	if (tbprintf("%lldG", size) <= len)
750 		goto ok;
751 	if (size == 0)
752 		goto err;
753 
754 	tb_start();
755 	size /= d;
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 void
815 print_fld_float(field_def *fld, double f, int prec)
816 {
817 	int len;
818 
819 	if (fld == NULL)
820 		return;
821 
822 	len = fld->width;
823 	if (len < 1)
824 		return;
825 
826 	tb_start();
827 	if (tbprintf("%*.*f", len, prec, f) > len)
828 		print_fld_str(fld, "*");
829 	else
830 		print_fld_tb(fld);
831 	tb_end();
832 }
833 
834 
835 /* ordering */
836 
837 void
838 set_order(const char *opt)
839 {
840 	order_type *o;
841 
842 	if (curr_view == NULL || curr_view->mgr == NULL)
843 		return;
844 
845 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
846 
847 	if (opt == NULL)
848 		return;
849 
850 	o = curr_view->mgr->order_list;
851 
852 	if (o == NULL)
853 		return;
854 
855 	for (;o->name != NULL; o++) {
856 		if (strcasecmp(opt, o->match) == 0) {
857 			curr_view->mgr->order_curr = o;
858 			return;
859 		}
860 	}
861 }
862 
863 int
864 set_order_hotkey(int ch)
865 {
866 	order_type *o;
867 	int key = ch;
868 
869 	if (curr_view == NULL || curr_view->mgr == NULL)
870 		return 0;
871 
872 	o = curr_view->mgr->order_list;
873 
874 	if (o == NULL)
875 		return 0;
876 
877 	for (;o->name != NULL; o++) {
878 		if (key == o->hotkey) {
879 			if (curr_view->mgr->order_curr == o) {
880 				sortdir *= -1;
881 			} else {
882 				curr_view->mgr->order_curr = o;
883 			}
884 			return 1;
885 		}
886 	}
887 
888 	return 0;
889 }
890 
891 void
892 next_order(void)
893 {
894 	order_type *o, *oc;
895 
896 	if (curr_view->mgr->order_list == NULL)
897 		return;
898 
899 	oc = curr_view->mgr->order_curr;
900 
901 	for (o = curr_view->mgr->order_list; o->name != NULL; o++) {
902 		if (oc == o) {
903 			o++;
904 			if (o->name == NULL)
905 				break;
906 			curr_view->mgr->order_curr = o;
907 			return;
908 		}
909 	}
910 
911 	curr_view->mgr->order_curr = curr_view->mgr->order_list;
912 }
913 
914 
915 /* main program functions */
916 
917 int
918 read_view(void)
919 {
920 	if (curr_mgr == NULL)
921 		return (0);
922 
923 	if (paused)
924 		return (0);
925 
926 	if (curr_mgr->read_fn != NULL)
927 		return (curr_mgr->read_fn());
928 
929 	return (0);
930 }
931 
932 
933 int
934 disp_update(void)
935 {
936 	int li;
937 
938 	if (maxprint < 0)
939 		dispstart = 0;
940 	else if (dispstart + maxprint > num_disp)
941 		dispstart = num_disp - maxprint;
942 
943 	if (dispstart < 0)
944 		dispstart = 0;
945 
946 	if (curr_view == NULL)
947 		return 0;
948 
949 	if (curr_mgr != NULL) {
950 		curr_line = 0;
951 
952 		if (curr_mgr->header_fn != NULL) {
953 			li = curr_mgr->header_fn();
954 			if (li < 0)
955 				return (1);
956 			curr_line = ++li;
957 			home_line = li + maxprint + 1;
958 		}
959 
960 		print_title();
961 
962 		if (curr_mgr->print_fn != NULL)
963 			curr_mgr->print_fn();
964 	}
965 
966 	return (0);
967 }
968 
969 void
970 sort_view(void)
971 {
972 	if (curr_mgr != NULL)
973 		if (curr_mgr->sort_fn != NULL)
974 			curr_mgr->sort_fn();
975 }
976 
977 void
978 sig_close(int sig)
979 {
980 	gotsig_close = 1;
981 }
982 
983 void
984 sig_resize(int sig)
985 {
986 	gotsig_resize = 1;
987 }
988 
989 void
990 sig_alarm(int sig)
991 {
992 	gotsig_alarm = 1;
993 }
994 
995 void
996 setup_term(int dmax)
997 {
998 	max_disp = dmax;
999 	maxprint = dmax;
1000 
1001 	if (rawmode) {
1002 		columns = rawwidth;
1003 		lines = DEFAULT_HEIGHT;
1004 		clear_linebuf();
1005 	} else {
1006 		if (dmax < 0)
1007 			dmax = 0;
1008 
1009 		screen = newterm(NULL, stdout, stdin);
1010 		if (screen == NULL) {
1011 			rawmode = 1;
1012 			interactive = 0;
1013 			setup_term(dmax);
1014 			return;
1015 		}
1016 		columns = COLS;
1017 		lines = LINES;
1018 
1019 		if (maxprint > lines - HEADER_LINES)
1020 			maxprint = lines - HEADER_LINES;
1021 
1022 		nonl();
1023 		keypad(stdscr, TRUE);
1024 		intrflush(stdscr, FALSE);
1025 
1026 		halfdelay(10);
1027 		noecho();
1028 	}
1029 
1030 	if (dmax == 0)
1031 		maxprint = lines - HEADER_LINES;
1032 
1033 	field_setup();
1034 }
1035 
1036 struct command *
1037 command_set(struct command *cmd, const char *init)
1038 {
1039 	struct command *prev = curr_cmd;
1040 
1041 	if (cmd) {
1042 		if (init) {
1043 			cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf));
1044 			if (cmd_len >= sizeof(cmdbuf)) {
1045 				cmdbuf[0] = '\0';
1046 				cmd_len = 0;
1047 			}
1048 		} else {
1049 			cmd_len = 0;
1050 			cmdbuf[0] = 0;
1051 		}
1052 	}
1053 	curr_message = NULL;
1054 	curr_cmd = cmd;
1055 	need_update = 1;
1056 	return prev;
1057 }
1058 
1059 const char *
1060 message_set(const char *msg) {
1061 	char *prev = curr_message;
1062 	if (msg)
1063 		curr_message = strdup(msg);
1064 	else
1065 		curr_message = NULL;
1066 	free(prev);
1067 	return NULL;
1068 }
1069 
1070 void
1071 print_cmdline(void)
1072 {
1073 	if (curr_cmd) {
1074 		attron(A_STANDOUT);
1075 		mvprintw(home_line, 0, "%s: ", curr_cmd->prompt);
1076 		attroff(A_STANDOUT);
1077 		printw("%s", cmdbuf);
1078 	} else if (curr_message) {
1079 		mvprintw(home_line, 0, "> %s", curr_message);
1080 	}
1081 	clrtoeol();
1082 }
1083 
1084 
1085 void
1086 cmd_keyboard(int ch)
1087 {
1088 	if (curr_cmd == NULL)
1089 		return;
1090 
1091 	if (ch > 0 && isprint(ch)) {
1092 		if (cmd_len < sizeof(cmdbuf) - 1) {
1093 			cmdbuf[cmd_len++] = ch;
1094 			cmdbuf[cmd_len] = 0;
1095 		} else
1096 			beep();
1097 	}
1098 
1099 	switch (ch) {
1100 	case KEY_ENTER:
1101 	case 0x0a:
1102 	case 0x0d:
1103 	{
1104 		struct command * c = command_set(NULL, NULL);
1105 		c->exec(cmdbuf);
1106 		break;
1107 	}
1108 	case KEY_BACKSPACE:
1109 	case KEY_DC:
1110 	case CTRL_H:
1111 		if (cmd_len > 0) {
1112 			cmdbuf[--cmd_len] = 0;
1113 		} else
1114 			beep();
1115 		break;
1116 	case 0x1b:
1117 	case CTRL_G:
1118 		if (cmd_len > 0) {
1119 			cmdbuf[0] = '\0';
1120 			cmd_len = 0;
1121 		} else
1122 			command_set(NULL, NULL);
1123 		break;
1124 	default:
1125 		break;
1126 	}
1127 }
1128 
1129 void
1130 keyboard(void)
1131 {
1132 	int ch;
1133 
1134 	ch = getch();
1135 
1136 	if (curr_cmd) {
1137 		cmd_keyboard(ch);
1138 		print_cmdline();
1139 		return;
1140 	}
1141 
1142 	if (curr_mgr != NULL)
1143 		if (curr_mgr->key_fn != NULL)
1144 			if (curr_mgr->key_fn(ch))
1145 				return;
1146 
1147 	if (curr_message != NULL) {
1148 		if (ch > 0) {
1149 			curr_message = NULL;
1150 			need_update = 1;
1151 		}
1152 	}
1153 
1154 	switch (ch) {
1155 	case ' ':
1156 		gotsig_alarm = 1;
1157 		break;
1158 	case 'o':
1159 		next_order();
1160 		need_sort = 1;
1161 		break;
1162 	case 'p':
1163 		paused = !paused;
1164 		gotsig_alarm = 1;
1165 		break;
1166 	case 'q':
1167 		gotsig_close = 1;
1168 		break;
1169 	case 'r':
1170 		sortdir *= -1;
1171 		need_sort = 1;
1172 		break;
1173 	case 'v':
1174 		/* FALLTHROUGH */
1175 	case KEY_RIGHT:
1176 		/* FALLTHROUGH */
1177 	case CTRL_F:
1178 		next_view();
1179 		break;
1180 	case KEY_LEFT:
1181 		/* FALLTHROUGH */
1182 	case CTRL_B:
1183 		prev_view();
1184 		break;
1185 	case KEY_DOWN:
1186 		/* FALLTHROUGH */
1187 	case CTRL_N:
1188 		dispstart++;
1189 		need_update = 1;
1190 		break;
1191 	case KEY_UP:
1192 		/* FALLTHROUGH */
1193 	case CTRL_P:
1194 		dispstart--;
1195 		need_update = 1;
1196 		break;
1197 	case KEY_NPAGE:
1198 		/* FALLTHROUGH */
1199 	case CTRL_V:
1200 		dispstart += maxprint;
1201 		need_update = 1;
1202 		break;
1203 	case KEY_PPAGE:
1204 		/* FALLTHROUGH */
1205 	case META_V:
1206 		dispstart -= maxprint;
1207 		need_update = 1;
1208 		break;
1209 	case KEY_HOME:
1210 		/* FALLTHROUGH */
1211 	case CTRL_A:
1212 		dispstart = 0;
1213 		need_update = 1;
1214 		break;
1215 	case KEY_END:
1216 		/* FALLTHROUGH */
1217 	case CTRL_E:
1218 		dispstart = num_disp;
1219 		need_update = 1;
1220 		break;
1221 	case CTRL_L:
1222 		clear();
1223 		need_update = 1;
1224 		break;
1225 	default:
1226 		break;
1227 	}
1228 
1229 	if (set_order_hotkey(ch))
1230 		need_sort = 1;
1231 	else
1232 		set_view_hotkey(ch);
1233 }
1234 
1235 void
1236 engine_initialize(void)
1237 {
1238 	signal(SIGTERM, sig_close);
1239 	signal(SIGINT, sig_close);
1240 	signal(SIGQUIT, sig_close);
1241 	signal(SIGWINCH, sig_resize);
1242 	signal(SIGALRM, sig_alarm);
1243 }
1244 
1245 void
1246 engine_loop(int countmax)
1247 {
1248 	int count = 0;
1249 
1250 	for (;;) {
1251 		if (gotsig_alarm) {
1252 			read_view();
1253 			need_sort = 1;
1254 			gotsig_alarm = 0;
1255 			ualarm(udelay, 0);
1256 		}
1257 
1258 		if (need_sort) {
1259 			sort_view();
1260 			need_sort = 0;
1261 			need_update = 1;
1262 
1263 			/* XXX if sort took too long */
1264 			if (gotsig_alarm) {
1265 				gotsig_alarm = 0;
1266 				ualarm(udelay, 0);
1267 			}
1268 		}
1269 
1270 		if (need_update) {
1271 			erase();
1272 			disp_update();
1273 			end_page();
1274 			need_update = 0;
1275 			if (countmax && ++count >= countmax)
1276 				break;
1277 		}
1278 
1279 		if (gotsig_close)
1280 			break;
1281 		if (gotsig_resize) {
1282 			if (rawmode == 0)
1283 				endwin();
1284 			setup_term(max_disp);
1285 			gotsig_resize = 0;
1286 			need_update = 1;
1287 		}
1288 
1289 		if (interactive && need_update == 0)
1290 			keyboard();
1291 		else if (interactive == 0)
1292 			usleep(udelay);
1293 	}
1294 
1295 	if (rawmode == 0)
1296 		endwin();
1297 }
1298