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 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 117 tb_end(void) 118 { 119 tb_ptr = NULL; 120 tb_len = 0; 121 } 122 123 int 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 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 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 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 230 clear_linebuf(void) 231 { 232 memset(linebuf, ' ', MAX_LINE_BUF); 233 } 234 235 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 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 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 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 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 397 print_fld_tb(field_def *fld) 398 { 399 print_fld_str(fld, tmp_buf); 400 tb_end(); 401 } 402 403 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 428 hide_field(field_def *fld) 429 { 430 if (fld == NULL) 431 return; 432 433 fld->flags |= FLD_FLAG_HIDDEN; 434 } 435 436 void 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 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 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 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 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 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 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 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 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 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 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 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 763 print_fld_size(field_def *fld, u_int64_t size) 764 { 765 print_fld_sdiv(fld, size, 1024); 766 } 767 768 void 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 820 print_fld_ssize(field_def *fld, int64_t size) 821 { 822 print_fld_ssdiv(fld, size, 1024); 823 } 824 825 void 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 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 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 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 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 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 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 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 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 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 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 1044 sig_close(int sig) 1045 { 1046 gotsig_close = 1; 1047 } 1048 1049 void 1050 sig_resize(int sig) 1051 { 1052 gotsig_resize = 1; 1053 } 1054 1055 void 1056 sig_alarm(int sig) 1057 { 1058 gotsig_alarm = 1; 1059 } 1060 1061 void 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 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 * 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 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 * 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 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 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 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 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 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 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 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