]> granicus.if.org Git - procps-ng/commitdiff
updated jims new topzop
authorcsmall <>
Tue, 18 Jun 2002 23:45:30 +0000 (23:45 +0000)
committercsmall <>
Tue, 18 Jun 2002 23:45:30 +0000 (23:45 +0000)
top.c
top.h

diff --git a/top.c b/top.c
index 95ad28936546a50b16bb105960c3a598c8b79220..b9e7dfb33e94c7e5891472e50d971cb994b7c0be 100644 (file)
--- a/top.c
+++ b/top.c
@@ -1,7 +1,10 @@
 /* top.c - Source file:         show Linux processes */
 /*
- * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
- *    All rights reserved.
+ * Copyright (c) June, 2002 -   James C. Warner
+ *    All rights reserved.      8921 Hilloway Road
+ *                              Eden Prairie, Minnesota 55347 USA
+ *                             <warnerjc@worldnet.att.net>
+ *
  * 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.
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Library General Public License for more details.
  */
-#include <asm/param.h>
+/* For their contributions to this program, the author wishes to thank:
+ *    Craig Small, <csmall@eye-net.com.au>
+ *    Albert D. Cahalan, <acahalan@cs.uml.edu>
+ */
 #include <sys/ioctl.h>
 #include <sys/resource.h>
 #include <sys/time.h>
         /* 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 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;
-
-        /* 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;
-
-        /* 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 */
-static int  No_ksyms = -1,      /* set to '0' if ksym avail, '1' otherwise   */
-            PSDBopen = 0;       /* set to '1' if psdb opened (now postponed) */
+        /* 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;
+
+        /* 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;
+
+        /* 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  */
 
-        /* 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 */
+        /* 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) */
+            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)  */
-
-        /* 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;
+            Secure_mode = 0;    /* set if some functionality restricted      */
+
 
         /* 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 <sched.h>" }
 #else
    { "Flags    ",   "%08lx ",   -1,     -1,     -1,  "Task Flags <sched.h>" }
@@ -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;
-
-        /* Development/debug stuff #######################################*/
-
-#ifdef TRAK_MAXCAPS
-static int  Max_pads = 0, Min_pads = MAXINT,
-            Max_rbuf = 0, Min_rbuf = MAXINT;
-#endif
+static int   Cap_can_goto = 0;
 
-#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
+
+        /* ////////////////////////////////////////////////////////////// */
+        /* Special Section: multiple windows/field groups  ---------------*/
+
+        /* 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  */
+        /* ////////////////////////////////////////////////////////////// */
 
 \f
 /*######  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);
 }
 
 \f
@@ -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 *)"";
+}
+
+\f
+/*######  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);
 }
 
 \f
@@ -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 <floyd@ptialaska.net> */
 #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
-}
-
 \f
-/*######  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;
@@ -910,217 +826,226 @@ static float time_elapsed (void)
 }
 
 \f
-/*######  Exit and Signal handled routines  ##############################*/
+/*######  Library Alternatives  ##########################################*/
 
         /*
-         * 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
+         * Handle our own memory stuff without the risk of leaving the
+         * user's terminal in an ugly state should things go sour. */
+static const char *alloc_msg = "Failed memory allocate (%d bytes)";
 
-#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
+static void *alloc_c (unsigned numb)
+{
+   void * p;
 
-   if (str && !eno) eno = 1;
-   exit(eno);
+   if (!numb) ++numb;
+   if (!(p = calloc(1, numb)))
+      std_err(fmtmk(alloc_msg, numb));
+   return p;
 }
 
 
-        /*
-         * Standard error handler to normalize the look of all err o/p */
-static void std_err (const char *str)
+static void *alloc_r (void *q, unsigned numb)
 {
-   static char buf[SMLBUFSIZ];
+   void *p;
 
-   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);
+   if (!numb) ++numb;
+   if (!(p = realloc(q, numb)))
+      std_err(fmtmk(alloc_msg, numb));
+   return p;
 }
 
 
         /*
-         * Normal end of execution.
-         * catches:
-         *    SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
-static void stop (int dont_care_sig)
+         * 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 <stdarg.h> */
+static proc_t **refreshprocs (proc_t **tbl)
 {
-   bye_bye(0, NULL);
+#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  (jeeze)      */
+   PROCTAB* PT;
+
+   if (Monpidsidx) {
+      PT = openproc(flags | PROC_PID, Monpids);
+         /* work around a previous bug in openproc (now corrected) */
+      PT->procfs = NULL;
+   } else
+      PT = openproc(flags);
+
+      /* i) Allocated Chunks:  *Existing* table;  refresh + reuse */
+   while (curmax < savmax) {
+      if (tbl[curmax]->cmdline) {
+         free(*tbl[curmax]->cmdline);
+         tbl[curmax]->cmdline = NULL;
+      }
+      if (!(ptsk = readproc(PT, tbl[curmax]))) break;
+      ++curmax;
+   }
+
+      /* ii) Unallocated Chunks:  *New* or *Existing* table;  extend + fill */
+   while (ptsk) {
+         /* realloc as we go, keeping 'tbl' ahead of 'currmax++' */
+      tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
+         /* here, readproc will allocate the underlying proc_t stg */
+      if ((ptsk = readproc(PT, NULL)))
+         tbl[curmax++] = ptsk;
+   }
+   closeproc(PT);
+
+      /* iii) Chunkless:  make 'eot' entry, after possible extension */
+   if (curmax >= savmax) {
+      tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
+         /* here, we must allocate the underlying proc_t stg ourselves */
+      tbl[curmax] = alloc_c(ENTsz);
+      savmax = curmax + 1;
+   }
+      /* this frame's end, but not necessarily end of allocated space */
+   tbl[curmax]->pid = -1;
+   return tbl;
+
+#undef PTRsz
+#undef ENTsz
 }
 
