display.c revision 146344
118334Speter/* 2169689Skan * Top users/processes display for Unix 3169689Skan * Version 3 4169689Skan * 518334Speter * This program may be freely redistributed, 690075Sobrien * but this entire comment MUST remain intact. 718334Speter * 890075Sobrien * Copyright (c) 1984, 1989, William LeFebvre, Rice University 990075Sobrien * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 1090075Sobrien * 1190075Sobrien * $FreeBSD: head/contrib/top/display.c 146344 2005-05-18 13:48:33Z keramida $ 1218334Speter */ 1390075Sobrien 1490075Sobrien/* 1590075Sobrien * This file contains the routines that display information on the screen. 1690075Sobrien * Each section of the screen has two routines: one for initially writing 1718334Speter * all constant and dynamic text, and one for only updating the text that 1818334Speter * changes. The prefix "i_" is used on all the "initial" routines and the 1990075Sobrien * prefix "u_" is used for all the "updating" routines. 20169689Skan * 21169689Skan * ASSUMPTIONS: 2218334Speter * None of the "i_" routines use any of the termcap capabilities. 2318334Speter * In this way, those routines can be safely used on terminals that 2418334Speter * have minimal (or nonexistant) terminal capabilities. 2518334Speter * 2618334Speter * The routines are called in this order: *_loadave, i_timeofday, 27169689Skan * *_procstates, *_cpustates, *_memory, *_message, *_header, 2818334Speter * *_process, u_endscreen. 2918334Speter */ 3050397Sobrien 31132718Skan#include "os.h" 32132718Skan#include <ctype.h> 3390075Sobrien#include <time.h> 3418334Speter#include <sys/time.h> 35169689Skan 3618334Speter#include "screen.h" /* interface to screen package */ 3790075Sobrien#include "layout.h" /* defines for screen position layout */ 3818334Speter#include "display.h" 3918334Speter#include "top.h" 4050397Sobrien#include "top.local.h" 4150397Sobrien#include "boolean.h" 4252284Sobrien#include "machine.h" /* we should eliminate this!!! */ 4390075Sobrien#include "utils.h" 4490075Sobrien 45169689Skan#ifdef DEBUG 46169689SkanFILE *debug; 47169689Skan#endif 4818334Speter 49169689Skan/* imported from screen.c */ 50169689Skanextern int overstrike; 51169689Skan 52169689Skanstatic int lmpid = 0; 53169689Skanstatic int last_hi = 0; /* used in u_process and u_endscreen */ 54169689Skanstatic int lastline = 0; 55169689Skanstatic int display_width = MAX_COLS; 56169689Skan 57169689Skan#define lineindex(l) ((l)*display_width) 58169689Skan 59169689Skanchar *printable(); 60169689Skan 61169689Skan/* things initialized by display_init and used thruout */ 62169689Skan 63169689Skan/* buffer of proc information lines for display updating */ 64169689Skanchar *screenbuf = NULL; 65169689Skan 66169689Skanstatic char **procstate_names; 67169689Skanstatic char **cpustate_names; 68169689Skanstatic char **memory_names; 69169689Skanstatic char **swap_names; 70169689Skan 7118334Speterstatic int num_procstates; 7218334Speterstatic int num_cpustates; 7318334Speterstatic int num_memory; 7418334Speterstatic int num_swap; 75132718Skan 76132718Skanstatic int *lprocstates; 7790075Sobrienstatic int *lcpustates; 78169689Skanstatic int *lmemory; 79132718Skanstatic int *lswap; 80169689Skan 81169689Skanstatic int *cpustate_columns; 82169689Skanstatic int cpustate_total_length; 83169689Skan 84132718Skanstatic enum { OFF, ON, ERASE } header_status = ON; 85132718Skan 86132718Skanstatic int string_count(); 87132718Skanstatic void summary_format(); 88169689Skanstatic void line_update(); 89132718Skan 90132718Skanint display_resize() 91132718Skan 92132718Skan{ 93132718Skan register int lines; 94132718Skan 95132718Skan /* first, deallocate any previous buffer that may have been there */ 96169689Skan if (screenbuf != NULL) 97169689Skan { 98132718Skan free(screenbuf); 99132718Skan } 100132718Skan 101132718Skan /* calculate the current dimensions */ 102132718Skan /* if operating in "dumb" mode, we only need one line */ 103132718Skan lines = smart_terminal ? screen_length - Header_lines : 1; 104132718Skan 105169689Skan if (lines < 0) 106169689Skan lines = 0; 107169689Skan /* we don't want more than MAX_COLS columns, since the machine-dependent 108169689Skan modules make static allocations based on MAX_COLS and we don't want 109169689Skan to run off the end of their buffers */ 11018334Speter display_width = screen_width; 111169689Skan if (display_width >= MAX_COLS) 112169689Skan { 113169689Skan display_width = MAX_COLS - 1; 114169689Skan } 115169689Skan 116169689Skan /* now, allocate space for the screen buffer */ 117169689Skan screenbuf = (char *)malloc(lines * display_width); 118169689Skan if (screenbuf == (char *)NULL) 119169689Skan { 120169689Skan /* oops! */ 121169689Skan return(-1); 122169689Skan } 123169689Skan 124169689Skan /* return number of lines available */ 125169689Skan /* for dumb terminals, pretend like we can show any amount */ 126169689Skan return(smart_terminal ? lines : Largest); 127169689Skan} 128169689Skan 129169689Skanint display_init(statics) 130169689Skan 131169689Skanstruct statics *statics; 132169689Skan 133169689Skan{ 134169689Skan register int lines; 135169689Skan register char **pp; 136169689Skan register int *ip; 137169689Skan register int i; 138169689Skan 139169689Skan /* call resize to do the dirty work */ 140169689Skan lines = display_resize(); 14118334Speter 14218334Speter /* only do the rest if we need to */ 14318334Speter if (lines > -1) 14418334Speter { 145132718Skan /* save pointers and allocate space for names */ 14618334Speter procstate_names = statics->procstate_names; 14718334Speter num_procstates = string_count(procstate_names); 14818334Speter lprocstates = (int *)malloc(num_procstates * sizeof(int)); 14990075Sobrien 15052284Sobrien cpustate_names = statics->cpustate_names; 15152284Sobrien 15218334Speter swap_names = statics->swap_names; 15390075Sobrien num_swap = string_count(swap_names); 15418334Speter lswap = (int *)malloc(num_swap * sizeof(int)); 15518334Speter num_cpustates = string_count(cpustate_names); 156117395Skan lcpustates = (int *)malloc(num_cpustates * sizeof(int)); 15718334Speter cpustate_columns = (int *)malloc(num_cpustates * sizeof(int)); 15818334Speter 15918334Speter memory_names = statics->memory_names; 16018334Speter num_memory = string_count(memory_names); 16118334Speter lmemory = (int *)malloc(num_memory * sizeof(int)); 16218334Speter 16318334Speter /* calculate starting columns where needed */ 16418334Speter cpustate_total_length = 0; 165132718Skan pp = cpustate_names; 16618334Speter ip = cpustate_columns; 16752284Sobrien while (*pp != NULL) 16818334Speter { 16918334Speter *ip++ = cpustate_total_length; 17018334Speter if ((i = strlen(*pp++)) > 0) 17118334Speter { 17218334Speter cpustate_total_length += i + 8; 17318334Speter } 17418334Speter } 175169689Skan } 17618334Speter 17718334Speter /* return number of lines available */ 17818334Speter return(lines); 17918334Speter} 18018334Speter 18118334Speteri_loadave(mpid, avenrun) 18218334Speter 18318334Speterint mpid; 18452284Sobriendouble *avenrun; 18518334Speter 18618334Speter{ 18718334Speter register int i; 18852284Sobrien 18918334Speter /* i_loadave also clears the screen, since it is first */ 19018334Speter clear(); 19118334Speter 19252284Sobrien /* mpid == -1 implies this system doesn't have an _mpid */ 19318334Speter if (mpid != -1) 19418334Speter { 19518334Speter printf("last pid: %5d; ", mpid); 19618334Speter } 19718334Speter 19818334Speter printf("load averages"); 19918334Speter 20018334Speter for (i = 0; i < 3; i++) 20118334Speter { 20296263Sobrien printf("%c %5.2f", 20396263Sobrien i == 0 ? ':' : ',', 20496263Sobrien avenrun[i]); 20596263Sobrien } 20696263Sobrien lmpid = mpid; 20718334Speter} 20818334Speter 20918334Speteru_loadave(mpid, avenrun) 21018334Speter 21118334Speterint mpid; 21218334Speterdouble *avenrun; 21318334Speter 214169689Skan{ 21518334Speter register int i; 21618334Speter 21718334Speter if (mpid != -1) 218169689Skan { 219169689Skan /* change screen only when value has really changed */ 22018334Speter if (mpid != lmpid) 22118334Speter { 222169689Skan Move_to(x_lastpid, y_lastpid); 22318334Speter printf("%5d", mpid); 22418334Speter lmpid = mpid; 22518334Speter } 226117395Skan 227117395Skan /* i remembers x coordinate to move to */ 228117395Skan i = x_loadave; 229117395Skan } 230132718Skan else 231117395Skan { 232117395Skan i = x_loadave_nompid; 233117395Skan } 234117395Skan 235117395Skan /* move into position for load averages */ 236117395Skan Move_to(i, y_loadave); 237117395Skan 238169689Skan /* display new load averages */ 239169689Skan /* we should optimize this and only display changes */ 240169689Skan for (i = 0; i < 3; i++) 241117395Skan { 242117395Skan printf("%s%5.2f", 243117395Skan i == 0 ? "" : ", ", 244117395Skan avenrun[i]); 245117395Skan } 246117395Skan} 24718334Speter 24818334Speteri_timeofday(tod) 24918334Speter 25018334Spetertime_t *tod; 251132718Skan 25218334Speter{ 253132718Skan /* 25452750Sobrien * Display the current time. 25518334Speter * "ctime" always returns a string that looks like this: 256169689Skan * 257169689Skan * Sun Sep 16 01:03:52 1973 258169689Skan * 012345678901234567890123 259169689Skan * 1 2 260169689Skan * 261169689Skan * We want indices 11 thru 18 (length 8). 262169689Skan */ 263169689Skan 264169689Skan if (smart_terminal) 265169689Skan { 266169689Skan Move_to(screen_width - 8, 0); 26718334Speter } 268169689Skan else 26918334Speter { 270169689Skan fputs(" ", stdout); 271169689Skan } 272169689Skan#ifdef DEBUG 27318334Speter { 27418334Speter char *foo; 275169689Skan foo = ctime(tod); 27618334Speter fputs(foo, stdout); 27790075Sobrien } 27890075Sobrien#endif 27918334Speter printf("%-8.8s\n", &(ctime(tod)[11])); 28018334Speter lastline = 1; 28118334Speter} 28218334Speter 28318334Speterstatic int ltotal = 0; 28418334Speterstatic char procstates_buffer[MAX_COLS]; 28518334Speter 28618334Speter/* 28718334Speter * *_procstates(total, brkdn, names) - print the process summary line 28818334Speter * 28918334Speter * Assumptions: cursor is at the beginning of the line on entry 29018334Speter * lastline is valid 291169689Skan */ 292169689Skan 293169689Skani_procstates(total, brkdn) 29450397Sobrien 295169689Skanint total; 29618334Speterint *brkdn; 297169689Skan 298169689Skan{ 299169689Skan register int i; 300169689Skan 30118334Speter /* write current number of processes and remember the value */ 302169689Skan printf("%d processes:", total); 303169689Skan ltotal = total; 304169689Skan 305169689Skan /* put out enough spaces to get to column 15 */ 30618334Speter i = digits(total); 307169689Skan while (i++ < 4) 30818334Speter { 30918334Speter putchar(' '); 31018334Speter } 31118334Speter 312169689Skan /* format and print the process state summary */ 31318334Speter summary_format(procstates_buffer, brkdn, procstate_names); 31452284Sobrien fputs(procstates_buffer, stdout); 31552284Sobrien 316169689Skan /* save the numbers for next time */ 317169689Skan memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 318169689Skan} 319169689Skan 32018334Speteru_procstates(total, brkdn) 32118334Speter 32218334Speterint total; 32318334Speterint *brkdn; 324169689Skan 325169689Skan{ 326169689Skan static char new[MAX_COLS]; 327169689Skan register int i; 328169689Skan 329169689Skan /* update number of processes only if it has changed */ 330169689Skan if (ltotal != total) 331169689Skan { 332169689Skan /* move and overwrite */ 333169689Skan#if (x_procstate == 0) 334169689Skan Move_to(x_procstate, y_procstate); 335169689Skan#else 336169689Skan /* cursor is already there...no motion needed */ 337169689Skan /* assert(lastline == 1); */ 338169689Skan#endif 339169689Skan printf("%d", total); 340169689Skan 341169689Skan /* if number of digits differs, rewrite the label */ 342169689Skan if (digits(total) != digits(ltotal)) 343169689Skan { 344169689Skan fputs(" processes:", stdout); 345169689Skan /* put out enough spaces to get to column 15 */ 346169689Skan i = digits(total); 34718334Speter while (i++ < 4) 348169689Skan { 349169689Skan putchar(' '); 35018334Speter } 351169689Skan /* cursor may end up right where we want it!!! */ 352169689Skan } 35318334Speter 354169689Skan /* save new total */ 355169689Skan ltotal = total; 356169689Skan } 357169689Skan 358169689Skan /* see if any of the state numbers has changed */ 359169689Skan if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) 360169689Skan { 361169689Skan /* format and update the line */ 362169689Skan summary_format(new, brkdn, procstate_names); 363169689Skan line_update(procstates_buffer, new, x_brkdn, y_brkdn); 364169689Skan memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); 365169689Skan } 366169689Skan} 367169689Skan 368169689Skan/* 369169689Skan * *_cpustates(states, names) - print the cpu state percentages 370169689Skan * 371169689Skan * Assumptions: cursor is on the PREVIOUS line 372169689Skan */ 373169689Skan 374169689Skanstatic int cpustates_column; 37518334Speter 37618334Speter/* cpustates_tag() calculates the correct tag to use to label the line */ 37718334Speter 378169689Skanchar *cpustates_tag() 379169689Skan 380169689Skan{ 381169689Skan register char *use; 382169689Skan 383169689Skan static char *short_tag = "CPU: "; 384169689Skan static char *long_tag = "CPU states: "; 385169689Skan 386169689Skan /* if length + strlen(long_tag) >= screen_width, then we have to 387169689Skan use the shorter tag (we subtract 2 to account for ": ") */ 388169689Skan if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) 389169689Skan { 390169689Skan use = short_tag; 39118334Speter } 39218334Speter else 39318334Speter { 39418334Speter use = long_tag; 395169689Skan } 39618334Speter 39718334Speter /* set cpustates_column accordingly then return result */ 39818334Speter cpustates_column = strlen(use); 39918334Speter return(use); 40018334Speter} 40118334Speter 40218334Speteri_cpustates(states) 403169689Skan 40418334Speterregister int *states; 405169689Skan 40618334Speter{ 40718334Speter register int i = 0; 40818334Speter register int value; 40918334Speter register char **names = cpustate_names; 41018334Speter register char *thisname; 411169689Skan 412169689Skan /* print tag and bump lastline */ 413169689Skan printf("\n%s", cpustates_tag()); 41418334Speter lastline++; 41518334Speter 41618334Speter /* now walk thru the names and print the line */ 41718334Speter while ((thisname = *names++) != NULL) 418169689Skan { 419169689Skan if (*thisname != '\0') 42018334Speter { 42118334Speter /* retrieve the value and remember it */ 42218334Speter value = *states++; 42318334Speter 424169689Skan /* if percentage is >= 1000, print it as 100% */ 425169689Skan printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"), 426169689Skan i++ == 0 ? "" : ", ", 42718334Speter ((float)value)/10., 42818334Speter thisname); 42918334Speter } 43018334Speter } 43118334Speter 43218334Speter /* copy over values into "last" array */ 43318334Speter memcpy(lcpustates, states, num_cpustates * sizeof(int)); 43418334Speter} 43518334Speter 43618334Speteru_cpustates(states) 43718334Speter 43818334Speterregister int *states; 43918334Speter 44018334Speter{ 44118334Speter register int value; 44218334Speter register char **names = cpustate_names; 44318334Speter register char *thisname; 44418334Speter register int *lp; 44518334Speter register int *colp; 44618334Speter 44718334Speter Move_to(cpustates_column, y_cpustates); 44818334Speter lastline = y_cpustates; 44918334Speter lp = lcpustates; 45018334Speter colp = cpustate_columns; 451132718Skan 45218334Speter /* we could be much more optimal about this */ 45318334Speter while ((thisname = *names++) != NULL) 45418334Speter { 45518334Speter if (*thisname != '\0') 45618334Speter { 45718334Speter /* did the value change since last time? */ 45818334Speter if (*lp != *states) 459169689Skan { 460169689Skan /* yes, move and change */ 461169689Skan Move_to(cpustates_column + *colp, y_cpustates); 462169689Skan lastline = y_cpustates; 46318334Speter 46418334Speter /* retrieve value and remember it */ 465169689Skan value = *states; 466169689Skan 467169689Skan /* if percentage is >= 1000, print it as 100% */ 468169689Skan printf((value >= 1000 ? "%4.0f" : "%4.1f"), 469169689Skan ((double)value)/10.); 470169689Skan 471169689Skan /* remember it for next time */ 472169689Skan *lp = value; 473169689Skan } 474169689Skan } 475169689Skan 476169689Skan /* increment and move on */ 477169689Skan lp++; 478169689Skan states++; 47918334Speter colp++; 48018334Speter } 48118334Speter} 48218334Speter 48318334Speterz_cpustates() 484169689Skan 485169689Skan{ 486169689Skan register int i = 0; 487169689Skan register char **names = cpustate_names; 48818334Speter register char *thisname; 48918334Speter register int *lp; 490169689Skan 491169689Skan /* show tag and bump lastline */ 492169689Skan printf("\n%s", cpustates_tag()); 493169689Skan lastline++; 494169689Skan 495169689Skan while ((thisname = *names++) != NULL) 496169689Skan { 497169689Skan if (*thisname != '\0') 498169689Skan { 499169689Skan printf("%s %% %s", i++ == 0 ? "" : ", ", thisname); 500169689Skan } 501169689Skan } 502169689Skan 503169689Skan /* fill the "last" array with all -1s, to insure correct updating */ 50418334Speter lp = lcpustates; 505169689Skan i = num_cpustates; 50618334Speter while (--i >= 0) 50718334Speter { 50818334Speter *lp++ = -1; 509169689Skan } 51018334Speter} 511169689Skan 51250397Sobrien/* 51318334Speter * *_memory(stats) - print "Memory: " followed by the memory summary string 51418334Speter * 51518334Speter * Assumptions: cursor is on "lastline" 51618334Speter * for i_memory ONLY: cursor is on the previous line 51718334Speter */ 51818334Speter 51918334Speterchar memory_buffer[MAX_COLS]; 520169689Skan 521169689Skani_memory(stats) 522169689Skan 523169689Skanint *stats; 524169689Skan 525169689Skan{ 526169689Skan fputs("\nMem: ", stdout); 527169689Skan lastline++; 528169689Skan 529169689Skan /* format and print the memory summary */ 530169689Skan summary_format(memory_buffer, stats, memory_names); 531169689Skan fputs(memory_buffer, stdout); 532169689Skan} 533169689Skan 534169689Skanu_memory(stats) 535169689Skan 536169689Skanint *stats; 537169689Skan 538169689Skan{ 539169689Skan static char new[MAX_COLS]; 540169689Skan 541169689Skan /* format the new line */ 542169689Skan summary_format(new, stats, memory_names); 543169689Skan line_update(memory_buffer, new, x_mem, y_mem); 544169689Skan} 545169689Skan 546169689Skan/* 547169689Skan * *_swap(stats) - print "Swap: " followed by the swap summary string 548169689Skan * 549169689Skan * Assumptions: cursor is on "lastline" 550169689Skan * for i_swap ONLY: cursor is on the previous line 551169689Skan */ 552169689Skan 553169689Skanchar swap_buffer[MAX_COLS]; 554169689Skan 555169689Skani_swap(stats) 556169689Skan 557169689Skanint *stats; 558169689Skan 559169689Skan{ 560169689Skan fputs("\nSwap: ", stdout); 561169689Skan lastline++; 562169689Skan 563169689Skan /* format and print the swap summary */ 564169689Skan summary_format(swap_buffer, stats, swap_names); 565169689Skan fputs(swap_buffer, stdout); 566169689Skan} 567169689Skan 568169689Skanu_swap(stats) 569169689Skan 570169689Skanint *stats; 571169689Skan 572169689Skan{ 573169689Skan static char new[MAX_COLS]; 574169689Skan 575169689Skan /* format the new line */ 576169689Skan summary_format(new, stats, swap_names); 577169689Skan line_update(swap_buffer, new, x_swap, y_swap); 578169689Skan} 579169689Skan 580169689Skan/* 581169689Skan * *_message() - print the next pending message line, or erase the one 582169689Skan * that is there. 583169689Skan * 584169689Skan * Note that u_message is (currently) the same as i_message. 585169689Skan * 586169689Skan * Assumptions: lastline is consistent 587169689Skan */ 588169689Skan 589169689Skan/* 590169689Skan * i_message is funny because it gets its message asynchronously (with 591169689Skan * respect to screen updates). 592169689Skan */ 593169689Skan 594169689Skanstatic char next_msg[MAX_COLS + 5]; 595169689Skanstatic int msglen = 0; 596169689Skan/* Invariant: msglen is always the length of the message currently displayed 597169689Skan on the screen (even when next_msg doesn't contain that message). */ 598169689Skan 599169689Skani_message() 600169689Skan 601169689Skan{ 602169689Skan while (lastline < y_message) 603169689Skan { 604169689Skan fputc('\n', stdout); 605169689Skan lastline++; 606169689Skan } 607169689Skan if (next_msg[0] != '\0') 608169689Skan { 609169689Skan standout(next_msg); 610169689Skan msglen = strlen(next_msg); 611169689Skan next_msg[0] = '\0'; 612169689Skan } 613169689Skan else if (msglen > 0) 614169689Skan { 615169689Skan (void) clear_eol(msglen); 616169689Skan msglen = 0; 617169689Skan } 618169689Skan} 619169689Skan 620169689Skanu_message() 621169689Skan 622169689Skan{ 623169689Skan i_message(); 624169689Skan} 625169689Skan 626169689Skanstatic int header_length; 627169689Skan 628169689Skan/* 629169689Skan * Trim a header string to the current display width and return a newly 630169689Skan * allocated area with the trimmed header. 631169689Skan */ 632169689Skan 633169689Skanchar * 634169689Skantrim_header(text) 635169689Skan 636169689Skanchar *text; 637169689Skan 638169689Skan{ 639169689Skan char *s; 640169689Skan int width; 641169689Skan 642169689Skan s = NULL; 643169689Skan width = display_width; 644169689Skan header_length = strlen(text); 645169689Skan if (header_length >= width) { 646169689Skan s = malloc((width + 1) * sizeof(char)); 647169689Skan if (s == NULL) 648169689Skan return (NULL); 649169689Skan strncpy(s, text, width); 650169689Skan s[width] = '\0'; 651169689Skan } 652169689Skan return (s); 653169689Skan} 654169689Skan 655169689Skan/* 656169689Skan * *_header(text) - print the header for the process area 657169689Skan * 658169689Skan * Assumptions: cursor is on the previous line and lastline is consistent 659169689Skan */ 660169689Skan 661169689Skani_header(text) 662169689Skan 663169689Skanchar *text; 664169689Skan 665169689Skan{ 666169689Skan char *s; 667169689Skan 668169689Skan s = trim_header(text); 669169689Skan if (s != NULL) 670169689Skan text = s; 671169689Skan 672169689Skan if (header_status == ON) 673169689Skan { 674169689Skan putchar('\n'); 675169689Skan fputs(text, stdout); 676169689Skan lastline++; 677169689Skan } 678169689Skan else if (header_status == ERASE) 679169689Skan { 680169689Skan header_status = OFF; 681169689Skan } 682169689Skan free(s); 683169689Skan} 684169689Skan 685169689Skan/*ARGSUSED*/ 686169689Skanu_header(text) 687169689Skan 688169689Skanchar *text; /* ignored */ 689169689Skan 690169689Skan{ 691169689Skan char *s; 692169689Skan 693169689Skan s = trim_header(text); 694169689Skan if (s != NULL) 695169689Skan text = s; 696169689Skan 697169689Skan if (header_status == ERASE) 698169689Skan { 699169689Skan putchar('\n'); 700169689Skan lastline++; 701169689Skan clear_eol(header_length); 702169689Skan header_status = OFF; 703169689Skan } 704169689Skan free(s); 705169689Skan} 706169689Skan 707169689Skan/* 708169689Skan * *_process(line, thisline) - print one process line 709169689Skan * 710169689Skan * Assumptions: lastline is consistent 711169689Skan */ 712169689Skan 713169689Skani_process(line, thisline) 714169689Skan 715169689Skanint line; 716169689Skanchar *thisline; 717169689Skan 718169689Skan{ 719169689Skan register char *p; 720169689Skan register char *base; 721169689Skan 722169689Skan /* make sure we are on the correct line */ 723169689Skan while (lastline < y_procs + line) 724169689Skan { 725169689Skan putchar('\n'); 726169689Skan lastline++; 727169689Skan } 728169689Skan 729169689Skan /* truncate the line to conform to our current screen width */ 730169689Skan thisline[display_width] = '\0'; 731169689Skan 732169689Skan /* write the line out */ 733169689Skan fputs(thisline, stdout); 734169689Skan 735169689Skan /* copy it in to our buffer */ 73618334Speter base = smart_terminal ? screenbuf + lineindex(line) : screenbuf; 737169689Skan p = strecpy(base, thisline); 738169689Skan 739169689Skan /* zero fill the rest of it */ 740169689Skan memzero(p, display_width - (p - base)); 741169689Skan} 742169689Skan 743169689Skanu_process(line, newline) 744169689Skan 745169689Skanint line; 746169689Skanchar *newline; 747169689Skan 748169689Skan{ 749169689Skan register char *optr; 750169689Skan register int screen_line = line + Header_lines; 751169689Skan register char *bufferline; 752169689Skan 753169689Skan /* remember a pointer to the current line in the screen buffer */ 754169689Skan bufferline = &screenbuf[lineindex(line)]; 755169689Skan 756169689Skan /* truncate the line to conform to our current screen width */ 757169689Skan newline[display_width] = '\0'; 758169689Skan 759169689Skan /* is line higher than we went on the last display? */ 760169689Skan if (line >= last_hi) 761169689Skan { 762169689Skan /* yes, just ignore screenbuf and write it out directly */ 763169689Skan /* get positioned on the correct line */ 76418334Speter if (screen_line - lastline == 1) 76518334Speter { 76618334Speter putchar('\n'); 76718334Speter lastline++; 76818334Speter } 769169689Skan else 77018334Speter { 771169689Skan Move_to(0, screen_line); 772169689Skan lastline = screen_line; 773169689Skan } 774169689Skan 775169689Skan /* now write the line */ 776169689Skan fputs(newline, stdout); 777169689Skan 778169689Skan /* copy it in to the buffer */ 779169689Skan optr = strecpy(bufferline, newline); 780169689Skan 781169689Skan /* zero fill the rest of it */ 782169689Skan memzero(optr, display_width - (optr - bufferline)); 783169689Skan } 784169689Skan else 785169689Skan { 786169689Skan line_update(bufferline, newline, 0, line + Header_lines); 787169689Skan } 78890075Sobrien} 78990075Sobrien 79018334Speteru_endscreen(hi) 79118334Speter 79218334Speterregister int hi; 79318334Speter 79450397Sobrien{ 79550397Sobrien register int screen_line = hi + Header_lines; 79618334Speter register int i; 79718334Speter 79890075Sobrien if (smart_terminal) 79990075Sobrien { 80090075Sobrien if (hi < last_hi) 801169689Skan { 802169689Skan /* need to blank the remainder of the screen */ 80390075Sobrien /* but only if there is any screen left below this line */ 80490075Sobrien if (lastline + 1 < screen_length) 805169689Skan { 806169689Skan /* efficiently move to the end of currently displayed info */ 80790075Sobrien if (screen_line - lastline < 5) 808169689Skan { 809132718Skan while (lastline < screen_line) 810132718Skan { 811132718Skan putchar('\n'); 81218334Speter lastline++; 813132718Skan } 814169689Skan } 815132718Skan else 816169689Skan { 81718334Speter Move_to(0, screen_line); 81818334Speter lastline = screen_line; 81918334Speter } 82018334Speter 82118334Speter if (clear_to_end) 82218334Speter { 823132718Skan /* we can do this the easy way */ 824132718Skan putcap(clear_to_end); 82518334Speter } 826169689Skan else 82718334Speter { 82852284Sobrien /* use clear_eol on each line */ 82918334Speter i = hi; 83018334Speter while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi) 83118334Speter { 83218334Speter putchar('\n'); 83350397Sobrien } 83418334Speter } 835169689Skan } 836169689Skan } 83718334Speter last_hi = hi; 83818334Speter 83918334Speter /* move the cursor to a pleasant place */ 840169689Skan Move_to(x_idlecursor, y_idlecursor); 84118334Speter lastline = y_idlecursor; 84218334Speter } 84318334Speter else 84418334Speter { 84518334Speter /* separate this display from the next with some vertical room */ 84618334Speter fputs("\n\n", stdout); 84718334Speter } 84818334Speter} 849169689Skan 850169689Skandisplay_header(t) 851169689Skan 852132718Skanint t; 85318334Speter 854169689Skan{ 85518334Speter if (t) 85618334Speter { 85718334Speter header_status = ON; 858169689Skan } 85918334Speter else if (header_status == ON) 86018334Speter { 86118334Speter header_status = ERASE; 86218334Speter } 86318334Speter} 86418334Speter 86590075Sobrien/*VARARGS2*/ 86690075Sobriennew_message(type, msgfmt, a1, a2, a3) 86718334Speter 86818334Speterint type; 86918334Speterchar *msgfmt; 87018334Spetercaddr_t a1, a2, a3; 871169689Skan 87218334Speter{ 87318334Speter register int i; 87418334Speter 87590075Sobrien /* first, format the message */ 87618334Speter (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3); 87718334Speter 878169689Skan if (msglen > 0) 879169689Skan { 88090075Sobrien /* message there already -- can we clear it? */ 881169689Skan if (!overstrike) 88290075Sobrien { 88390075Sobrien /* yes -- write it and clear to end */ 884169689Skan i = strlen(next_msg); 88590075Sobrien if ((type & MT_delayed) == 0) 88690075Sobrien { 887169689Skan type & MT_standout ? standout(next_msg) : 888169689Skan fputs(next_msg, stdout); 88990075Sobrien (void) clear_eol(msglen - i); 89090075Sobrien msglen = i; 89190075Sobrien next_msg[0] = '\0'; 89290075Sobrien } 89390075Sobrien } 89490075Sobrien } 895169689Skan else 896169689Skan { 89790075Sobrien if ((type & MT_delayed) == 0) 89890075Sobrien { 899169689Skan type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout); 90018334Speter msglen = strlen(next_msg); 90118334Speter next_msg[0] = '\0'; 902169689Skan } 90318334Speter } 904132718Skan} 905132718Skan 906169689Skanclear_message() 907169689Skan 908169689Skan{ 909169689Skan if (clear_eol(msglen) == 1) 910169689Skan { 911169689Skan putchar('\r'); 912169689Skan } 913169689Skan} 914169689Skan 915169689Skanreadline(buffer, size, numeric) 916169689Skan 917169689Skanchar *buffer; 91818334Speterint size; 91950397Sobrienint numeric; 920132718Skan 921169689Skan{ 922169689Skan register char *ptr = buffer; 923132718Skan register char ch; 924132718Skan register char cnt = 0; 92550397Sobrien register char maxcnt = 0; 92650397Sobrien 92718334Speter /* allow room for null terminator */ 92818334Speter size -= 1; 92918334Speter 93018334Speter /* read loop */ 93118334Speter while ((fflush(stdout), read(0, ptr, 1) > 0)) 932169689Skan { 93318334Speter /* newline means we are done */ 93418334Speter if ((ch = *ptr) == '\n' || ch == '\r') 935169689Skan { 93618334Speter break; 93718334Speter } 938169689Skan 93918334Speter /* handle special editing characters */ 940169689Skan if (ch == ch_kill) 941169689Skan { 942169689Skan /* kill line -- account for overstriking */ 943169689Skan if (overstrike) 944169689Skan { 945169689Skan msglen += maxcnt; 946169689Skan } 947169689Skan 948169689Skan /* return null string */ 94918334Speter *buffer = '\0'; 95018334Speter putchar('\r'); 95118334Speter return(-1); 95218334Speter } 95318334Speter else if (ch == ch_erase) 95418334Speter { 95518334Speter /* erase previous character */ 95618334Speter if (cnt <= 0) 957169689Skan { 958169689Skan /* none to erase! */ 959169689Skan putchar('\7'); 960132718Skan } 961169689Skan else 962132718Skan { 963132718Skan fputs("\b \b", stdout); 964132718Skan ptr--; 965132718Skan cnt--; 966132718Skan } 967169689Skan } 968169689Skan /* check for character validity and buffer overflow */ 969169689Skan else if (cnt == size || (numeric && !isdigit(ch)) || 970169689Skan !isprint(ch)) 971169689Skan { 972169689Skan /* not legal */ 973169689Skan putchar('\7'); 974132718Skan } 975132718Skan else 976132718Skan { 977132718Skan /* echo it and store it in the buffer */ 978132718Skan putchar(ch); 979169689Skan ptr++; 980169689Skan cnt++; 981169689Skan if (cnt > maxcnt) 982169689Skan { 983169689Skan maxcnt = cnt; 984169689Skan } 985169689Skan } 986132718Skan } 987132718Skan 988132718Skan /* all done -- null terminate the string */ 989132718Skan *ptr = '\0'; 990132718Skan 991169689Skan /* account for the extra characters in the message area */ 992132718Skan /* (if terminal overstrikes, remember the furthest they went) */ 993169689Skan msglen += overstrike ? maxcnt : cnt; 994169689Skan 995169689Skan /* return either inputted number or string length */ 996169689Skan putchar('\r'); 997169689Skan return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); 998169689Skan} 999169689Skan 1000132718Skan/* internal support routines */ 1001169689Skan 1002132718Skanstatic int string_count(pp) 1003169689Skan 1004169689Skanregister char **pp; 1005169689Skan 1006169689Skan{ 1007169689Skan register int cnt; 1008169689Skan 1009169689Skan cnt = 0; 1010169689Skan while (*pp++ != NULL) 1011169689Skan { 1012169689Skan cnt++; 1013169689Skan } 1014169689Skan return(cnt); 1015169689Skan} 1016169689Skan 1017169689Skanstatic void summary_format(str, numbers, names) 1018132718Skan 1019169689Skanchar *str; 1020169689Skanint *numbers; 1021169689Skanregister char **names; 1022169689Skan 1023169689Skan{ 1024169689Skan register char *p; 1025169689Skan register int num; 1026169689Skan register char *thisname; 1027169689Skan register int useM = No; 1028169689Skan 1029169689Skan /* format each number followed by its string */ 1030169689Skan p = str; 1031169689Skan while ((thisname = *names++) != NULL) 1032169689Skan { 1033169689Skan /* get the number to format */ 1034132718Skan num = *numbers++; 1035132718Skan 1036132718Skan /* display only non-zero numbers */ 1037132718Skan if (num > 0) 1038132718Skan { 1039132718Skan /* is this number in kilobytes? */ 1040132718Skan if (thisname[0] == 'K') 1041169689Skan { 1042132718Skan /* yes: format it as a memory value */ 1043132718Skan p = strecpy(p, format_k(num)); 1044132718Skan 1045132718Skan /* skip over the K, since it was included by format_k */ 1046132718Skan p = strecpy(p, thisname+1); 1047132718Skan } 1048132718Skan else 1049169689Skan { 1050169689Skan p = strecpy(p, itoa(num)); 1051169689Skan p = strecpy(p, thisname); 1052132718Skan } 1053132718Skan } 1054132718Skan 1055132718Skan /* ignore negative numbers, but display corresponding string */ 1056132718Skan else if (num < 0) 1057132718Skan { 1058132718Skan p = strecpy(p, thisname); 1059132718Skan } 1060132718Skan } 1061132718Skan 1062132718Skan /* if the last two characters in the string are ", ", delete them */ 1063132718Skan p -= 2; 1064132718Skan if (p >= str && p[0] == ',' && p[1] == ' ') 1065132718Skan { 1066132718Skan *p = '\0'; 1067132718Skan } 1068132718Skan} 1069132718Skan 1070132718Skanstatic void line_update(old, new, start, line) 1071132718Skan 1072132718Skanregister char *old; 1073132718Skanregister char *new; 1074169689Skanint start; 1075132718Skanint line; 1076132718Skan 1077169689Skan{ 1078132718Skan register int ch; 1079132718Skan register int diff; 1080132718Skan register int newcol = start + 1; 1081132718Skan register int lastcol = start; 1082132718Skan char cursor_on_line = No; 1083132718Skan char *current; 1084169689Skan 1085169689Skan /* compare the two strings and only rewrite what has changed */ 1086169689Skan current = old; 1087169689Skan#ifdef DEBUG 1088169689Skan fprintf(debug, "line_update, starting at %d\n", start); 1089169689Skan fputs(old, debug); 1090169689Skan fputc('\n', debug); 1091169689Skan fputs(new, debug); 1092169689Skan fputs("\n-\n", debug); 1093169689Skan#endif 1094169689Skan 1095169689Skan /* start things off on the right foot */ 1096169689Skan /* this is to make sure the invariants get set up right */ 1097169689Skan if ((ch = *new++) != *old) 1098169689Skan { 1099169689Skan if (line - lastline == 1 && start == 0) 1100169689Skan { 1101169689Skan putchar('\n'); 1102169689Skan } 1103169689Skan else 1104169689Skan { 1105169689Skan Move_to(start, line); 1106169689Skan } 1107169689Skan cursor_on_line = Yes; 1108169689Skan putchar(ch); 1109169689Skan *old = ch; 1110169689Skan lastcol = 1; 1111169689Skan } 1112169689Skan old++; 1113169689Skan 1114169689Skan /* 1115132718Skan * main loop -- check each character. If the old and new aren't the 1116169689Skan * same, then update the display. When the distance from the 1117169689Skan * current cursor position to the new change is small enough, 1118169689Skan * the characters that belong there are written to move the 1119169689Skan * cursor over. 1120132718Skan * 1121132718Skan * Invariants: 1122132718Skan * lastcol is the column where the cursor currently is sitting 1123132718Skan * (always one beyond the end of the last mismatch). 1124132718Skan */ 1125132718Skan do /* yes, a do...while */ 1126169689Skan { 1127169689Skan if ((ch = *new++) != *old) 1128169689Skan { 1129169689Skan /* new character is different from old */ 1130132718Skan /* make sure the cursor is on top of this character */ 1131132718Skan diff = newcol - lastcol; 1132132718Skan if (diff > 0) 1133132718Skan { 1134132718Skan /* some motion is required--figure out which is shorter */ 1135132718Skan if (diff < 6 && cursor_on_line) 1136169689Skan { 1137132718Skan /* overwrite old stuff--get it out of the old buffer */ 1138169689Skan printf("%.*s", diff, ¤t[lastcol-start]); 1139169689Skan } 1140169689Skan else 1141169689Skan { 1142132718Skan /* use cursor addressing */ 1143169689Skan Move_to(newcol, line); 1144169689Skan cursor_on_line = Yes; 1145169689Skan } 1146169689Skan /* remember where the cursor is */ 1147169689Skan lastcol = newcol + 1; 1148169689Skan } 1149169689Skan else 1150169689Skan { 1151169689Skan /* already there, update position */ 1152169689Skan lastcol++; 1153169689Skan } 1154169689Skan 1155169689Skan /* write what we need to */ 1156169689Skan if (ch == '\0') 1157169689Skan { 1158169689Skan /* at the end--terminate with a clear-to-end-of-line */ 1159169689Skan (void) clear_eol(strlen(old)); 1160169689Skan } 1161169689Skan else 1162169689Skan { 1163169689Skan /* write the new character */ 1164169689Skan putchar(ch); 1165169689Skan } 1166169689Skan /* put the new character in the screen buffer */ 1167169689Skan *old = ch; 1168169689Skan } 1169169689Skan 1170169689Skan /* update working column and screen buffer pointer */ 1171169689Skan newcol++; 1172169689Skan old++; 1173169689Skan 1174169689Skan } while (ch != '\0'); 1175169689Skan 1176132718Skan /* zero out the rest of the line buffer -- MUST BE DONE! */ 1177132718Skan diff = display_width - newcol; 1178132718Skan if (diff > 0) 1179132718Skan { 1180132718Skan memzero(old, diff); 1181169689Skan } 1182132718Skan 1183132718Skan /* remember where the current line is */ 1184132718Skan if (cursor_on_line) 1185169689Skan { 1186132718Skan lastline = line; 1187169689Skan } 1188169689Skan} 1189169689Skan 1190169689Skan/* 1191132718Skan * printable(str) - make the string pointed to by "str" into one that is 1192132718Skan * printable (i.e.: all ascii), by converting all non-printable 1193132718Skan * characters into '?'. Replacements are done in place and a pointer 1194132718Skan * to the original buffer is returned. 1195132718Skan */ 1196132718Skan 1197132718Skanchar *printable(str) 1198132718Skan 1199132718Skanchar *str; 1200132718Skan 1201132718Skan{ 1202169689Skan register char *ptr; 1203169689Skan register char ch; 1204169689Skan 1205169689Skan ptr = str; 1206169689Skan while ((ch = *ptr) != '\0') 1207132718Skan { 1208169689Skan if (!isprint(ch)) 1209169689Skan { 1210132718Skan *ptr = '?'; 1211132718Skan } 1212132718Skan ptr++; 1213132718Skan } 1214169689Skan return(str); 1215132718Skan} 1216132718Skan 1217132718Skani_uptime(bt, tod) 1218132718Skan 1219132718Skanstruct timeval* bt; 1220132718Skantime_t *tod; 1221132718Skan 1222132718Skan{ 1223132718Skan time_t uptime; 1224169689Skan int days, hrs, mins, secs; 1225132718Skan 1226132718Skan if (bt->tv_sec != -1) { 1227132718Skan uptime = *tod - bt->tv_sec; 1228132718Skan uptime += 30; 1229132718Skan days = uptime / 86400; 1230132718Skan uptime %= 86400; 1231132718Skan hrs = uptime / 3600; 1232132718Skan uptime %= 3600; 1233132718Skan mins = uptime / 60; 1234132718Skan secs = uptime % 60; 1235132718Skan 1236169689Skan /* 1237169689Skan * Display the uptime. 1238169689Skan */ 1239169689Skan 1240132718Skan if (smart_terminal) 1241132718Skan { 1242132718Skan Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0); 1243169689Skan } 1244132718Skan else 1245132718Skan { 1246132718Skan fputs(" ", stdout); 124718334Speter } 124818334Speter printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs); 124918334Speter } 1250132718Skan} 125118334Speter