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