+\f
+/*######  Startup routines  ##############################################*/
 
         /*
-         * Suspend ourself.
-         * catches:
-         *    SIGTSTP, SIGTTIN and SIGTTOU */
-static void suspend (int dont_care_sig)
+         * 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)
 {
-        /* 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);
+   int i;
+
+      /* setup our program name -- big! */
+   Myname = strrchr(me, '/');
+   if (Myname) ++Myname; else Myname = me;
+
+      /* establish cpu particulars -- even bigger! */
+#ifdef PRETEND4CPUS
+   Cpu_tot = 4;
+#else
+   Cpu_tot = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+   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;
+
+      /* get virtual page size -- nearing huge! */
+   Page_size = getpagesize();
 }
 
 
         /*
-         * Set the screen dimensions and call the real workhorse.
-         * catches:
-         *    SIGWINCH and SIGCONT */
-static void window_resize (int dont_care_sig)
+         * 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)
 {
-   struct winsize w;
+   static const char *err_rc = "bad rcfile, you should delete '%s'";
+   char fbuf[RCFBUFSIZ];
+   FILE *fp;
+   float delay = DEF_DELAY;
+   char id;
+   int i;
 
-   Screen_cols = columns;
-   Screen_rows = lines;
-   if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w))) {
-      Screen_cols = w.ws_col;
-      Screen_rows = w.ws_row;
+   strcpy(RCfile_Sys, fmtmk("/etc/%src", Myname));
+   if (getenv("HOME"))
+      strcpy(RCfile, fmtmk("%s%c", getenv("HOME"), '/'));
+   strcat(RCfile, fmtmk(".%src", Myname));
+
+   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);
+      }
+      fclose(fp);
    }
-      /* he'll calculate header size, length of command field, etc ... */
-   mkheadings();
+      /* lastly, establish the true runtime secure mode and delay time */
+   Secure_mode = getuid() ? Secure_mode : 0;
+   if (!Secure_mode || !getuid()) Delay_time = delay;
 }
 
-\f
-/*######  Startup routines  ##############################################*/
 
         /*
-         * Parse command line arguments
-         * (and force ol' main into a much needed diet)
+         * 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 user's wishes... */
-static void parse_argvs (char **argv)
+         *       best effort to honor the loser's (oops, user's) wishes... */
+static void parse_args (char **args)
 {
-   /* 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) */
+   /* 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;
 
-   ++argv;
-   while (*argv) {
-      char *cp = *(argv++);
+   while (*args) {
+      char *cp = *(args++);
 
       while (*cp) {
          switch (*cp) {
@@ -1131,11 +1056,11 @@ static void parse_argvs (char **argv)
                Batch = 1;
                break;
             case 'c':
-               Show_cmdlin = !Show_cmdlin;
+               TOGw(Curwin, Show_CMDLIN);
                break;
             case 'd':
                if (cp[1]) ++cp;
-               else if (*argv) cp = *argv++;
+               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))
@@ -1146,20 +1071,21 @@ static void parse_argvs (char **argv)
                std_err(fmtmk("\t%s\nusage:\t%s%s"
                   , procps_version, Myname, usage));
             case 'i':
-               Show_idleps = !Show_idleps;
+               TOGw(Curwin, Show_IDLEPS);
+               Curwin->maxtasks = 0;
                break;
             case 'n':
                if (cp[1]) cp++;
-               else if (*argv) cp = *argv++;
+               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':
-               if (cp[1]) cp++;
-               else if (*argv) cp = *argv++;
-               else std_err("-p requires argument");
                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])
@@ -1170,25 +1096,25 @@ static void parse_argvs (char **argv)
                   Monpidsidx++;
                   if (!(p = strchr(cp, ',')))
                      break;
-                  cp = ++p;
+                  cp = p;
                } while (*cp);
                break;
             case 's':
                Secure_mode = 1;
                break;
             case 'S':
-               Show_ctimes = !Show_ctimes;
+               TOGw(Curwin, 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: switch (*cp) */
 
-      } /* end: while *cp */
-   } /* end: while *argv */
+            /* 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) {
@@ -1197,218 +1123,16 @@ static void parse_argvs (char **argv)
       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)
+static void whack_terminal (void)
 {
    struct termios newtty;
 
-      /* first the ncurses part... */
+      /* first the curses part... */
 #ifdef PRETENDNOCAP
    setupterm((char *)"dumb", STDOUT_FILENO, NULL);
 #else
