1 /* $Id: engine.c,v 1.21 2017/04/05 15:57:11 deraadt 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 void 894 set_order(const char *opt) 895 { 896 order_type *o; 897 898 if (curr_view == NULL || curr_view->mgr == NULL) 899 return; 900 901 curr_view->mgr->order_curr = curr_view->mgr->order_list; 902 903 if (opt == NULL) 904 return; 905 906 o = curr_view->mgr->order_list; 907 908 if (o == NULL) 909 return; 910 911 for (;o->name != NULL; o++) { 912 if (strcasecmp(opt, o->match) == 0) { 913 curr_view->mgr->order_curr = o; 914 return; 915 } 916 } 917 } 918 919 int 920 set_order_hotkey(int ch) 921 { 922 order_type *o; 923 int key = ch; 924 925 if (curr_view == NULL || curr_view->mgr == NULL) 926 return 0; 927 928 o = curr_view->mgr->order_list; 929 930 if (o == NULL) 931 return 0; 932 933 for (;o->name != NULL; o++) { 934 if (key == o->hotkey) { 935 if (curr_view->mgr->order_curr == o) { 936 sortdir *= -1; 937 } else { 938 curr_view->mgr->order_curr = o; 939 } 940 return 1; 941 } 942 } 943 944 return 0; 945 } 946 947 void 948 next_order(void) 949 { 950 order_type *o, *oc; 951 952 if (curr_view->mgr->order_list == NULL) 953 return; 954 955 oc = curr_view->mgr->order_curr; 956 957 for (o = curr_view->mgr->order_list; o->name != NULL; o++) { 958 if (oc == o) { 959 o++; 960 if (o->name == NULL) 961 break; 962 curr_view->mgr->order_curr = o; 963 return; 964 } 965 } 966 967 curr_view->mgr->order_curr = curr_view->mgr->order_list; 968 } 969 970 971 /* main program functions */ 972 973 int 974 read_view(void) 975 { 976 if (curr_mgr == NULL) 977 return (0); 978 979 if (paused) 980 return (0); 981 982 if (curr_mgr->read_fn != NULL) 983 return (curr_mgr->read_fn()); 984 985 return (0); 986 } 987 988 989 int 990 disp_update(void) 991 { 992 int li; 993 994 if (maxprint < 0) 995 dispstart = 0; 996 else if (dispstart + maxprint > num_disp) 997 dispstart = num_disp - maxprint; 998 999 if (dispstart < 0) 1000 dispstart = 0; 1001 1002 if (curr_view == NULL) 1003 return 0; 1004 1005 if (curr_mgr != NULL) { 1006 curr_line = 0; 1007 1008 if (curr_mgr->header_fn != NULL) { 1009 li = curr_mgr->header_fn(); 1010 if (li < 0) 1011 return (1); 1012 curr_line = ++li; 1013 home_line = li + maxprint + 1; 1014 } 1015 1016 print_title(); 1017 1018 if (curr_mgr->print_fn != NULL) 1019 curr_mgr->print_fn(); 1020 } 1021 1022 return (0); 1023 } 1024 1025 void 1026 sort_view(void) 1027 { 1028 if (curr_mgr != NULL) 1029 if (curr_mgr->sort_fn != NULL) 1030 curr_mgr->sort_fn(); 1031 } 1032 1033 void 1034 sig_close(int sig) 1035 { 1036 gotsig_close = 1; 1037 } 1038 1039 void 1040 sig_resize(int sig) 1041 { 1042 gotsig_resize = 1; 1043 } 1044 1045 void 1046 sig_alarm(int sig) 1047 { 1048 gotsig_alarm = 1; 1049 } 1050 1051 void 1052 setup_term(int dmax) 1053 { 1054 max_disp = dmax; 1055 maxprint = dmax; 1056 1057 if (rawmode) { 1058 columns = rawwidth; 1059 lines = DEFAULT_HEIGHT; 1060 clear_linebuf(); 1061 } else { 1062 if (dmax < 0) 1063 dmax = 0; 1064 1065 screen = newterm(NULL, stdout, stdin); 1066 if (screen == NULL) { 1067 rawmode = 1; 1068 interactive = 0; 1069 setup_term(dmax); 1070 return; 1071 } 1072 columns = COLS; 1073 lines = LINES; 1074 1075 if (maxprint > lines - HEADER_LINES) 1076 maxprint = lines - HEADER_LINES; 1077 1078 nonl(); 1079 keypad(stdscr, TRUE); 1080 intrflush(stdscr, FALSE); 1081 1082 halfdelay(10); 1083 noecho(); 1084 } 1085 1086 if (dmax == 0) 1087 maxprint = lines - HEADER_LINES; 1088 1089 field_setup(); 1090 } 1091 1092 void 1093 do_resize_term(void) 1094 { 1095 struct winsize ws; 1096 1097 if (rawmode) 1098 return; 1099 1100 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) 1101 return; 1102 1103 resizeterm(ws.ws_row, ws.ws_col); 1104 1105 columns = COLS; 1106 lines = LINES; 1107 1108 maxprint = max_disp; 1109 1110 if (maxprint == 0 || maxprint > lines - HEADER_LINES) 1111 maxprint = lines - HEADER_LINES; 1112 1113 clear(); 1114 1115 field_setup(); 1116 } 1117 1118 struct command * 1119 command_set(struct command *cmd, const char *init) 1120 { 1121 struct command *prev = curr_cmd; 1122 1123 if (cmd) { 1124 if (init) { 1125 cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf)); 1126 if (cmd_len >= sizeof(cmdbuf)) { 1127 cmdbuf[0] = '\0'; 1128 cmd_len = 0; 1129 } 1130 } else { 1131 cmd_len = 0; 1132 cmdbuf[0] = 0; 1133 } 1134 } 1135 curr_message = NULL; 1136 curr_cmd = cmd; 1137 need_update = 1; 1138 return prev; 1139 } 1140 1141 const char * 1142 message_set(const char *msg) { 1143 char *prev = curr_message; 1144 if (msg) 1145 curr_message = strdup(msg); 1146 else 1147 curr_message = NULL; 1148 free(prev); 1149 return NULL; 1150 } 1151 1152 void 1153 print_cmdline(void) 1154 { 1155 if (curr_cmd) { 1156 attron(A_STANDOUT); 1157 mvprintw(home_line, 0, "%s: ", curr_cmd->prompt); 1158 attroff(A_STANDOUT); 1159 printw("%s", cmdbuf); 1160 } else if (curr_message) { 1161 mvprintw(home_line, 0, "> %s", curr_message); 1162 } 1163 clrtoeol(); 1164 } 1165 1166 1167 void 1168 cmd_keyboard(int ch) 1169 { 1170 if (curr_cmd == NULL) 1171 return; 1172 1173 if (ch > 0 && isprint(ch)) { 1174 if (cmd_len < sizeof(cmdbuf) - 1) { 1175 cmdbuf[cmd_len++] = ch; 1176 cmdbuf[cmd_len] = 0; 1177 } else 1178 beep(); 1179 } 1180 1181 switch (ch) { 1182 case KEY_ENTER: 1183 case 0x0a: 1184 case 0x0d: 1185 { 1186 struct command * c = command_set(NULL, NULL); 1187 c->exec(cmdbuf); 1188 break; 1189 } 1190 case KEY_BACKSPACE: 1191 case KEY_DC: 1192 case CTRL_H: 1193 if (cmd_len > 0) { 1194 cmdbuf[--cmd_len] = 0; 1195 } else 1196 beep(); 1197 break; 1198 case 0x1b: 1199 case CTRL_G: 1200 if (cmd_len > 0) { 1201 cmdbuf[0] = '\0'; 1202 cmd_len = 0; 1203 } else 1204 command_set(NULL, NULL); 1205 break; 1206 default: 1207 break; 1208 } 1209 } 1210 1211 void 1212 keyboard(void) 1213 { 1214 int ch; 1215 1216 ch = getch(); 1217 1218 if (curr_cmd) { 1219 cmd_keyboard(ch); 1220 print_cmdline(); 1221 return; 1222 } 1223 1224 if (curr_mgr != NULL) 1225 if (curr_mgr->key_fn != NULL) 1226 if (curr_mgr->key_fn(ch)) 1227 return; 1228 1229 if (curr_message != NULL) { 1230 if (ch > 0) { 1231 curr_message = NULL; 1232 need_update = 1; 1233 } 1234 } 1235 1236 switch (ch) { 1237 case ' ': 1238 gotsig_alarm = 1; 1239 break; 1240 case 'o': 1241 next_order(); 1242 need_sort = 1; 1243 break; 1244 case 'p': 1245 paused = !paused; 1246 gotsig_alarm = 1; 1247 break; 1248 case 'q': 1249 gotsig_close = 1; 1250 break; 1251 case 'r': 1252 sortdir *= -1; 1253 need_sort = 1; 1254 break; 1255 case 'v': 1256 /* FALLTHROUGH */ 1257 case KEY_RIGHT: 1258 /* FALLTHROUGH */ 1259 case CTRL_F: 1260 next_view(); 1261 break; 1262 case KEY_LEFT: 1263 /* FALLTHROUGH */ 1264 case CTRL_B: 1265 prev_view(); 1266 break; 1267 case KEY_DOWN: 1268 /* FALLTHROUGH */ 1269 case CTRL_N: 1270 dispstart++; 1271 need_update = 1; 1272 break; 1273 case KEY_UP: 1274 /* FALLTHROUGH */ 1275 case CTRL_P: 1276 dispstart--; 1277 need_update = 1; 1278 break; 1279 case KEY_NPAGE: 1280 /* FALLTHROUGH */ 1281 case CTRL_V: 1282 dispstart += maxprint; 1283 need_update = 1; 1284 break; 1285 case KEY_PPAGE: 1286 /* FALLTHROUGH */ 1287 case META_V: 1288 dispstart -= maxprint; 1289 need_update = 1; 1290 break; 1291 case KEY_HOME: 1292 /* FALLTHROUGH */ 1293 case CTRL_A: 1294 dispstart = 0; 1295 need_update = 1; 1296 break; 1297 case KEY_END: 1298 /* FALLTHROUGH */ 1299 case CTRL_E: 1300 dispstart = num_disp; 1301 need_update = 1; 1302 break; 1303 case CTRL_L: 1304 clear(); 1305 need_update = 1; 1306 break; 1307 default: 1308 break; 1309 } 1310 1311 if (set_order_hotkey(ch)) 1312 need_sort = 1; 1313 else 1314 set_view_hotkey(ch); 1315 } 1316 1317 void 1318 engine_initialize(void) 1319 { 1320 signal(SIGTERM, sig_close); 1321 signal(SIGINT, sig_close); 1322 signal(SIGQUIT, sig_close); 1323 signal(SIGWINCH, sig_resize); 1324 signal(SIGALRM, sig_alarm); 1325 } 1326 1327 void 1328 engine_loop(int countmax) 1329 { 1330 int count = 0; 1331 1332 for (;;) { 1333 if (gotsig_alarm) { 1334 read_view(); 1335 need_sort = 1; 1336 gotsig_alarm = 0; 1337 ualarm(udelay, 0); 1338 } 1339 1340 if (need_sort) { 1341 sort_view(); 1342 need_sort = 0; 1343 need_update = 1; 1344 1345 /* XXX if sort took too long */ 1346 if (gotsig_alarm) { 1347 gotsig_alarm = 0; 1348 ualarm(udelay, 0); 1349 } 1350 } 1351 1352 if (need_update) { 1353 erase(); 1354 if (!averageonly || 1355 (averageonly && count == countmax - 1)) 1356 disp_update(); 1357 end_page(); 1358 need_update = 0; 1359 if (countmax && ++count >= countmax) 1360 break; 1361 } 1362 1363 if (gotsig_close) 1364 break; 1365 if (gotsig_resize) { 1366 do_resize_term(); 1367 gotsig_resize = 0; 1368 need_update = 1; 1369 } 1370 1371 if (interactive && need_update == 0) 1372 keyboard(); 1373 else if (interactive == 0) 1374 usleep(udelay); 1375 } 1376 1377 if (rawmode == 0) 1378 endwin(); 1379 } 1380 1381 int 1382 check_termcap(void) 1383 { 1384 char *term_name; 1385 int status; 1386 static struct termios screen_settings; 1387 1388 if (!interactive) 1389 /* pretend we have a dumb terminal */ 1390 return(1); 1391 1392 /* get the terminal name */ 1393 term_name = getenv("TERM"); 1394 if (term_name == NULL) 1395 return(1); 1396 1397 /* now get the termcap entry */ 1398 if ((status = tgetent(NULL, term_name)) != 1) { 1399 if (status == -1) 1400 warnx("can't open termcap file"); 1401 else 1402 warnx("no termcap entry for a `%s' terminal", 1403 term_name); 1404 1405 /* pretend it's dumb and proceed */ 1406 return(1); 1407 } 1408 1409 /* "hardcopy" immediately indicates a very stupid terminal */ 1410 if (tgetflag("hc")) 1411 return(1); 1412 1413 /* get necessary capabilities */ 1414 if (tgetstr("cl", NULL) == NULL || tgetstr("cm", NULL) == NULL) 1415 return(1); 1416 1417 /* if stdout is not a terminal, pretend we are a dumb terminal */ 1418 if (tcgetattr(STDOUT_FILENO, &screen_settings) == -1) 1419 return(1); 1420 1421 return(0); 1422 } 1423