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