@@ -1418,7 +1142,6 @@ static void terminal_set (void)
    if (!Batch) {
       if (-1 == tcgetattr(STDIN_FILENO, &Savedtty))
          std_err("tcgetattr() failed");
-      capsmk();
       newtty = Savedtty;
       newtty.c_lflag &= ~ICANON;
       newtty.c_lflag &= ~ECHO;
@@ -1451,8 +1174,9 @@ static void terminal_set (void)
          * 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 - 3;
+   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
@@ -1463,22 +1187,24 @@ static void display_fields (void)
       for (p = Fieldstab[i].head; ' ' == *p; ++p)
          ;
       printf("%s%c %c: %-10s = %s"
-         , tg2((i / rmax) * cmax, (i % rmax) + 3)
-         , strchr(CurFields, i + 'A') ? '*' : ' '
+         , tg2((i / rmax) * cmax, (i % rmax) + yRSVD)
+         , strchr(Curwin->fieldscur, i + 'A') ? '*' : ' '
          , i + 'A'
          , p
          , Fieldstab[i].desc);
    }
-   putp(Row_color_norm);
+   putp(Curwin->capclr_rownorm);
    x = FIELDS_xtra;
    while ((p = strchr(x, '\n'))) {
       ++i;
       printf("%s%.*s"
-         , tg2((i / rmax) * cmax, (i % rmax) + 3)
+         , tg2((i / rmax) * cmax, (i % rmax) + yRSVD)
          , p - x, x);
       x = ++p;
    }
    putp(Caps_off);
+
+#undef yRSVD
 }
 
 
@@ -1486,7 +1212,7 @@ static void display_fields (void)
          * Change order of displayed fields. */
 static void fields_reorder (void)
 {
-   static char prompt[] =
+   static const char *prompt =
       "Upper case characters move field left, lower case right";
    char c, *p;
    int i;
@@ -1495,15 +1221,15 @@ static void fields_reorder (void)
    display_fields();
    do {
       show_special(fmtmk(FIELDS_current
-         , Cap_home, Myname, CurFields, prompt));
+         , 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(CurFields, i + 'A')))
-      || ((p = strchr(CurFields, i + 'a')))) {
+      if (((p = strchr(Curwin->fieldscur, i + 'A')))
+      || ((p = strchr(Curwin->fieldscur, i + 'a')))) {
          if (isupper(c)) p--;
-         if (('\0' != p[1]) && (p >= CurFields)) {
+         if (('\0' != p[1]) && (p >= Curwin->fieldscur)) {
             c    = p[0];
             p[0] = p[1];
             p[1] = c;
@@ -1511,7 +1237,6 @@ static void fields_reorder (void)
       }
    } while (1);
    putp(Cap_curs_norm);
-   mkheadings();
 }
 
 
@@ -1519,7 +1244,7 @@ static void fields_reorder (void)
          * Toggle displayed fields. */
 static void fields_toggle (void)
 {
-   static char prompt[] =
+   static const char *prompt =
       "Toggle fields with a-x, type any other key to return";
    char c, *p;
    int i;
@@ -1528,471 +1253,443 @@ static void fields_toggle (void)
    do {
       display_fields();
       show_special(fmtmk(FIELDS_current
-         , Cap_home, Myname, CurFields, prompt));
+         , 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(CurFields, i + 'A')))
+      if ((p = strchr(Curwin->fieldscur, i + 'A')))
          *p = i + 'a';
-      else if ((p = strchr(CurFields, i + 'a')))
+      else if ((p = strchr(Curwin->fieldscur, i + 'a')))
          *p = i + 'A';
    } while (1);
    putp(Cap_curs_norm);
-   mkheadings();
 }
 
 \f
