1 /* $Id: engine.c,v 1.18 2015/01/19 07:39:24 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 while (cur < pos - len) { 351 tbprintf(" "); 352 cur++; 353 } 354 tbprintf("%s", buf); 355 cur += len; 356 } 357 358 print_fld_tb(fld); 359 } 360 361 void 362 print_fld_bar(field_def *fld, int value) 363 { 364 int i, tw, val, cur; 365 366 if (fld->width < 1) 367 return; 368 369 val = 0; 370 tw = fld->arg / 2; 371 372 tb_start(); 373 cur = 0; 374 for(i = 0; i < fld->width; i++) { 375 tw += fld->arg; 376 377 while (tw >= fld->width) { 378 val++; 379 tw -= fld->width; 380 } 381 if (val > value) 382 break; 383 tbprintf("#"); 384 } 385 386 print_fld_tb(fld); 387 } 388 389 void 390 print_fld_tb(field_def *fld) 391 { 392 print_fld_str(fld, tmp_buf); 393 tb_end(); 394 } 395 396 void 397 print_title(void) 398 { 399 field_def **fp; 400 401 if (curr_view != NULL && curr_view->view != NULL) { 402 for (fp = curr_view->view; *fp != NULL; fp++) { 403 switch((*fp)->align) { 404 case FLD_ALIGN_LEFT: 405 case FLD_ALIGN_RIGHT: 406 case FLD_ALIGN_CENTER: 407 case FLD_ALIGN_COLUMN: 408 print_fld_str(*fp, (*fp)->title); 409 break; 410 case FLD_ALIGN_BAR: 411 print_bar_title(*fp); 412 break; 413 } 414 } 415 } 416 end_line(); 417 } 418 419 /* view related functions */ 420 void 421 hide_field(field_def *fld) 422 { 423 if (fld == NULL) 424 return; 425 426 fld->flags |= FLD_FLAG_HIDDEN; 427 } 428 429 void 430 show_field(field_def *fld) 431 { 432 if (fld == NULL) 433 return; 434 435 fld->flags &= ~((unsigned int) FLD_FLAG_HIDDEN); 436 } 437 438 void 439 reset_fields(void) 440 { 441 field_def **fp; 442 field_def *fld; 443 444 if (curr_view == NULL) 445 return; 446 447 if (curr_view->view == NULL) 448 return; 449 450 for (fp = curr_view->view; *fp != NULL; fp++) { 451 fld = *fp; 452 fld->start = -1; 453 fld->width = fld->norm_width; 454 } 455 } 456 457 void 458 field_setup(void) 459 { 460 field_def **fp; 461 field_def *fld; 462 int st, fwid, change; 463 int width = columns; 464 465 reset_fields(); 466 467 dispstart = 0; 468 st = 0; 469 470 for (fp = curr_view->view; *fp != NULL; fp++) { 471 fld = *fp; 472 if (fld->flags & FLD_FLAG_HIDDEN) 473 continue; 474 475 if (width <= 1) 476 break; 477 478 if (st != 1) 479 width--; 480 481 fld->start = 1; 482 fwid = fld->width; 483 st++; 484 if (fwid >= width) { 485 fld->width = width; 486 width = 0; 487 } else 488 width -= fwid; 489 } 490 491 change = 0; 492 while (width > 0) { 493 change = 0; 494 for (fp = curr_view->view; *fp != NULL; fp++) { 495 fld = *fp; 496 if (fld->flags & FLD_FLAG_HIDDEN) 497 continue; 498 if ((fld->width < fld->max_width) && 499 (fld->increment <= width)) { 500 int w = fld->width + fld->increment; 501 if (w > fld->max_width) 502 w = fld->max_width; 503 width += fld->width - w; 504 fld->width = w; 505 change = 1; 506 } 507 if (width <= 0) break; 508 } 509 if (change == 0) break; 510 } 511 512 st = 0; 513 for (fp = curr_view->view; *fp != NULL; fp++) { 514 fld = *fp; 515 if (fld->flags & FLD_FLAG_HIDDEN) 516 continue; 517 if (fld->start < 0) break; 518 fld->start = st; 519 st += fld->width + 1; 520 } 521 } 522 523 void 524 set_curr_view(struct view_ent *ve) 525 { 526 field_view *v; 527 528 reset_fields(); 529 530 if (ve == NULL) { 531 curr_view_ent = NULL; 532 curr_view = NULL; 533 curr_mgr = NULL; 534 return; 535 } 536 537 v = ve->view; 538 539 if ((curr_view != NULL) && (curr_mgr != v->mgr)) { 540 gotsig_alarm = 1; 541 if (v->mgr != NULL && v->mgr->select_fn != NULL) 542 v->mgr->select_fn(); 543 } 544 545 curr_view_ent = ve; 546 curr_view = v; 547 curr_mgr = v->mgr; 548 field_setup(); 549 need_update = 1; 550 } 551 552 void 553 add_view(field_view *fv) 554 { 555 struct view_ent *ent; 556 557 if (fv == NULL) 558 return; 559 560 if (fv->view == NULL || fv->name == NULL || fv->mgr == NULL) 561 return; 562 563 ent = malloc(sizeof(struct view_ent)); 564 if (ent == NULL) 565 return; 566 567 ent->view = fv; 568 TAILQ_INSERT_TAIL(&view_head, ent, entries); 569 570 if (curr_view == NULL) 571 set_curr_view(ent); 572 } 573 574 int 575 set_view(const char *opt) 576 { 577 struct view_ent *ve, *vm = NULL; 578 field_view *v; 579 int len; 580 581 if (opt == NULL || (len = strlen(opt)) == 0) 582 return 1; 583 584 TAILQ_FOREACH(ve, &view_head, entries) { 585 v = ve->view; 586 if (strncasecmp(opt, v->name, len) == 0) { 587 if (vm) 588 return 1; 589 vm = ve; 590 } 591 } 592 593 if (vm) { 594 set_curr_view(vm); 595 return 0; 596 } 597 598 return 1; 599 } 600 601 void 602 foreach_view(void (*callback)(field_view *)) 603 { 604 struct view_ent *ve; 605 606 TAILQ_FOREACH(ve, &view_head, entries) { 607 callback(ve->view); 608 } 609 } 610 611 int 612 set_view_hotkey(int ch) 613 { 614 struct view_ent *ve; 615 field_view *v; 616 int key = tolower(ch); 617 618 TAILQ_FOREACH(ve, &view_head, entries) { 619 v = ve->view; 620 if (key == v->hotkey) { 621 set_curr_view(ve); 622 return 1; 623 } 624 } 625 626 return 0; 627 } 628 629 void 630 next_view(void) 631 { 632 struct view_ent *ve; 633 634 if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL) 635 return; 636 637 ve = TAILQ_NEXT(curr_view_ent, entries); 638 if (ve == NULL) 639 ve = TAILQ_FIRST(&view_head); 640 641 set_curr_view(ve); 642 } 643 644 void 645 prev_view(void) 646 { 647 struct view_ent *ve; 648 649 if (TAILQ_EMPTY(&view_head) || curr_view_ent == NULL) 650 return; 651 652 ve = TAILQ_PREV(curr_view_ent, view_list, entries); 653 if (ve == NULL) 654 ve = TAILQ_LAST(&view_head, view_list); 655 656 set_curr_view(ve); 657 } 658 659 /* generic field printing */ 660 661 void 662 print_fld_age(field_def *fld, unsigned int age) 663 { 664 int len; 665 unsigned int h, m, s; 666 667 if (fld == NULL) 668 return; 669 len = fld->width; 670 671 if (len < 1) 672 return; 673 674 s = age % 60; 675 m = age / 60; 676 h = m / 60; 677 m %= 60; 678 679 tb_start(); 680 if (tbprintf("%02u:%02u:%02u", h, m, s) <= len) 681 goto ok; 682 683 tb_start(); 684 if (tbprintf("%u", age) <= len) 685 goto ok; 686 687 tb_start(); 688 age /= 60; 689 if (tbprintf("%um", age) <= len) 690 goto ok; 691 if (age == 0) 692 goto err; 693 694 tb_start(); 695 age /= 60; 696 if (tbprintf("%uh", age) <= len) 697 goto ok; 698 if (age == 0) 699 goto err; 700 701 tb_start(); 702 age /= 24; 703 if (tbprintf("%ud", age) <= len) 704 goto ok; 705 706 err: 707 print_fld_str(fld, "*"); 708 tb_end(); 709 return; 710 711 ok: 712 print_fld_tb(fld); 713 } 714 715 void 716 print_fld_sdiv(field_def *fld, u_int64_t size, int d) 717 { 718 int len; 719 720 if (fld == NULL) 721 return; 722 723 len = fld->width; 724 if (len < 1) 725 return; 726 727 tb_start(); 728 if (tbprintft("%llu", size) <= len) 729 goto ok; 730 731 tb_start(); 732 size /= d; 733 if (tbprintft("%lluK", size) <= len) 734 goto ok; 735 if (size == 0) 736 goto err; 737 738 tb_start(); 739 size /= d; 740 if (tbprintft("%lluM", size) <= len) 741 goto ok; 742 if (size == 0) 743 goto err; 744 745 tb_start(); 746 size /= d; 747 if (tbprintft("%lluG", size) <= len) 748 goto ok; 749 if (size == 0) 750 goto err; 751 752 tb_start(); 753 size /= d; 754 if (tbprintft("%lluT", size) <= len) 755 goto ok; 756 757 err: 758 print_fld_str(fld, "*"); 759 tb_end(); 760 return; 761 762 ok: 763 print_fld_tb(fld); 764 } 765 766 void 767 print_fld_size(field_def *fld, u_int64_t size) 768 { 769 print_fld_sdiv(fld, size, 1024); 770 } 771 772 void 773 print_fld_ssdiv(field_def *fld, int64_t size, int d) 774 { 775 int len; 776 777 if (fld == NULL) 778 return; 779 780 len = fld->width; 781 if (len < 1) 782 return; 783 784 tb_start(); 785 if (tbprintft("%lld", size) <= len) 786 goto ok; 787 788 tb_start(); 789 size /= d; 790 if (tbprintft("%lldK", size) <= len) 791 goto ok; 792 if (size == 0) 793 goto err; 794 795 tb_start(); 796 size /= d; 797 if (tbprintft("%lldM", size) <= len) 798 goto ok; 799 if (size == 0) 800 goto err; 801 802 tb_start(); 803 size /= d; 804 if (tbprintft("%lldG", size) <= len) 805 goto ok; 806 if (size == 0) 807 goto err; 808 809 tb_start(); 810 size /= d; 811 if (tbprintft("%lldT", size) <= len) 812 goto ok; 813 814 err: 815 print_fld_str(fld, "*"); 816 tb_end(); 817 return; 818 819 ok: 820 print_fld_tb(fld); 821 } 822 823 void 824 print_fld_ssize(field_def *fld, int64_t size) 825 { 826 print_fld_ssdiv(fld, size, 1024); 827 } 828 829 void 830 print_fld_rate(field_def *fld, double rate) 831 { 832 if (rate < 0) { 833 print_fld_str(fld, "*"); 834 } else { 835 print_fld_size(fld, rate); 836 } 837 } 838 839 void 840 print_fld_bw(field_def *fld, double bw) 841 { 842 if (bw < 0) { 843 print_fld_str(fld, "*"); 844 } else { 845 print_fld_sdiv(fld, bw, 1000); 846 } 847 } 848 849 void 850 print_fld_uint(field_def *fld, unsigned int size) 851 { 852 int len; 853 854 if (fld == NULL) 855 return; 856 857 len = fld->width; 858 if (len < 1) 859 return; 860 861 tb_start(); 862 if (tbprintft("%u", size) > len) 863 print_fld_str(fld, "*"); 864 else 865 print_fld_tb(fld); 866 tb_end(); 867 } 868 869 void 870 print_fld_float(field_def *fld, double f, int prec) 871 { 872 int len; 873 874 if (fld == NULL) 875 return; 876 877 len = fld->width; 878 if (len < 1) 879 return; 880 881 tb_start(); 882 if (tbprintf("%*.*f", len, prec, f) > len) 883 print_fld_str(fld, "*"); 884 else 885 print_fld_tb(fld); 886 tb_end(); 887 } 888 889 890 /* ordering */ 891 892 void 893 set_order(const char *opt) 894 { 895 order_type *o; 896 897 if (curr_view == NULL || curr_view->mgr == NULL) 898 return; 899 900 curr_view->mgr->order_curr = curr_view->mgr->order_list; 901 902 if (opt == NULL) 903 return; 904 905 o = curr_view->mgr->order_list; 906 907 if (o == NULL) 908 return; 909 910 for (;o->name != NULL; o++) { 911 if (strcasecmp(opt, o->match) == 0) { 912 curr_view->mgr->order_curr = o; 913 return; 914 } 915 } 916 } 917 918 int 919 set_order_hotkey(int ch) 920 { 921 order_type *o; 922 int key = ch; 923 924 if (curr_view == NULL || curr_view->mgr == NULL) 925 return 0; 926 927 o = curr_view->mgr->order_list; 928 929 if (o == NULL) 930 return 0; 931 932 for (;o->name != NULL; o++) { 933 if (key == o->hotkey) { 934 if (curr_view->mgr->order_curr == o) { 935 sortdir *= -1; 936 } else { 937 curr_view->mgr->order_curr = o; 938 } 939 return 1; 940 } 941 } 942 943 return 0; 944 } 945 946 void 947 next_order(void) 948 { 949 order_type *o, *oc; 950 951 if (curr_view->mgr->order_list == NULL) 952 return; 953 954 oc = curr_view->mgr->order_curr; 955 956 for (o = curr_view->mgr->order_list; o->name != NULL; o++) { 957 if (oc == o) { 958 o++; 959 if (o->name == NULL) 960 break; 961 curr_view->mgr->order_curr = o; 962 return; 963 } 964 } 965 966 curr_view->mgr->order_curr = curr_view->mgr->order_list; 967 } 968 969 970 /* main program functions */ 971 972 int 973 read_view(void) 974 { 975 if (curr_mgr == NULL) 976 return (0); 977 978 if (paused) 979 return (0); 980 981 if (curr_mgr->read_fn != NULL) 982 return (curr_mgr->read_fn()); 983 984 return (0); 985 } 986 987 988 int 989 disp_update(void) 990 { 991 int li; 992 993 if (maxprint < 0) 994 dispstart = 0; 995 else if (dispstart + maxprint > num_disp) 996 dispstart = num_disp - maxprint; 997 998 if (dispstart < 0) 999 dispstart = 0; 1000 1001 if (curr_view == NULL) 1002 return 0; 1003 1004 if (curr_mgr != NULL) { 1005 curr_line = 0; 1006 1007 if (curr_mgr->header_fn != NULL) { 1008 li = curr_mgr->header_fn(); 1009 if (li < 0) 1010 return (1); 1011 curr_line = ++li; 1012 home_line = li + maxprint + 1; 1013 } 1014 1015 print_title(); 1016 1017 if (curr_mgr->print_fn != NULL) 1018 curr_mgr->print_fn(); 1019 } 1020 1021 return (0); 1022 } 1023 1024 void 1025 sort_view(void) 1026 { 1027 if (curr_mgr != NULL) 1028 if (curr_mgr->sort_fn != NULL) 1029 curr_mgr->sort_fn(); 1030 } 1031 1032 void 1033 sig_close(int sig) 1034 { 1035 gotsig_close = 1; 1036 } 1037 1038 void 1039 sig_resize(int sig) 1040 { 1041 gotsig_resize = 1; 1042 } 1043 1044 void 1045 sig_alarm(int sig) 1046 { 1047 gotsig_alarm = 1; 1048 } 1049 1050 void 1051 setup_term(int dmax) 1052 { 1053 max_disp = dmax; 1054 maxprint = dmax; 1055 1056 if (rawmode) { 1057 columns = rawwidth; 1058 lines = DEFAULT_HEIGHT; 1059 clear_linebuf(); 1060 } else { 1061 if (dmax < 0) 1062 dmax = 0; 1063 1064 screen = newterm(NULL, stdout, stdin); 1065 if (screen == NULL) { 1066 rawmode = 1; 1067 interactive = 0; 1068 setup_term(dmax); 1069 return; 1070 } 1071 columns = COLS; 1072 lines = LINES; 1073 1074 if (maxprint > lines - HEADER_LINES) 1075 maxprint = lines - HEADER_LINES; 1076 1077 nonl(); 1078 keypad(stdscr, TRUE); 1079 intrflush(stdscr, FALSE); 1080 1081 halfdelay(10); 1082 noecho(); 1083 } 1084 1085 if (dmax == 0) 1086 maxprint = lines - HEADER_LINES; 1087 1088 field_setup(); 1089 } 1090 1091 void 1092 do_resize_term(void) 1093 { 1094 struct winsize ws; 1095 1096 if (rawmode) 1097 return; 1098 1099 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1) 1100 return; 1101 1102 resizeterm(ws.ws_row, ws.ws_col); 1103 1104 columns = COLS; 1105 lines = LINES; 1106 1107 maxprint = max_disp; 1108 1109 if (maxprint == 0 || maxprint > lines - HEADER_LINES) 1110 maxprint = lines - HEADER_LINES; 1111 1112 clear(); 1113 1114 field_setup(); 1115 } 1116 1117 struct command * 1118 command_set(struct command *cmd, const char *init) 1119 { 1120 struct command *prev = curr_cmd; 1121 1122 if (cmd) { 1123 if (init) { 1124 cmd_len = strlcpy(cmdbuf, init, sizeof(cmdbuf)); 1125 if (cmd_len >= sizeof(cmdbuf)) { 1126 cmdbuf[0] = '\0'; 1127 cmd_len = 0; 1128 } 1129 } else { 1130 cmd_len = 0; 1131 cmdbuf[0] = 0; 1132 } 1133 } 1134 curr_message = NULL; 1135 curr_cmd = cmd; 1136 need_update = 1; 1137 return prev; 1138 } 1139 1140 const char * 1141 message_set(const char *msg) { 1142 char *prev = curr_message; 1143 if (msg) 1144 curr_message = strdup(msg); 1145 else 1146 curr_message = NULL; 1147 free(prev); 1148 return NULL; 1149 } 1150 1151 void 1152 print_cmdline(void) 1153 { 1154 if (curr_cmd) { 1155 attron(A_STANDOUT); 1156 mvprintw(home_line, 0, "%s: ", curr_cmd->prompt); 1157 attroff(A_STANDOUT); 1158 printw("%s", cmdbuf); 1159 } else if (curr_message) { 1160 mvprintw(home_line, 0, "> %s", curr_message); 1161 } 1162 clrtoeol(); 1163 } 1164 1165 1166 void 1167 cmd_keyboard(int ch) 1168 { 1169 if (curr_cmd == NULL) 1170 return; 1171 1172 if (ch > 0 && isprint(ch)) { 1173 if (cmd_len < sizeof(cmdbuf) - 1) { 1174 cmdbuf[cmd_len++] = ch; 1175 cmdbuf[cmd_len] = 0; 1176 } else 1177 beep(); 1178 } 1179 1180 switch (ch) { 1181 case KEY_ENTER: 1182 case 0x0a: 1183 case 0x0d: 1184 { 1185 struct command * c = command_set(NULL, NULL); 1186 c->exec(cmdbuf); 1187 break; 1188 } 1189 case KEY_BACKSPACE: 1190 case KEY_DC: 1191 case CTRL_H: 1192 if (cmd_len > 0) { 1193 cmdbuf[--cmd_len] = 0; 1194 } else 1195 beep(); 1196 break; 1197 case 0x1b: 1198 case CTRL_G: 1199 if (cmd_len > 0) { 1200 cmdbuf[0] = '\0'; 1201 cmd_len = 0; 1202 } else 1203 command_set(NULL, NULL); 1204 break; 1205 default: 1206 break; 1207 } 1208 } 1209 1210 void 1211 keyboard(void) 1212 { 1213 int ch; 1214 1215 ch = getch(); 1216 1217 if (curr_cmd) { 1218 cmd_keyboard(ch); 1219 print_cmdline(); 1220 return; 1221 } 1222 1223 if (curr_mgr != NULL) 1224 if (curr_mgr->key_fn != NULL) 1225 if (curr_mgr->key_fn(ch)) 1226 return; 1227 1228 if (curr_message != NULL) { 1229 if (ch > 0) { 1230 curr_message = NULL; 1231 need_update = 1; 1232 } 1233 } 1234 1235 switch (ch) { 1236 case ' ': 1237 gotsig_alarm = 1; 1238 break; 1239 case 'o': 1240 next_order(); 1241 need_sort = 1; 1242 break; 1243 case 'p': 1244 paused = !paused; 1245 gotsig_alarm = 1; 1246 break; 1247 case 'q': 1248 gotsig_close = 1; 1249 break; 1250 case 'r': 1251 sortdir *= -1; 1252 need_sort = 1; 1253 break; 1254 case 'v': 1255 /* FALLTHROUGH */ 1256 case KEY_RIGHT: 1257 /* FALLTHROUGH */ 1258 case CTRL_F: 1259 next_view(); 1260 break; 1261 case KEY_LEFT: 1262 /* FALLTHROUGH */ 1263 case CTRL_B: 1264 prev_view(); 1265 break; 1266 case KEY_DOWN: 1267 /* FALLTHROUGH */ 1268 case CTRL_N: 1269 dispstart++; 1270 need_update = 1; 1271 break; 1272 case KEY_UP: 1273 /* FALLTHROUGH */ 1274 case CTRL_P: 1275 dispstart--; 1276 need_update = 1; 1277 break; 1278 case KEY_NPAGE: 1279 /* FALLTHROUGH */ 1280 case CTRL_V: 1281 dispstart += maxprint; 1282 need_update = 1; 1283 break; 1284 case KEY_PPAGE: 1285 /* FALLTHROUGH */ 1286 case META_V: 1287 dispstart -= maxprint; 1288 need_update = 1; 1289 break; 1290 case KEY_HOME: 1291 /* FALLTHROUGH */ 1292 case CTRL_A: 1293 dispstart = 0; 1294 need_update = 1; 1295 break; 1296 case KEY_END: 1297 /* FALLTHROUGH */ 1298 case CTRL_E: 1299 dispstart = num_disp; 1300 need_update = 1; 1301 break; 1302 case CTRL_L: 1303 clear(); 1304 need_update = 1; 1305 break; 1306 default: 1307 break; 1308 } 1309 1310 if (set_order_hotkey(ch)) 1311 need_sort = 1; 1312 else 1313 set_view_hotkey(ch); 1314 } 1315 1316 void 1317 engine_initialize(void) 1318 { 1319 signal(SIGTERM, sig_close); 1320 signal(SIGINT, sig_close); 1321 signal(SIGQUIT, sig_close); 1322 signal(SIGWINCH, sig_resize); 1323 signal(SIGALRM, sig_alarm); 1324 } 1325 1326 void 1327 engine_loop(int countmax) 1328 { 1329 int count = 0; 1330 1331 for (;;) { 1332 if (gotsig_alarm) { 1333 read_view(); 1334 need_sort = 1; 1335 gotsig_alarm = 0; 1336 ualarm(udelay, 0); 1337 } 1338 1339 if (need_sort) { 1340 sort_view(); 1341 need_sort = 0; 1342 need_update = 1; 1343 1344 /* XXX if sort took too long */ 1345 if (gotsig_alarm) { 1346 gotsig_alarm = 0; 1347 ualarm(udelay, 0); 1348 } 1349 } 1350 1351 if (need_update) { 1352 erase(); 1353 if (!averageonly || 1354 (averageonly && count == countmax - 1)) 1355 disp_update(); 1356 end_page(); 1357 need_update = 0; 1358 if (countmax && ++count >= countmax) 1359 break; 1360 } 1361 1362 if (gotsig_close) 1363 break; 1364 if (gotsig_resize) { 1365 do_resize_term(); 1366 gotsig_resize = 0; 1367 need_update = 1; 1368 } 1369 1370 if (interactive && need_update == 0) 1371 keyboard(); 1372 else if (interactive == 0) 1373 usleep(udelay); 1374 } 1375 1376 if (rawmode == 0) 1377 endwin(); 1378 } 1379 1380 int 1381 check_termcap(void) 1382 { 1383 char *term_name; 1384 int status; 1385 static struct termios screen_settings; 1386 1387 if (!interactive) 1388 /* pretend we have a dumb terminal */ 1389 return(1); 1390 1391 /* get the terminal name */ 1392 term_name = getenv("TERM"); 1393 if (term_name == NULL) 1394 return(1); 1395 1396 /* now get the termcap entry */ 1397 if ((status = tgetent(NULL, term_name)) != 1) { 1398 if (status == -1) 1399 warnx("can't open termcap file"); 1400 else 1401 warnx("no termcap entry for a `%s' terminal", 1402 term_name); 1403 1404 /* pretend it's dumb and proceed */ 1405 return(1); 1406 } 1407 1408 /* "hardcopy" immediately indicates a very stupid terminal */ 1409 if (tgetflag("hc")) 1410 return(1); 1411 1412 /* get necessary capabilities */ 1413 if (tgetstr("cl", NULL) == NULL || tgetstr("cm", NULL) == NULL) 1414 return(1); 1415 1416 /* if stdout is not a terminal, pretend we are a dumb terminal */ 1417 if (tcgetattr(STDOUT_FILENO, &screen_settings) == -1) 1418 return(1); 1419 1420 return(0); 1421 } 1422