From a81052c69251b2349fbe3e90e2970b80b798df59 Mon Sep 17 00:00:00 2001 From: csmall <> Date: Tue, 18 Jun 2002 23:45:30 +0000 Subject: [PATCH] updated jims new topzop --- top.c | 3217 ++++++++++++++++++++++++++++++--------------------------- top.h | 482 +++++---- 2 files changed, 2016 insertions(+), 1683 deletions(-) diff --git a/top.c b/top.c index 95ad2893..b9e7dfb3 100644 --- a/top.c +++ b/top.c @@ -1,7 +1,10 @@ /* top.c - Source file: show Linux processes */ /* - * Copyright (c) 2002 - James C. Warner, - * All rights reserved. + * Copyright (c) June, 2002 - James C. Warner + * All rights reserved. 8921 Hilloway Road + * Eden Prairie, Minnesota 55347 USA + * + * * This file may be used subject to the terms and conditions of the * GNU Library General Public License Version 2, or any later version * at your option, as published by the Free Software Foundation. @@ -10,7 +13,10 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. */ -#include +/* For their contributions to this program, the author wishes to thank: + * Craig Small, + * Albert D. Cahalan, + */ #include #include #include @@ -46,8 +52,10 @@ /* need: signal_name_to_number */ #include "proc/sig.h" #endif +#ifdef USE_LIB_STA3 /* need: status */ #include "proc/status.h" +#endif /* need: meminfo stuff */ #include "proc/sysinfo.h" /* need: procps_version */ @@ -57,7 +65,6 @@ #include "top.h" - /*###### Miscellaneous global stuff ####################################*/ /* The original and new terminal attributes */ @@ -65,9 +72,8 @@ static struct termios Savedtty, Rawtty; static int Ttychanged = 0; - /* Program names used in error messages and 'rc' file names */ -static char *Myname, - *Myrealname; + /* Program name used in error messages and 'rc' file names */ +static char *Myname; /* The Name of the config file(s), dynamically constructed */ static char RCfile [OURPATHSZ], @@ -75,6 +81,9 @@ static char RCfile [OURPATHSZ], /* The run-time acquired page size */ static int Page_size; +#ifdef UGH_ITS_4_RH +static unsigned Mem_pages; +#endif /* SMP and Irix/Solaris mode support */ static int Cpu_tot, @@ -90,61 +99,30 @@ static int Msg_awaiting = 0; /* Configurable Display support ##################################*/ -static unsigned PFlags [PFLAGSSIZ]; -static char CurFields [PFLAGSSIZ], - ColHeadings [SMLBUFSIZ], - ColUsername [USRNAMSIZ]; -static int NumFields; + /* Current screen dimensions. + note: the number of processes displayed is tracked on a per window + basis (see the WIN_t). Max_lines is the total number of + screen rows after deducting summary information overhead. */ + /* Current terminal screen size. */ +static int Screen_cols, Screen_rows, Max_lines; - /* Current window size. - note: Max_tasks is the specified number of processes to display - (zero for infinite), and Max_lines is the actual number - which, in turn, depends on screen size and summary info. */ -static int Screen_cols, Screen_rows, - Max_lines, Max_tasks; + /* This is really the number of lines needed to display the summary + information (0 - nn), but is used as the relative row where we + stick the cursor between frames. */ +static int Msg_row; - /* Number of lines needed to display the summary information - which is never less than 2 since it also must accomodate the - message line + column headings -- set soley by mkheadings! */ -static int HSum_lines; + /* Global/Non-windows mode stuff that IS persistent (in rcfile) */ +static int Show_altscr; /* 'A' - 'Alt' display mode (multi windows) */ +static int Show_irixps = 1; /* 'I' - Irix vs. Solaris mode (SMP-only) */ +static float Delay_time = DEF_DELAY; /* how long to sleep between updates */ - /* Maximum length to display of a process' command name/line - (excluding padding due to terminfo strings) */ -static int Max_cmd; - - /* Controls how long we sleep between screen updates. - Accurate to microseconds (when not in 'Batch' mode). */ -static float Delay_time = DEF_DELAY; - - /* Special flags dealing with the kernel symbol table */ + /* Global/Non-windows mode stuff that is NOT persistent */ static int No_ksyms = -1, /* set to '0' if ksym avail, '1' otherwise */ - PSDBopen = 0; /* set to '1' if psdb opened (now postponed) */ - - /* The Mode flags. - Except for Batch and Loops, all of them are preserved in the - rc file. Thus, once personalized it stays personalized! - note: the 'letter' shown is the On condition, some default to Off */ -static int Batch = 0, /* batch mode, collect no input, dumb output */ + PSDBopen = 0, /* set to '1' if psdb opened (now postponed) */ + Batch = 0, /* batch mode, collect no input, dumb output */ Loops = -1, /* number of iterations, -1 loops forever */ - Secure_mode = 0, /* set if some functionality restricted */ - Irix_mode = 1, /* 'I' - Irix vs. Solaris view (SMP-only) */ - Sort_normal = 1, /* 'R' - reversed column sort order */ - HSum_loadav = 1, /* 'l' - display load avg and uptime summary */ - HSum_states = 1, /* 't' - display task/cpu(s) states summary */ - HSum_memory = 1, /* 'm' - display memory summary */ - Show_ctimes = 1, /* 'S' - show times as cumulative */ - Show_cmdlin = 0, /* 'c' - show cmdline vs. name */ - Show_idleps = 1, /* 'i' - show idle processes (all processes) */ - Show_hicols = 0, /* 'x' - show sort column highlighted */ - Show_hirows = 1, /* 'y' - show running task(s) highlighted */ - Show_colors = 0, /* 'z' - show in color (vs. mono) */ - Show_hibold = 1, /* 'b' - rows and/or col bold (vs. reverse) */ - Show_cpusum = 1; /* '1' - show combined cpu stats (vs. each) */ + Secure_mode = 0; /* set if some functionality restricted */ - /* Current current sort type (this too is preserved in the rc file) - * and handler -- goodbye libproc mult_lvl_cmp, etc. overkill. */ -static int Sort_type = -1; -static QSORT_t Sort_func = NULL; /* These are our gosh darn 'Fields' ! They MUST be kept in sync with pflags !! */ @@ -172,13 +150,17 @@ static FTAB_t Fieldstab[] = { { " SHR ", "%4.4s ", 4, SK_Kb, -1, "Shared Mem size (kb)" }, { "nFLT ", "%4.4s ", 4, SK_no, -1, "Page Fault count" }, { "nDRT ", "%4.4s ", 4, SK_no, -1, "Dirty Pages count" }, +#ifdef USE_LIB_STA3 { "STA ", "%3.3s ", -1, -1, -1, "Process Status" }, +#else + { "S ", "%c ", -1, -1, -1, "Process Status" }, +#endif /** next entry's special: '.head' will be formatted using table entry's own '.fmts' plus runtime supplied conversion args! */ { "Command ", "%-*.*s ", -1, -1, S_CMD, "Command line/name" }, { "WCHAN ", "%-9.9s ", -1, -1, -1, "Sleeping in Function" }, /** next entry's special: the 0's will be replaced with '.'! */ -#ifdef UPCASE_HEXES +#ifdef CASEUP_HEXES { "Flags ", "%08lX ", -1, -1, -1, "Task Flags " } #else { "Flags ", "%08lx ", -1, -1, -1, "Task Flags " } @@ -187,12 +169,6 @@ static FTAB_t Fieldstab[] = { /* Miscellaneous Color stuff #####################################*/ - /* Our three basic colors -- - they can be manipulated by the 'tweak_colors' routine */ -static int Base_color = BASEcolor, - Msgs_color = MSGScolor, - Head_color = HEADcolor; - /* Some cap's stuff to reduce runtime calls -- to accomodate 'Batch' mode, they begin life as empty strings */ static char Cap_bold [CAPBUFSIZ] = "", @@ -205,56 +181,52 @@ static char Cap_bold [CAPBUFSIZ] = "", Cap_norm [CAPBUFSIZ] = "", Cap_reverse [CAPBUFSIZ] = "", Caps_off [CAPBUFSIZ] = ""; -static char Sum_color [CLRBUFSIZ] = "", - Msg_color [CLRBUFSIZ] = "", - Pmt_color [CLRBUFSIZ] = "", - Hdr_color [CLRBUFSIZ] = "", - Row_color_norm [CLRBUFSIZ] = "", - Row_color_high [CLRBUFSIZ] = ""; -static int Cap_can_goto = 0, - Len_row_high = 0, - Len_row_norm = 0; +static int Cap_can_goto = 0; - /* Development/debug stuff #######################################*/ -#ifdef TRAK_MAXCAPS -static int Max_pads = 0, Min_pads = MAXINT, - Max_rbuf = 0, Min_rbuf = MAXINT; -#endif + /* ////////////////////////////////////////////////////////////// */ + /* Special Section: multiple windows/field groups ---------------*/ -#ifdef TRAK_MAXBUFS - BUF2INT(fmtmk, buf) - BUF2INT(show_special, tmp) - BUF2INT(ask_str, buf) - BUF2INT(scale_num, buf) - BUF2INT(scale_tics, buf) - BUF2INT(std_err, buf) - BUF2INT(frame_states, tmp) - BUF2INT(mkcol, tmp) - BUF2INT(show_a_task, cbuf) - BUF2INT(show_a_task, rbuf) - BUF2INT(rcfiles_read, fbuf) - BUF2INT(rcfiles_read, RCfile) - BUF2INT(rcfiles_read, RCfile_Sys) - BUF2INT(do_key, ColUsername) - BUF2INT(mkheadings, CurFields) - BUF2INT(mkheadings, ColHeadings) - BUF2INT(main, not_really_tmp) -#endif + /* The pointers to our four WIN_t's, and which of those is considered + the 'current' window (ie. which window is associated with any summ + info displayed and to which window commands are directed) */ +static WIN_t *Winstk [GROUPSMAX], + *Curwin; + + /* Frame oriented stuff that can't remain local to any 1 function + and/or that would be too cumbersome managed as parms */ +static int Frame_maxtask, /* last known number of active tasks */ + /* ie. current 'size' of proc table */ + Frame_srtflg, /* the subject window sort direction */ + Frame_ctimes, /* the subject window's ctimes flag */ + Frame_cmdlin; /* the subject window's cmdlin flag */ + /* ////////////////////////////////////////////////////////////// */ /*###### Sort callbacks ################################################*/ + /* + * About the naming variance -- + * pids are often treated as our secondary sort key, + * thus this guy must be alphabetically first in this section */ +static int pid_sort (proc_t **P, proc_t **Q) +{ + if ( (*P)->pid < (*Q)->pid ) return SORT_lt; + if ( (*P)->pid > (*Q)->pid ) return SORT_gt; + return 0; +} + + static int sort_cmd (proc_t **P, proc_t **Q) { /* if a process doesn't have a cmdline, we'll consider it a kernel thread -- since show_a_task gives such tasks special treatment, we must too */ - if (Show_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { + if (Frame_cmdlin && ((*P)->cmdline || (*Q)->cmdline)) { if (!(*P)->cmdline) return SORT_lt; if (!(*Q)->cmdline) return SORT_gt; - if ( 0 > strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) ) + if ( 0 > strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Curwin->maxcmdln) ) return SORT_lt; - if ( 0 < strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) ) + if ( 0 < strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Curwin->maxcmdln) ) return SORT_gt; } else { /* this part also handles the compare if both are kernel threads */ @@ -269,6 +241,8 @@ static int sort_cpu (proc_t **P, proc_t **Q) { if ( (*P)->pcpu < (*Q)->pcpu ) return SORT_lt; if ( (*P)->pcpu > (*Q)->pcpu ) return SORT_gt; + /* still equal? we'll try to fix that... */ + return pid_sort(P, Q); return 0; } @@ -278,23 +252,14 @@ static int sort_mem (proc_t **P, proc_t **Q) if ( (*P)->resident < (*Q)->resident ) return SORT_lt; if ( (*P)->resident > (*Q)->resident ) return SORT_gt; /* still equal? we'll try to fix that... */ - if ( (*P)->size < (*Q)->size ) return SORT_lt; - if ( (*P)->size > (*Q)->size ) return SORT_gt; - return 0; -} - - -static int sort_pid (proc_t **P, proc_t **Q) -{ - if ( (*P)->pid < (*Q)->pid ) return SORT_lt; - if ( (*P)->pid > (*Q)->pid ) return SORT_gt; + return pid_sort(P, Q); return 0; } static int sort_tme (proc_t **P, proc_t **Q) { - if (Show_ctimes) { + if (Frame_ctimes) { if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) ) return SORT_lt; @@ -316,7 +281,7 @@ static int sort_tty (proc_t **P, proc_t **Q) if ( (*P)->tty < (*Q)->tty ) return SORT_lt; if ( (*P)->tty > (*Q)->tty ) return SORT_gt; /* still equal? we'll try to fix that... */ - return sort_pid(P, Q); + return pid_sort(P, Q); } @@ -325,7 +290,7 @@ static int sort_usr (proc_t **P, proc_t **Q) if ( 0 > strcmp((*P)->euser, (*Q)->euser) ) return SORT_lt; if ( 0 < strcmp((*P)->euser, (*Q)->euser) ) return SORT_gt; /* still equal? we'll try to fix that... */ - return sort_pid(P, Q); + return sort_tty(P, Q); } @@ -363,9 +328,6 @@ static const char *fmtmk (const char *fmts, ...) va_start(va, fmts); vsprintf(buf, fmts, va); va_end(va); -#ifdef TRAK_MAXBUFS - MAXBUFS(fmtmk, buf); -#endif return (const char *)buf; } @@ -393,7 +355,123 @@ static char *strim (int sp, char *str) * This guy just facilitates Batch and protects against dumb ttys. */ static char *tg2 (int x, int y) { - return Cap_can_goto ? tgoto(cursor_address, x, y) : (char *)"\n"; + return Cap_can_goto ? tgoto(cursor_address, x, y) : (char *)""; +} + + +/*###### Exit/Interrput routines #######################################*/ + + /* + * The usual program end -- + * called only by functions in this section. */ +static void bye_bye (int eno, const char *str) +{ +#ifdef UGH_ITS_4_RH + if (PSDBopen) + close_psdb(); +#endif + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + printf("%s%s\n", tg2(0, Screen_rows), Cap_curs_norm); + +#ifdef ATEOJ_REPORT + fprintf(stderr, + "\nbye_bye's Summary report:" + "\n\tProgram" + "\n\t Page_size = %d, Cpu_tot = %d" + "\n\tTerminal: %s" + "\n\t device = %s, ncurses = v%s" + "\n\t max_colors = %d, max_pairs = %d" + "\n\t Cap_can_goto = %s" + "\n\t Screen_cols = %d, Screen_rows = %d" + "\n\t Max_lines = %d" + "\n\tWindows and Curwin->" + "\n\t sizeof(WIN_t) = %u, GROUPSMAX = %d" + "\n\t winname = %s, grpname = %s," +#ifdef CASEUP_HEXES + "\n\t winflags = %08X, maxpflgs = %d" +#else + "\n\t winflags = %08x, maxpflgs = %d" +#endif + "\n\t fieldscur = %s" + "\n\t winlines = %d, maxtasks = %d, maxcmdln = %d" + "\n\t sorttype = %c" + "\n" + , Page_size, Cpu_tot +#ifdef PRETENDNOCAP + , "dumb" +#else + , termname() +#endif + , ttyname(STDOUT_FILENO), NCURSES_VERSION + , max_colors, max_pairs + , Cap_can_goto ? "yes" : "No!" + , Screen_cols, Screen_rows + , Max_lines + , sizeof(WIN_t), GROUPSMAX + , Curwin->winname, Curwin->grpname + , Curwin->winflags, Curwin->maxpflgs + , Curwin->fieldscur + , Curwin->winlines, Curwin->maxtasks, Curwin->maxcmdln + , Curwin->sorttype + ); +#endif + + if (str) { + if (eno) perror(str); + else { + fputs(str, stderr); + eno = 1; + } + } + exit(eno); +} + + + /* + * Normal end of execution. + * catches: + * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ +static void stop (int dont_care_sig) +{ + bye_bye(0, NULL); +} + + + /* + * Standard error handler to normalize the look of all err o/p */ +static void std_err (const char *str) +{ + static char buf[SMLBUFSIZ]; + + fflush(stdout); + /* we'll use our own buffer so callers can still use fmtmk() and, yes the + leading tab is not the standard convention, but the standard is wrong + -- OUR msg won't get lost in screen clutter, like so many others! */ + sprintf(buf, "\t%s: %s\n", Myname, str); + if (!Ttychanged) { + fprintf(stderr, buf); + exit(1); + } + /* not to worry, he'll change our exit code to 1 due to 'buf' */ + bye_bye(0, buf); +} + + + /* + * Suspend ourself. + * catches: + * SIGTSTP, SIGTTIN and SIGTTOU */ +static void suspend (int dont_care_sig) +{ + /* reset terminal */ + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); + printf("%s%s", tg2(0, Screen_rows), Cap_curs_norm); + fflush(stdout); + raise(SIGSTOP); + /* later, after SIGCONT... */ + if (!Batch) + tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); } @@ -405,14 +483,14 @@ static char *tg2 (int x, int y) * columns and an actual printf row! * note: we avoid the use of background color so as to maximize * compatibility with the user's xterm settings */ -static void capsmk (void) +static void capsmk (WIN_t *q) { /* macro to test if a basic (non-color) capability is valid thanks: Floyd Davidson */ #define tIF(s) s ? s : "" static int capsdone = 0; - /* just a precaution for the future, we aren't called now... */ + /* we must NOT disturb our 'empty' terminfo strings! */ if (Batch) return; /* these are the unchangeable puppies, so we only do 'em once */ @@ -430,30 +508,31 @@ static void capsmk (void) if (tgoto(cursor_address, 1, 1)) Cap_can_goto = 1; capsdone = 1; } - - /* now do the changeable guys... */ - if (Show_colors && max_colors > 0) { - strcpy(Sum_color, tparm(set_a_foreground, Base_color)); - sprintf(Msg_color, "%s%s" - , tparm(set_a_foreground, Msgs_color), Cap_reverse); - sprintf(Pmt_color, "%s%s" - , tparm(set_a_foreground, Msgs_color), Cap_bold); - sprintf(Hdr_color, "%s%s" - , tparm(set_a_foreground, Head_color), Cap_reverse); - sprintf(Row_color_norm, "%s%s" - , Caps_off, tparm(set_a_foreground, Base_color)); + /* the key to NO run-time costs for configurable colors -- we spend a + little time with the user now setting up our terminfo strings, and + the job's done until he/she/it has a change-of-heart */ + if (CHKw(q, Show_COLORS) && max_colors > 0) { + strcpy(q->capclr_sum, tparm(set_a_foreground, q->summclr)); + sprintf(q->capclr_msg, "%s%s" + , tparm(set_a_foreground, q->msgsclr), Cap_reverse); + sprintf(q->capclr_pmt, "%s%s" + , tparm(set_a_foreground, q->msgsclr), Cap_bold); + sprintf(q->capclr_hdr, "%s%s" + , tparm(set_a_foreground, q->headclr), Cap_reverse); + sprintf(q->capclr_rownorm, "%s%s" + , Caps_off, tparm(set_a_foreground, q->taskclr)); } else { - Sum_color[0] = '\0'; - strcpy(Msg_color, Cap_reverse); - strcpy(Pmt_color, Cap_bold); - strcpy(Hdr_color, Cap_reverse); - strcpy(Row_color_norm, Cap_norm); + q->capclr_sum[0] = '\0'; + strcpy(q->capclr_msg, Cap_reverse); + strcpy(q->capclr_pmt, Cap_bold); + strcpy(q->capclr_hdr, Cap_reverse); + strcpy(q->capclr_rownorm, Cap_norm); } - - sprintf(Row_color_high, "%s%s" - , Row_color_norm, Show_hibold ? Cap_bold : Cap_reverse); - Len_row_norm = strlen(Row_color_norm); - Len_row_high = strlen(Row_color_high); + /* this guy's a composite, so we do him outside the if */ + sprintf(q->capclr_rowhigh, "%s%s" + , q->capclr_rownorm, CHKw(q, Show_HIBOLD) ? Cap_bold : Cap_reverse); + q->len_rownorm = strlen(q->capclr_rownorm); + q->len_rowhigh = strlen(q->capclr_rowhigh); #undef tIF } @@ -463,7 +542,8 @@ static void capsmk (void) * Show an error, but not right now. * Due to the postponed opening of ksym, using open_psdb_message, * if P_WCHAN had been selected and the program is restarted, the - * message would otherwise be displayed prematurely */ + * message would otherwise be displayed prematurely. + * (old top handles that situation with typical inelegance) */ static void msg_save (const char *fmts, ...) { char tmp[SMLBUFSIZ]; @@ -483,8 +563,8 @@ static void msg_save (const char *fmts, ...) static void show_msg (const char *str) { printf("%s%s %s %s%s" - , tg2(0, MSG_line) - , Msg_color + , tg2(0, Msg_row) + , Curwin->capclr_msg , str , Caps_off , Cap_clr_eol); @@ -499,8 +579,8 @@ static void show_msg (const char *str) static void show_pmt (const char *str) { printf("%s%s%s: %s%s" - , tg2(0, MSG_line) - , Pmt_color + , tg2(0, Msg_row) + , Curwin->capclr_pmt , str , Cap_curs_huge , Caps_off); @@ -514,10 +594,9 @@ static void show_pmt (const char *str) * Our special formatting consists of: * "some text <_delimiter_> some more text <_delimiter_>...\n" * Where <_delimiter_> is a single byte in the range of: - * \01 through \07 (more properly \0001 - \0007) + * \01 through \10 (in decimalizee, 1 - 8) * and is used to select an 'attribute' from a capabilities table * which is then applied to the *preceding* substring. - * * Once recognized, the delimiter is replaced with a null character * and viola, we've got a substring ready to output! Strings or * substrings without delimiters will receive the Cap_norm attribute. @@ -532,15 +611,20 @@ static void show_pmt (const char *str) * Tabs must always be avoided or our efforts are wasted and * lines will wrap. To lessen but not eliminate the risk of * terminfo string truncation, such non-display stuff should - * be placed at the beginning of a "short" line. */ + * be placed at the beginning of a "short" line. + * (and as for tabs, gimme 1 more color then no worries, mate) */ static void show_special (const char *glob) -{ - static char *captab[] = { /* Cap's/Delim's */ - Cap_norm, Cap_norm, Cap_bold, /* \00, \01, \02 */ - Sum_color, /* \03 */ - Msg_color, Pmt_color, /* \04, \05 */ - Hdr_color, /* \06 */ - Row_color_high }; /* \07 */ +{ /* note: the following is for documentation only, + the real captab is now found in a group's WIN_t ! + +------------------------------------------------------+ + | char *captab[] = { : Cap's/Delim's | + | Cap_norm, Cap_norm, Cap_bold, = \00, \01, \02 | + | Sum_color, = \03 | + | Msg_color, Pmt_color, = \04, \05 | + | Hdr_color, = \06 | + | Row_color_high, = \07 | + | Row_color_norm }; = \10 [octal!] | + +------------------------------------------------------+ */ char tmp[BIGBUFSIZ], *cap, *lin_end, *sub_beg, *sub_end; int room; @@ -549,9 +633,6 @@ static void show_special (const char *glob) /* create a local copy we can extend and otherwise abuse */ memcpy(tmp, glob, (unsigned)(lin_end - glob)); -#ifdef TRAK_MAXBUFS - MAXBUFS(show_special, tmp); -#endif /* zero terminate this part and prepare to parse substrings */ tmp[lin_end - glob] = '\0'; room = Screen_cols; @@ -559,16 +640,17 @@ static void show_special (const char *glob) while (*sub_beg) { switch (*sub_end) { - case '\00' : /* no end delim, captab makes normal */ + case 0: /* no end delim, captab makes normal */ *(sub_end + 1) = '\0'; /* extend str end, then fall through */ - case '\01' ... '\07' : - cap = captab[(int)*sub_end]; + case 1: case 2: case 3: case 4: + case 5: case 6: case 7: case 8: + cap = Curwin->captab[(int)*sub_end]; *sub_end = '\0'; printf("%s%.*s%s", cap, room, sub_beg, Caps_off); room -= (sub_end - sub_beg); sub_beg = ++sub_end; break; - default : /* nothin' special, just text */ + default: /* nothin' special, just text */ ++sub_end; } @@ -577,7 +659,6 @@ static void show_special (const char *glob) printf("%s\n", Cap_clr_eol); /* emulate truncated newline */ glob = ++lin_end; /* point to next line (maybe) */ - } /* end: while 'lines' */ /* if there's anything left in the glob (by virtue of no trailing '\n'), @@ -587,80 +668,12 @@ static void show_special (const char *glob) fflush(stdout); } - - /* - * Change colors used in display */ -static void tweak_colors (void) -{ -#define kbdABORT 'q' -#define kbdAPPLY '\n' - int clrssav = Show_colors, boldsav = Show_hibold, - basesav = Base_color, msgssav = Msgs_color, headsav = Head_color; - int clr = Base_color, *pclr = &Base_color; - char ch, tgt = 'B'; - - if (0 >= max_colors) { - show_msg("\aNo colors to map!"); - return; - } - if (!Show_colors) { - Show_colors = 1; - capsmk(); - } - printf("%s%s", Cap_clr_scr, Cap_curs_huge); - - do { - /* this string is well above ISO C89's minimum requirements! */ - show_special(fmtmk(COLOR_sample - , Cap_home, Myname, procps_version, tgt, clr)); - chin(0, &ch, 1); - switch (ch) { - case 'B' : - pclr = &Base_color; - clr = *pclr; - tgt = ch; - break; - case 'M' : - pclr = &Msgs_color; - clr = *pclr; - tgt = ch; - break; - case 'H' : - pclr = &Head_color; - clr = *pclr; - tgt = ch; - break; - case '0' ... '7' : - clr = ch - '0'; - break; - case 'b' : - Show_hibold = !Show_hibold; - break; - case 'z' : - Show_colors = !Show_colors; - break; - } - *pclr = clr; - capsmk(); - } while (kbdAPPLY != ch && kbdABORT != ch); - - if (kbdABORT == ch) { - Show_colors = clrssav; Show_hibold = boldsav; - Base_color = basesav; Msgs_color = msgssav; Head_color = headsav; - capsmk(); - } - putp(Cap_curs_norm); - -#undef kbdABORT -#undef kbdAPPLY -} - -/*###### Small utility routines ########################################*/ +/*###### Small Utility routines ########################################*/ /* * Get a string from the user */ -static char *ask_str (const char *prompt) +static char *ask4str (const char *prompt) { static char buf[GETBUFSIZ]; @@ -669,9 +682,6 @@ static char *ask_str (const char *prompt) chin(1, buf, sizeof(buf) - 1); putp(Cap_curs_norm); -#ifdef TRAK_MAXBUFS - MAXBUFS(ask_str, buf); -#endif return strim(0, buf); } @@ -683,9 +693,9 @@ static float get_float (const char *prompt) char *line; float f; - if (!(*(line = ask_str(prompt)))) return -1; + if (!(*(line = ask4str(prompt)))) return -1; /* note: we're not allowing negative floats */ - if (strcspn(line, ".1234567890")) { + if (strcspn(line, ",.1234567890")) { show_msg("\aNot valid"); return -1; } @@ -701,7 +711,7 @@ static int get_int (const char *prompt) char *line; int n; - if (!(*(line = ask_str(prompt)))) return -1; + if (!(*(line = ask4str(prompt)))) return -1; /* note: we've got to allow negative ints (renice) */ if (strcspn(line, "-1234567890")) { show_msg("\aNot valid"); @@ -712,94 +722,6 @@ static int get_int (const char *prompt) } - /* - * Set the number of fields/columns to display. - * Create the field/column headings and set maximum cmdline length. - * Establish the heading/summary lines currently in use. - * Adjust the number of tasks to display. */ -static void mkheadings (void) -{ - const char *h; - int i, needpsdb = 0; - - /* build our PFlags array and establish a tentative NumFields */ - for (i = 0, NumFields = 0; i < (int)strlen(CurFields); i++) { - if (isupper(CurFields[i])) - PFlags[NumFields++] = CurFields[i] - 'A'; - } - - /* build a preliminary ColHeadings not to exceed screen width */ - ColHeadings[0] = '\0'; - for (i = 0; i < NumFields; i++) { - h = Fieldstab[PFlags[i]].head; - /* oops, won't fit -- we're outta here... */ - if (Screen_cols < (int)(strlen(ColHeadings) + strlen(h))) break; - strcat(ColHeadings, h); - } - - /* establish the final NumFields and prepare to grow the command - column heading via Max_cmd -- it may be a fib if P_CMD wasn't - encountered, but that's ok because it won't be displayed anyway */ - NumFields = i; - Max_cmd = Screen_cols - - (strlen(ColHeadings) - strlen(Fieldstab[P_CMD].head)) - 1; - - /* now we can build the true run-time ColHeadings and format the - command column heading if P_CMD is really being displayed */ - ColHeadings[0] = '\0'; - for (i = 0; i < NumFields; i++) { - /* are we gonna' need the kernel symbol table? */ - if (P_WCHAN == PFlags[i]) needpsdb = 1; - h = Fieldstab[PFlags[i]].head; - if (P_CMD == PFlags[i]) - strcat(ColHeadings, fmtmk(Fieldstab[P_CMD].fmts, Max_cmd, Max_cmd, h)); - else - strcat(ColHeadings, h); - } - - /* set the number of heading lines and calc display height */ - HSum_lines = SUMMINLINS; - if (HSum_loadav) HSum_lines += 1; - if (HSum_states) { - if (Show_cpusum) HSum_lines += 2; - /* no tellin' how darn many cpus they might have -- if they exceed - screen height, they'll have to suffer scroll... - (Max_lines may go negative, which is as good as 0) */ - else HSum_lines += Cpu_tot + 1; - } - if (HSum_memory) HSum_lines += 2; - - Max_lines = Screen_rows - HSum_lines; - if (Max_tasks && Max_tasks < Max_lines) - Max_lines = Max_tasks; - - /* do we (still) need the kernel symbol table? */ - if (needpsdb) { - if (-1 == No_ksyms) { - No_ksyms = 0; - if (open_psdb_message(NULL, msg_save)) - /* why so counter-intuitive, couldn't open_psdb_message - mirror sysmap_mmap -- that func does all the work anyway? */ - No_ksyms = 1; - else - PSDBopen = 1; - } - } -#ifdef UGH_ITS_4_RH - else if (PSDBopen) { - close_psdb(); - PSDBopen = 0; - No_ksyms = -1; - } -#endif - -#ifdef TRAK_MAXBUFS - MAXBUFS(mkheadings, CurFields); - MAXBUFS(mkheadings, ColHeadings); -#endif -} - - /* * Do some scaling stuff. * We'll interpret 'num' as one of the following types and @@ -813,7 +735,7 @@ static char *scale_num (unsigned num, const unsigned width, const unsigned type) /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */ static double scale[] = { 1024, 1024*1024, 1024*1024*1024, 0 }; /* kilo, mega, giga, none */ -#ifdef UPCASE_SCALE +#ifdef CASEUP_SCALE static char nextup[] = { 'K', 'M', 'G', 0 }; #else static char nextup[] = { 'k', 'm', 'g', 0 }; @@ -824,9 +746,6 @@ static char *scale_num (unsigned num, const unsigned width, const unsigned type) /* try an unscaled version first... */ sprintf(buf, "%d", num); -#ifdef TRAK_MAXBUFS - MAXBUFS(scale_num, buf); -#endif if (strlen(buf) <= width) return buf; @@ -856,7 +775,7 @@ static char *scale_tics (TICS_t tics, const unsigned width) const char *fmt; } ttab[] = { /* minutes hours days weeks */ -#ifdef UPCASE_SCALE +#ifdef CASEUP_SCALE { 60, "%uM" }, { 60, "%uH" }, { 24, "%uD" }, { 7, "%uW" } #else { 60, "%um" }, { 60, "%uh" }, { 24, "%ud" }, { 7, "%uw" } @@ -869,12 +788,9 @@ static char *scale_tics (TICS_t tics, const unsigned width) t = tics / Hertz; sprintf(buf, "%d:%02d.%02d" /* minutes:seconds.tenths */ , t/60, t%60, (int)((tics*100)/Hertz)%100); -#ifdef TRAK_MAXBUFS - MAXBUFS(scale_tics, buf); -#endif - if (strlen(buf) <= width) return buf; + sprintf(buf, "%d:%02d", t/60, t%60); /* minutes:seconds */ if (strlen(buf) <= width) return buf; @@ -909,639 +825,6 @@ static float time_elapsed (void) return et; } - -/*###### Exit and Signal handled routines ##############################*/ - - /* - * The usual program end -- - * called only by functions in this section. */ -static void bye_bye (int eno, const char *str) -{ -#ifdef UGH_ITS_4_RH - if (PSDBopen) - close_psdb(); -#endif - if (!Batch) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); - printf("%s%s\n", tg2(0, Screen_rows), Cap_curs_norm); - - if (str) { - if (eno) perror(str); - else fputs(str, stderr); - } -#ifdef TRAK_MAXCAPS - fprintf(stderr, - "\nTRAK_MAXCAPS report:" - "\n\tBasic lengths:" - "\n\t Cap_home = %d, Cap_bold = %d, Cap_norm = %d, Cap_reverse = %d" - "\n\t Cap_clr_eol = %d, Cap_clr_eos = %d, Cap_clr_scr = %d" - "\n\t Cap_curs_norm = %d, Cap_curs_huge = %d" - "\n\t Caps_off = %d" - "\n\tColor lengths:" - "\n\t Sum_color = %d, Msg_color = %d, Pmt_color = %d, Hdr_color = %d" - "\n\t Row_color_norm = %d, Len_row_norm = '%d'" - "\n\t Row_color_high = %d, Len_row_high = '%d'" - "\n\tMax_pads = %d, Min_pads = %d" - "\n\tMax_rbuf = %d, Min_rbuf = %d" - "\n" - , strlen(Cap_home), strlen(Cap_bold), strlen(Cap_norm), strlen(Cap_reverse) - , strlen(Cap_clr_eol), strlen(Cap_clr_eos), strlen(Cap_clr_scr) - , strlen(Cap_curs_norm), strlen(Cap_curs_huge) - , strlen(Caps_off) - , strlen(Sum_color), strlen(Msg_color), strlen(Pmt_color), strlen(Hdr_color) - , strlen(Row_color_norm), Len_row_norm - , strlen(Row_color_high), Len_row_high - , Max_pads, Min_pads - , Max_rbuf, Min_rbuf); -#endif -#ifdef TRAK_MAXBUFS - fprintf(stderr, - "\nTRAK_MAXBUFS report:" - "\n\tused size\tfunction, buffer" - "\n\t---- ----\t-----------------------" - ARPTBUF(fmtmk, buf) - ARPTBUF(show_special, tmp) - ARPTBUF(ask_str, buf) - ARPTBUF(scale_num, buf) - ARPTBUF(scale_tics, buf) - ARPTBUF(std_err, buf) - ARPTBUF(frame_states, tmp) - ARPTBUF(mkcol, tmp) - ARPTBUF(show_a_task, cbuf) - ARPTBUF(show_a_task, rbuf) - ARPTBUF(rcfiles_read, fbuf) - ARPTBUF(rcfiles_read, RCfile) - ARPTBUF(rcfiles_read, RCfile_Sys) - ARPTBUF(do_key, ColUsername) - ARPTBUF(mkheadings, CurFields) - ARPTBUF(mkheadings, ColHeadings) - ARPTBUF(main, not_really_tmp) - "\n" - , AUSEBUF(fmtmk, buf), ASIZBUF(fmtmk, buf) - , AUSEBUF(show_special, tmp), ASIZBUF(show_special, tmp) - , AUSEBUF(ask_str, buf), ASIZBUF(ask_str, buf) - , AUSEBUF(scale_num, buf), ASIZBUF(scale_num, buf) - , AUSEBUF(scale_tics, buf), ASIZBUF(scale_tics, buf) - , AUSEBUF(std_err, buf), ASIZBUF(std_err, buf) - , AUSEBUF(frame_states, tmp), ASIZBUF(frame_states, tmp) - , AUSEBUF(mkcol, tmp), ASIZBUF(mkcol, tmp) - , AUSEBUF(show_a_task, cbuf), ASIZBUF(show_a_task, cbuf) - , AUSEBUF(show_a_task, rbuf), ASIZBUF(show_a_task, rbuf) - , AUSEBUF(rcfiles_read, fbuf), ASIZBUF(rcfiles_read, fbuf) - , AUSEBUF(rcfiles_read, RCfile) , ASIZBUF(rcfiles_read, RCfile) - , AUSEBUF(rcfiles_read, RCfile_Sys) , ASIZBUF(rcfiles_read, RCfile_Sys) - , AUSEBUF(do_key, ColUsername), ASIZBUF(do_key, ColUsername) - , AUSEBUF(mkheadings, CurFields), ASIZBUF(mkheadings, CurFields) - , AUSEBUF(mkheadings, ColHeadings), ASIZBUF(mkheadings, ColHeadings) - , AUSEBUF(main, not_really_tmp), ASIZBUF(main, not_really_tmp)); -#endif - -#if defined(TRAK_MAXCAPS) || defined(TRAK_MAXBUFS) - fprintf(stderr, - "\nbye_bye's Summary report:" - "\n\tprogram names:" - "\n\t Myrealname = %s, Myname = %s" - "\n\tterminal = '%s'" - "\n\t device = %s, ncurses = v%s" - "\n\t max_colors = %d, max_pairs = %d" - "\n\t Cap_can_goto = %s" - "\n\tScreen_cols = %d, Screen_rows = %d" - "\n\tNumFields = %d, HSum_lines = %d" - "\n\tMax_lines = %d, Max_cmd = %d, Max_tasks = %d" - "\n\tPage_size = %d" - "\n" - , Myrealname, Myname -#ifdef PRETENDNOCAP - , "dumb" -#else - , termname() -#endif - , ttyname(STDOUT_FILENO), NCURSES_VERSION - , max_colors, max_pairs - , Cap_can_goto ? "yes" : "No!" - , Screen_cols, Screen_rows - , NumFields, HSum_lines - , Max_lines, Max_cmd, Max_tasks - , Page_size); -#endif - - if (str && !eno) eno = 1; - exit(eno); -} - - - /* - * Standard error handler to normalize the look of all err o/p */ -static void std_err (const char *str) -{ - static char buf[SMLBUFSIZ]; - - fflush(stdout); - /* we'll use our own buffer so callers can still use fmtmk() */ - sprintf(buf, "\t%s: %s\n", Myname, str); -#ifdef TRAK_MAXBUFS - MAXBUFS(std_err, buf); -#endif - if (!Ttychanged) { - fprintf(stderr, buf); - exit(1); - } - /* not to worry, he'll change our exit code to 1 due to 'buf' */ - bye_bye(0, buf); -} - - - /* - * Normal end of execution. - * catches: - * SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */ -static void stop (int dont_care_sig) -{ - bye_bye(0, NULL); -} - - - /* - * Suspend ourself. - * catches: - * SIGTSTP, SIGTTIN and SIGTTOU */ -static void suspend (int dont_care_sig) -{ - /* reset terminal */ - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty); - printf("%s%s", tg2(0, Screen_rows), Cap_curs_norm); - fflush(stdout); - raise(SIGSTOP); - /* later, after SIGCONT... */ - if (!Batch) - tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty); -} - - - /* - * Set the screen dimensions and call the real workhorse. - * catches: - * SIGWINCH and SIGCONT */ -static void window_resize (int dont_care_sig) -{ - struct winsize w; - - Screen_cols = columns; - Screen_rows = lines; - if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w))) { - Screen_cols = w.ws_col; - Screen_rows = w.ws_row; - } - /* he'll calculate header size, length of command field, etc ... */ - mkheadings(); -} - - -/*###### Startup routines ##############################################*/ - - /* - * Parse command line arguments - * (and force ol' main into a much needed diet) - * Note: it's assumed that the rc file(s) have already been read - * and our job is to see if any of those options are to be - * overridden -- we'll force some on and negate others in our - * best effort to honor the user's wishes... */ -static void parse_argvs (char **argv) -{ - /* difference(s) from traditional top: - -q (zero delay time) eliminated as redundant, use -d0 - -p (pid monitoring) allows comma delimited list - no deprecated/illegal use of 'goto' with 'breakargv:' - bunched args are actually handled properly and none are ignored - (we tolerate No whitespace and No switches -- maybe too tolerant) */ - const char *usage = " -hv | -bcisS -d delay -n iterations -p pid [,pid ...]\n"; - float tmp_delay = MAXFLOAT; - char *p; - - ++argv; - while (*argv) { - char *cp = *(argv++); - - while (*cp) { - switch (*cp) { - case '\0': - case '-': - break; - case 'b': - Batch = 1; - break; - case 'c': - Show_cmdlin = !Show_cmdlin; - break; - case 'd': - if (cp[1]) ++cp; - else if (*argv) cp = *argv++; - else std_err("-d requires argument"); - /* a negative delay will be dealt with shortly... */ - if (1 != sscanf(cp, "%f", &tmp_delay)) - std_err(fmtmk("bad delay '%s'", cp)); - break; - case 'h': case 'H': - case 'v': case 'V': - std_err(fmtmk("\t%s\nusage:\t%s%s" - , procps_version, Myname, usage)); - case 'i': - Show_idleps = !Show_idleps; - break; - case 'n': - if (cp[1]) cp++; - else if (*argv) cp = *argv++; - else std_err("-n requires argument"); - if (1 != sscanf(cp, "%d", &Loops) || 0 > Loops) - std_err(fmtmk("bad iterations arg '%s'", cp)); - break; - case 'p': - if (cp[1]) cp++; - else if (*argv) cp = *argv++; - else std_err("-p requires argument"); - do { - if (Monpidsidx >= MONPIDMAX) - std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); - if (1 != sscanf(cp, "%d", &Monpids[Monpidsidx]) - || 0 > Monpids[Monpidsidx]) - std_err(fmtmk("bad pid '%s'", cp)); - if (!Monpids[Monpidsidx]) - Monpids[Monpidsidx] = getpid(); - Monpidsidx++; - if (!(p = strchr(cp, ','))) - break; - cp = ++p; - } while (*cp); - break; - case 's': - Secure_mode = 1; - break; - case 'S': - Show_ctimes = !Show_ctimes; - break; - default : - std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" - , *cp, Myname, usage)); - - } /* end: switch */ - /* advance cp and jump over any numerical args used above */ - cp += strspn(++cp, "-.1234567890 "); - - } /* end: while *cp */ - } /* end: while *argv */ - - /* fixup delay time, maybe... */ - if (MAXFLOAT != tmp_delay) { - if (Secure_mode || 0 > tmp_delay) - msg_save("Delay time Not changed"); - else - Delay_time = tmp_delay; - } - - /* set the default sort type, maybe... */ - if (-1 == Sort_type || Monpidsidx) { - Sort_type = S_PID; - Sort_func = (QSORT_t)sort_pid; - } -} - - - /* - * Parse the options string as read from line two of the "local" - * configuration file. */ -static void parse_rc (char *opts) -{ - unsigned i; - - for (i = 0; i < strlen(opts); i++) { - switch (opts[i]) { - case 'b': - Show_hibold = 1; - break; - case 'c': - Show_cmdlin = 1; - break; - case 'C': - Sort_type = S_CMD; - Sort_func = (QSORT_t)sort_cmd; - break; - case 'E': - Sort_type = S_USR; - Sort_func = (QSORT_t)sort_usr; - break; - case 'i': - Show_idleps = 1; - break; - case 'I': - Irix_mode = 1; - break; - case 'l': - HSum_loadav = 1; - break; - case 'm': - HSum_memory = 1; - break; - case 'M': - Sort_type = S_MEM; - Sort_func = (QSORT_t)sort_mem; - break; - case 'P': - Sort_type = S_PID; - Sort_func = (QSORT_t)sort_pid; - break; - case 'R': - Sort_normal = 1; - break; - case 'S': - Show_ctimes = 1; - break; - case 't': - HSum_states = 1; - break; - case 'T': - Sort_type = S_TME; - Sort_func = (QSORT_t)sort_tme; - break; - case 'U': - Sort_type = S_CPU; - Sort_func = (QSORT_t)sort_cpu; - break; - case 'x': - Show_hicols = 1; - break; - case 'y': - Show_hirows = 1; - break; - case 'Y': - Sort_type = S_TTY; - Sort_func = (QSORT_t)sort_tty; - break; - case 'z': - Show_colors = 1; - break; - case '1': - Show_cpusum = 1; - break; - case ' ' : /* these serve as rc file comments */ - case '\t': /* and will be treated as 'eol' */ - case '\n': - return; - default : - std_err(fmtmk("bad config option - '%c'", opts[i])); - } - } -} - - - /* - * Build the two RC file names then try to read 'em. - * - * '/etc/RCfile_Sys' contains two lines consisting of the secure mode - * switch and an update interval. It's presence limits what ordinary - * users are allowed to do. - * - * '$HOME/RCfile' contains five lines. - * line 1: specifies the fields that are to be displayed - * and their order. Uppercase letters == displayed, - * lowercase letters == not shown. - * line 2: specifies miscellaneous display options and - * toggles along with the prefered sort order. - * line 3: specifies maximum processes, 0 == unlimited. - * line 4: specifies the update interval. If run in secure - * mode, this value will be ignored except for root. - * line 5: specifies the 3 basic color values. */ -static void rcfiles_read (void) -{ - char fbuf[RCFBUFSIZ]; - FILE *fp; - - strcpy(RCfile_Sys, fmtmk("/etc/%src", Myrealname)); -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, RCfile_Sys); -#endif - if (getenv("HOME")) - strcpy(RCfile, fmtmk("%s%c", getenv("HOME"), '/')); - strcat(RCfile, fmtmk(".%src", Myname)); -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, RCfile); -#endif - - fp = fopen(RCfile_Sys, "r"); - if (fp) { - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #1 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - if (strchr(fbuf, 's')) Secure_mode = 1; - - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #2 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - fclose(fp); - sscanf(fbuf, "%f", &Delay_time); - } - - fp = fopen(RCfile, "r"); - if (fp) { - fbuf[0] = '\0'; - if (fgets(fbuf, sizeof(fbuf), fp)) { /* rc file, line #1 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - strcpy(CurFields, strim(0, fbuf)); - /* Now that we've found an rc file, we'll honor those - preferences by first turning off everything... */ - Irix_mode = 0; - HSum_states = HSum_memory = HSum_loadav = Show_cpusum = 0; - Show_cmdlin = Show_ctimes = Show_idleps = Sort_normal = 0; - Show_colors = Show_hicols = Show_hirows = Show_hibold = 0; - - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #2 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - /* we could subsume this next guy since we're the only caller - -- but we're both too fat already... */ - parse_rc(strim(0, fbuf)); - - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #3 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - sscanf(fbuf, "%d", &Max_tasks); - - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #4 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - if (!Secure_mode || !getuid()) - sscanf(fbuf, "%f", &Delay_time); - - fbuf[0] = '\0'; - fgets(fbuf, sizeof(fbuf), fp); /* rc file, line #5 */ -#ifdef TRAK_MAXBUFS - MAXBUFS(rcfiles_read, fbuf); -#endif - sscanf(fbuf, "%d,%d,%d", &Base_color, &Msgs_color, &Head_color); - } - fclose(fp); - } - /* protect against meddling leading to a possible fault -- - shorter would kinda' work, longer ain't healthy for us! */ - if (strlen(CurFields) != strlen(DEF_FIELDS)) - strcpy(CurFields, DEF_FIELDS); - - /* lastly, establish the true runtime secure mode */ - Secure_mode = getuid() ? Secure_mode : 0; -} - - - /* - * Set up the terminal attributes */ -static void terminal_set (void) -{ - struct termios newtty; - - /* first the ncurses part... */ -#ifdef PRETENDNOCAP - setupterm((char *)"dumb", STDOUT_FILENO, NULL); -#else - setupterm(NULL, STDOUT_FILENO, NULL); -#endif - /* now our part... */ - if (!Batch) { - if (-1 == tcgetattr(STDIN_FILENO, &Savedtty)) - std_err("tcgetattr() failed"); - capsmk(); - newtty = Savedtty; - newtty.c_lflag &= ~ICANON; - newtty.c_lflag &= ~ECHO; - newtty.c_cc[VMIN] = 1; - newtty.c_cc[VTIME] = 0; - - Ttychanged = 1; - if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty)) { - putp(Cap_clr_scr); - std_err(fmtmk("Failed tty set: %s", strerror(errno))); - } - tcgetattr(STDIN_FILENO, &Rawtty); - putp(Cap_clr_scr); - fflush(stdout); - } -} - - -/*###### Field Selection/Ordering routines #############################*/ - - /* - * Display the current fields and their order. - * Upper case indicates a displayed field, display order is - * according to the order of the letters. - * - * A short description of each field is shown as well and is - * marked by a leading asterisk (*) if currently displayed. - * - * After all fields have been displayed, some extra explanatory - * text is then output */ -static void display_fields (void) -{ - const char *p, *x; - int i, cmax = Screen_cols / 2, rmax = Screen_rows - 3; - - /* we're relying on our callers to first clear the screen -- - thus 'fields_toggle' can avoid screen flicker since he's - too lazy to handle his own asterisk (*) logic */ - putp(Cap_bold); - for (i = 0; i < MAXtbl(Fieldstab); ++i) { - /* advance past any leading spaces */ - for (p = Fieldstab[i].head; ' ' == *p; ++p) - ; - printf("%s%c %c: %-10s = %s" - , tg2((i / rmax) * cmax, (i % rmax) + 3) - , strchr(CurFields, i + 'A') ? '*' : ' ' - , i + 'A' - , p - , Fieldstab[i].desc); - } - putp(Row_color_norm); - x = FIELDS_xtra; - while ((p = strchr(x, '\n'))) { - ++i; - printf("%s%.*s" - , tg2((i / rmax) * cmax, (i % rmax) + 3) - , p - x, x); - x = ++p; - } - putp(Caps_off); -} - - - /* - * Change order of displayed fields. */ -static void fields_reorder (void) -{ - static char prompt[] = - "Upper case characters move field left, lower case right"; - char c, *p; - int i; - - printf("%s%s", Cap_clr_scr, Cap_curs_huge); - display_fields(); - do { - show_special(fmtmk(FIELDS_current - , Cap_home, Myname, CurFields, prompt)); - chin(0, &c, 1); - i = toupper(c) - 'A'; - if (i < 0 || i >= MAXtbl(Fieldstab)) - break; - if (((p = strchr(CurFields, i + 'A'))) - || ((p = strchr(CurFields, i + 'a')))) { - if (isupper(c)) p--; - if (('\0' != p[1]) && (p >= CurFields)) { - c = p[0]; - p[0] = p[1]; - p[1] = c; - } - } - } while (1); - putp(Cap_curs_norm); - mkheadings(); -} - - - /* - * Toggle displayed fields. */ -static void fields_toggle (void) -{ - static char prompt[] = - "Toggle fields with a-x, type any other key to return"; - char c, *p; - int i; - - printf("%s%s", Cap_clr_scr, Cap_curs_huge); - do { - display_fields(); - show_special(fmtmk(FIELDS_current - , Cap_home, Myname, CurFields, prompt)); - chin(0, &c, 1); - i = toupper(c) - 'A'; - if (i < 0 || i >= MAXtbl(Fieldstab)) - break; - if ((p = strchr(CurFields, i + 'A'))) - *p = i + 'a'; - else if ((p = strchr(CurFields, i + 'a'))) - *p = i + 'A'; - } while (1); - putp(Cap_curs_norm); - mkheadings(); -} - /*###### Library Alternatives ##########################################*/ @@ -1576,15 +859,15 @@ static void *alloc_r (void *q, unsigned numb) * This guy is modeled on libproc's readproctab function except * we reuse and extend any prior proc_t's. He's been customized * for our specific needs and to avoid the use of */ -static proc_t **readprocs (proc_t **tbl) +static proc_t **refreshprocs (proc_t **tbl) { #define PTRsz sizeof(proc_t *) /* eyeball candy */ #define ENTsz sizeof(proc_t) static int flags = PROC_FILLMEM | PROC_FILLCMD | PROC_FILLUSR | PROC_FILLSTATUS | PROC_FILLSTAT; - static unsigned savmax = 0; /* first time, bypass: (i) */ - proc_t *ptsk = (proc_t *)-1; /* first time, force: (ii) */ - unsigned curmax = 0; /* every time */ + static unsigned savmax = 0; /* first time, Bypass: (i) */ + proc_t *ptsk = (proc_t *)-1; /* first time, Force: (ii) */ + unsigned curmax = 0; /* every time (jeeze) */ PROCTAB* PT; if (Monpidsidx) { @@ -1630,349 +913,759 @@ static proc_t **readprocs (proc_t **tbl) } -/*###### Main screen routines ##########################################*/ +/*###### Startup routines ##############################################*/ /* - * Process keyboard input during the main loop plus the three - * special immediate keys used with help processing. - * (thus making us only slightly recursive) */ -static void do_key (unsigned c) + * No mater what *they* say, we handle the really really BIG and + * IMPORTANT stuff upon which all those lessor functions depend! */ +static void before (char *me) { -#define kbdCTRL_L 12 - /* standardized 'secure mode' errors */ - const char *smerror = "\aCan't %s in secure mode"; + int i; - switch (c) { - case 'b': - if (!Show_hicols && !Show_hirows) - show_msg("\aNothing to highlight!"); - else { - Show_hibold = !Show_hibold; - } - capsmk(); - break; + /* setup our program name -- big! */ + Myname = strrchr(me, '/'); + if (Myname) ++Myname; else Myname = me; - case 'c': - Show_cmdlin = !Show_cmdlin; - break; - - case 'C': - Sort_type = S_CMD; - Sort_func = (QSORT_t)sort_cmd; - break; - - case 'E': - Sort_type = S_USR; - Sort_func = (QSORT_t)sort_usr; - break; - - case 'f': - case 'F': - fields_toggle(); - break; - - case 'i': - Show_idleps = !Show_idleps; - break; - - case 'I': - if (Cpu_tot > 1) { - Irix_mode = !Irix_mode; - show_msg(fmtmk("Irix mode %s", Irix_mode ? "On" : "Off")); - } else - show_msg("\aIrix mode requires SMP!"); - break; - - case 'k': - if (Secure_mode) { - show_msg(fmtmk(smerror, "kill")); - } else { - int sig, pid = get_int("PID to kill"); - - if (-1 != pid) { -#ifdef UGH_ITS_4_RH - sig = get_signal2( + /* establish cpu particulars -- even bigger! */ +#ifdef PRETEND4CPUS + Cpu_tot = 4; #else - sig = signal_name_to_number( + Cpu_tot = sysconf(_SC_NPROCESSORS_ONLN); #endif - ask_str(fmtmk("Kill PID %d with signal [%i]" - , pid, DEF_SIGNAL))); - if (-1 == sig) sig = DEF_SIGNAL; - if (sig && kill(pid, sig)) - show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s" - , pid, sig, strerror(errno))); - } - } - break; + if (1 > Cpu_tot) Cpu_tot = 1; + Cpu_map = alloc_r(NULL, sizeof(int) * Cpu_tot); + for (i = 0; i < Cpu_tot; i++) + Cpu_map[i] = i; - case 'l': - HSum_loadav = !HSum_loadav; - mkheadings(); - break; + /* get virtual page size -- nearing huge! */ + Page_size = getpagesize(); +} - case 'm': - HSum_memory = !HSum_memory; - mkheadings(); - break; - case 'M': - Sort_type = S_MEM; - Sort_func = (QSORT_t)sort_mem; - break; + /* + * Build the two RC file names then try to read 'em. */ + /* '/etc/RCfile_Sys' contains two lines consisting of the secure + * mode switch and an update interval. It's presence limits what + * ordinary users are allowed to do. */ + /* '$HOME/RCfile' contains multiple lines - 2 global + 3 per window. + * line 1: a shameless advertisement + * line 2: an id, Show_altcsr, Show_irixps, Delay_time and Curwin. + * If running in secure mode via the /etc/rcfile, + * Delay_time will be ignored except for root. + * For each of the 4 windows: + * line a: contains w->winname, fieldscur + * line b: contains w->winflags, sorttype, maxtasks + * line c: contains w->summclr, msgsclr, headclr, taskclr */ +static void configs_read (void) +{ + static const char *err_rc = "bad rcfile, you should delete '%s'"; + char fbuf[RCFBUFSIZ]; + FILE *fp; + float delay = DEF_DELAY; + char id; + int i; - case 'n': - case '#': - { int num; + strcpy(RCfile_Sys, fmtmk("/etc/%src", Myname)); + if (getenv("HOME")) + strcpy(RCfile, fmtmk("%s%c", getenv("HOME"), '/')); + strcat(RCfile, fmtmk(".%src", Myname)); - if (-1 != (num = get_int("Processes to display (0 = unlimited)"))) { - Max_tasks = num; - window_resize(0); - } + fp = fopen(RCfile_Sys, "r"); + if (fp) { + fbuf[0] = '\0'; + fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #1 */ + if (strchr(fbuf, 's')) Secure_mode = 1; + + fbuf[0] = '\0'; + fgets(fbuf, sizeof(fbuf), fp); /* sys rc file, line #2 */ + fclose(fp); + sscanf(fbuf, "%f", &delay); + } + fp = fopen(RCfile, "r"); + if (fp) { + fgets(fbuf, sizeof(fbuf), fp); /* ignore shameless advertisement */ + if (5 != (fscanf(fp, "Id:%c, " + "Show_altscr=%d, Show_irixps=%d, Delay_time=%f, Curwin=%d\n" + , &id, &Show_altscr, &Show_irixps, &delay, &i))) + std_err(fmtmk(err_rc, RCfile)); + + /* you saw that, right? (fscanf stickin' it to 'i') */ + Curwin = Winstk[i]; + for (i = 0; i < GROUPSMAX; i++) { + /* we won't check fscanf returns from here on out -- we'll be + hunky-dory with nothing in an rcfile except the 1st 2 lines */ + fscanf(fp, "%s\tfieldscur=%s\n" + , Winstk[i]->winname, Winstk[i]->fieldscur); + /* + Our winname buffer is 4 bytes big - 3 chars + '\0'. + Q. let's say someone manually edits the rcfile and increases + a winname name to oh, 500 bytes - what's the net result? + A. fscanf wipes out 496 bytes of adjacent stg (fieldscur, etc), + then we catch it with strlen and end via std_err - no worries! + we might not have been so lucky if our WIN_t was laid out + differently or statically allocated/stack based!! */ + if (RCF_FILEID != id || WINNAMSIZ <= strlen(Winstk[i]->winname) + || strlen(DEF_FIELDS) != strlen(Winstk[i]->fieldscur)) + std_err(fmtmk(err_rc, RCfile)); + fscanf(fp, "\twinflags=%d, sorttype=%c, maxtasks=%d \n" + , &Winstk[i]->winflags + , (char *)&Winstk[i]->sorttype + , &Winstk[i]->maxtasks); + fscanf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d \n" + , &Winstk[i]->summclr + , &Winstk[i]->msgsclr + , &Winstk[i]->headclr + , &Winstk[i]->taskclr); } - break; + fclose(fp); + } + /* lastly, establish the true runtime secure mode and delay time */ + Secure_mode = getuid() ? Secure_mode : 0; + if (!Secure_mode || !getuid()) Delay_time = delay; +} - case 'o': - case 'O': - fields_reorder(); - break; - case 'P': - Sort_type = S_PID; - Sort_func = (QSORT_t)sort_pid; - break; + /* + * Parse command line arguments. + * Note: it's assumed that the rc file(s) have already been read + * and our job is to see if any of those options are to be + * overridden -- we'll force some on and negate others in our + * best effort to honor the loser's (oops, user's) wishes... */ +static void parse_args (char **args) +{ + /* differences between us and the former top: + -q (zero delay) eliminated as redundant, incomplete and inappropriate + use: "nice -n-10 top -d0" to achieve what was only claimed + -p (pid monitoring) allows, not requires, a comma delimited list + . no deprecated/illegal use of 'breakargv:' with goto + . bunched args are actually handled properly and none are ignored + . we tolerate NO whitespace and NO switches -- maybe too tolerant? */ + const char *usage = " -hv | -bcisS -d delay -n iterations -p pid [,pid ...]\n"; + float tmp_delay = MAXFLOAT; + char *p; - case 'q': - stop(0); + while (*args) { + char *cp = *(args++); - case 'r': - if (Secure_mode) - show_msg(fmtmk(smerror, "renice")); - else { - int pid, val; - - pid = get_int("PID to renice"); - if (-1 == pid) break; - val = get_int(fmtmk("Renice PID %d to value", pid)); - if (setpriority(PRIO_PROCESS, (unsigned)pid, val)) - show_msg(fmtmk("\aRenice of PID %d to %d failed: %s" - , pid, val, strerror(errno))); - } - break; - - case 'R': - Sort_normal = !Sort_normal; - break; - - case 's': - case 'd': - if (Secure_mode) - show_msg(fmtmk(smerror, "change delay")); - else { - float tmp = - get_float(fmtmk("Change delay from %.1f to", Delay_time)); - if (tmp > -1) Delay_time = tmp; - } - break; - - case 'S': - Show_ctimes = !Show_ctimes; - show_msg(fmtmk("Cumulative time %s", Show_ctimes ? "On" : "Off")); - break; - - case 't': - HSum_states = !HSum_states; - mkheadings(); - break; - - case 'T': - Sort_type = S_TME; - Sort_func = (QSORT_t)sort_tme; - break; - - case 'u': - strcpy(ColUsername, ask_str("Which User (Blank for All)")); -#ifdef TRAK_MAXBUFS - MAXBUFS(do_key, ColUsername); -#endif - break; - - case 'U': - Sort_type = S_CPU; - Sort_func = (QSORT_t)sort_cpu; - break; - - case 'W': - { FILE *fp = fopen(RCfile, "w"); - - if (fp) { - fprintf(fp, "%s\t\t# Fields displayed and ordering\n" - , CurFields); - fprintf(fp, "%s%s%s%s%s%s%s%s%s%s%s%s%s%c" - "\t\t\t\t# Misc options (spelled poorly)\n" - , Show_cmdlin ? "c" : "", Sort_normal ? "R" : "" - , HSum_loadav ? "l" : "", Show_hirows ? "y" : "" - , HSum_memory ? "m" : "", Show_hicols ? "x" : "" - , Show_idleps ? "i" : "", Show_ctimes ? "S" : "" - , Irix_mode ? "I" : "", HSum_states ? "t" : "" - , Show_hibold ? "b" : "", Show_colors ? "z" : "" - , Show_cpusum ? "1" : "", Sort_type); - fprintf(fp, "%d\t\t\t\t\t# Number of tasks shown\n" - , Max_tasks); - fprintf(fp, "%.1f\t\t\t\t\t# Delay between updates\n" - , Delay_time); - fprintf(fp, "%u,%u,%u\t\t\t\t\t# Base, Msgs & Head colors\n" - , Base_color, Msgs_color, Head_color); - fclose(fp); - show_msg(fmtmk("Wrote configuration to '%s'", RCfile)); - } else - show_msg(fmtmk("\aFailed '%s' open: %s", RCfile, strerror(errno))); - } - break; - - case 'x': - Show_hicols = !Show_hicols; - capsmk(); - break; - - case 'y': - Show_hirows = !Show_hirows; - capsmk(); - break; - - case 'Y': - Sort_type = S_TTY; - Sort_func = (QSORT_t)sort_tty; - break; - - case 'z': - Show_colors = !Show_colors; - capsmk(); - break; - - case 'Z': - tweak_colors(); - break; - - case '?': - case 'h': - { char ch; - - printf("%s%s", Cap_clr_scr, Cap_curs_huge); - /* this string is well above ISO C89's minimum requirements! */ - show_special(fmtmk(HELP_data - , Myname, procps_version - , Secure_mode ? "On" : "Off", Show_ctimes ? "On" : "Off", Delay_time - , Secure_mode ? "" : HELP_unsecured)); - chin(0, &ch, 1); - putp(Cap_curs_norm); - /* our help screen currently provides for three 'immediate' keys, - two of which conflict with the main process display keys */ - switch (ch) { - case 'j' : - strcpy(CurFields, JOB_FIELDS); - ch = 'T'; /* force sort on time */ + while (*cp) { + switch (*cp) { + case '\0': + case '-': break; - case 'm' : - strcpy(CurFields, MEM_FIELDS); - ch = 'M'; /* force sort on %mem/res */ + case 'b': + Batch = 1; break; - case 'u' : - strcpy(CurFields, USR_FIELDS); - ch = 'E'; /* force sort on user */ + case 'c': + TOGw(Curwin, Show_CMDLIN); + break; + case 'd': + if (cp[1]) ++cp; + else if (*args) cp = *args++; + else std_err("-d requires argument"); + /* a negative delay will be dealt with shortly... */ + if (1 != sscanf(cp, "%f", &tmp_delay)) + std_err(fmtmk("bad delay '%s'", cp)); + break; + case 'h': case 'H': + case 'v': case 'V': + std_err(fmtmk("\t%s\nusage:\t%s%s" + , procps_version, Myname, usage)); + case 'i': + TOGw(Curwin, Show_IDLEPS); + Curwin->maxtasks = 0; + break; + case 'n': + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-n requires argument"); + if (1 != sscanf(cp, "%d", &Loops) || 0 > Loops) + std_err(fmtmk("bad iterations arg '%s'", cp)); + break; + case 'p': + do { + if (cp[1]) cp++; + else if (*args) cp = *args++; + else std_err("-p argument missing"); + if (Monpidsidx >= MONPIDMAX) + std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX)); + if (1 != sscanf(cp, "%d", &Monpids[Monpidsidx]) + || 0 > Monpids[Monpidsidx]) + std_err(fmtmk("bad pid '%s'", cp)); + if (!Monpids[Monpidsidx]) + Monpids[Monpidsidx] = getpid(); + Monpidsidx++; + if (!(p = strchr(cp, ','))) + break; + cp = p; + } while (*cp); + break; + case 's': + Secure_mode = 1; + break; + case 'S': + TOGw(Curwin, Show_CTIMES); break; default : - ch = 0; - } - if (ch) { - /* besides resetting the sort environment with the call to us - below, the 'Show_' manipulations will provide our most subtle - hint as to what the user has just wrought */ - Show_hibold = Show_hicols = Sort_normal = 1; - capsmk(); - mkheadings(); - do_key((unsigned)ch); + std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s" + , *cp, Myname, usage)); + + } /* end: switch (*cp) */ + + /* advance cp and jump over any numerical args used above */ + if (*cp) cp += strspn(++cp, "- ,.1234567890"); + } /* end: while (*cp) */ + } /* end: while (*args) */ + + /* fixup delay time, maybe... */ + if (MAXFLOAT != tmp_delay) { + if (Secure_mode || 0 > tmp_delay) + msg_save("Delay time Not changed"); + else + Delay_time = tmp_delay; + } +} + + + /* + * Set up the terminal attributes */ +static void whack_terminal (void) +{ + struct termios newtty; + + /* first the curses part... */ +#ifdef PRETENDNOCAP + setupterm((char *)"dumb", STDOUT_FILENO, NULL); +#else + setupterm(NULL, STDOUT_FILENO, NULL); +#endif + /* now our part... */ + if (!Batch) { + if (-1 == tcgetattr(STDIN_FILENO, &Savedtty)) + std_err("tcgetattr() failed"); + newtty = Savedtty; + newtty.c_lflag &= ~ICANON; + newtty.c_lflag &= ~ECHO; + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + + Ttychanged = 1; + if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty)) { + putp(Cap_clr_scr); + std_err(fmtmk("Failed tty set: %s", strerror(errno))); + } + tcgetattr(STDIN_FILENO, &Rawtty); + putp(Cap_clr_scr); + fflush(stdout); + } +} + + +/*###### Field Selection/Ordering routines #############################*/ + + /* + * Display the current fields and their order. + * Upper case indicates a displayed field, display order is + * according to the order of the letters. + * + * A short description of each field is shown as well and is + * marked by a leading asterisk (*) if currently displayed. + * + * After all fields have been displayed, some extra explanatory + * text is then output */ +static void display_fields (void) +{ +#define yRSVD 3 + const char *p, *x; + int i, cmax = Screen_cols / 2, rmax = Screen_rows - yRSVD; + + /* we're relying on our callers to first clear the screen -- + thus 'fields_toggle' can avoid screen flicker since he's + too lazy to handle his own asterisk (*) logic */ + putp(Cap_bold); + for (i = 0; i < MAXtbl(Fieldstab); ++i) { + /* advance past any leading spaces */ + for (p = Fieldstab[i].head; ' ' == *p; ++p) + ; + printf("%s%c %c: %-10s = %s" + , tg2((i / rmax) * cmax, (i % rmax) + yRSVD) + , strchr(Curwin->fieldscur, i + 'A') ? '*' : ' ' + , i + 'A' + , p + , Fieldstab[i].desc); + } + putp(Curwin->capclr_rownorm); + x = FIELDS_xtra; + while ((p = strchr(x, '\n'))) { + ++i; + printf("%s%.*s" + , tg2((i / rmax) * cmax, (i % rmax) + yRSVD) + , p - x, x); + x = ++p; + } + putp(Caps_off); + +#undef yRSVD +} + + + /* + * Change order of displayed fields. */ +static void fields_reorder (void) +{ + static const char *prompt = + "Upper case characters move field left, lower case right"; + char c, *p; + int i; + + printf("%s%s", Cap_clr_scr, Cap_curs_huge); + display_fields(); + do { + show_special(fmtmk(FIELDS_current + , Cap_home, Myname, Curwin->fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + i = toupper(c) - 'A'; + if (i < 0 || i >= MAXtbl(Fieldstab)) + break; + if (((p = strchr(Curwin->fieldscur, i + 'A'))) + || ((p = strchr(Curwin->fieldscur, i + 'a')))) { + if (isupper(c)) p--; + if (('\0' != p[1]) && (p >= Curwin->fieldscur)) { + c = p[0]; + p[0] = p[1]; + p[1] = c; } } - break; - - case ' ': - case kbdCTRL_L: - putp(Cap_clr_scr); - break; - - case '1': - Show_cpusum = !Show_cpusum; - mkheadings(); - break; - - case '\n': /* just ignore it */ - break; - - default: - show_msg("\aUnknown command -- try 'h' for help"); - } - -#undef kbdCTRL_L -#undef smERROR + } while (1); + putp(Cap_curs_norm); } -#ifdef UGH_ITS_4_RH - /* - * Obtain memory information and display it. - * Return the total memory available as a page count which is - * then used in % memory calc's. */ -static unsigned frame_memory (void) -{ - /* don't be mislead by the proc/sysinfo subscripts, they're just poorly - chosen names for enumerations apparently designed to make source - lines as imbalanced and as long as possible */ - unsigned long long **memarray; - - if (!(memarray = meminfo())) - std_err("Failed /proc/meminfo read"); - - if (HSum_memory) { - show_special(fmtmk(MEMORY_line1 - , BYTES_2K(memarray[meminfo_main][meminfo_total]) - , BYTES_2K(memarray[meminfo_main][meminfo_used]) - , BYTES_2K(memarray[meminfo_main][meminfo_free]) - , BYTES_2K(memarray[meminfo_main][meminfo_buffers]))); - - show_special(fmtmk(MEMORY_line2 - , BYTES_2K(memarray[meminfo_swap][meminfo_total]) - , BYTES_2K(memarray[meminfo_swap][meminfo_used]) - , BYTES_2K(memarray[meminfo_swap][meminfo_free]) - , BYTES_2K(memarray[meminfo_total][meminfo_cached]))); - } - - return PAGE_CNT(memarray[meminfo_main][meminfo_total]); -} - -#else - /* - * Obtain memory information and display it. */ -static void frame_memory (void) + * Toggle displayed fields. */ +static void fields_toggle (void) { - meminfo(); - if (HSum_memory) { - show_special(fmtmk(MEMORY_line1 - , kb_main_total - , kb_main_used - , kb_main_free - , kb_main_buffers)); + static const char *prompt = + "Toggle fields with a-x, type any other key to return"; + char c, *p; + int i; - show_special(fmtmk(MEMORY_line2 - , kb_swap_total - , kb_swap_used - , kb_swap_free - , kb_main_cached)); + printf("%s%s", Cap_clr_scr, Cap_curs_huge); + do { + display_fields(); + show_special(fmtmk(FIELDS_current + , Cap_home, Myname, Curwin->fieldscur, Curwin->grpname, prompt)); + chin(0, &c, 1); + i = toupper(c) - 'A'; + if (i < 0 || i >= MAXtbl(Fieldstab)) + break; + if ((p = strchr(Curwin->fieldscur, i + 'A'))) + *p = i + 'a'; + else if ((p = strchr(Curwin->fieldscur, i + 'a'))) + *p = i + 'A'; + } while (1); + putp(Cap_curs_norm); +} + + +/*###### Windows/Field Groups support #################################*/ + + /* + * Set the number of fields/columns to display; + * Create the field columns heading; and then + * Set maximum cmdline length. */ +static void win_colsheads (WIN_t *q) +{ + const char *h; + int i, needpsdb = 0; + + /* build window's procflags array and establish a tentative maxpflgs */ + for (i = 0, q->maxpflgs = 0; i < (int)strlen(q->fieldscur); i++) { + if (isupper(q->fieldscur[i])) + q->procflags[q->maxpflgs++] = q->fieldscur[i] - 'A'; + } + + /* build a preliminary columns header not to exceed screen width + (and account for a possible leading window number) */ + if (Show_altscr) strcpy(q->columnhdr, " "); else q->columnhdr[0] = '\0'; + for (i = 0; i < q->maxpflgs; i++) { + h = Fieldstab[q->procflags[i]].head; + /* oops, won't fit -- we're outta here... */ + if (Screen_cols < (int)(strlen(q->columnhdr) + strlen(h))) break; + strcat(q->columnhdr, h); + } + + /* establish the final maxpflgs and prepare to grow the command + column heading via maxcmdln -- it may be a fib if P_CMD wasn't + encountered, but that's ok because it won't be displayed anyway */ + q->maxpflgs = i; + q->maxcmdln = Screen_cols + - (strlen(q->columnhdr) - strlen(Fieldstab[P_CMD].head)) - 1; + + /* now we can build the true run-time columns header and format the + command column heading if P_CMD is really being displayed -- + show_a_task is aware of the addition of winnum to the header */ + sprintf(q->columnhdr, "%s", Show_altscr ? fmtmk("%d", mkWNO(q)) : ""); + for (i = 0; i < q->maxpflgs; i++) { + /* are we gonna' need the kernel symbol table? */ + if (P_WCHAN == q->procflags[i]) needpsdb = 1; + h = Fieldstab[q->procflags[i]].head; + if (P_CMD == q->procflags[i]) + strcat(q->columnhdr + , fmtmk(Fieldstab[P_CMD].fmts, q->maxcmdln, q->maxcmdln, h)); + else + strcat(q->columnhdr, h); + } + + /* do we need the kernel symbol table (and is it already open?) */ + if (needpsdb) { + if (-1 == No_ksyms) { + No_ksyms = 0; + if (open_psdb_message(NULL, msg_save)) + /* why so counter-intuitive, couldn't open_psdb_message + mirror sysmap_mmap -- that func does all the work anyway? */ + No_ksyms = 1; + else + PSDBopen = 1; + } } } -#endif /* end: UGH_ITS_4_RH */ + /* + * Value a window's name and make the associated group name. */ +static void win_names (WIN_t *q, const char *name) +{ + /* these safeguards are totally unnecessary if our caller(s) are + internal, they can be trusted -- it's those darn users that we + worry 'bout... */ + sprintf(q->winname, "%.*s", WINNAMSIZ -1, name); + sprintf(q->grpname, "%d:%.*s", mkWNO(q), WINNAMSIZ -1, name); +} + + + /* + * Display a window/field group (ie. make it "current"). */ +static void win_select (int ch) +{ + static const char *prompt = "Choose field group (1 - 4)"; + + /* if there's no ch, it means we're supporting the normal do_key routine, + so we must try to get our own darn ch by begging the user... */ + if (!ch) { + show_pmt(prompt); + chin(0, (char *)&ch, 1); + } + switch (ch) { + case 'a': /* we don't carry 'a' / 'w' in our */ + Curwin = Curwin->next; /* user prompt - those case labels */ + break; /* are just here for a good friend */ + case 'w': /* of ours, wins_colors (however, */ + Curwin = Curwin->prev; /* those letters work via the pmt */ + break; /* too -- but really the end-loser */ + case '1': /* should just press the darn key */ + Curwin = Winstk[Def_WINDOW]; /* in the first place) */ + break; + case '2': + Curwin = Winstk[Job_WINDOW]; + break; + case '3': + Curwin = Winstk[Mem_WINDOW]; + break; + case '4': + Curwin = Winstk[Usr_WINDOW]; + break; + } +} + + + /* + * Establish the requested sort environment */ +static void win_sortset (WIN_t *q, const int which) +{ + /* note: everyone and their brother uses only the enum's to identify + sort type, but we use both the actual value and the enum so + somewhere outside the header it's apparent which is what */ + switch (which) { + case 'C': + q->sorttype = S_CMD; + q->sortfunc = (QSORT_t)sort_cmd; + break; + case 'E': + q->sorttype = S_USR; + q->sortfunc = (QSORT_t)sort_usr; + break; + case 'M': + q->sorttype = S_MEM; + q->sortfunc = (QSORT_t)sort_mem; + break; + case 'P': + q->sorttype = S_PID; + q->sortfunc = (QSORT_t)pid_sort; + break; + case 'T': + q->sorttype = S_TME; + q->sortfunc = (QSORT_t)sort_tme; + break; + case 'U': + q->sorttype = S_CPU; + q->sortfunc = (QSORT_t)sort_cpu; + break; + case 'Y': + q->sorttype = S_TTY; + q->sortfunc = (QSORT_t)sort_tty; + break; + } +} + + /* + * Just warn the user when a command can't be honored. */ +static int win_warn (void) +{ + show_msg(fmtmk("\aCommand disabled, activate window #%d with '-' or '_'" + , mkWNO(Curwin))); + + /* we gotta' return false 'cause we're somewhat well known within + macro society, by way of that sassy little tertiary operator... */ + return 0; +} + + + /* + * Change colors *Helper* function to save/restore settings; + * ensure colors will show; and rebuild the terminfo strings. */ +static void winsclr (WIN_t *q, int save) +{ + static int flgssav, summsav, msgssav, headsav, tasksav; + + if (save) { + flgssav = q->winflags; summsav = q->summclr; + msgssav = q->msgsclr; headsav = q->headclr; tasksav = q->taskclr; + SETw(q, Show_COLORS); + } else { + q->winflags = flgssav; q->summclr = summsav; + q->msgsclr = msgssav; q->headclr = headsav; q->taskclr = tasksav; + } + capsmk(q); +} + + + /* + * Change colors used in display */ +static void wins_colors (void) +{ +#define kbdABORT 'q' +#define kbdAPPLY '\n' + int clr = Curwin->taskclr, *pclr = &Curwin->taskclr; + char ch, tgt = 'T'; + + if (0 >= max_colors) { + show_msg("\aNo colors to map!"); + return; + } + winsclr(Curwin, 1); + printf("%s%s", Cap_clr_scr, Cap_curs_huge); + + do { + /* this string is well above ISO C89's minimum requirements! */ + show_special(fmtmk(COLOR_help + , Cap_home, Myname, procps_version, Curwin->grpname + , CHKw(Curwin, Show_HIBOLD) ? "On" : "Off" + , CHKw(Curwin, Show_COLORS) ? "On" : "Off" + , tgt, clr, Curwin->winname)); + chin(0, &ch, 1); + switch (ch) { + case 'S': + pclr = &Curwin->summclr; + clr = *pclr; + tgt = ch; + break; + case 'M': + pclr = &Curwin->msgsclr; + clr = *pclr; + tgt = ch; + break; + case 'H': + pclr = &Curwin->headclr; + clr = *pclr; + tgt = ch; + break; + case 'T': + pclr = &Curwin->taskclr; + clr = *pclr; + tgt = ch; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + clr = ch - '0'; + *pclr = clr; + break; + case 'b': + TOGw(Curwin, Show_HIBOLD); + break; + case 'z': + TOGw(Curwin, Show_COLORS); + break; + case 'a': + case 'w': + win_select(ch); + winsclr(Curwin, 1); + clr = Curwin->taskclr, pclr = &Curwin->taskclr; + tgt = 'T'; + break; + } + capsmk(Curwin); + } while (kbdAPPLY != ch && kbdABORT != ch); + + if (kbdABORT == ch) + winsclr(Curwin, 0); + putp(Cap_curs_norm); + +#undef kbdABORT +#undef kbdAPPLY +} + + + /* + * Manipulate flag(s) for all our windows. */ +static void wins_reflag (int what, int flg) +{ + WIN_t *w; + + w = Curwin; + do { + switch (what) { + case Flgs_TOG: + TOGw(w, flg); + break; + case Flgs_SET: + SETw(w, flg); + break; + case Flgs_OFF: + OFFw(w, flg); + break; + } + w = w->next; + } while (w != Curwin); + + /* a flag with special significance -- user wants to rebalance display + so darn it, we gotta' spin thru all those windows one mo' time and + 'off' one number then force on two flags... */ + /* (jeeze, doesn't this idiot know there are just 4 windows?) */ + if (EQUWINS_cwo == flg) { + w = Curwin; + do { + w->maxtasks = 0; + SETw(w, Show_IDLEPS | VISIBLE_tsk); + w = w->next; + } while (w != Curwin); + } +} + + + /* + * Set the screen dimensions and call the real workhorse. + * (also) catches: + * SIGWINCH and SIGCONT */ +static void wins_resize (int dont_care_sig) +{ + struct winsize wz; + WIN_t *w; + + Screen_cols = columns; + Screen_rows = lines; + if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &wz))) { + Screen_cols = wz.ws_col; + Screen_rows = wz.ws_row; + } + w = Curwin; + do { + win_colsheads(w); + w = w->next; + } while (w != Curwin); +} + + + /* + * Set up the raw/incomplete field group windows -- + * they'll be finished off after startup completes. + * (and very likely that will override most/all of our efforts) + * ( --- life-is-NOT-fair --- ) */ +static void windows_stage1 (void) +{ + static struct { + const char *name; + const char *flds; + int sort; + int clrs[4]; /* summ, msgs, heads, task */ + } wtab[] = { + { "Def", DEF_FIELDS, S_PID, + { COLOR_RED, COLOR_RED, COLOR_YELLOW, COLOR_RED } }, + { "Job", JOB_FIELDS, S_TME, + { COLOR_CYAN, COLOR_CYAN, COLOR_WHITE, COLOR_CYAN } }, + { "Mem", MEM_FIELDS, S_MEM, + { COLOR_MAGENTA, COLOR_MAGENTA, COLOR_BLUE, COLOR_MAGENTA } }, + { "Usr", USR_FIELDS, S_USR, + { COLOR_YELLOW, COLOR_YELLOW, COLOR_GREEN, COLOR_YELLOW } }, + }; + WIN_t *w; + char *pc; + int i, x, *pi; + + /* get all our window structs in one big chunk */ + w = alloc_c(sizeof(WIN_t) * GROUPSMAX); + + for (i = 0; i < GROUPSMAX; i++) { + Winstk[i] = w; + w->winnum = i; + strcpy(w->winname, wtab[i].name); + strcpy(w->fieldscur, wtab[i].flds); + w->sorttype = wtab[i].sort; + w->winflags = DEF_WINFLGS; + for (x = 0, pi = &w->summclr; x < 4; x++, pi++) + *pi = wtab[i].clrs[x]; + w->captab[0] = Cap_norm; + w->captab[1] = Cap_norm; + w->captab[2] = Cap_bold; + /* complete this win's captab, but not the brute force way... */ + for (x = 3, pc = w->capclr_sum; x < CAPTABMAX; x++) { + w->captab[x] = pc; + pc += CLRBUFSIZ; + } + w->next = w + 1; + w->prev = w - 1; + ++w; + } + /* fixup the circular chains... */ + Winstk[Usr_WINDOW]->next = Winstk[0]; + Winstk[0]->prev = Winstk[Usr_WINDOW]; + Curwin = Winstk[0]; + Show_altscr = 0; +} + + + /* + * This guy just completes the field group windows after the + * rcfiles have been read and command line arguments parsed + * (he's also key to the success of that darn Batch mode). */ +static void windows_stage2 (void) +{ + int i; + + if (Batch) { + Show_altscr = 0; + OFFw(Curwin, Show_COLORS); + } + wins_resize(0); + for (i = 0; i < GROUPSMAX; i++) { + win_names(Winstk[i], Winstk[i]->winname); + win_sortset(Winstk[i], Winstk[i]->sorttype); + capsmk(Winstk[i]); + } +} + + +/*###### Per-Frame Display support #####################################*/ + /* * State display *Helper* function to calc and display the state * percentages for a single cpu. In this way, we can support @@ -1980,8 +1673,8 @@ static void frame_memory (void) * 1 - single cpu machines * 2 - modest smp boxes with room for each cpu's percentages * 3 - massive smp guys leaving little or no room for process - * display and thus requiring the Show_cpusum toggle */ -static void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx) + * display and thus requiring the cpu summary toggle */ +static void cpudo (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx) { /* we'll trim to zero if we get negative time ticks, which has happened with some SMP kernels (pre-2.4?) */ @@ -1989,6 +1682,10 @@ static void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx) TICS_t u_tics, s_tics, n_tics, i_tics; long u_frme, s_frme, n_frme, i_frme, tot_frme, tz; +#ifdef PRETEND4CPUS + rewind(fp); + fmt = CPU_FMTS_JUST1; +#endif if (4 != fscanf(fp, fmt, &u_tics, &n_tics, &s_tics, &i_tics)) std_err("Failed /proc/stat read"); @@ -2021,12 +1718,13 @@ static void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx) /* * Calc the number of tasks in each state (run, sleep, etc) * Calc percent cpu usage for each task (pcpu) - * Calc the cpu(s) percent in each state (user, system, nice, idle) */ + * Calc the cpu(s) percent in each state (user, system, nice, idle) + * AND establish the total number of tasks for this frame! */ static void frame_states (proc_t **p, int show) { static HIST_t *hist_sav = NULL; static unsigned hist_siz; - static int hist_tot, showsav; + static int viewsav; static CPUS_t *smpcpu; HIST_t *hist_new; unsigned total, running, sleeping, stopped, zombie; @@ -2034,23 +1732,22 @@ static void frame_states (proc_t **p, int show) int i; if (!hist_sav) { - hist_tot = 0; + Frame_maxtask = 0; /* room for 512 HIST_t's (if Page_size == 4k) */ hist_siz = (Page_size / sizeof(HIST_t)); hist_sav = alloc_c(hist_siz); /* note: we allocate one more CPUS_t than Cpu_tot so that the last slot can hold tics representing the /proc/stat cpu summary - (first line read) -- that slot supports Show_cpusum */ + (first line read) -- that slot supports summary cpu info */ smpcpu = alloc_c((1 + Cpu_tot) * sizeof(CPUS_t)); - showsav = Show_cpusum; + viewsav = CHKw(Curwin, View_CPUSUM); } - hist_new = alloc_c(hist_siz); total = running = sleeping = stopped = zombie = 0; etime = time_elapsed(); /* make a pass through the data to get stats */ - while (-1 != p[total]->pid) { + while (-1 != p[total]->pid) { /* calculations //// */ TICS_t tics; proc_t *this = p[total]; @@ -2069,145 +1766,186 @@ static void frame_states (proc_t **p, int show) running++; break; } - if (total * sizeof(HIST_t) >= hist_siz) { hist_siz += (Page_size / sizeof(HIST_t)); hist_sav = alloc_r(hist_sav, hist_siz); hist_new = alloc_r(hist_new, hist_siz); } - /* calculate time in this process; the sum of user time (utime) - + system time (stime) -- but PLEASE dont waste time and effort - calculating and saving data that goes unused, like the old top! */ + + system time (stime) -- but PLEASE dont waste time and effort on + calcs and saves that go unused, like the old top! */ hist_new[total].pid = this->pid; hist_new[total].tics = tics = (this->utime + this->stime); /* find matching entry from previous pass and make ticks elapsed */ - for (i = 0; i < hist_tot; i++) { + for (i = 0; i < Frame_maxtask; i++) { if (this->pid == hist_sav[i].pid) { tics -= hist_sav[i].tics; break; } } - /* finally calculate an integer version of %cpu for this task and plug it into the unfilled slot in proc_t */ this->pcpu = (tics * 1000 / Hertz) / etime; if (this->pcpu > 999) this->pcpu = 999; - /* if in Solaris mode, adjust cpu percentage not only for the cpu the process is running on, but for all cpus together */ - if (!Irix_mode) this->pcpu /= Cpu_tot; + if (!Show_irixps) this->pcpu /= Cpu_tot; total++; - } /* end: while 'p[total]->pid' */ + } /* end: while 'pids' */ - if (show) { + + if (show) { /* display ///////// */ FILE *fp; - /* whoa, we've changed modes -- gotta' clean old histories */ - if (Show_cpusum != showsav) { - /* fresh start for the last slot in the history area */ - if (Show_cpusum) memset(&smpcpu[Cpu_tot], '\0', sizeof(CPUS_t)); - /* fresh start for the true smpcpu history area */ - else memset(smpcpu, '\0', Cpu_tot * sizeof(CPUS_t)); - showsav = Show_cpusum; - } - /* display Task states */ show_special(fmtmk(STATES_line1 , total, running, sleeping, stopped, zombie)); + Msg_row += 1; + + /* clean old histories if we've changed modes */ + if (CHKw(Curwin, View_CPUSUM) != viewsav) { + if (CHKw(Curwin, View_CPUSUM)) + /* fresh start for the last slot in the history area */ + memset(&smpcpu[Cpu_tot], '\0', sizeof(CPUS_t)); + else + /* fresh start for the true smpcpu history area */ + memset(smpcpu, '\0', Cpu_tot * sizeof(CPUS_t)); + viewsav = CHKw(Curwin, View_CPUSUM); + } - /* now arrange to calculate and display states for all Cpu's */ if (!(fp = fopen("/proc/stat", "r"))) std_err(fmtmk("Failed /proc/stat open: %s", strerror(errno))); - if (Show_cpusum) + if (CHKw(Curwin, View_CPUSUM)) { /* retrieve and display just the 1st /proc/stat line */ - frame_smp(fp, CPU_FMTS_JUST1, &smpcpu[Cpu_tot], "Cpu(s) state:"); - else { + cpudo(fp, CPU_FMTS_JUST1, &smpcpu[Cpu_tot], "Cpu(s) state:"); + Msg_row += 1; + } else { char tmp[SMLBUFSIZ]; /* skip the 1st line, which reflects total cpu states */ if (!fgets(tmp, sizeof(tmp), fp)) std_err("Failed /proc/stat read"); -#ifdef TRAK_MAXBUFS - MAXBUFS(frame_states, tmp); -#endif /* now do each cpu's states separately */ for (i = 0; i < Cpu_tot; i++) { sprintf(tmp, "%-6scpu%-2d:" /* [ cpu states as ] */ , i ? " " : "State" /* 'State cpu0 : ... ' */ - , Irix_mode ? i : Cpu_map[i]); /* ' cpu1 : ... ' */ - frame_smp(fp, CPU_FMTS_MULTI, &smpcpu[i], tmp); + , Show_irixps ? i : Cpu_map[i]); /* ' cpu1 : ... ' */ + cpudo(fp, CPU_FMTS_MULTI, &smpcpu[i], tmp); + Msg_row += 1; } } - fclose(fp); } /* end: if 'show' */ /* save this frame's information */ - hist_tot = total; memcpy(hist_sav, hist_new, hist_siz); free(hist_new); - /* finally, sort the processes on whatever... */ - qsort(p, total, sizeof(proc_t *), (QSORT_t)Sort_func); + /* shout results to the world (and us too, the next time around) */ + Frame_maxtask = total; } +#ifdef UGH_ITS_4_RH + /* + * Obtain memory information and display it. + * 'Return' the total memory available as a page count. */ +static void frame_storage (void) +{ /* don't be mislead by the proc/sysinfo subscripts, they're just poorly + chosen names for enum's apparently designed to make source lines as + imbalanced and long as possible (not to mention the constant, recurring + run-time costs of subscript resolution in the first place - duh?!) */ + unsigned long long **memarray; + + if (!(memarray = meminfo())) + std_err("Failed /proc/meminfo read"); + + if (CHKw(Curwin, View_MEMORY)) { + show_special(fmtmk(MEMORY_line1 + , BYTES_2K(memarray[meminfo_main][meminfo_total]) + , BYTES_2K(memarray[meminfo_main][meminfo_used]) + , BYTES_2K(memarray[meminfo_main][meminfo_free]) + , BYTES_2K(memarray[meminfo_main][meminfo_buffers]))); + + show_special(fmtmk(MEMORY_line2 + , BYTES_2K(memarray[meminfo_swap][meminfo_total]) + , BYTES_2K(memarray[meminfo_swap][meminfo_used]) + , BYTES_2K(memarray[meminfo_swap][meminfo_free]) + , BYTES_2K(memarray[meminfo_total][meminfo_cached]))); + Msg_row += 2; + } + + Mem_pages = PAGE_CNT(memarray[meminfo_main][meminfo_total]); +} + +#else + + /* + * Obtain memory information and display it. */ +static void frame_storage (void) +{ + meminfo(); + if (CHKw(Curwin, View_MEMORY)) { + show_special(fmtmk(MEMORY_line1 + , kb_main_total, kb_main_used, kb_main_free, kb_main_buffers)); + show_special(fmtmk(MEMORY_line2 + , kb_swap_total, kb_swap_used, kb_swap_free, kb_main_cached)); + Msg_row += 2; + } +} +#endif /* end: UGH_ITS_4_RH */ + + /* * Task display *Helper* function to handle highlighted * column transitions. */ -static void mkcol (unsigned idx, int sta, int *pad, char *buf, ...) +static void mkcol (WIN_t *q, unsigned idx, int sta, int *pad, char *buf, ...) { char tmp[COLBUFSIZ]; va_list va; va_start(va, buf); - if (!Show_hicols || Sort_type != Fieldstab[idx].sort) { + if (!CHKw(q, Show_HICOLS) || q->sorttype != Fieldstab[idx].sort) { vsprintf(buf, Fieldstab[idx].fmts, va); } else { vsprintf(tmp, Fieldstab[idx].fmts, va); - sprintf(buf, "%s%s", Row_color_high, tmp); - *pad += Len_row_high; - if (!Show_hirows || 'R' != sta) { - strcat(buf, Row_color_norm); - *pad += Len_row_norm; + sprintf(buf, "%s%s", q->capclr_rowhigh, tmp); + *pad += q->len_rowhigh; + if (!CHKw(q, Show_HIROWS) || 'R' != sta) { + strcat(buf, q->capclr_rownorm); + *pad += q->len_rownorm; } } va_end(va); -#ifdef TRAK_MAXBUFS - MAXBUFS(mkcol, tmp); -#endif } /* - * Displays information for a single task. */ -#ifdef UGH_ITS_4_RH -static void show_a_task (proc_t *task, unsigned mempgs) -#else -static void show_a_task (proc_t *task) -#endif + * Display information for a single task row. */ +static void show_a_task (WIN_t *q, proc_t *task) { /* the following macro is used for those columns that are NOT sortable so as to avoid the function call overhead since mkcol cannot be made inline -- if additional sort columns are added, change the appropriate switch label's usage to lower case and thus invoke the real function */ -#define MKCOL(idx,sta,pad,buf,arg) \ +#define MKCOL(q,idx,sta,pad,buf,arg) \ sprintf(buf, Fieldstab[idx].fmts, arg) char rbuf[ROWBUFSIZ]; int i, x, pad; - pad = 0; - rbuf[0] = '\0'; + /* since win_colsheads adds a number to the window's column header, + we must begin a row with that in mind... */ + pad = Show_altscr; + if (pad) strcpy(rbuf, " "); else rbuf[0] = '\0'; - for (i = 0; i < NumFields; i++) { + for (i = 0; i < q->maxpflgs; i++) { char cbuf[COLBUFSIZ]; unsigned f, s, w; cbuf[0] = '\0'; - f = PFlags[i]; + f = q->procflags[i]; s = Fieldstab[f].scale; w = Fieldstab[f].width; @@ -2215,107 +1953,106 @@ static void show_a_task (proc_t *task) case P_CMD: { char *cmdptr, cmdnam[ROWBUFSIZ]; - if (Show_cmdlin) { + if (!CHKw(q, Show_CMDLIN)) + cmdptr = task->cmd; + else { cmdnam[0] = '\0'; if (task->cmdline) { x = 0; do { - /* whoa, during a kernel build, parts of the make - process will create cmdlines in excess of 3000 bytes - but *without* the typical intervening nulls */ + /* during a kernel build, parts of the make will create + cmdlines in excess of 3000 bytes but *without* the + intervening nulls -- so we must limit our strcat... */ strcat(cmdnam - , fmtmk("%.*s ", Max_cmd, task->cmdline[x++])); + , fmtmk("%.*s ", q->maxcmdln, task->cmdline[x++])); /* whoa, gnome's xscreensaver had a ^I in his cmdline creating a line wrap when the window was maximized & - the tab came into view -- that in turn, whacked - our heading lines so we'll strim those !#@*#!... */ + the tab came into view -- so whack those suckers... */ strim(1, cmdnam); - /* enough, don't ya think? */ - if (Max_cmd < (int)strlen(cmdnam)) - break; + if (q->maxcmdln < (int)strlen(cmdnam)) break; } while (task->cmdline[x]); } else { - /* if this process really doesn't have a cmdline, we'll - consider it a kernel thread and display it uniquely - [ we need sort_cmd's complicity in this plot ] */ + /* if cmdline is absent, consider it a kernel thread and + display it uniquely (we'll need sort_cmd's complicity) */ strcpy(cmdnam, fmtmk("( %s )", task->cmd)); } cmdptr = cmdnam; - } else - cmdptr = task->cmd; - /* hurry up, before cmdptr goes out of scope... */ - mkcol(f, task->state, &pad, cbuf, Max_cmd, Max_cmd, cmdptr); + } + mkcol(q, f, task->state, &pad + , cbuf, q->maxcmdln, q->maxcmdln, cmdptr); } break; case P_CODE: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->trs), w, s)); break; case P_CPU: - mkcol(f, task->state, &pad, cbuf, (float)task->pcpu / 10); + mkcol(q, f, task->state, &pad, cbuf, (float)task->pcpu / 10); break; case P_DATA: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->drs), w, s)); break; case P_DIRTY: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num((unsigned)task->dt, w, s)); break; case P_FAULT: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(task->maj_flt, w, s)); break; case P_FLAGS: - MKCOL(f, task->state, &pad, cbuf, task->flags); + MKCOL(q, f, task->state, &pad, cbuf, task->flags); for (x = 0; x < (int)strlen(cbuf); x++) if ('0' == cbuf[x]) cbuf[x] = '.'; break; case P_GROUP: - MKCOL(f, task->state, &pad, cbuf, task->egroup); + MKCOL(q, f, task->state, &pad, cbuf, task->egroup); break; case P_MEM: - mkcol(f, task->state, &pad, cbuf + mkcol(q, f, task->state, &pad, cbuf #ifdef UGH_ITS_4_RH - , (float)task->resident * 100 / mempgs); + , (float)task->resident * 100 / Mem_pages); #else , (float)PAGES_2K(task->resident) * 100 / kb_main_total); #endif break; case P_NCPU: #ifdef UGH_ITS_4_RH - MKCOL(f, task->state, &pad, cbuf, task->lproc); + MKCOL(q, f, task->state, &pad, cbuf, task->lproc); #else - MKCOL(f, task->state, &pad, cbuf, task->processor); + MKCOL(q, f, task->state, &pad, cbuf, task->processor); #endif break; case P_NI: - MKCOL(f, task->state, &pad, cbuf, task->nice); + MKCOL(q, f, task->state, &pad, cbuf, task->nice); break; case P_PID: - mkcol(f, task->state, &pad, cbuf, task->pid); + mkcol(q, f, task->state, &pad, cbuf, task->pid); break; case P_PPID: - MKCOL(f, task->state, &pad, cbuf, task->ppid); + MKCOL(q, f, task->state, &pad, cbuf, task->ppid); break; case P_PR: - MKCOL(f, task->state, &pad, cbuf, task->priority); + MKCOL(q, f, task->state, &pad, cbuf, task->priority); break; case P_RES: - /* 'rss' vs 'resident' (which includes IO memory) ? - -- we'll ensure that VIRT = SWAP + RES */ - mkcol(f, task->state, &pad, cbuf + mkcol(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->resident), w, s)); break; case P_SHR: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->share), w, s)); break; case P_STA: - MKCOL(f, task->state, &pad, cbuf, status(task)); +#ifdef USE_LIB_STA3 + MKCOL(q, f, task->state, &pad, cbuf, status(task)); +#else + MKCOL(q, f, task->state, &pad, cbuf, task->state); +#endif break; case P_SWAP: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->size - task->resident), w, s)); break; case P_TIME: @@ -2323,181 +2060,631 @@ static void show_a_task (proc_t *task) { TICS_t t; t = task->utime + task->stime; - if (Show_ctimes) + if (CHKw(q, Show_CTIMES)) t += (task->cutime + task->cstime); - mkcol(f, task->state, &pad, cbuf, scale_tics(t, w)); + mkcol(q, f, task->state, &pad, cbuf, scale_tics(t, w)); } break; case P_TTY: { char tmp[TNYBUFSIZ]; - dev_to_tty(tmp, Fieldstab[f].width - , task->tty, task->pid, ABBREV_DEV); - mkcol(f, task->state, &pad, cbuf, tmp); + dev_to_tty(tmp, (int)w, task->tty, task->pid, ABBREV_DEV); + mkcol(q, f, task->state, &pad, cbuf, tmp); } break; case P_UID: - MKCOL(f, task->state, &pad, cbuf, task->euid); + MKCOL(q, f, task->state, &pad, cbuf, task->euid); break; case P_USER: - mkcol(f, task->state, &pad, cbuf, task->euser); + mkcol(q, f, task->state, &pad, cbuf, task->euser); break; case P_VIRT: - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , scale_num(PAGES_2K(task->size), w, s)); break; case P_WCHAN: if (No_ksyms) -#ifdef UPCASE_HEXES - MKCOL(f, task->state, &pad, cbuf +#ifdef CASEUP_HEXES + MKCOL(q, f, task->state, &pad, cbuf , fmtmk("x%08lX", (long)task->wchan)); #else - MKCOL(f, task->state, &pad, cbuf + MKCOL(q, f, task->state, &pad, cbuf , fmtmk("x%08lx", (long)task->wchan)); #endif else - MKCOL(f, task->state, &pad, cbuf, wchan(task->wchan)); + MKCOL(q, f, task->state, &pad, cbuf, wchan(task->wchan)); break; - } /* end: switch 'PFlags[i]' */ -#ifdef TRAK_MAXBUFS - MAXBUFS(show_a_task, cbuf); -#endif - strcat(rbuf, cbuf); + } /* end: switch 'flg' */ - } /* end: for 'NumFields' */ -#ifdef TRAK_MAXBUFS - MAXBUFS(show_a_task, rbuf); -#endif + strcat(rbuf, cbuf); + } /* end: for 'maxpflgs' */ /* This row buffer could be stuffed with parameterized strings. We are thus advised to always use tputs/putp, but it works just fine with good ol' printf... */ - printf("\n%s%.*s%s%s" - , (Show_hirows && 'R' == task->state) ? Row_color_high : Row_color_norm + printf("\n%s%.*s%s%s", (CHKw(q, Show_HIROWS) && 'R' == task->state) + ? q->capclr_rowhigh : q->capclr_rownorm , Screen_cols + pad , rbuf , Caps_off , Cap_clr_eol); -#ifdef TRAK_MAXCAPS - if (pad > Max_pads) Max_pads = pad; - if (pad < Min_pads) Min_pads = pad; - /* now that we have TRAK_MAXBUFS, the next two duplicate some effort */ - if ((int)strlen(rbuf) > Max_rbuf) Max_rbuf = (int)strlen(rbuf); - if ((int)strlen(rbuf) < Min_rbuf) Min_rbuf = (int)strlen(rbuf); -#endif - #undef MKCOL } + +/*###### Main Screen routines ##########################################*/ /* - * Read all process info and display it. */ -static void show_everything (void) + * Process keyboard input during the main loop */ +static void do_key (unsigned c) { - static proc_t **p_table = NULL; - int ntask, nline; -#ifdef UGH_ITS_4_RH - unsigned mempgs; +#ifndef QUIT_NORMALQ +#define kbdESCAPE 27 +#endif +#define kbdCTRL_L 12 + /* standardized 'secure mode' errors */ + const char *err_secure = "\aCan't %s in secure mode"; + /* standardized 'smp' errors */ +#ifdef WARN_NOT_SMP + const char *err_smp = "\aSorry, only 1 cpu detected"; #endif - if (!p_table) { - p_table = readprocs(p_table); - frame_states(p_table, 0); - sleep(1); - } else - putp(Batch ? "\n" : Cap_home); + switch (c) { + /* begin windows grouping /////////////////////////// */ + case '-': /* 'Dash' lower case ----------------------- */ + if (Show_altscr) + TOGw(Curwin, VISIBLE_tsk); + break; - /* - ** Display Load averages */ - if (HSum_loadav) - show_special(fmtmk(LOADAV_line, Myname, sprint_uptime())); + case '_': /* 'Dash' upper case ----------------------- */ + if (Show_altscr) /* switcharoo, all viz & inviz ............. */ + wins_reflag(Flgs_TOG, VISIBLE_tsk); + break; - /* - ** Display System stats (also calc 'pcpu' and sort p_table) */ - p_table = readprocs(p_table); - frame_states(p_table, HSum_states); + case '=': /* 'Equals' lower case --------------------- */ + if (Show_altscr) { /* equalize task display ................... */ + Curwin->maxtasks = 0; + SETw(Curwin, Show_IDLEPS | VISIBLE_tsk); + } + break; - /* - ** Display Memory and Swap space usage */ -#ifdef UGH_ITS_4_RH - mempgs = frame_memory(); + case '+': /* 'Equals' upper case --------------------- */ + /* Special New Provision: + . escape from monitoring selected pids ('-p' cmdline switch) + -- just seems to go naturally with this new '+' command key + . and who knows, maybe the documentation will NOT be overlooked */ + Monpidsidx = 0; + if (Show_altscr) /* equalize ALL task windows (& make viz) .. */ + SETw(Curwin, EQUWINS_cwo); + break; + + case 'A': + Show_altscr = !Show_altscr; + wins_resize(0); + break; + + case 'a': + if (Show_altscr) Curwin = Curwin->next; + break; + + case 'F': + case 'O': + win_select(0); /* we won't restrict this one, ok? ......... */ + break; + + case 'g': + if (Show_altscr) { + char tmp[GETBUFSIZ]; + strcpy(tmp, ask4str(fmtmk("Rename window '%s' to (0-3 chars)" + , Curwin->winname))); + if (tmp[0]) win_names(Curwin, tmp); + } + break; + + case 'w': + if (Show_altscr) Curwin = Curwin->prev; + break; + /* end windows grouping ///////////////////////////// */ + case 'b': + if (VIZCHKc) { + if (!CHKw(Curwin, Show_HICOLS) && !CHKw(Curwin, Show_HIROWS)) + show_msg("\aNothing to highlight!"); + else { + TOGw(Curwin, Show_HIBOLD); + capsmk(Curwin); + } + } + break; + + case 'c': + VIZTOGc(Show_CMDLIN); + break; + + case 'f': + if (VIZCHKc) { + fields_toggle(); + win_colsheads(Curwin); + } + break; + + case 'i': + VIZTOGc(Show_IDLEPS); + break; + + case 'I': +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) { + Show_irixps = !Show_irixps; + show_msg(fmtmk("Irix mode %s", Show_irixps ? "On" : "Off")); + } else + show_msg(err_smp); #else - frame_memory(); + Show_irixps = !Show_irixps; + show_msg(fmtmk("Irix mode %s", Show_irixps ? "On" : "Off")); #endif + break; - /* - ** Display Headings for columns */ - printf("%s%s%s%s%s" - , tg2(0, MSG_line + 1) - , Hdr_color - , ColHeadings - , Caps_off - , Cap_clr_eol); + case 'k': + if (Secure_mode) { + show_msg(fmtmk(err_secure, "kill")); + } else { + int sig, pid = get_int("PID to kill"); - /* Finally! Loop through to find each task, and display it ... - ... lather, rinse, repeat */ - ntask = nline = 0; - while (-1 != p_table[ntask]->pid && nline < Max_lines) { - if ((Show_idleps - || ('S' != p_table[ntask]->state && 'Z' != p_table[ntask]->state)) - && ((!ColUsername[0]) - || (!strcmp(ColUsername, p_table[ntask]->euser)) ) ) { - /* - ** Display a process Row */ + if (-1 != pid) { #ifdef UGH_ITS_4_RH - show_a_task(p_table[ntask], mempgs); + sig = get_signal2( #else - show_a_task(p_table[ntask]); + sig = signal_name_to_number( #endif - if (!Batch) ++nline; + ask4str(fmtmk("Kill PID %d with signal [%i]" + , pid, DEF_SIGNAL))); + if (-1 == sig) sig = DEF_SIGNAL; + if (sig && kill(pid, sig)) + show_msg(fmtmk("\aKill of PID '%d' with '%d' failed: %s" + , pid, sig, strerror(errno))); + } + } + break; + + case 'l': + TOGw(Curwin, View_LOADAV); + break; + + case 'm': + TOGw(Curwin, View_MEMORY); + break; + + case 'n': + case '#': + if (VIZCHKc) { + int num; + if (-1 < (num = get_int( + fmtmk("Maximum tasks = %d, change to (0 is unlimited)" + , Curwin->maxtasks)))) + Curwin->maxtasks = num; + } + break; + + case 'o': + if (VIZCHKc) { + fields_reorder(); + win_colsheads(Curwin); + } + break; + + case 'r': + if (Secure_mode) + show_msg(fmtmk(err_secure, "renice")); + else { + int pid, val; + + pid = get_int("PID to renice"); + if (-1 == pid) break; + val = get_int(fmtmk("Renice PID %d to value", pid)); + if (setpriority(PRIO_PROCESS, (unsigned)pid, val)) + show_msg(fmtmk("\aRenice of PID %d to %d failed: %s" + , pid, val, strerror(errno))); + } + break; + + case 'R': + VIZTOGc(Qsrt_NORMAL); + break; + + case 's': + case 'd': + if (Secure_mode) + show_msg(fmtmk(err_secure, "change delay")); + else { + float tmp = + get_float(fmtmk("Change delay from %.1f to", Delay_time)); + if (tmp > -1) Delay_time = tmp; + } + break; + + case 'S': + if (VIZCHKc) { + TOGw(Curwin, Show_CTIMES); + show_msg(fmtmk("Cumulative time %s" + , CHKw(Curwin, Show_CTIMES) ? "On" : "Off")); + } + break; + + case 't': + TOGw(Curwin, View_STATES); + break; + + case 'u': + if (VIZCHKc) + strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)")); + break; + + case 'W': + { FILE *fp = fopen(RCfile, "w"); int i; + + if (fp) { + fprintf(fp, "RCfile for \"%s with windows\"\t\t# shameless braggin'\n" + , Myname); + fprintf(fp, "Id:%c, " + "Show_altscr=%d, Show_irixps=%d, Delay_time=%.1f, Curwin=%d\n" + , RCF_FILEID + , Show_altscr, Show_irixps, Delay_time, Curwin - Winstk[0]); + for (i = 0; i < GROUPSMAX; i++) { + fprintf(fp, "%s\tfieldscur=%s\n" + , Winstk[i]->winname, Winstk[i]->fieldscur); + fprintf(fp, "\twinflags=%d, sorttype=%c, maxtasks=%d\n" + , Winstk[i]->winflags + , Winstk[i]->sorttype + , Winstk[i]->maxtasks); + fprintf(fp, "\tsummclr=%d, msgsclr=%d, headclr=%d, taskclr=%d\n" + , Winstk[i]->summclr + , Winstk[i]->msgsclr + , Winstk[i]->headclr + , Winstk[i]->taskclr); + } + fclose(fp); + show_msg(fmtmk("Wrote configuration to '%s'", RCfile)); + } else + show_msg(fmtmk("\aFailed '%s' open: %s", RCfile, strerror(errno))); } - ++ntask; + break; + + case 'x': + if (VIZCHKc) { + TOGw(Curwin, Show_HICOLS); + capsmk(Curwin); + } + break; + + case 'y': + if (VIZCHKc) { + TOGw(Curwin, Show_HIROWS); + capsmk(Curwin); + } + break; + + case 'z': + if (VIZCHKc) { + TOGw(Curwin, Show_COLORS); + capsmk(Curwin); + } + break; + + case 'Z': + wins_colors(); + break; + + case '?': + case 'h': + { char ch; + + printf("%s%s", Cap_clr_scr, Cap_curs_huge); + /* this string is well above ISO C89's minimum requirements! */ + show_special(fmtmk(KEYS_help + , Myname, procps_version + , Curwin->winname + , CHKw(Curwin, Show_CTIMES) ? "On" : "Off" + , Delay_time + , Secure_mode ? "On" : "Off" + , Secure_mode ? "" : KEYS_help_unsecured)); + chin(0, &ch, 1); + if ('?' == ch || 'h' == ch) { + do { + putp(Cap_clr_scr); + show_special(fmtmk(WINDOWS_help + , Myname, Curwin->grpname + , Winstk[Def_WINDOW]->winname + , Winstk[Job_WINDOW]->winname + , Winstk[Mem_WINDOW]->winname + , Winstk[Usr_WINDOW]->winname)); + chin(0, &ch, 1); + win_select(ch); + } while ('\n' != ch); + } + putp(Cap_curs_norm); + } + break; + + case ' ': + case kbdCTRL_L: + putp(Cap_clr_scr); + break; + + case '1': +#ifdef WARN_NOT_SMP + if (Cpu_tot > 1) + TOGw(Curwin, View_CPUSUM); + else + show_msg(err_smp); +#else + TOGw(Curwin, View_CPUSUM); +#endif + break; + + case S_CMD: case S_CPU: case S_MEM: + case S_PID: case S_TME: case S_TTY: case S_USR: + if (VIZCHKc) + win_sortset(Curwin, (int)c); + break; + + case '\n': /* just ignore it */ + break; + +#ifdef QUIT_NORMALQ + case 'q': +#else + case kbdESCAPE: +#endif + stop(0); + + default: + show_msg("\aUnknown command - try 'h' for help"); } - printf("%s%s%s", Cap_clr_eos, tg2(0, MSG_line), Cap_clr_eol); +#ifndef QUIT_NORMALQ +#undef kbdESCAPE +#endif +#undef kbdCTRL_L +} + + + /* + * Begin a new frame by: + * 1) Refreshing the all important proc table + * 2) Displaying uptime and load average (maybe) + * 3) Arranging for task/cpu states to be displayed + * 4) Arranging for memory & swap usage to be displayed + * and then, returning a pointer to the pointers to the proc_t's! */ +static proc_t **do_summary (void) +{ + static proc_t **p_table = NULL; + + if (!p_table) { + /* whoa first time, gotta' prime the pump... */ + p_table = refreshprocs(NULL); + frame_states(p_table, 0); + putp(Cap_clr_scr); + sleep(1); + } else + putp(Batch ? "\n\n" : Cap_home); + + + /* + ** Display Load averages */ + if (CHKw(Curwin, View_LOADAV)) { + if (!Show_altscr) + show_special(fmtmk(LOADAV_line, Myname, sprint_uptime())); + else + show_special(fmtmk(CHKw(Curwin, VISIBLE_tsk) + ? LOADAV_line_alt + : LOADAV_line + , Curwin->grpname, sprint_uptime())); + Msg_row += 1; + } + + /* + ** Display Tasks and Cpu(s) states and also calc 'pcpu', + ** but NO p_table sort yet -- that's done on a per window basis! */ + p_table = refreshprocs(p_table); + frame_states(p_table, CHKw(Curwin, View_STATES)); + + /* + ** Display Memory and Swap space usage */ + frame_storage(); + + SETw(Curwin, NEWFRAM_cwo); + return p_table; +} + + + /* + * Squeeze as many tasks as we can into a single window, + * after sorting the passed proc table. */ +static void do_window (proc_t **ppt, WIN_t *q, int *lscr) +{ +#ifdef SORT_SUPRESS + /* the 1 flag that DOES and 2 flags that MAY impact our proc table qsort */ +#define srtMASK ~( Qsrt_NORMAL | Show_CMDLIN | Show_CTIMES ) + static QSORT_t sav_func = NULL; + static int sav_flgs = -1; +#endif + int i, lwin; + + /* + ** Display Column Headings -- and distract 'em while we sort (maybe) */ + printf("\n%s%s%s%s", q->capclr_hdr, q->columnhdr, Caps_off, Cap_clr_eol); + +#ifdef SORT_SUPRESS + if (CHKw(Curwin, NEWFRAM_cwo) + || sav_func != q->sortfunc + || sav_flgs != (q->winflags & srtMASK)) { + sav_flgs = (q->winflags & srtMASK); + sav_func = q->sortfunc; +#endif + Frame_srtflg = CHKw(q, Qsrt_NORMAL); /* this one's always needed! */ + Frame_ctimes = CHKw(q, Show_CTIMES); /* this and next, only maybe */ + Frame_cmdlin = CHKw(q, Show_CMDLIN); + qsort(ppt, (unsigned)Frame_maxtask, sizeof(proc_t *), (QSORT_t)q->sortfunc); +#ifdef SORT_SUPRESS + } +#endif + /* account for column headings */ + if (!Batch) (*lscr)++; + lwin = 1; + i = 0; + + while ( -1 != ppt[i]->pid && *lscr < Max_lines + && (!q->winlines || (lwin <= q->winlines)) ) { + if ((CHKw(q, Show_IDLEPS) + || ('S' != ppt[i]->state && 'Z' != ppt[i]->state)) + && ((!q->colusrnam[0]) + || (!strcmp(q->colusrnam, ppt[i]->euser)) ) ) { + /* + ** Display a process Row */ + show_a_task(q, ppt[i]); + if (!Batch) (*lscr)++; + ++lwin; + } + ++i; + } + /* for this frame that window's toast, cleanup for next time */ + q->winlines = 0; + OFFw(Curwin, FLGSOFF_cwo); + +#ifdef SORT_SUPRESS +#undef srtMASK +#endif +} + + + /* + * This guy's just a *Helper* function who apportions the + * remaining amount of screen real estate under multiple windows + * -- i swear that's the whole truth, so-help-me ! */ +static void sohelpme (int wix, int max) +{ + WIN_t *w; + int i, rsvd, size, wins; + + /* calc remaining number of visible windows + total 'user' lines */ + for (i = wix, rsvd = 0, wins = 0; i < GROUPSMAX; i++) { + if (CHKw(Winstk[i], VISIBLE_tsk)) { + rsvd += Winstk[i]->maxtasks; + ++wins; + if (max <= rsvd) break; + } + } + if (!wins) wins = 1; + /* set aside 'rsvd' & deduct 1 line/window for the columns heading */ + size = (max - wins) - rsvd; + if (0 <= size) size = max; + size = (max - wins) / wins; + + /* for remaining windows, set do_window's winlines to either the + user's maxtask (1st choice) or our 'foxized' size calculation + (foxized adj. - 'fair and balanced') */ + for (i = wix ; i < GROUPSMAX; i++) { + if (CHKw(Winstk[i], VISIBLE_tsk)) { + Winstk[i]->winlines = + Winstk[i]->maxtasks ? Winstk[i]->maxtasks : size; + w = Winstk[i]; + } + } + /* award any remaining lines to the screen's last visible window + (for the best/most stable display) */ + w->winlines += (max - wins) % wins; +} + + + /* + * Initiate the Frame Display Update cycle at someone's whim! + * This routine doesn't do much, mostly he just calls others. + * + * (Whoa, wait a minute, we DO caretake that Msg_row guy and) + * (we CALCULATE that IMPORTANT Max_lines thingy so that the) + * (*subordinate* functions invoked know WHEN the user's had) + * (ENOUGH already. And at Frame End, it SHOULD be apparent) + * (WE am d'MAN -- clearing UNUSED screen LINES and ensuring) + * (the CURSOR is STUCK in just the RIGHT place, know what I) + * (mean? Huh, "doesn't DO MUCH"! Never, EVER think or say) + * (THAT about THIS function again, Ok? Good that's better.) + * + * (ps. we ARE the UNEQUALED justification KING of COMMENTS!) + * (No, I don't mean significance/relevance, only alignment.) + * + * (What's that? Are you sure? Old main's REALLY GOOD too?) + * (You say he even JUSTIFIES comments in his FUNCTION BODY?) + * (Jeeze, how COULD I have known? That sob's NOT IN SCOPE!) + */ +static void so_lets_see_em (void) +{ + proc_t **ppt; + int i, scrlins; + + Msg_row = scrlins = 0; + ppt = do_summary(); + Max_lines = (Screen_rows - Msg_row) - 1; + + if (CHKw(Curwin, EQUWINS_cwo)) + wins_reflag(Flgs_OFF, EQUWINS_cwo); + /* sure hope each window's columns header begins with a newline... */ + putp(tg2(0, Msg_row)); + + if (!Show_altscr) { + /* only 1 window to show so, piece o' cake */ + Curwin->winlines = Curwin->maxtasks; + do_window(ppt, Curwin, &scrlins); + } else { + /* maybe NO window is visible but assume, pieces o' cakes */ + for (i = 0 ; i < GROUPSMAX; i++) { + if (CHKw(Winstk[i], VISIBLE_tsk)) { + sohelpme(i, Max_lines - scrlins); + do_window(ppt, Winstk[i], &scrlins); + } + if (Max_lines <= scrlins) break; + } + } + /* clear to end-of-screen (critical if last window is 'idleps off'), + then put the cursor in-its-place, and rid us of any prior frame's msg + (main loop must iterate such that we're always called before sleep) */ + printf("%s%s%s", Cap_clr_eos, tg2(0, Msg_row), Cap_clr_eol); fflush(stdout); } - + /*###### Entry point ###################################################*/ + /* + * Darling, you DO look simply MARVELOUS -- have you been dieting? + * Or maybe it's because YOU and WINDOWS seem such a HAPPY couple. + * + * Of course NO. Not THOSE deathly BLUE WINDOWS! I mean your 'A' + * mode (alt display) windows. Yes, yes those completely OPTIONAL + * ones. We ALL know you'd NEVER FORCE that interface on ANY user + * - unlike You-Know-Who! Well I've got to run. But you're doing + * it just SPLENDIDLY! You go right on doing it EXACTLY the SAME! + */ int main (int dont_care_argc, char **argv) -{ - char not_really_tmp[OURPATHSZ]; - int i; +{ /* + Ok, she's gone now. Don't you mind her, she means well but yes, she is + a bit of a busy-body. Always playing the matchmaker role, trying to do + away with unmarried windows and bachelors. So, back to business buddy! - /* setup our program name(s)... */ - Myname = strrchr(argv[0], '/'); - if (Myname) ++Myname; else Myname = argv[0]; - Myrealname = Myname; - memset(not_really_tmp, '\0', sizeof(not_really_tmp)); - /* proper permissions should deny symlinks to /usr/bin for ordinary - users, but root may have employed them -- Myrealname will be used - in constructing the global rc filename ... */ - if (-1 != readlink(argv[0], not_really_tmp, sizeof(not_really_tmp) - 1)) { - Myrealname = strrchr(not_really_tmp, '/'); - if (Myrealname) ++Myrealname; else Myrealname = not_really_tmp; -#ifdef TRAK_MAXBUFS - MAXBUFS(main, not_really_tmp); -#endif - } + You're hungry, you said? How'd you like a sandwich? No, no, no -- not + the usual slopped together, hacked up illogic. I'm talkin' a carefully + reasoned, artfully crafted, extremely capable, well behaved executable! - /* setup some important system stuff... */ - Page_size = getpagesize(); - Cpu_tot = sysconf(_SC_NPROCESSORS_ONLN); - if (1 > Cpu_tot) Cpu_tot = 1; - Cpu_map = alloc_r(NULL, sizeof(int) * Cpu_tot); - for (i = 0; i < Cpu_tot; i++) - Cpu_map[i] = i; - - rcfiles_read(); - parse_argvs(argv); - terminal_set(); - window_resize(0); - - /* set up signal handlers */ + Well then, here, try THIS sandwich... + +-------------+ */ + windows_stage1(); /* top (sic) slice */ + before(*argv); /* > seasonings, < */ + configs_read(); /* > spread etc, < */ + parse_args(&argv[1]); /* > lean stuff, < */ + whack_terminal(); /* > onions etc. < */ + windows_stage2(); /* as bottom slice */ + /* +-------------+ */ signal(SIGALRM, stop); signal(SIGHUP, stop); signal(SIGINT, stop); @@ -2507,25 +2694,24 @@ int main (int dont_care_argc, char **argv) signal(SIGTSTP, suspend); signal(SIGTTIN, suspend); signal(SIGTTOU, suspend); - signal(SIGCONT, window_resize); - signal(SIGWINCH, window_resize); + signal(SIGCONT, wins_resize); + signal(SIGWINCH, wins_resize); - /* loop, collecting process info and sleeping */ do { struct timeval tv; fd_set fs; char c; + /* This is it? */ + so_lets_see_em(); /* Impossible! */ - show_everything(); if (Msg_awaiting) show_msg(Msg_delayed); if (0 < Loops) --Loops; if (!Loops) stop(0); if (Batch) sleep((unsigned)Delay_time); - else { - /* Linux reports time not slept, so we must reinit every time */ - tv.tv_sec = Delay_time; + else { /* Linux reports time not slept, */ + tv.tv_sec = Delay_time; /* so we must reinit every time. */ tv.tv_usec = (Delay_time - (int)Delay_time) * 1000000; FD_ZERO(&fs); FD_SET(STDIN_FILENO, &fs); @@ -2535,5 +2721,32 @@ int main (int dont_care_argc, char **argv) } } while (1); + /* + (listen before we return, aren't you sort of sad for 'so_lets_see-em'?) + (so, uh, why don't we just move this main guy to near the beginning of) + (the C source file. then that poor old function would be sure to have) + (at least a chance at scopin' us out, ya know what i mean? so what do) + (ya think? all things considered, would that be a proper thing to do?) + + Now there's an EXCELLENT idea! After all, SOME programmers DO code the + main function ANY OLD PLACE they feel like. And in doing THAT, they're + helping to keep those that FOLLOW out of mischief, busy HUNTING for the + thing. Further, by moving it we can contribute to PROTOTYPE + PROLIFERATION for every function main calls. Don't you KNOW that those + declarations OFTEN LINGER, usually long AFTER the REAL definitions have + DISAPPEARED, since programs do evolve? Yep that's a GREAT idea you got + there, NICE GOING! But, here's an opposing view: ANYONE who'd put main + ANYWHERE such that its LOCATION cannot be REACHED with JUST 1 KEYSTROKE + better turn in their Coding_Badge and Pocket_Protector then -- BE GONE! + The main function has only ONE proper HOME: always the LAST function in + that C Listing. End-of-Story, No-More-Discussion, so BE QUIET already! + + \---------------------------------------------------------------------/ + Sheeesh, didn't that dufus know the return statement can't be executed, + or a signal & stop() will do-us-in? Oh Lordy, I is DROWNING in morons; + they done REACHED clear up to my OUTER braces. We's all DOOMED, I say! + /---------------------------------------------------------------------\ + */ return 0; } + diff --git a/top.h b/top.h index 84f3a772..b7b8ce56 100644 --- a/top.h +++ b/top.h @@ -1,7 +1,10 @@ /* top.h - Header file: show Linux processes */ /* - * Copyright (c) 2002 - James C. Warner, - * All rights reserved. + * Copyright (c) June, 2002 - James C. Warner + * All rights reserved. 8921 Hilloway Road + * Eden Prairie, Minnesota 55347 USA + * + * * This file may be used subject to the terms and conditions of the * GNU Library General Public License Version 2, or any later version * at your option, as published by the Free Software Foundation. @@ -10,28 +13,92 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Library General Public License for more details. */ +/* For their contributions to this program, the author wishes to thank: + * Craig Small, + * Albert D. Cahalan, + */ #ifndef _Itop #define _Itop - /* Determines for whom we're destined, debian or !#@*#! redhat ----- */ + /* Determines for whom we're destined ------------------------------ */ //#define UGH_ITS_4_RH /* use the redhat libproc conventions */ - /* Defines intended to be played with ------------------------------ */ -//#define UPCASE_HEXES /* show any hex values in upper case */ -//#define UPCASE_SCALE /* show scaled times & memory upper case */ -//#define UPCASE_SUMMK /* show memory summary kilobytes with 'K' */ - -//#define COLOR_SLEEPY /* purples and blues (sissy colors) */ -//#define COLOR_CONSOL /* for linux consoles (white on black) */ + /* Defines intended to be experimented with ------------------------ */ +//#define CASEUP_HEXES /* show any hex values in upper case */ +//#define CASEUP_SCALE /* show scaled times & memory upper case */ +//#define CASEUP_SUMMK /* show memory summary kilobytes with 'K' */ +//#define QUIT_NORMALQ /* use 'q' to quit, not new default */ +//#define SORT_SUPRESS /* *attempt* to reduce qsort overhead */ +//#define USE_LIB_STA3 /* use lib status (3 ch) vs. proc_t (1 ch) */ +//#define WARN_NOT_SMP /* restrict '1' & 'I' commands to true smp */ /* Development/Debugging defines ----------------------------------- */ -//#define TRAK_MAXCAPS /* track & report misc terminfo stuff */ -//#define TRAK_MAXBUFS /* track & report internal buffer usage */ - - /* Special define to test extremely dumb terminals! ---------------- */ +//#define ATEOJ_REPORT /* report a bunch of stuff, at end-of-job */ +//#define PRETEND4CPUS /* pretend we're smp with 4 ticsers (sic) */ //#define PRETENDNOCAP /* use a terminal without essential caps */ +/*###### Some Miscellaneous constants ##################################*/ + + /* The default delay twix updates */ +#define DEF_DELAY 2.0 + + /* The length of time a 'message' is displayed */ +#define MSG_SLEEP 2 + + /* The default value for the 'k' (kill) request */ +#define DEF_SIGNAL SIGTERM + + /* Specific process id monitoring support (command line only) */ +#define MONPIDMAX 20 + + /* Miscellaneous buffer sizes with liberal values + -- mostly just to pinpoint source code usage/dependancies */ +#define PFLAGSSIZ 32 +#define CAPBUFSIZ 32 +#define CLRBUFSIZ 64 +#define GETBUFSIZ 32 +#define TNYBUFSIZ 32 +#define SMLBUFSIZ 256 +#define MEDBUFSIZ 512 +#define OURPATHSZ 1024 +#define BIGBUFSIZ 2048 +#define RCFBUFSIZ SMLBUFSIZ +#define USRNAMSIZ GETBUFSIZ +#define COLBUFSIZ SMLBUFSIZ + CLRBUFSIZ +#define ROWBUFSIZ MEDBUFSIZ + CLRBUFSIZ + + +/*###### Some Miscellaneous Macro definitions ##########################*/ + + /* Used as return arguments to achieve normal/reversed sorts + in the sort callbacks */ +#define SORT_lt ( Frame_srtflg ? 1 : -1 ) +#define SORT_gt ( Frame_srtflg ? -1 : 1 ) + + /* Convert some proc stuff into vaules we can actually use */ +#define BYTES_2K(n) (unsigned)( (n) >> 10 ) +#define PAGES_2B(n) (unsigned)( (n) * Page_size ) +#define PAGES_2K(n) BYTES_2K(PAGES_2B(n)) +#define PAGE_CNT(n) (unsigned)( (n) / Page_size ) + + /* Yield table size as 'int' */ +#define MAXtbl(t) ( (int)(sizeof(t)/sizeof(t[0])) ) + + +/*###### Special Macros (debug and/or informative) #####################*/ + + /* Orderly end, with any sort of message - see fmtmk */ +#define debug_END(s) { \ + static void std_err (const char *); \ + fputs(Cap_clr_scr, stdout); \ + std_err(s); \ + } + + /* A poor man's breakpoint, if he's too lazy to learn gdb */ +#define its_YOUR_fault { *((char *)0) = '!'; } + + /*###### Some Typedef's and Enum's #####################################*/ /* This typedef attempts to ensure consistent 'ticks' handling. */ @@ -56,9 +123,10 @@ typedef struct { TICS_t tics; } HIST_t; - /* This structure serves single (non-smp), multiple (smp) and - consolidated (smp, but Show_cpusum == 1) environments. It is - used to calculate a frame's cpu(s) state percentages */ + /* This structure stores the prior frame's tics used in history + calculations. It exists primarily for SMP support but serves + all environments. There will always Cpu_tot + 1 allocated + -- see frame_states for details. */ typedef struct { TICS_t u, /* ticks count as represented in /proc/stat */ n, /* (not in the order of our display) */ @@ -80,7 +148,7 @@ enum scale_num { }; /* Flags for each possible field. - At the moment 32 are supported [ see PFlags/PFLAGSSIZ ] */ + At the moment 32 are supported [ see PFLAGSSIZ ] */ enum pflag { P_PID, P_PPID, P_UID, P_USER, P_GROUP, P_TTY, P_PR, P_NI, @@ -91,108 +159,120 @@ enum pflag { }; -/*###### Some Miscellaneous Macro definitions ##########################*/ + /* ////////////////////////////////////////////////////////////// */ + /* Special Section: multiple windows/field groups ---------------*/ + /* (kind of a header within a header: constants, macros & types) */ - /* Message line placement at runtime */ -#define MSG_line ( HSum_lines - 2 ) +#define GROUPSMAX 4 /* the max number of simultaneous windows */ +#define WINNAMSIZ 4 /* max size of a window's basic name */ +#define GRPNAMSIZ 6 /* window's name + number as in: '#:...' */ +#define CAPTABMAX 9 /* a window's captab used by show_special */ - /* Used as return argument to achieve normal/reversed sorts - in the sort callbacks */ -#define SORT_lt ( Sort_normal ? 1 : -1 ) -#define SORT_gt ( Sort_normal ? -1 : 1 ) +#define Def_WINDOW 0 /* these represent the pre-allocated */ +#define Job_WINDOW 1 /* WIN_t's relative position in the */ +#define Mem_WINDOW 2 /* window stack pointer array... */ +#define Usr_WINDOW 3 - /* Convert some proc stuff into vaules we can actually use */ -#define BYTES_2K(n) (unsigned)( (n) >> 10 ) -#define PAGES_2B(n) (unsigned)( (n) * Page_size ) -#define PAGES_2K(n) BYTES_2K(PAGES_2B(n)) -#define PAGE_CNT(n) (unsigned)( (n) / Page_size ) +#define Flgs_TOG 1 /* these are used to direct wins_reflag */ +#define Flgs_SET 2 +#define Flgs_OFF 3 - /* Yield table size as 'int' */ -#define MAXtbl(t) ( (int)(sizeof(t)/sizeof(t[0])) ) + /* The Persistent 'Mode' flags! + All of these are preserved in the rc file, as a single integer. + Thus, once personalized, this top will stay personalized across + restarts (not like the old top, who only remembered fields)! + Note: the letter shown is the corresponding 'command' toggle + */ + /* 'View_' flags affect the summary information, taken from 'Curwin' */ +#define View_CPUSUM 0x8000 /* '1' - show combined cpu stats (vs. each) */ +#define View_LOADAV 0x4000 /* 'l' - display load avg and uptime summary */ +#define View_STATES 0x2000 /* 't' - display task/cpu(s) states summary */ +#define View_MEMORY 0x1000 /* 'm' - display memory summary */ + /* 'Show_' & 'Qsrt_' flags are for task display in a visible window */ +#define Show_COLORS 0x0800 /* 'z' - show in color (vs. mono) */ +#define Show_HIBOLD 0x0400 /* 'b' - rows and/or cols bold (vs. reverse) */ +#define Show_HICOLS 0x0200 /* 'x' - show sort column highlighted */ +#define Show_HIROWS 0x0100 /* 'y' - show running tasks highlighted */ +#define Show_CMDLIN 0x0080 /* 'c' - show cmdline vs. name */ +#define Show_CTIMES 0x0040 /* 'S' - show times as cumulative */ +#define Show_IDLEPS 0x0020 /* 'i' - show idle processes (all tasks) */ +#define Qsrt_NORMAL 0x0010 /* 'R' - reversed column sort (high to low) */ + /* these flag(s) have no command as such - they're for internal use */ +#define VISIBLE_tsk 0x0008 /* tasks are showable in 'Show_altscr' mode */ +#define NEWFRAM_cwo 0x0004 /* new frame (if anyone cares) - in Curwin */ +#define EQUWINS_cwo 0x0002 /* rebalance tasks next frame (off 'i'/ 'n') */ + /* ...set in Curwin, but impacts all windows */ +#define flag_unused 0x0001 /* ----------------------- future use, maybe */ + /* Current-window-only flags -- always turned off at end-of-window! */ +#define FLGSOFF_cwo EQUWINS_cwo | NEWFRAM_cwo -/*###### Special Macros (debug and/or informative) #####################*/ + /* Default flags if there's no rcfile to provide user customizations */ +#define DEF_WINFLGS ( View_LOADAV | View_STATES | View_CPUSUM | View_MEMORY | \ + Show_HIBOLD | Show_HIROWS | Show_IDLEPS | Qsrt_NORMAL | VISIBLE_tsk ) - /* Orderly end, with any sort of message - see fmtmk */ -#define debug_END(s) { \ - static void std_err (const char *); \ - fputs(Cap_clr_scr, stdout); \ - std_err(s); \ - } + /* Used to test/manipulate the window flags */ +#define CHKw(q,f) (int)(q->winflags & (f)) +#define TOGw(q,f) q->winflags ^= (f) +#define SETw(q,f) q->winflags |= (f) +#define OFFw(q,f) q->winflags &= ~(f) +#define VIZCHKc (!Show_altscr || Curwin->winflags & VISIBLE_tsk) \ + ? 1 : win_warn() +#define VIZTOGc(f) (!Show_altscr || Curwin->winflags & VISIBLE_tsk) \ + ? TOGw(Curwin, f) : win_warn() -#ifdef TRAK_MAXBUFS - /* Provide for tracking maximum buffer sizes */ -#define ARPTBUF(f,b) "\n\t%4d of %d\t" #f ", " #b -#define ASIZBUF(f,b) Siz_ ## f ## _ ## b -#define AUSEBUF(f,b) Use_ ## f ## _ ## b -#define BUF2INT(f,b) static int AUSEBUF(f,b) = 0, ASIZBUF(f,b) = 0; -#define MAXBUFS(f,b) { \ - if ((int)strlen(b) > AUSEBUF(f,b)) \ - AUSEBUF(f,b) = (int)strlen(b); \ - ASIZBUF(f,b) = (int)sizeof(b); \ - } -#endif + /* Just create a printable window number -- represented here since + we can't decide which to prefer (relative 0 or 1) */ +#define mkWNO(q) q->winnum + 1 - -/*###### Some Miscellaneous constants ##################################*/ - - /* The default value for the 'k' (kill) request */ -#define DEF_SIGNAL SIGTERM - - /* The default delay twix updates */ -#define DEF_DELAY 2.0 - - /* The length of time a 'message' is displayed */ -#define MSG_SLEEP 1 - - /* Minimum summary lines (thus, just msg line + col heads) */ -#define SUMMINLINS 2 - - /* Specific process id monitoring support (command line only) */ -#define MONPIDMAX 12 - - /* Miscellaneous buffer sizes with liberal values - -- mostly just to pinpoint source code usage/dependancies */ -#define PFLAGSSIZ 32 -#define CAPBUFSIZ 32 -#define CLRBUFSIZ 64 -#define GETBUFSIZ 32 -#define TNYBUFSIZ 32 -#define SMLBUFSIZ 256 -#define MEDBUFSIZ 512 -#define OURPATHSZ 1024 -#define BIGBUFSIZ 2048 -#define RCFBUFSIZ SMLBUFSIZ -#define USRNAMSIZ GETBUFSIZ -#define COLBUFSIZ SMLBUFSIZ + CLRBUFSIZ -#define ROWBUFSIZ MEDBUFSIZ + CLRBUFSIZ - - /* Color stuff... - note: we avoid the use of background color so as to maximize - compatibility with the user's xterm settings */ -#ifdef COLOR_SLEEPY -#define BASEcolor COLOR_MAGENTA -#define MSGScolor COLOR_CYAN -#define HEADcolor COLOR_BLUE -#else -#ifdef COLOR_CONSOL -#define BASEcolor COLOR_CYAN -#define MSGScolor COLOR_RED -#define HEADcolor COLOR_WHITE -#else -#define BASEcolor COLOR_RED -#define MSGScolor COLOR_RED -#define HEADcolor COLOR_YELLOW -#endif -#endif + /* This structure stores configurable information for each window. + By expending a little effort in its creation and user requested + maintainence, the only real additional per frame cost of having + windows is a *potential* extra sort -- but that's just on ptrs! + */ +typedef struct win { + struct win *next, /* next window in window stack */ + *prev; /* prior window in window stack */ + int winnum, /* this window's num/array pos */ + winlines; /* task window's rows (volatile) */ + int winflags; /* 'view', 'show' and 'sort' mode flags */ + char grpname [GRPNAMSIZ], /* window number:name, printable */ + winname [WINNAMSIZ], /* window name, user changeable */ + fieldscur [PFLAGSSIZ], /* fields displayed and ordered */ + columnhdr [SMLBUFSIZ], /* column headings for procflags */ + colusrnam [USRNAMSIZ]; /* if selected by the 'u' command */ + unsigned procflags [PFLAGSSIZ]; /* fieldscur subset as: int/enum */ + int maxpflgs, /* number of procflags (upcase fieldscur) */ + maxtasks, /* user requested maximum, 0 equals all */ + maxcmdln, /* max length of a process' command line */ + sorttype; /* the last chosen sort field (as enum) */ + QSORT_t sortfunc; /* sort function for this window's tasks */ + int summclr, /* color num used in summ info */ + msgsclr, /* " in msgs/pmts */ + headclr, /* " in cols head */ + taskclr; /* " in task display */ + char capclr_sum [CLRBUFSIZ], /* terminfo strings built from */ + capclr_msg [CLRBUFSIZ], /* above clrs (& rebuilt too), */ + capclr_pmt [CLRBUFSIZ], /* but NO recurring costs !!! */ + capclr_hdr [CLRBUFSIZ], /* note: sum, msg and pmt strs */ + capclr_rowhigh [CLRBUFSIZ], /* are only used when this */ + capclr_rownorm [CLRBUFSIZ]; /* window is the 'Curwin'! */ + int len_rownorm, /* lengths of the corresponding terminfo */ + len_rowhigh; /* strings to save mkcol() a strlen call */ + char *captab [CAPTABMAX]; /* captab needed by show_special */ +} WIN_t; + /* ////////////////////////////////////////////////////////////// */ /*###### Display Support *Data* ########################################*/ + /* An rcfile 'footprint' used to invalidate existing */ +#define RCF_FILEID 'c' + /* The default fields displayed and their order, if nothing is specified by the loser, oops user */ #define DEF_FIELDS "AbcDefGHiJkLMNOPqrstuVWxy" - /* Help screen selectable grouped fields */ + /* Pre-configured grouped fields */ #define JOB_FIELDS "ABWdefikqrstuxyLJMGHVNOPC" #define MEM_FIELDS "AMNOPQRSTUWbcdefiklxyVGHJ" #define USR_FIELDS "CDEFABWghiknopqrstuxyLJMV" @@ -206,14 +286,15 @@ enum pflag { /* Summary Lines specially formatted string(s) -- see 'show_special' for syntax details + other cautions. */ -#define LOADAV_line "%s -\03%s\n" +#define LOADAV_line "%s -%s\n" +#define LOADAV_line_alt "%s\06 -%s\n" #define STATES_line1 "Tasks:\03" \ " %3u \02total,\03 %3u \02running,\03 %3u \02sleeping,\03" \ " %3u \02stopped,\03 %3u \02zombie\03\n" #define STATES_line2 "%s\03" \ " %#5.1f%% \02user,\03 %#5.1f%% \02system,\03" \ " %#5.1f%% \02nice,\03 %#5.1f%% \02idle\03\n" -#ifdef UPCASE_SUMMK +#ifdef CASEUP_SUMMK #define MEMORY_line1 "Mem: \03" \ " %8uK \02total,\03 %8uK \02used,\03" \ " %8uK \02free,\03 %8uK \02buffers\03\n" @@ -229,76 +310,80 @@ enum pflag { " %8uk \02free,\03 %8uk \02cached\03\n" #endif - /* Colors Help specially formatted string(s) -- see 'show_special' for syntax details + other cautions. */ -#define COLOR_sample \ +#define COLOR_help \ "%s%s's \01Help for color mapping\02 - %s\n" \ + "current window: \01%s\06\n" \ "\n" \ - " color -\03 08:41:24 up 1 day, 6:07, 7 users, load average:\n" \ + " color -\03 04:25:44 up 8 days, 50 min, 7 users, load average:\n" \ " Tasks:\03 64 \02total,\03 2 \02running,\03 62 \02sleeping,\03 0 \02stopped,\03\n" \ " State cpu0 :\03 76.5%% \02user,\03 11.2%% \02system,\03 0.0%% \02nice,\03\n" \ " \01 Nasty Message! \04 -or- \01Input Prompt\05\n" \ - " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \ - " 17284 \03 pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bas\03\n" \ - " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R< color -b\07\n" \ - " 11005 \03 ? \07 9 0 0.0 0:02.50 2852 1008 S amor -se\03\n" \ - " 2924 \03 ? \07 9 -1 0.0 1:08.16 30040 20m S< X :0\03\n" \ - " (normal toggles available: \01b\02 = bold/reverse; \01z\02 = color/mono)\n" \ - "\n\n" \ + " \01 PID TTY PR NI %%CPU TIME+ VIRT SWAP STA Command \06\n" \ + " 17284 \10pts/2 \07 8 0 0.0 0:00.75 1380 0 S /bin/bash \10\n" \ + " \01 8601 pts/1 7 -10 0.4 0:00.03 916 0 R < color -b \07\n" \ + " 11005 \10? \07 9 0 0.0 0:02.50 2852 1008 S amor -ses \10\n" \ + " available toggles: \01b\02 =bold/reverse (\01%s\02), \01z\02 =color/mono (\01%s\02)\n" \ + "\n" \ "Select \01target\02 as upper case letter:\n" \ - " B\02 = Base color,\01 H\02 = Column Headings,\01 M\02 = Messages/Prompts\n" \ + " S\02 = Summary Data,\01 M\02 = Messages/Prompts,\n" \ + " H\02 = Column Heads,\01 T\02 = Task Information\n" \ "Select \01color\02 as number:\n" \ " 0\02 = black,\01 1\02 = red, \01 2\02 = green,\01 3\02 = yellow,\n" \ " 4\02 = blue, \01 5\02 = magenta,\01 6\02 = cyan, \01 7\02 = white\n" \ "\n" \ "Selected: \01target\02 \01 %c \04; \01color\02 \01 %d \04\n" \ - " press 'q' to abort or to commit " \ + " press 'q' to abort changes to window '\01%s\02'\n" \ + " press 'a' or 'w' to commit & change another, to commit and end " \ "" - /* Keyboard Help specially formatted string(s) -- see 'show_special' for syntax details + other cautions. */ -#define HELP_data \ - "%s's \01Help for interactive commands\02 - %s\n" \ - "status: \01Secure mode \03%s\02;\01 Cumulative mode \03%s\02; \01Delay time \03%.1f\02 seconds\n" \ - " sp or ^L Redraw screen\n" \ - " o or O Rearrange fields\n" \ - " f or F Add and remove fields, or select group now from:\n" \ - " \01j\02) job fields; \01m\02) memory fields; \01u\02) user fields\n" \ - " Z Change color mapping\05\n" \ - "\n" \ - "C,M,P,T,U,Y,E Sort: \01C\02) cmd; \01M\02) mem; \01P\02) pid; \01T\02) time; \01U\02) cpu; \01Y\02) tty; \01E\02) user\n" \ - " R Toggle normal/reverse sort for any of above\n" \ - " l,t,m Toggle summary: \01l\02) load avg; \01t\02) task/cpu stats; \01m\02) mem info\n" \ - " c,i,S Toggle: \01c\02) cmd name/line; \01i\02) idle tasks; \01S\02) cumulative time\n" \ - " x,y\05 Toggle highlights: \01x\02) sort field; \01y\02) running tasks\n" \ - " z,b\05 Toggle: \01z\02) color/mono; \01b\02) bold/reverse if 'x' or 'y'\n" \ - " 1,I SMP views:\01 1\02) single/separate states; \01I\02) Irix/Solaris mode\n" \ +#ifdef QUIT_NORMALQ +#define HELP_STOPkey " q " +#else +#define HELP_STOPkey " " +#endif +#define KEYS_help \ + "%s's - \01Help for Interactive Commands\02 - %s\n" \ + "Window %s\06: \01Cumulative mode \03%s\02. \01System\06: \01Delay time \03%.1f secs\02; \01Secure mode \03%s\02.\n" \ + " sp or ^L Redraw screen\n" \ + " o Rearrange current window's fields\n" \ + " f Add and remove current window's fields\n" \ + " Z Change color mappings for any window\05\n" \ "\n" \ + "(7 letters) . Sort: \01C\02) cmd; \01M\02) mem; \01P\02) pid; \01T\02) time; \01U\02) cpu; \01Y\02) tty; \01E\02) user\n" \ + " R . Toggle normal/reverse sort for any of above\n" \ + " l,t,m Toggle summary: \01l\02) load avg; \01t\02) task/cpu stats; \01m\02) mem info\n" \ + " c,i,S . Toggle: \01c\02) cmd name/line; \01i\02) idle tasks; \01S\02) cumulative time\n" \ + " x,y\05 . Toggle highlights: \01x\02) sort field; \01y\02) running tasks\n" \ + " z,b\05 . Toggle: \01z\02) color/mono; \01b\02) bold/reverse, only if 'x' or 'y'\n" \ + " 1,I Toggle SMP view:\01 1\02) single/separate states; \01I\02) Irix/Solaris mode\n" \ "%s" \ - " u Show specific user only\n" \ - " # or n Set maximum tasks displayed\n" \ - " W Write configuration file\n" \ - " q Quit\n" \ - "Press any key to continue " \ + " u . Show specific user only\n" \ + " # or n . Set maximum tasks displayed\n" \ + " W Write configuration file\n" \ + HELP_STOPkey "Quit\n" \ + " ( commands shown with '.' require a \01visible\02 task display \01window\02 ) \n" \ + "Press '\01h\02' or '\01?\02' for help with \01Windows\02,\n" \ + "any other key to continue " \ "" /* This guy goes above the 'u' help text (maybe) */ -#define HELP_unsecured \ - " k Kill a task\n" \ - " r Renice a task\n" \ - " s or d Set update interval\n" \ +#define KEYS_help_unsecured \ + " k Kill a task\n" \ + " r Renice a task\n" \ + " s or d Set update interval\n" \ "" - /* Fields Reorder/Toggle specially formatted string(s) -- see 'show_special' for syntax details + other cautions note: the leading newline below serves really dumb terminals; if there's no 'cursor_home', the screen will be a mess but this part will still be functional. */ #define FIELDS_current \ - "\n%s%s's\01 Current Fields\02: \01 %s \04\n%s " \ + "\n%s%s's\01 Current Fields\02: \01 %s \04 for window \01%s\06\n%s " \ "" /* Some extra explanatory text which accompanies the Fields display. @@ -322,6 +407,36 @@ enum pflag { " VIRT = SWAP + RES\n" \ " RES = CODE + DATA\n" \ "" + + /* Windows/Field Group Help specially formatted string(s) -- + see 'show_special' for syntax details + other cautions. */ +#define WINDOWS_help \ + "%s's \01Help for Windows and Field Groups\02 - \"Current\" = \01 %s \06\n" \ + "\n" \ + ". Use multiple \01windows\02, each with separate config opts (color,fields,sort,etc)\n" \ + ". The '\01Current\05' window controls the \01Summary Area\02 and responds to your \01Commands\02\n" \ + " . that window's \01task display\02 can be turned \01Off\02 & \01On\02, growing/shrinking others\n" \ + " . with \01NO\02 task display, some commands will be \01disabled\02 ('i','R','n','c', etc)\n" \ + " until a \01different window\02 has been activated, making it the '\01Current\05' window\n" \ + ". You \01change\02 a '\01Current\05' window by: \01 1\02) cycling forward/backward; \01 2\02) choosing\n" \ + " a specific window with 'O' or 'F'; or \01 3\02) exiting the color mapping screen\n" \ + ". Commands \01available anytime\02 -------------\n" \ + " \01A\02 . Alternate display mode toggle, show \01Single\02 / \01Multiple\02 windows\n" \ + " O or F . Choose another field group and make it '\01Current\05', or change now\n" \ + " by selecting a number: \01 1\02 :%s; \01 2\02 :%s; \01 3\02 :%s; \01 4\02 :%s\n" \ + ". Commands \01requiring\02 '\01A\02' mode -----------\n" \ + " g . Change the '\01Current\05' window name (field group)\n" \ + " \01*\04 a , w . Cycle through all four windows: \01a\02 Forward; \01w\02 Backward\n" \ + " \01*\04 - , _ . Show/Hide: '\01-\05' \01Current\02 window; '\01_\05' all \01Visible\02/\01Invisible\02\n" \ + " The screen will be divided evenly between task displays. But you can make\n" \ + " some \01larger\02 or \01smaller\02, using '\01n\02' and '\01i\02' commands. Then later you could:\n" \ + " \01*\04 = , + . Rebalance tasks: '\01=\05' \01Current\02 window; '\01+\05' \01Every\02 window\n" \ + " (this also forces the \01current\02 or \01every\02 window to become visible)\n" \ + "\n" \ + "In '\01A\02' mode, '\01*\04' keys are your \01essential\02 commands. Please try the '\01a\02' and '\01w\02'\n" \ + "commands plus the 'O'/'F' sub-commands NOW. Press to make 'Current' " \ + "" + /*###### Some Prototypes (ha!) #########################################*/ @@ -330,10 +445,10 @@ enum pflag { * Note also that functions are alphabetical within a group to aid * source code navigation, which often influences choice of identifers. */ /*------ Sort callbacks ------------------------------------------------*/ +//atic int pid_sort (proc_t **P, proc_t **Q); //atic int sort_cmd (proc_t **P, proc_t **Q); //atic int sort_cpu (proc_t **P, proc_t **Q); //atic int sort_mem (proc_t **P, proc_t **Q); -//atic int sort_pid (proc_t **P, proc_t **Q); //atic int sort_tme (proc_t **P, proc_t **Q); //atic int sort_tty (proc_t **P, proc_t **Q); //atic int sort_usr (proc_t **P, proc_t **Q); @@ -342,56 +457,61 @@ enum pflag { //atic const char *fmtmk (const char *fmts, ...); //atic char *strim (int sp, char *str); //atic char *tg2 (int x, int y); +/*------ Exit/Interrput routines ---------------------------------------*/ +//atic void bye_bye (int eno, const char *str); +//atic void stop (int dont_care_sig); +//atic void std_err (const char *str); +//atic void suspend (int dont_care_sig); /*------ Misc Color/Highlighting support -------------------------------*/ -//atic void capsmk (void); +//atic void capsmk (WIN_t *q); //atic void msg_save (const char *fmts, ...); //atic void show_msg (const char *str); //atic void show_pmt (const char *str); //atic void show_special (const char *glob); -//atic void tweak_colors (void); -/*------ Small utility routines ----------------------------------------*/ -//atic char *ask_str (const char *prompt); +/*------ Small Utility routines ----------------------------------------*/ +//atic char *ask4str (const char *prompt); //atic float get_float (const char *prompt); //atic int get_int (const char *prompt); -//atic void mkheadings (void); //atic char *scale_num (unsigned num, const unsigned width, const unsigned type); //atic char *scale_tics (TICS_t tics, const unsigned width); //atic float time_elapsed (void); -/*------ Exit and Signal handled routines ------------------------------*/ -//atic void bye_bye (int eno, const char *str); -//atic void std_err (const char *str); -//atic void stop (int dont_care_sig); -//atic void suspend (int dont_care_sig); -//atic void window_resize (int dont_care_sig); +/*------ Library Alternatives ------------------------------------------*/ +//atic void *alloc_c (unsigned numb); +//atic void *alloc_r (void *q, unsigned numb); +//atic proc_t **refreshprocs (proc_t **tbl); /*------ Startup routines ----------------------------------------------*/ -//atic void parse_argvs (char **argv); -//atic void parse_rc (char *opts); -//atic void rcfiles_read (void); -//atic void terminal_set (void); +//atic void before (char *me); +//atic void configs_read (void); +//atic void parse_args (char **args); +//atic void whack_terminal (void); /*------ Field Selection/Ordering routines -----------------------------*/ //atic void display_fields (void); //atic void fields_reorder (void); //atic void fields_toggle (void); -/*------ Library Alternatives ------------------------------------------*/ -//atic void *alloc_c (unsigned numb); -//atic void *alloc_r (void *q, unsigned numb); -//atic proc_t **readprocs (proc_t **tbl); -/*------ Main screen routines ------------------------------------------*/ -//atic void do_key (unsigned c); -#ifdef UGH_ITS_4_RH -//atic unsigned frame_memory (void); -#else -//atic void frame_memory (void); -#endif -//atic void frame_smp (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx); +/*------ Windows/Field Groups support ----------------------------------*/ +//atic void win_colsheads (WIN_t *q); +//atic void win_names (WIN_t *q, const char *name); +//atic void win_select (int ch); +//atic void win_sortset (WIN_t *q, const int which); +//atic int win_warn (void); +//atic void winsclr (WIN_t *q, int save); +//atic void wins_colors (void); +//atic void wins_reflag (int what, int flg); +//atic void wins_resize (int dont_care_sig); +//atic void windows_stage1 (void); +//atic void windows_stage2 (void); +/*------ Per-Frame Display support -------------------------------------*/ +//atic void cpudo (FILE *fp, const char *fmt, CPUS_t *cpu, const char *pfx); //atic void frame_states (proc_t **p, int show); -//atic void mkcol (unsigned idx, int sta, int *pad, char *buf, ...); -#ifdef UGH_ITS_4_RH -//atic void show_a_task (proc_t *task, unsigned mempgs); -#else -//atic void show_a_task (proc_t *task); -#endif -//atic void show_everything (void); +//atic void frame_storage (void); +//atic void mkcol (WIN_t *q, unsigned idx, int sta, int *pad, char *buf, ...); +//atic void show_a_task (WIN_t *q, proc_t *task); +/*------ Main Screen routines ------------------------------------------*/ +//atic void do_key (unsigned c); +//atic proc_t **do_summary (void); +//atic void do_window (proc_t **ppt, WIN_t *q, int *lscr); +//atic void sohelpme (int wix, int max); +//atic void so_lets_see_em (void); /*------ Entry point ---------------------------------------------------*/ // int main (int dont_care_argc, char **argv);