-/*######  Library Alternatives  ##########################################*/
+/*######  Windows/Field Groups support  #################################*/
 
         /*
-         * Handle our own memory stuff without the risk of leaving the
-         * user's terminal in an ugly state should things go sour. */
-static const char *alloc_msg = "Failed memory allocate (%d bytes)";
-
-static void *alloc_c (unsigned numb)
+         * 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)
 {
-   void * p;
+   const char *h;
+   int i, needpsdb = 0;
 
-   if (!numb) ++numb;
-   if (!(p = calloc(1, numb)))
-      std_err(fmtmk(alloc_msg, numb));
-   return p;
+      /* 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;
+      }
+   }
 }
 
 
-static void *alloc_r (void *q, unsigned numb)
+        /*
+         * Value a window's name and make the associated group name. */
+static void win_names (WIN_t *q, const char *name)
 {
-   void *p;
-
-   if (!numb) ++numb;
-   if (!(p = realloc(q, numb)))
-      std_err(fmtmk(alloc_msg, numb));
-   return p;
+   /* 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);
 }
 
 
         /*
-         * 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 <stdarg.h> */
-static proc_t **readprocs (proc_t **tbl)
+         * Display a window/field group (ie. make it "current"). */
+static void win_select (int ch)
 {
-#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               */
-   PROCTAB* PT;
-
-   if (Monpidsidx) {
-      PT = openproc(flags | PROC_PID, Monpids);
-         /* work around a previous bug in openproc (now corrected) */
-      PT->procfs = NULL;
-   } else
-      PT = openproc(flags);
-
-      /* i) Allocated Chunks:  *Existing* table;  refresh + reuse */
-   while (curmax < savmax) {
-      if (tbl[curmax]->cmdline) {
-         free(*tbl[curmax]->cmdline);
-         tbl[curmax]->cmdline = NULL;
-      }
-      if (!(ptsk = readproc(PT, tbl[curmax]))) break;
-      ++curmax;
-   }
+   static const char *prompt = "Choose field group (1 - 4)";
 
-      /* ii) Unallocated Chunks:  *New* or *Existing* table;  extend + fill */
-   while (ptsk) {
-         /* realloc as we go, keeping 'tbl' ahead of 'currmax++' */
-      tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
-         /* here, readproc will allocate the underlying proc_t stg */
-      if ((ptsk = readproc(PT, NULL)))
-         tbl[curmax++] = ptsk;
+   /* 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);
    }
-   closeproc(PT);
-
-      /* iii) Chunkless:  make 'eot' entry, after possible extension */
-   if (curmax >= savmax) {
-      tbl = alloc_r(tbl, (curmax + 1) * PTRsz);
-         /* here, we must allocate the underlying proc_t stg ourselves */
-      tbl[curmax] = alloc_c(ENTsz);
-      savmax = curmax + 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;
    }
-      /* this frame's end, but not necessarily end of allocated space */
-   tbl[curmax]->pid = -1;
-   return tbl;
-
-#undef PTRsz
-#undef ENTsz
 }
 
-\f
-/*######  Main screen 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)
+         * Establish the requested sort environment */
+static void win_sortset (WIN_t *q, const int which)
 {
-#define kbdCTRL_L  12
-      /* standardized 'secure mode' errors */
-   const char *smerror = "\aCan't %s in secure mode";
-
-   switch (c) {
-      case 'b':
-         if (!Show_hicols && !Show_hirows)
-            show_msg("\aNothing to highlight!");
-         else {
-            Show_hibold = !Show_hibold;
-         }
-         capsmk();
-         break;
-
-      case 'c':
-         Show_cmdlin = !Show_cmdlin;
-         break;
-
+   /* 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':
-         Sort_type = S_CMD;
-         Sort_func = (QSORT_t)sort_cmd;
+         q->sorttype = S_CMD;
+         q->sortfunc = (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(
-#else
-               sig = signal_name_to_number(
-#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;
-
-      case 'l':
-         HSum_loadav = !HSum_loadav;
-         mkheadings();
+         q->sorttype = S_USR;
+         q->sortfunc = (QSORT_t)sort_usr;
          break;
-
-      case 'm':
-         HSum_memory = !HSum_memory;
-         mkheadings();
-         break;
-
       case 'M':
-         Sort_type = S_MEM;
-         Sort_func = (QSORT_t)sort_mem;
-         break;
-
-      case 'n':
-      case '#':
-      {  int num;
-
-         if (-1 != (num = get_int("Processes to display (0 = unlimited)"))) {
-            Max_tasks = num;
-            window_resize(0);
-         }
-      }
-         break;
-
-      case 'o':
-      case 'O':
-         fields_reorder();
+         q->sorttype = S_MEM;
+         q->sortfunc = (QSORT_t)sort_mem;
          break;
-
       case 'P':
-         Sort_type = S_PID;
-         Sort_func = (QSORT_t)sort_pid;
-         break;
-
-      case 'q':
-         stop(0);
-
-      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();
+         q->sorttype = S_PID;
+         q->sortfunc = (QSORT_t)pid_sort;
          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
+         q->sorttype = S_TME;
+         q->sortfunc = (QSORT_t)sort_tme;
          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();
+         q->sorttype = S_CPU;
+         q->sortfunc = (QSORT_t)sort_cpu;
          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 */
-               break;
-            case 'm' :
-               strcpy(CurFields, MEM_FIELDS);
-               ch = 'M';        /* force sort on %mem/res */
-               break;
-            case 'u' :
-               strcpy(CurFields, USR_FIELDS);
-               ch = 'E';        /* force sort on user */
-               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);
-         }
-      }
-         break;
-
-      case ' ':
-      case kbdCTRL_L:
-         putp(Cap_clr_scr);
+         q->sorttype = S_TTY;
+         q->sortfunc = (QSORT_t)sort_tty;
          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
 }
 
-
-#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)
+         * Just warn the user when a command can't be honored. */
+static int win_warn (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])));
-   }
+   show_msg(fmtmk("\aCommand disabled, activate window #%d with '-' or '_'"
+      , mkWNO(Curwin)));
 
