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