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