-   return PAGE_CNT(memarray[meminfo_main][meminfo_total]);
+   /* we gotta' return false 'cause we're somewhat well known within
+      macro society, by way of that sassy little tertiary operator... */
+   return 0;
 }
 
-#else
 
         /*
-         * Obtain memory information and display it. */
-static void frame_memory (void)
+         * 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)
 {
-   meminfo();
-   if (HSum_memory) {
-      show_special(fmtmk(MEMORY_line1
-         , kb_main_total
-         , kb_main_used
-         , kb_main_free
-         , kb_main_buffers));
+   static int flgssav, summsav, msgssav, headsav, tasksav;
 
-      show_special(fmtmk(MEMORY_line2
-         , kb_swap_total
-         , kb_swap_used
-         , kb_swap_free
-         , kb_main_cached));
+   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);
 }
-#endif /* end: UGH_ITS_4_RH */
 
 
         /*
-         * State display *Helper* function to calc and display the state
-         * percentages for a single cpu.  In this way, we can support
-         * the following environments without the usual code bloat.
-         *    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)
+         * Change colors used in display */
+static void wins_colors (void)
 {
-        /* we'll trim to zero if we get negative time ticks,
-           which has happened with some SMP kernels (pre-2.4?) */
-#define TRIMz(x)  ((tz = (long)x) < 0 ? 0 : tz)
-   TICS_t u_tics, s_tics, n_tics, i_tics;
-   long   u_frme, s_frme, n_frme, i_frme, tot_frme, tz;
-
-   if (4 != fscanf(fp, fmt, &u_tics, &n_tics, &s_tics, &i_tics))
-      std_err("Failed /proc/stat read");
+#define kbdABORT  'q'
+#define kbdAPPLY  '\n'
+   int clr = Curwin->taskclr, *pclr = &Curwin->taskclr;
+   char ch, tgt = 'T';
 
-   u_frme = TRIMz(u_tics - cpu->u);
+   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]);
+   }
+}
+
+\f
+/*######  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
+         * the following environments without the usual code bloat.
+         *    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 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?) */
+#define TRIMz(x)  ((tz = (long)x) < 0 ? 0 : tz)
+   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");
+
+   u_frme = TRIMz(u_tics - cpu->u);
    s_frme = TRIMz(s_tics - cpu->s);
    n_frme = TRIMz(n_tics - cpu->n);
    i_frme = TRIMz(i_tics - cpu->i);
