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