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