@@ -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,435 +1766,925 @@ 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) {
-      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;
-      }
+   if (show) {                                          /* display ///////// */
+      FILE  *fp;
 
          /* 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 */
+   memcpy(hist_sav, hist_new, hist_siz);
+   free(hist_new);
+      /* 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 (WIN_t *q, unsigned idx, int sta, int *pad, char *buf, ...)
+{
+   char tmp[COLBUFSIZ];
+   va_list va;
+
+   va_start(va, buf);
+   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", 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);
+}
+
+
+        /*
+         * 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(q,idx,sta,pad,buf,arg) \
+           sprintf(buf, Fieldstab[idx].fmts, arg)
+   char rbuf[ROWBUFSIZ];
+   int i, x, pad;
+
+      /* 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 < q->maxpflgs; i++) {
+      char cbuf[COLBUFSIZ];
+      unsigned f, s, w;
+
+      cbuf[0] = '\0';
+      f = q->procflags[i];
+      s = Fieldstab[f].scale;
+      w = Fieldstab[f].width;
+
+      switch (f) {
+         case P_CMD:
+         {  char *cmdptr, cmdnam[ROWBUFSIZ];
+
+            if (!CHKw(q, Show_CMDLIN))
+               cmdptr = task->cmd;
+            else {
+               cmdnam[0] = '\0';
+               if (task->cmdline) {
+                  x = 0;
+                  do {
+                     /* 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 ", 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 -- so whack those suckers... */
+                     strim(1, cmdnam);
+                     if (q->maxcmdln < (int)strlen(cmdnam)) break;
+                  } while (task->cmdline[x]);
+               } else {
+                  /* 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;
+            }
+            mkcol(q, f, task->state, &pad
+               , cbuf, q->maxcmdln, q->maxcmdln, cmdptr);
+         }
+            break;
+         case P_CODE:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->trs), w, s));
+            break;
+         case P_CPU:
+            mkcol(q, f, task->state, &pad, cbuf, (float)task->pcpu / 10);
+            break;
+         case P_DATA:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->drs), w, s));
+            break;
+         case P_DIRTY:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num((unsigned)task->dt, w, s));
+            break;
+         case P_FAULT:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num(task->maj_flt, w, s));
+            break;
+         case P_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(q, f, task->state, &pad, cbuf, task->egroup);
+            break;
+         case P_MEM:
+            mkcol(q, f, task->state, &pad, cbuf
+#ifdef UGH_ITS_4_RH
+               , (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(q, f, task->state, &pad, cbuf, task->lproc);
+#else
+            MKCOL(q, f, task->state, &pad, cbuf, task->processor);
+#endif
+            break;
+         case P_NI:
+            MKCOL(q, f, task->state, &pad, cbuf, task->nice);
+            break;
+         case P_PID:
+            mkcol(q, f, task->state, &pad, cbuf, task->pid);
+            break;
+         case P_PPID:
+            MKCOL(q, f, task->state, &pad, cbuf, task->ppid);
+            break;
+         case P_PR:
+            MKCOL(q, f, task->state, &pad, cbuf, task->priority);
+            break;
+         case P_RES:
+            mkcol(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->resident), w, s));
+            break;
+         case P_SHR:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->share), w, s));
+            break;
+         case P_STA:
+#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(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->size - task->resident), w, s));
+            break;
+         case P_TIME:
+         case P_TIME2:
+         {  TICS_t t;
+
+            t = task->utime + task->stime;
+            if (CHKw(q, Show_CTIMES))
+               t += (task->cutime + task->cstime);
+            mkcol(q, f, task->state, &pad, cbuf, scale_tics(t, w));
+         }
+            break;
+         case P_TTY:
+         {  char tmp[TNYBUFSIZ];
+
+            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(q, f, task->state, &pad, cbuf, task->euid);
+            break;
+         case P_USER:
+            mkcol(q, f, task->state, &pad, cbuf, task->euser);
+            break;
+         case P_VIRT:
+            MKCOL(q, f, task->state, &pad, cbuf
+               , scale_num(PAGES_2K(task->size), w, s));
+            break;
+         case P_WCHAN:
+            if (No_ksyms)
+#ifdef CASEUP_HEXES
+               MKCOL(q, f, task->state, &pad, cbuf
+                  , fmtmk("x%08lX", (long)task->wchan));
+#else
+               MKCOL(q, f, task->state, &pad, cbuf
+                  , fmtmk("x%08lx", (long)task->wchan));
+#endif
+            else
+               MKCOL(q, f, task->state, &pad, cbuf, wchan(task->wchan));
+            break;
+
+        } /* end: switch 'flg' */
+
+        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", (CHKw(q, Show_HIROWS) && 'R' == task->state)
+      ? q->capclr_rowhigh : q->capclr_rownorm
+      , Screen_cols + pad
+      , rbuf
+      , Caps_off
+      , Cap_clr_eol);
+
+#undef MKCOL
+}
+
+\f
+/*######  Main Screen routines  ##########################################*/
+
+        /*
+         * Process keyboard input during the main loop */
+static void do_key (unsigned c)
+{
+#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
+
+   switch (c) {
+                       /* begin windows grouping /////////////////////////// */
+      case '-':                 /* 'Dash' lower case ----------------------- */
+         if (Show_altscr)
+            TOGw(Curwin, VISIBLE_tsk);
+         break;
+
+      case '_':                 /* 'Dash' upper case ----------------------- */
+         if (Show_altscr)       /* switcharoo, all viz & inviz ............. */
+            wins_reflag(Flgs_TOG, VISIBLE_tsk);
+         break;
+
+      case '=':                 /* 'Equals' lower case --------------------- */
+         if (Show_altscr) {     /* equalize task display ................... */
+            Curwin->maxtasks = 0;
+            SETw(Curwin, Show_IDLEPS | VISIBLE_tsk);
+         }
+         break;
+
+      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
+         Show_irixps = !Show_irixps;
+         show_msg(fmtmk("Irix mode %s", Show_irixps ? "On" : "Off"));
+#endif
+         break;
+
+      case 'k':
+         if (Secure_mode) {
+            show_msg(fmtmk(err_secure, "kill"));
+         } else {
+            int sig, pid = get_int("PID to kill");
+
+            if (-1 != pid) {
+#ifdef UGH_ITS_4_RH
+               sig = get_signal2(
+#else
+               sig = signal_name_to_number(
+#endif
+                  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;
 
-      fclose(fp);
-   } /* end: if 'show' */
+      case 'S':
+         if (VIZCHKc) {
+            TOGw(Curwin, Show_CTIMES);
+            show_msg(fmtmk("Cumulative time %s"
+               , CHKw(Curwin, Show_CTIMES) ? "On" : "Off"));
+         }
+         break;
 
-      /* 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);
-}
+      case 't':
+         TOGw(Curwin, View_STATES);
+         break;
 
+      case 'u':
+         if (VIZCHKc)
+            strcpy(Curwin->colusrnam, ask4str("Which user (blank for all)"));
+         break;
 
-        /*
-         * Task display *Helper* function to handle highlighted
-         * column transitions.  */
-static void mkcol (unsigned idx, int sta, int *pad, char *buf, ...)
-{
-   char tmp[COLBUFSIZ];
-   va_list va;
+      case 'W':
+      {  FILE *fp = fopen(RCfile, "w"); int i;
 
-   va_start(va, buf);
-   if (!Show_hicols || Sort_type != 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;
+         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)));
       }
-   }
-   va_end(va);
-#ifdef TRAK_MAXBUFS
-   MAXBUFS(mkcol, tmp);
-#endif
-}
-
+         break;
 
-        /*
-         * 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
-{
-   /* 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) \
-           sprintf(buf, Fieldstab[idx].fmts, arg)
-   char rbuf[ROWBUFSIZ];
-   int i, x, pad;
+      case 'x':
+         if (VIZCHKc) {
+            TOGw(Curwin, Show_HICOLS);
+            capsmk(Curwin);
+         }
+         break;
 
-   pad = 0;
-   rbuf[0] = '\0';
+      case 'y':
+         if (VIZCHKc) {
+            TOGw(Curwin, Show_HIROWS);
+            capsmk(Curwin);
+         }
+         break;
 
-   for (i = 0; i < NumFields; i++) {
-      char cbuf[COLBUFSIZ];
-      unsigned f, s, w;
+      case 'z':
+         if (VIZCHKc) {
+            TOGw(Curwin, Show_COLORS);
+            capsmk(Curwin);
+         }
+         break;
 
-      cbuf[0] = '\0';
-      f = PFlags[i];
-      s = Fieldstab[f].scale;
-      w = Fieldstab[f].width;
+      case 'Z':
+         wins_colors();
+         break;
 
-      switch (f) {
-         case P_CMD:
-         {  char *cmdptr, cmdnam[ROWBUFSIZ];
+      case '?':
+      case 'h':
+      {  char ch;
 
-            if (Show_cmdlin) {
-               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 */
-                     strcat(cmdnam
-                        , fmtmk("%.*s ", Max_cmd, 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 !#@*#!... */
-                     strim(1, cmdnam);
-                        /* enough, don't ya think? */
-                     if (Max_cmd < (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 ] */
-                  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);
+         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);
          }
-            break;
-         case P_CODE:
-            MKCOL(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);
-            break;
-         case P_DATA:
-            MKCOL(f, task->state, &pad, cbuf
-               , scale_num(PAGES_2K(task->drs), w, s));
-            break;
-         case P_DIRTY:
-            MKCOL(f, task->state, &pad, cbuf
-               , scale_num((unsigned)task->dt, w, s));
-            break;
-         case P_FAULT:
-            MKCOL(f, task->state, &pad, cbuf
-               , scale_num(task->maj_flt, w, s));
-            break;
-         case P_FLAGS:
-            MKCOL(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);
-            break;
-         case P_MEM:
-            mkcol(f, task->state, &pad, cbuf
-#ifdef UGH_ITS_4_RH
-               , (float)task->resident * 100 / mempgs);
-#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);
-#else
-            MKCOL(f, task->state, &pad, cbuf, task->processor);
-#endif
-            break;
-         case P_NI:
-            MKCOL(f, task->state, &pad, cbuf, task->nice);
-            break;
-         case P_PID:
-            mkcol(f, task->state, &pad, cbuf, task->pid);
-            break;
-         case P_PPID:
-            MKCOL(f, task->state, &pad, cbuf, task->ppid);
-            break;
-         case P_PR:
-            MKCOL(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
-               , scale_num(PAGES_2K(task->resident), w, s));
-            break;
-         case P_SHR:
-            MKCOL(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));
-            break;
-         case P_SWAP:
-            MKCOL(f, task->state, &pad, cbuf
-               , scale_num(PAGES_2K(task->size - task->resident), w, s));
-            break;
-         case P_TIME:
-         case P_TIME2:
-         {  TICS_t t;
+         putp(Cap_curs_norm);
+      }
+         break;
 
-            t = task->utime + task->stime;
-            if (Show_ctimes)
-               t += (task->cutime + task->cstime);
-            mkcol(f, task->state, &pad, cbuf, scale_tics(t, w));
-         }
-            break;
-         case P_TTY:
-         {  char tmp[TNYBUFSIZ];
+      case ' ':
+      case kbdCTRL_L:
+         putp(Cap_clr_scr);
+         break;
 
-            dev_to_tty(tmp, Fieldstab[f].width
-               , task->tty, task->pid, ABBREV_DEV);
-            mkcol(f, task->state, &pad, cbuf, tmp);
-         }
-            break;
-         case P_UID:
-            MKCOL(f, task->state, &pad, cbuf, task->euid);
-            break;
-         case P_USER:
-            mkcol(f, task->state, &pad, cbuf, task->euser);
-            break;
-         case P_VIRT:
-            MKCOL(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
-                  , fmtmk("x%08lX", (long)task->wchan));
+      case '1':
+#ifdef WARN_NOT_SMP
+         if (Cpu_tot > 1)
+            TOGw(Curwin, View_CPUSUM);
+         else
+            show_msg(err_smp);
 #else
