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