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