-               MKCOL(f, task->state, &pad, cbuf
-                  , fmtmk("x%08lx", (long)task->wchan));
+         TOGw(Curwin, View_CPUSUM);
 #endif
-            else
-               MKCOL(f, task->state, &pad, cbuf, wchan(task->wchan));
-            break;
+         break;
 
-        } /* end: switch 'PFlags[i]' */
-#ifdef TRAK_MAXBUFS
-        MAXBUFS(show_a_task, cbuf);
-#endif
-        strcat(rbuf, cbuf);
+      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;
 
-   } /* end: for 'NumFields' */
-#ifdef TRAK_MAXBUFS
-   MAXBUFS(show_a_task, rbuf);
+#ifdef QUIT_NORMALQ
+      case 'q':
+#else
+      case kbdESCAPE:
 #endif
+         stop(0);
 
-   /* 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
-      , Screen_cols + pad
-      , rbuf
-      , Caps_off
-      , Cap_clr_eol);
+      default:
+         show_msg("\aUnknown command - try 'h' for help");
+   }
 
-#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);
+#ifndef QUIT_NORMALQ
+#undef kbdESCAPE
 #endif
-
-#undef MKCOL
+#undef kbdCTRL_L
 }
 
 
         /*
-         * Read all process info and display it. */
-static void show_everything (void)
+         * 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;
-   int ntask, nline;
-#ifdef UGH_ITS_4_RH
-   unsigned mempgs;
-#endif
 
    if (!p_table) {
-      p_table = readprocs(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" : Cap_home);
+      putp(Batch ? "\n\n" : Cap_home);
 
-  /*
-   ** Display Load averages */
-   if (HSum_loadav)
-      show_special(fmtmk(LOADAV_line, Myname, sprint_uptime()));
 
-  /*
-   ** Display System stats (also calc 'pcpu' and sort p_table) */
-   p_table = readprocs(p_table);
-   frame_states(p_table, HSum_states);
+      /*
+       ** 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 Memory and Swap space usage */
-#ifdef UGH_ITS_4_RH
-   mempgs = frame_memory();
-#else
-   frame_memory();
-#endif
+      /*
+       ** 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;
+}
 
-  /*
-   ** Display Headings for columns */
-   printf("%s%s%s%s%s"
-      , tg2(0, MSG_line + 1)
-      , Hdr_color
-      , ColHeadings
-      , Caps_off
-      , Cap_clr_eol);
 
-   /* 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 */
-#ifdef UGH_ITS_4_RH
-         show_a_task(p_table[ntask], mempgs);
-#else
-         show_a_task(p_table[ntask]);
+         * 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
-         if (!Batch) ++nline;
+      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;
       }
-      ++ntask;
+      ++i;
    }
