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