+      /* for this frame that window's toast, cleanup for next time */
+   q->winlines = 0;
+   OFFw(Curwin, FLGSOFF_cwo);
 
-   printf("%s%s%s", Cap_clr_eos, tg2(0, MSG_line), Cap_clr_eol);
-   fflush(stdout);
+#ifdef SORT_SUPRESS
+#undef srtMASK
+#endif
 }
 
-\f
-/*######  Entry point  ###################################################*/
 
-int main (int dont_care_argc, char **argv)
+        /*
+         * 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)
 {
-   char not_really_tmp[OURPATHSZ];
-   int i;
+   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;
+}
 
-      /* 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
+
+        /*
+         * 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);
+}
 
-      /* 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);
+/*######  Entry point  ###################################################*/
 
-      /* set up signal handlers */
+        /*
+         * 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)
+{ /*
+   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!
+
+   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!
+
+   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
+   <bleepin'> 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 84f3a772d064e7ef83f9a9ffd456e860ea9b1eed..b7b8ce569fb2c206b693c2bfbd090097c53a5993 100644 (file)
--- a/top.h
+++ b/top.h
@@ -1,7 +1,10 @@
 /* top.h - Header file:         show Linux processes */
 /*
- * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
- *    All rights reserved.
+ * Copyright (c) June, 2002 -   James C. Warner
+ *    All rights reserved.      8921 Hilloway Road
+ *                              Eden Prairie, Minnesota 55347 USA
+ *                             <warnerjc@worldnet.att.net>
+ *
  * 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.
  * 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, <csmall@eye-net.com.au>
+ *    Albert D. Cahalan, <acahalan@cs.uml.edu>
+ */
 #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 <Esc>  */
+//#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  ##########################*/
-
-        /* Message line placement at runtime */
-#define MSG_line  ( HSum_lines - 2 )
-
-        /* 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 )
-
-        /* 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); \
-        }
-
-#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
-
-
-/*######  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
+        /* ////////////////////////////////////////////////////////////// */
+        /* Special Section: multiple windows/field groups  ---------------*/
+        /* (kind of a header within a header:  constants, macros & types) */
+
+#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 */
+
+#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
+
+#define Flgs_TOG  1             /* these are used to direct wins_reflag   */
+#define Flgs_SET  2
+#define Flgs_OFF  3
+
+        /* 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
+
+        /* 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 )
+
+        /* 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()
+
+        /* 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
+
+        /* 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 <Enter> to commit " \
+   "   press 'q' to abort changes to window '\01%s\02'\n" \
+   "   press 'a' or 'w' to commit & change another, <Enter> 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  "  <Esc>       "
+#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 <Enter> to make 'Current' " \
+   ""
+
 \f
 /*######  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);