]> granicus.if.org Git - procps-ng/commitdiff
Added jims top and moved old files
authorcsmall <>
Thu, 30 May 2002 03:44:46 +0000 (03:44 +0000)
committercsmall <>
Thu, 30 May 2002 03:44:46 +0000 (03:44 +0000)
oldtop.c [new file with mode: 0644]
oldtop.h [new file with mode: 0644]
top.c
top.h

diff --git a/oldtop.c b/oldtop.c
new file mode 100644 (file)
index 0000000..2faaf0c
--- /dev/null
+++ b/oldtop.c
@@ -0,0 +1,1766 @@
+/*
+ * top.c              - show top CPU processes
+ *
+ * Copyright (c) 1992 Branko Lankester
+ * Copyright (c) 1992 Roger Binns
+ * Copyright (c) 1997 Michael K. Johnson
+ *
+ * Snarfed and HEAVILY modified in december 1992 for procps
+ * by Michael K. Johnson, johnsonm@sunsite.unc.edu.
+ *
+ * Modified Michael K. Johnson's ps to make it a top program.
+ * Also borrowed elements of Roger Binns kmem based top program.
+ * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com)
+ * 1/93
+ *
+ * Modified by Michael K. Johnson to be more efficient in cpu use
+ * 2/21/93
+ *
+ * Changed top line to use uptime for the load average.  Also
+ * added SIGTSTP handling.  J. Cowley, 19 Mar 1993.
+ *
+ * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu)
+ * 1994/04/02.  Secure mode added.  "d" option added.  Argument parsing
+ * improved.  Switched order of tick display to user, system, nice, idle,
+ * because it makes more sense that way.  Style regularized (to K&R,
+ * more or less).  Cleaned up much throughout.  Added cumulative mode.
+ * Help screen improved.
+ *
+ * Fixed kill buglet brought to my attention by Rob Hooft.
+ * Problem was mixing of stdio and read()/write().  Added
+ * getnum() to solve problem.
+ * 12/30/93 Michael K. Johnson
+ *
+ * Added toggling output of idle processes via 'i' key.
+ * 3/29/94 Gregory K. Nickonov
+ *
+ * Fixed buglet where rawmode wasn't getting restored.
+ * Added defaults for signal to send and nice value to use.
+ * 5/4/94 Jon Tombs.
+ *
+ * Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
+ * Merged previous changes to 0.8 into 0.95.
+ * Allowed the use of symbolic names (e.g., "HUP") for signal input.
+ * Rewrote getnum() into getstr(), getint(), getsig(), etc.
+ * 
+ * Modified 1995  Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> 
+ * added kmem top functionality (configurable fields)
+ * configurable order of process display
+ * Added options for dis/enabling uptime, statistics, and memory info.
+ * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields)
+ *
+ * Modified 1996/05/18 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+ * Use of new interface and general cleanup. The code should be far more
+ * readable than before.
+ *
+ * Modified 1996/06/25 Zygo Blaxell <zblaxell@ultratech.net>
+ * Added field scaling code for programs that run more than two hours or
+ * take up more than 100 megs.  We have lots of both on our production line.
+ *
+ * Modified 1998/02/21 Kirk Bauer <kirk@kaybee.org>
+ * Added the 'u' option to display only a selected user... plus it will
+ * take into account that not all 20 top processes are actually shown,
+ * so it can fit more onto the screen.  I think this may help the
+ * 'don't show idle' mode, but I'm not sure.
+ *
+ * Modified 1997/07/27 & 1999/01/27 Tim Janik <timj@gtk.org>
+ * added `-p' option to display specific process ids.
+ * process sorting is by default disabled in this case.
+ * added `N' and `A' keys to sort the tasks Numerically by pid or
+ * sort them by Age (newest first).
+ *
+ * Modified 1999/10/22 Tim Janik <timj@gtk.org>
+ * miscellaneous minor fixes, including "usage: ..." output for
+ * unrecognized options.
+ *
+ * Modified 2000/02/07 Jakub Jelinek <jakub@redhat.com>
+ * Only load System.map when we are going to display WCHAN.
+ * Show possible error messages from that load using SHOWMESSAGE.
+ *
+ * Modified 2000/07/10 Michael K. Johnson <johnsonm@redhat.com>
+ * Integrated a patch to display SMP information.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+#include <termcap.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <sys/param.h>
+#include <locale.h>
+
+#include "proc/sysinfo.h"
+#include "proc/procps.h"
+#include "proc/whattime.h"
+#include "proc/sig.h"
+#include "proc/version.h"
+#include "proc/readproc.h"
+#include "proc/status.h"
+#include "proc/devname.h"
+#include "proc/compare.h"
+
+#define PUTP(x) (tputs(x,1,putchar))
+#define BAD_INPUT -30
+
+#include "top.h"  /* new header for top specific things */
+
+static int *cpu_mapping;
+static int nr_cpu;
+
+/*#######################################################################
+ *####  Startup routines: parse_options, get_options,      ##############
+ *####                    setup_terminal and main          ##############
+ *#######################################################################
+ */
+
+      /*
+       * parse the options string as read from the config file(s).
+       * if top is in secure mode, disallow changing of the delay time between
+       * screen updates.
+       */
+static void parse_options(char *Options, int secure)
+{
+    int i;
+    for (i = 0; i < strlen(Options); i++) {
+       switch (Options[i]) {
+         case '2':
+         case '3':
+         case '4':
+         case '5':
+         case '6':
+         case '7':
+         case '8':
+         case '9':
+           if (!secure)
+               Sleeptime = (float) Options[i] - '0';
+           break;
+         case 'S':
+           Cumulative = 1;
+           headers[22][1] = 'C';
+           break;
+         case 's':
+           Secure = 1;
+           break;
+         case 'i':
+           Noidle = 1;
+           break;
+         case 'm':
+           show_memory = 0;
+           header_lines -= 2;
+           break;
+         case 'M':
+           sort_type = S_MEM;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)mem_sort);
+           break;
+         case 'l':
+           show_loadav = 0;
+           header_lines -= 1;
+           break;
+         case 'P':
+           sort_type = S_PCPU;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)pcpu_sort);
+           break;
+         case 'N':
+           sort_type = S_NONE;
+           reset_sort_options();
+           break;
+         case 'A':
+           sort_type = S_AGE;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)age_sort);
+           break;
+         case 't':
+           show_stats = 0;
+           header_lines -= 2;
+           break;
+         case 'T':
+           sort_type = S_TIME;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)time_sort);
+           break;
+         case 'c':
+           show_cmd = 0;
+           break;
+         case '\n':
+           break;
+         case 'I': 
+           Irixmode = 0;
+           break;
+         default:
+           fprintf(stderr, "Wrong configuration option %c\n", i);
+           exit(1);
+           break;
+       }
+    }
+}
+
+/* 
+ * Read the configuration file(s). There are two files, once SYS_TOPRC 
+ * which should only contain the secure switch and a sleeptime
+ * value iff ordinary users are to use top in secure mode only.
+ * 
+ * The other file is $HOME/RCFILE. 
+ * The configuration file should contain two lines (any of which may be
+ *  empty). The first line specifies the fields that are to be displayed
+ * in the order you want them to. Uppercase letters specify fields 
+ * displayed by default, lowercase letters specify fields not shown by
+ * default. The order of the letters in this line corresponds to the 
+ * order of the displayed fileds.
+ *
+ * all Options but 'q' can be read from this config file
+ * The delay time option syntax differs from the commandline syntax:
+ *   only integer values between 2 and 9 seconds are recognized
+ *   (this is for standard configuration, so I think this should do).
+ *
+ * usually this file is not edited by hand, but written from top using
+ * the 'W' command. 
+ */
+
+static void get_options(void)
+{
+    FILE *fp;
+    char *pt;
+    char *rcfile = NULL;        /* path to rc file... */
+    char *home = NULL;          /* path of user's home directory... */
+    size_t home_length = 0;     /* length of path... */
+    char Options[256] = "";
+    int i;
+
+    nr_cpu = sysconf (_SC_NPROCESSORS_ONLN);
+    if (nr_cpu < 1) nr_cpu = 1;
+    cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu);
+    /* read cpuname */
+    for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i;
+    header_lines = 6 + nr_cpu;
+    fp = fopen(SYS_TOPRC, "r");
+    if (fp != NULL) {
+       fgets(Options, 254, fp);
+       fclose(fp);
+    }
+    parse_options(Options, 0);
+    strcpy(Options, "");
+
+    if ( (home = getenv("HOME")) != NULL) {
+          home_length = strlen(home);
+    }
+
+    if ( (rcfile = malloc(home_length + strlen(RCFILE) + 2))) {
+        if (home != NULL) {
+            strcpy(rcfile, home);
+            strcat(rcfile, "/");
+        }
+        strcat(rcfile, RCFILE);
+        fp = fopen(rcfile, "r");
+        if (fp == NULL) {
+            strcpy(Fields, DEFAULT_SHOW);
+        } else {
+            if (fgets(Fields, 254, fp) != NULL) {
+                pt = strchr(Fields, '\n');
+                if (pt) *pt = 0;
+            }
+            fgets(Options, 254, fp);
+            fclose(fp);
+        }
+
+        free(rcfile);
+    }    
+    parse_options(Options, getuid()? Secure : 0);
+}
+
+/*
+ * Set up the terminal attributes.
+ */
+static void setup_terminal(void)
+{
+    char *termtype;
+    struct termios newtty;
+    if (!Batch)
+       termtype = getenv("TERM");
+    else 
+       termtype = "dumb";
+    if (!termtype) {
+       /* In theory, $TERM should never not be set, but in practice,
+          some gettys don't.  Fortunately, vt100 is nearly always
+          correct (or pretty close). */
+       termtype = "VT100";
+       /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */
+       /* exit(1); */
+    }
+
+    /*
+     * Get termcap entries and window size.
+     */
+    if(tgetent(NULL, termtype) != 1) {
+       fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n",
+               termtype);
+       exit(1);
+    }
+
+    cm = tgetstr("cm", 0);
+    top_clrtobot = tgetstr("cd", 0);
+    cl = tgetstr("cl", 0);
+    top_clrtoeol = tgetstr("ce", 0);
+    ho = tgetstr("ho", 0);
+    md = tgetstr("md", 0);
+    mr = tgetstr("mr", 0);
+    me = tgetstr("me", 0);
+
+
+    if (Batch) return; /* the rest doesn't apply to batch mode */
+    if (tcgetattr(0, &Savetty) == -1) {
+        perror(PROGNAME ": tcgetattr() failed");
+       error_end(errno);
+    }
+    newtty = Savetty;
+    newtty.c_lflag &= ~ICANON;
+    newtty.c_lflag &= ~ECHO;
+    newtty.c_cc[VMIN] = 1;
+    newtty.c_cc[VTIME] = 0;
+    if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) {
+       printf("cannot put tty into raw mode\n");
+       error_end(1);
+    }
+    tcgetattr(0, &Rawtty);
+}
+
+static int parseint(const char *src, const char *err)
+{
+    char *endp;
+    int num;
+    int len;
+    num = strtol(src, &endp, 0);
+    if (*endp == '\0') return num;
+    /* also accept prefixes of: infinite, infinity, maximum, all */
+    len = strlen(src);
+    if(len<1) goto fail;
+    if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX;
+    if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX;
+    if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX;
+    if(len<4 && !strncmp(src,"all"     ,len)) return INT_MAX;
+fail:
+    fprintf(stderr, err, src);
+    exit(1);
+}
+
+static double parseflt(const char *src, const char *err)
+{
+    char *endp;
+    double num;
+    int len;
+    num = strtod(src, &endp);
+    if (*endp == '\0') return num;
+    /* also accept prefixes of: infinite, infinity, maximum, all */
+    len = strlen(src);
+    if(len<1) goto fail;
+    if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX;
+    if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX;
+    if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX;
+    if(len<4 && !strncmp(src,"all"     ,len)) return (double)INT_MAX;
+fail:
+    fprintf(stderr, err, src);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    /* For select(2). */
+    struct timeval tv;
+    fd_set in;
+    /* For parsing arguments. */
+    char *cp;
+    /* The key read in. */
+    char c;
+
+    struct sigaction sact;
+
+    setlocale(LC_ALL, "");
+    get_options();
+    
+    /* set to PCPU sorting */
+    register_sort_function( -1, (cmp_t)pcpu_sort);
+    
+    /*
+     * Parse arguments.
+     */
+    (void)argc;
+    argv++;
+    while (*argv) {
+       cp = *argv++;
+       while (*cp) {
+           switch (*cp) {
+             case 'd':
+               if (cp[1]) {
+                   Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n");
+                   goto breakargv;
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n");
+                   goto breakargv;
+               } else {
+                   fprintf(stderr, "-d requires an argument\n");
+                   exit(1);
+               }
+               break;
+             case 'n':
+               if (cp[1]) {
+                   Loops = parseint(++cp, PROGNAME ": Bad value %s'\n");
+                   goto breakargv;
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n");
+                   goto breakargv;
+               }
+               break;
+                                       
+             case 'q':
+               if (!getuid())
+                   /* set priority to -10 in order to stay above kswapd */
+                   if (setpriority(PRIO_PROCESS, getpid(), -10)) {
+                       /* We check this just for paranoia.  It's not
+                          fatal, and shouldn't happen. */
+                       perror(PROGNAME ": setpriority() failed");
+                   }
+               Sleeptime = 0;
+               break;
+             case 'p':
+               if (monpids_index >= monpids_max) {
+                   fprintf(stderr, PROGNAME ": More than %u process ids specified\n",
+                           monpids_max);
+                   exit(1);
+               }
+               if (cp[1]) {
+                   if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 ||
+                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+                       exit(1);
+                   }
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 ||
+                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+                       exit(1);
+                   }
+               } else {
+                   fprintf(stderr, "-p requires an argument\n");
+                   exit(1);
+               }
+               if (!monpids[monpids_index])
+                   monpids[monpids_index] = getpid();
+               /* default to no sorting when monitoring process ids */
+               if (!monpids_index++) {
+                   sort_type = S_NONE;
+                   reset_sort_options();
+               }
+               cp = "_";
+               break;
+             case 'b':
+               Batch = 1;
+               break;
+             case 'c':
+               show_cmd = !show_cmd;
+               break;
+             case 'S':
+               Cumulative = 1;
+               break;
+             case 'i':
+               Noidle = 1;
+               break;
+             case 's':
+                 Secure = 1;
+                 break;
+             case 'C': 
+                 CPU_states = 1;
+                 break;
+             case '-':
+               break;          /* Just ignore it */
+             case 'v':
+             case 'V':
+               fprintf(stdout, "top (%s)\n", procps_version);
+               exit(0);
+             case 'h': 
+               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+               exit(0);
+             default:
+               fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp);
+               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+               exit(1);
+           }
+           cp++;
+       }
+    breakargv:
+    }
+    
+    if (nr_cpu > 1 && CPU_states)
+      header_lines++;
+
+    meminfo();  /* need kb_main_total value filled in */
+
+    setup_terminal();
+    window_size(0);
+    /*
+     * Set up signal handlers.
+     */
+    sact.sa_handler = end;
+    sact.sa_flags = 0;
+    sigemptyset(&sact.sa_mask);
+    sigaction(SIGHUP, &sact, NULL);
+    sigaction(SIGINT, &sact, NULL);
+    sigaction(SIGQUIT, &sact, NULL);
+    sact.sa_handler = stop;
+    sact.sa_flags = SA_RESTART;
+    sigaction(SIGTSTP, &sact, NULL);
+    sact.sa_handler = window_size;
+    sigaction(SIGWINCH, &sact, NULL);
+    sigaction(SIGCONT, &sact, NULL);
+
+    /* loop, collecting process info and sleeping */
+    while (1) {
+       if (Loops > 0)
+               Loops--;
+       /* display the tasks */
+       show_procs();
+       /* sleep & wait for keyboard input */
+       if (Loops == 0)
+           end(0);
+        if (!Batch)
+        {
+               tv.tv_sec = Sleeptime;
+               tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000;
+               FD_ZERO(&in);
+               FD_SET(0, &in);
+               if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1)
+                       do_key(c);
+        } else {
+          sleep(Sleeptime);
+       }
+    }
+}
+
+/*#######################################################################
+ *#### Signal handled routines: error_end, end, stop, window_size     ###
+ *#### Small utilities: make_header, getstr, getint, getfloat, getsig ###
+ *#######################################################################
+ */
+
+
+       /*
+        *  end when exiting with an error.
+        */
+static void error_end(int rno)
+{
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 1));
+    fputs("\r\n", stdout);
+    exit(rno);
+}
+/*
+        * Normal end of execution.
+        */
+static void end(int signo)
+{
+    (void)signo;
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 1));
+    fputs("\r\n", stdout);
+    exit(0);
+}
+
+/*
+        * SIGTSTP catcher.
+        */
+static void stop(int signo)
+{
+    (void)signo;
+    /* Reset terminal. */
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 3));
+    fflush(stdout);
+    raise(SIGSTOP);
+    /* Later... */
+    if (!Batch)
+       tcsetattr (0, TCSAFLUSH, &Rawtty);
+}
+
+/*
+       * Reads the window size and clear the window.  This is called on setup,
+       * and also catches SIGWINCHs, and adjusts Maxlines.  Basically, this is
+       * the central place for window size stuff.
+       */
+static void window_size(int signo)
+{
+    struct winsize ws;
+    (void)signo;
+    if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){
+       Cols = ws.ws_col;
+       Lines = ws.ws_row;
+    }else{
+       Cols = tgetnum("co");
+       Lines = tgetnum("li");
+    }
+    if (!Batch)
+        clear_screen();
+    /*
+     * calculate header size, length of cmdline field ...
+     */
+     Numfields = make_header();
+}
+/*
+       * this prints a possible message from open_psdb_message
+       */
+static void top_message(const char *format, ...) {
+    va_list arg;
+    int n;
+    char buffer[512];
+
+    va_start (arg, format);
+    n = vsnprintf (buffer, 512, format, arg);
+    va_end (arg);
+    if (n > -1 && n < 512)
+       SHOWMESSAGE(("%s", buffer));
+}
+
+/*
+       * this adjusts the lines needed for the header to the current value
+       */
+static int make_header(void)
+{
+    int i, j;
+
+    j = 0;
+    for (i = 0; i < strlen(Fields); i++) {
+       if (Fields[i] < 'a') {   
+           pflags[j++] = Fields[i] - 'A';
+           if (Fields[i] == 'U' && CL_wchan_nout == -1) {
+               CL_wchan_nout = 0;
+               /* for correct handling of WCHAN fields, we have to do distingu
+                * between kernel versions */
+               /* get kernel symbol table, if needed */
+               if (open_psdb_message(NULL, top_message)) {
+                   CL_wchan_nout = 1;
+               } else {
+                   psdbsucc = 1;
+               }
+           }
+       }
+    }
+    strcpy(Header, "");
+    for (i = 0; i < j; i++)
+       strcat(Header, headers[pflags[i]]);
+    /* readjust window size ... */
+    Maxcmd = Cols - strlen(Header) + 7;
+    Maxlines = Display_procs ? Display_procs : Lines - header_lines;
+    if (Maxlines > Lines - header_lines)
+       Maxlines = Lines - header_lines;
+    return (j);
+}
+
+
+
+/*
+ * Get a string from the user; the base of getint(), et al.  This really
+ * ought to handle long input lines and errors better.  NB: The pointer
+ * returned is a statically allocated buffer, so don't expect it to
+ * persist between calls.
+ */
+static char *getstr(void)
+{
+    static char line[BUFSIZ];  /* BUFSIZ from <stdio.h>; arbitrary */
+    int i = 0;
+
+    /* Must make sure that buffered IO doesn't kill us. */
+    fflush(stdout);
+    fflush(stdin);             /* Not POSIX but ok */
+
+    do {
+       read(STDIN_FILENO, &line[i], 1);
+    } while (line[i++] != '\n' && i < sizeof(line));
+    line[--i] = 0;
+
+    return (line);
+}
+
+
+/*
+ * Get an integer from the user.  Display an error message and
+ * return BAD_INPUT if it's invalid; else return the number.
+ */
+static int getint(void)
+{
+    char *line;
+    int i;
+    int r;
+
+    line = getstr();
+
+    for (i = 0; line[i]; i++) {
+       if (!isdigit(line[i]) && line[i] != '-') {
+           SHOWMESSAGE(("That's not a number!"));
+           return (BAD_INPUT);
+       }
+    }
+
+    /* An empty line is a legal error (hah!). */
+    if (!line[0])
+       return (BAD_INPUT);
+
+    sscanf(line, "%d", &r);
+    return (r);
+}
+
+
+/*
+ * Get a float from the user.  Just like getint().
+ */
+static float getfloat(void)
+{
+    char *line;
+    int i;
+    float r;
+    char *savelocale;
+
+    line = getstr();
+
+    for (i = 0; line[i]; i++) {
+       if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') {
+           SHOWMESSAGE(("That's not a float!"));
+           return (BAD_INPUT);
+       }
+    }
+
+    /* An empty line is a legal error (hah!). */
+    if (!line[0])
+       return (BAD_INPUT);
+
+    savelocale = setlocale(LC_NUMERIC, NULL);
+    setlocale(LC_NUMERIC, "C");
+    sscanf(line, "%f", &r); 
+    setlocale(LC_NUMERIC, savelocale);
+    return (r);
+}
+
+
+/*
+        * Get a signal number or name from the user.  Return the number, or -1
+        * on error.
+        */
+static int getsig(void)
+{
+    char *line;
+
+    /* This is easy. */
+    line = getstr();
+    return signal_name_to_number(line);
+}
+
+/*#######################################################################
+ *####  Routine for sorting on used time, resident memory and %CPU  #####
+ *####  It would be easy to include full sorting capability as in   #####
+ *####  ps, but I think there is no real use for something that     #####
+ *####  complicated. Using register_sort_function or parse_sort_opt #####
+ *####  you just have to do the natural thing and it will work.     #####
+ *#######################################################################
+ */
+
+static int time_sort (proc_t **P, proc_t **Q)
+{
+    if (Cumulative) {
+       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < 
+           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return -1;
+       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) >
+           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return 1;
+    } else {
+       if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+           return -1;
+       if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+           return 1;
+    }
+    return 0;
+}
+
+static int pcpu_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->pcpu < (*Q)->pcpu )      return -1;
+    if( (*P)->pcpu > (*Q)->pcpu )      return 1;
+    return 0;
+}
+
+static int mem_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->vm_rss < (*Q)->vm_rss )      return -1;
+    if( (*P)->vm_rss > (*Q)->vm_rss )      return 1;  
+    return 0;
+}
+
+int age_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->start_time < (*Q)->start_time )      return -1;
+    if( (*P)->start_time > (*Q)->start_time )      return 1;
+    return 0;
+}
+
+/*#######################################################################
+ *####  Routines handling the field selection/ordering screens:  ########
+ *####    show_fields, change_order, change_fields               ########
+ *#######################################################################
+ */
+
+        /*
+        * Display the specification line of all fields. Upper case indicates
+        * a displayed field, display order is according to the order of the 
+        * letters. A short description of each field is shown as well.
+        * The description of a displayed field is marked by a leading 
+        * asterisk (*).
+        */
+static void show_fields(void)
+{
+    int i, row, col;
+    char *p;
+
+    clear_screen();
+    PUTP(tgoto(cm, 3, 0));
+    printf("Current Field Order: %s\n", Fields);
+    for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) {
+       row = i % (Lines - 3) + 3;
+       col = i / (Lines - 3) * 40;
+       PUTP(tgoto(cm, col, row));
+       for (p = headers[i]; *p == ' '; ++p);
+       printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A',
+              p, headers2[i]);
+    }
+}
+
+/*
+        * change order of displayed fields
+        */
+static void change_order(void)
+{
+    char c, ch, *p;
+    int i;
+
+    show_fields();
+    for (;;) {
+       PUTP(tgoto(cm, 0, 0));
+       PUTP(top_clrtoeol);
+       PUTP(tgoto(cm, 3, 0));
+       PUTP(mr);
+       printf("Current Field Order: %s", Fields);
+       PUTP(me);
+       putchar('\n');
+       PUTP(tgoto(cm, 0, 1));
+       printf("Upper case characters move a field to the left, lower case to the right");
+       fflush(stdout);
+       if (!Batch) { /* should always be true, but... */
+           tcsetattr(0, TCSAFLUSH, &Rawtty);
+           read(0, &c, 1);
+           tcsetattr(0, TCSAFLUSH, &Savetty);
+       }
+       i = toupper(c) - 'A';
+       if ((p = strchr(Fields, i + 'A')) != NULL) {
+           if (isupper(c))
+               p--;
+           if ((p[1] != '\0') && (p >= Fields)) {
+               ch = p[0];
+               p[0] = p[1];
+               p[1] = ch;
+           }
+       } else if ((p = strchr(Fields, i + 'a')) != NULL) {
+           if (isupper(c))
+               p--;
+           if ((p[1] != '\0') && (p >= Fields)) {
+               ch = p[0];
+               p[0] = p[1];
+               p[1] = ch;
+           }
+       } else {
+           break;
+       }
+    }
+    Numfields = make_header();
+}
+/*
+        * toggle displayed fields
+        */
+static void change_fields(void)
+{
+    int i, changed = 0;
+    int row, col;
+    char c, *p;
+    char tmp[2] = " ";
+
+    show_fields();
+    for (;;) {
+       PUTP(tgoto(cm, 0, 0));
+       PUTP(top_clrtoeol);
+       PUTP(tgoto(cm, 3, 0));
+       PUTP(mr);
+       printf("Current Field Order: %s", Fields);
+       PUTP(me);
+       putchar('\n');
+       PUTP(tgoto(cm, 0, 1));
+       if (!Batch) { /* should always be true, but... */
+           printf("Toggle fields with a-x, any other key to return: ");
+           fflush(stdout);
+           tcsetattr(0, TCSAFLUSH, &Rawtty);
+           read(0, &c, 1);
+           tcsetattr(0, TCSAFLUSH, &Savetty);
+       }
+       i = toupper(c) - 'A';
+       if (i >= 0 && i < sizeof headers / sizeof headers[0]) {
+           row = i % (Lines - 3) + 3;
+           col = i / (Lines - 3) * 40;
+           PUTP(tgoto(cm, col, row));
+           if ((p = strchr(Fields, i + 'A')) != NULL) {        /* deselect Field */
+               *p = i + 'a';
+               putchar(' ');
+           } else if ((p = strchr(Fields, i + 'a')) != NULL) {         /* select previously */
+               *p = i + 'A';   /* deselected field */
+               putchar('*');
+           } else {            /* select new field */
+               tmp[0] = i + 'A';
+               strcat(Fields, tmp);
+               putchar('*');
+           }
+           changed = 1;
+           fflush(stdout);
+       } else
+           break;
+    }
+    if (changed)
+       Numfields = make_header();
+}
+
+/* Do the scaling stuff: interprets time in seconds, formats it to
+ * fit width, and returns pointer to static char*.
+ */
+static char *scale_time(int t,int width) 
+{
+       static char buf[100];
+
+       /* Try successively higher units until it fits */
+
+       sprintf(buf,"%d:%02d",t/60,t%60);       /* minutes:seconds */
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=60;  /* minutes */
+       sprintf(buf,"%dm",t);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=60;  /* hours */
+       sprintf(buf,"%dh",t);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=24;  /* days */
+       sprintf(buf,"%dd",t);
+       if (strlen(buf)<=width) 
+               return buf;
+       
+       t/=7;   /* weeks */
+       sprintf(buf,"%dw",t);
+       return buf;     /* this is our last try; 
+                               if it still doesn't fit, too bad. */
+
+       /* FIXME: if someone has a 16-way SMP running over a year... */
+}
+
+/*   scale_k(k,width,unit)  - interprets k as a count, formats to fit width.
+                            if unit is 0, k is a byte count; 1 is a kilobyte
+                           count; 2 for megabytes; 3 for gigabytes.
+*/
+
+static char *scale_k(int k,int width,int unit) 
+{
+               /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
+       static double scale[]={1024,1024*1024,1024*1024*1024,0};
+               /* kilo, mega, giga, tera */
+       static char unitletters[]={'K','M','G','T',0};
+       static char buf[100];
+       char *up;
+       double *dp;
+
+       /* Try successively higher units until it fits */
+
+       sprintf(buf,"%d",k);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) {
+               sprintf(buf,"%.1f%c",k / *dp,*up);
+               if (strlen(buf)<=width) 
+                       return buf;
+               sprintf(buf,"%d%c",(int)(k / *dp),*up);
+               if (strlen(buf)<=width) 
+                       return buf;
+       }
+
+       /* Give up; give them what we got on our shortest attempt */
+       return buf;
+}
+
+/*
+ *#######################################################################
+ *####  Routines handling the main top screen:                   ########
+ *####    show_task_info, show_procs, show_memory, do_stats      ########
+ *#######################################################################
+ */
+       /*
+        * Displays infos for a single task
+        */
+static void show_task_info(proc_t *task)
+{
+    int i,j;
+    unsigned int t;
+    char *cmdptr;
+    char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p;
+
+    for (i = 0; i < Numfields; i++) {
+       tmp[0] = 0;
+       switch (pflags[i]) {
+         case P_PID:
+           sprintf(tmp, "%5d ", task->pid);
+           break;
+         case P_PPID:
+           sprintf(tmp, "%5d ", task->ppid);
+           break;
+         case P_EUID:
+           sprintf(tmp, "%4d ", task->euid);
+           break;
+         case P_EUSER:
+           sprintf(tmp, "%-8.8s ", task->euser);
+           break;
+         case P_PCPU:
+           sprintf(tmp, "%4.1f ", (float)task->pcpu / 10);
+           break;
+         case P_LCPU:
+           sprintf(tmp, "%2d ", task->processor);
+           break;
+         case P_PMEM: {
+              unsigned pmem;
+             pmem = task->vm_rss * 1000ULL / kb_main_total;
+             if (pmem > 999) pmem = 999;
+             sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U);
+           }
+           break;
+         case P_TTY: {
+             char outbuf[9];
+             dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV);
+             sprintf(tmp, "%-8.8s ", outbuf);
+           }
+           break;
+         case P_PRI:
+           sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0));
+           break;
+         case P_NICE:
+           sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0));
+           break;
+         case P_PAGEIN:
+           sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0));
+           break;
+         case P_TSIZ:
+           sprintf(tmp, "%5.5s ",
+               scale_k(((task->end_code - task->start_code) / 1024), 5, 1));
+           break;
+         case P_DSIZ:
+           sprintf(tmp, "%5.5s ",
+               scale_k(((task->vsize - task->end_code) / 1024), 5, 1));
+           break;
+         case P_SIZE:
+           sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1));
+           break;
+         case P_TRS:
+           sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1));
+           break;
+         case P_SWAP:
+           sprintf(tmp, "%4.4s ",
+               scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1));
+           break;
+         case P_SHARE:
+           sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1));
+           break;
+         case P_A:
+           sprintf(tmp, "%3.3s ", "NYI");
+           break;
+         case P_WP:
+           sprintf(tmp, "%3.3s ", "NYI");
+           break;
+         case P_DT:
+           sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0));
+           break;
+         case P_RSS:   /* rss, not resident (which includes IO memory) */
+           sprintf(tmp, "%4.4s ",
+               scale_k((task->rss << CL_pg_shift), 4, 1));
+           break;
+         case P_WCHAN:
+           if (!CL_wchan_nout)
+               sprintf(tmp, "%-9.9s ", wchan(task->wchan));
+           else
+               sprintf(tmp, "%-9lx", task->wchan);
+           break;
+         case P_STAT:
+           sprintf(tmp, "%-4.4s ", status(task));
+           break;
+         case P_TIME:
+           t = (task->utime + task->stime) / Hertz;
+           if (Cumulative)
+               t += (task->cutime + task->cstime) / Hertz;
+           sprintf(tmp, "%6.6s ", scale_time(t,6));
+           break;
+         case P_COMMAND:
+           if (!show_cmd && task->cmdline && *(task->cmdline)) {
+               j=0;
+               while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){
+/* #if 0 */ /* This is useless? FIXME */
+                   if (j > 0)
+                       strcat(tmp3, " ");
+/* #endif */
+                   strncat(tmp3, (task->cmdline)[j], 1000);
+                   j++; 
+               }
+               cmdptr = tmp3;
+           } else {
+               cmdptr = task->cmd;
+           }
+           if (strlen(cmdptr) > Maxcmd)
+               cmdptr[Maxcmd - 1] = 0;
+           sprintf(tmp, "%s", cmdptr);
+           tmp3[0]=0;
+           break;
+         case P_FLAGS:
+           sprintf(tmp, "%8lx ", task->flags);
+           break;
+       }
+       strcat(tmp2, tmp);
+    }
+    if (strlen(tmp2) > Cols - 1)
+       tmp2[Cols - 1] = 0;
+
+    /* take care of cases like:
+       perl -e 'foo
+          bar foo bar
+          foo
+          # end of perl script'
+    */
+    for (p=tmp2;*p;++p)
+        if (!isgraph(*p))
+            *p=' ';
+
+    printf("\n%s", tmp2);
+    PUTP(top_clrtoeol);
+}
+
+/*
+ * This is the real program!  Read process info and display it.
+ * One could differentiate options of readproctable2, perhaps it
+ * would be useful to support the PROC_UID and PROC_TTY
+ * as command line options.
+ */
+static void show_procs(void)
+{
+    static proc_t **p_table=NULL;
+    static int proc_flags;
+    int count;
+    int ActualLines;
+    float elapsed_time;
+    static int first=0;
+
+    if (first==0) {
+       proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT;
+       if (monpids_index)
+           proc_flags |= PROC_PID;
+       p_table=readproctab2(proc_flags, p_table, monpids);
+       elapsed_time = get_elapsed_time();
+       do_stats(p_table, elapsed_time, 0);
+       sleep(1);
+       first=1;
+    }
+    if (first && Batch)
+           fputs("\n\n",stdout);
+    /* Display the load averages. */
+    PUTP(ho);
+    PUTP(md);
+    if (show_loadav) {
+       printf("%s", sprint_uptime());
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    p_table=readproctab2(proc_flags, p_table, monpids);
+    /* Immediately find out the elapsed time for the frame. */
+    elapsed_time = get_elapsed_time();
+    /* Display the system stats, calculate percent CPU time
+     * and sort the list. */
+    do_stats(p_table, elapsed_time,1);
+    /* Display the memory and swap space usage. */
+    show_meminfo();
+    if (strlen(Header) + 2 > Cols)
+       Header[Cols - 2] = 0;
+    PUTP(mr);
+    fputs(Header, stdout);
+    PUTP(top_clrtoeol);
+    PUTP(me);
+
+    /*
+     * Finally!  Loop through to find the top task, and display it.
+     * Lather, rinse, repeat.
+     */
+    count = 0;
+    ActualLines = 0;
+    while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) {
+       char Stat;
+
+       Stat = p_table[count]->state;
+
+       if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) &&
+            ( (CurrUser[0] == '\0') ||
+             (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
+
+           /*
+            * Show task info.
+            */
+           show_task_info(p_table[count]);
+           if (!Batch)
+               ActualLines++;
+       }
+       count++;
+    }
+    PUTP(top_clrtobot);
+    PUTP(tgoto(cm, 0, header_lines - 2));
+    fflush(stdout);
+}
+
+
+/*
+ * Finds the current time (in microseconds) and calculates the time
+ * elapsed since the last update. This is essential for computing
+ * percent CPU usage.
+ */
+static float get_elapsed_time(void)
+{
+    struct timeval t;
+    static struct timeval oldtime;
+    struct timezone timez;
+    float elapsed_time;
+
+    gettimeofday(&t, &timez);
+    elapsed_time = (t.tv_sec - oldtime.tv_sec)
+       + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
+    oldtime.tv_sec  = t.tv_sec;
+    oldtime.tv_usec = t.tv_usec;
+    return (elapsed_time);
+}
+
+/*
+ * Reads the memory info and displays it.  Returns the total memory
+ * available, for use in percent memory usage calculations.
+ */
+static void show_meminfo(void)
+{
+    meminfo(); /* read+parse /proc/meminfo */
+    if (show_memory) {
+       printf(
+           "Mem:  %8dK total, %8dK used, %8dK free, %8dK buffers",
+           kb_main_total,
+           kb_main_used,
+           kb_main_free,
+           kb_main_buffers
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+       printf(
+           "Swap: %8dK total, %8dK used, %8dK free, %8dK cached",
+           kb_swap_total,
+           kb_swap_used,
+           kb_swap_free,
+           kb_main_cached
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    PUTP(me);
+    PUTP(top_clrtoeol);
+    putchar('\n');
+}
+
+
+/*
+ * Calculates the number of tasks in each state (running, sleeping, etc.).
+ * Calculates the CPU time in each state (system, user, nice, etc).
+ * Calculates percent cpu usage for each task.
+ */
+static void do_stats(proc_t** p, float elapsed_time, int pass)
+{
+    proc_t *this;
+    int arrindex, total_time, cpumap, i, n = 0;
+    int sleeping = 0, stopped = 0, zombie = 0, running = 0;
+    double system_ticks, user_ticks, nice_ticks, idle_ticks;
+    static int prev_count = 0;
+    int systime, usrtime;
+     /* start with one page as a reasonable allocate size */
+     static int save_history_size =
+         sizeof(long)*1024 / sizeof(struct save_hist);
+     static struct save_hist *save_history;
+     struct save_hist *New_save_hist;
+     if (!save_history)
+       save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+     New_save_hist  = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+
+    /*
+     * Make a pass through the data to get stats.
+     */
+    arrindex = 0;
+    while (p[n]->pid != -1) {
+       this = p[n];
+       switch (this->state) {
+         case 'S':
+         case 'D':
+           sleeping++;
+           break;
+         case 'T':
+           stopped++;
+           break;
+         case 'Z':
+           zombie++;
+           break;
+         case 'R':
+           running++;
+           break;
+         default:
+           /* Don't know how to handle this one. */
+           break;
+        }
+
+       /*
+        * Calculate time in this process.  Time is sum of user time
+        * (usrtime) plus system time (systime).
+        */
+       total_time = this->utime + this->stime;
+        if (arrindex >= save_history_size) {
+            save_history_size *= 2;
+            save_history  = xrealloc(save_history, sizeof(struct save_hist)*save_history_size);
+            New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size);
+        }
+       New_save_hist[arrindex].ticks = total_time;
+       New_save_hist[arrindex].pid = this->pid;
+       systime = this->stime;
+       usrtime = this->utime;
+       New_save_hist[arrindex].stime = systime;
+       New_save_hist[arrindex].utime = usrtime;
+
+       /* find matching entry from previous pass */
+       for (i = 0; i < prev_count; i++) {
+           if (save_history[i].pid == this->pid) {
+               total_time -= save_history[i].ticks;
+               systime -= save_history[i].stime;
+               usrtime -= save_history[i].utime;
+
+               i = prev_count;
+           }
+       }
+
+       /*
+        * Calculate percent cpu time for this task.
+        */
+       this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time;
+       if (this->pcpu > 999)
+           this->pcpu = 999;
+
+       arrindex++;
+       n++;
+    }
+
+    /*
+     * Display stats.
+     */
+    if (pass > 0 && show_stats) {
+       printf("%d processes: %d sleeping, %d running, %d zombie, "
+              "%d stopped",
+              n, sleeping, running, zombie, stopped);
+       PUTP(top_clrtoeol);
+       putchar('\n');
+       four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks);
+       printf("CPU states:"
+           " %# 5.1f%% user, %# 5.1f%% system,"
+           " %# 5.1f%% nice, %# 5.1f%% idle",
+            user_ticks,
+            system_ticks,
+            nice_ticks,
+            idle_ticks
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    /*
+     * Save this frame's information.
+     */
+    for (i = 0; i < n; i++) {
+       /* copy the relevant info for the next pass */
+       save_history[i].pid = New_save_hist[i].pid;
+       save_history[i].ticks = New_save_hist[i].ticks;
+       save_history[i].stime = New_save_hist[i].stime;
+       save_history[i].utime = New_save_hist[i].utime;
+    }
+    free(New_save_hist);
+
+    prev_count = n;
+    qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+}
+
+
+/*
+ * Process keyboard input during the main loop
+ */
+static void do_key(char c)
+{
+    int numinput, i;
+    char rcfile[MAXNAMELEN];
+    FILE *fp;
+
+    /*
+     * First the commands which don't require a terminal mode switch.
+     */
+    if (c == 'q')
+       end(0);
+    else if (c == ' ')
+        return;
+    else if (c == 12) {
+       clear_screen();
+       return;
+    } else if (c == 'I') {
+       Irixmode=(Irixmode) ? 0 : 1;
+       return;
+    }
+
+    /*
+     * Switch the terminal to normal mode.  (Will the original
+     * attributes always be normal?  Does it matter?  I suppose the
+     * shell will be set up the way the user wants it.)
+     */
+    if (!Batch) tcsetattr(0, TCSANOW, &Savetty);
+
+    /*
+     * Handle the rest of the commands.
+     */
+    switch (c) {
+      case '?':
+      case 'h':
+       PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr);
+       printf("Proc-Top Revision 1.2");
+       PUTP(me); putchar('\n');
+       printf("Secure mode ");
+       PUTP(md);
+       fputs(Secure ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("; cumulative mode ", stdout);
+       PUTP(md);
+       fputs(Cumulative ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("; noidle mode ", stdout);
+       PUTP(md);
+       fputs(Noidle ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("\n\n", stdout);
+       printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
+       if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+       (void) getchar();
+       break;
+      case 'i':
+       Noidle = !Noidle;
+       SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
+       break;
+      case 'u':
+       SHOWMESSAGE(("Which User (Blank for All): "));
+       strcpy(CurrUser,getstr());
+       break;
+      case 'k':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't kill in secure mode"));
+       else {
+           int pid, signo;
+           PUTP(md);
+           SHOWMESSAGE(("PID to kill: "));
+           pid = getint();
+           if (pid == BAD_INPUT)
+               break;
+           PUTP(top_clrtoeol);
+           SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid));
+           PUTP(me);
+           signo = getsig();
+           /* FIXME: -1 may mean an unknown signal */
+           if (signo == -1)
+               signo = SIGTERM;
+           if (kill(pid, signo))
+               SHOWMESSAGE(("\aKill of PID %d with %d failed: %s",
+                            pid, signo, strerror(errno)));
+       }
+       break;
+      case 'l':
+       SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off"));
+       if (show_loadav) {
+           show_loadav = 0;
+           header_lines--;
+       } else {
+           show_loadav = 1;
+           header_lines++;
+       }
+       Numfields = make_header();
+       break;
+      case 'm':
+       SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off"));
+       if (show_memory) {
+           show_memory = 0;
+           header_lines -= 2;
+       } else {
+           show_memory = 1;
+           header_lines += 2;
+       }
+       Numfields = make_header();
+       break;
+      case 'M':
+        SHOWMESSAGE(("Sort by memory usage"));
+       sort_type = S_MEM;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)mem_sort);
+       break;
+      case 'n':
+      case '#':
+       printf("Processes to display (0 for unlimited): ");
+       numinput = getint();
+       if (numinput != -1) {
+           Display_procs = numinput;
+           window_size(0);
+       }
+       break;
+      case 'r':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't renice in secure mode"));
+       else {
+           int pid, val;
+
+           printf("PID to renice: ");
+           pid = getint();
+           if (pid == BAD_INPUT)
+               break;
+           PUTP(tgoto(cm, 0, header_lines - 2));
+           PUTP(top_clrtoeol);
+           printf("Renice PID %d to value: ", pid);
+           val = getint();
+           if (val == BAD_INPUT)
+               val = 10;
+           if (setpriority(PRIO_PROCESS, pid, val))
+               SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s",
+                            pid, val, strerror(errno)));
+       }
+       break;
+      case 'P':
+        SHOWMESSAGE(("Sort by CPU usage"));
+       sort_type = S_PCPU;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)pcpu_sort);
+       break;
+      case 'A':
+       SHOWMESSAGE(("Sort by age"));
+       sort_type = S_AGE;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)age_sort);
+       break;
+      case 'N':
+       SHOWMESSAGE(("Sort numerically by pid"));
+       sort_type = S_NONE;
+       reset_sort_options();
+       break;
+    case 'c':
+        show_cmd = !show_cmd;
+       SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line"));
+       break;
+      case 'S':
+       Cumulative = !Cumulative;
+       SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off"));
+       if (Cumulative)
+           headers[22][1] = 'C';
+       else
+           headers[22][1] = ' ';
+       Numfields = make_header();
+       break;
+      case 's':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't change delay in secure mode"));
+       else {
+           double tmp;
+           printf("Delay between updates: ");
+           tmp = getfloat();
+           if (!(tmp < 0))
+               Sleeptime = tmp;
+       }
+       break;
+      case 't':
+       SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off"));
+       if (show_stats) {
+           show_stats = 0;
+           header_lines -= 2;
+       } else {
+           show_stats = 1;
+           header_lines += 2;
+       }
+       Numfields = make_header();
+       break;
+      case 'T':
+       SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : ""));
+       sort_type = S_TIME;
+       reset_sort_options();
+       register_sort_function( -1, (cmp_t)time_sort);  
+       break;
+      case 'f':
+      case 'F':
+       change_fields();
+       break;
+      case 'o':
+      case 'O':
+       change_order();
+       break;
+      case 'W':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't write configuration in secure mode"));
+       else {
+           if (getenv("HOME")) {
+               strcpy(rcfile, getenv("HOME"));
+             strcat(rcfile, "/");
+              strcat(rcfile, RCFILE);
+              fp = fopen(rcfile, "w");
+              if (fp != NULL) {
+                  fprintf(fp, "%s\n", Fields);
+                 i = (int) Sleeptime;
+                  if (i < 2)
+                      i = 2;
+                  if (i > 9)
+                      i = 9;
+                  fprintf(fp, "%d", i);
+                  if (Secure)
+                      fprintf(fp, "%c", 's');
+                  if (Cumulative)
+                      fprintf(fp, "%c", 'S');
+                  if (!show_cmd)
+                             fprintf(fp, "%c", 'c');
+                  if (Noidle)
+                      fprintf(fp, "%c", 'i');
+                  if (!show_memory)
+                      fprintf(fp, "%c", 'm');
+                  if (!show_loadav)
+                             fprintf(fp, "%c", 'l');
+                  if (!show_stats)
+                      fprintf(fp, "%c", 't');
+                  if (!Irixmode)
+                      fprintf(fp, "%c", 'I');
+                  fprintf(fp, "\n");
+                 fclose(fp);
+                 SHOWMESSAGE(("Wrote configuration to %s", rcfile));
+              } else {
+                  SHOWMESSAGE(("Couldn't open %s", rcfile));
+              }
+            } else {
+                SHOWMESSAGE(("Couldn't get $HOME -- not saving"));
+            }
+          }
+        break;
+      default:
+       SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c));
+    }
+
+    /*
+     * Return to raw mode.
+     */
+    if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+    return;
+}
+
+
+/*#####################################################################
+ *#######   A readproctable function that uses already allocated  #####
+ *#######   table entries.                                        #####
+ *#####################################################################
+ */
+#define Do(x) (flags & PROC_ ## x)
+
+static proc_t** readproctab2(int flags, proc_t** tab, ...) {
+    PROCTAB* PT = NULL;
+    static proc_t *buff;
+    int n = 0;
+    static int len = 0;
+    va_list ap;
+
+    va_start(ap, tab);         /* pass through args to openproc */
+    if (Do(UID)) {
+       /* temporary variables to ensure that va_arg() instances
+        * are called in the right order
+        */
+       uid_t* u;
+       int i;
+
+       u = va_arg(ap, uid_t*);
+       i = va_arg(ap, int);
+       PT = openproc(flags, u, i);
+    }
+    else if (Do(PID)) {
+       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+       /* work around a bug in openproc() */
+       PT->procfs = NULL;
+       /* share some process time, since we skipped opendir("/proc") */
+       usleep (50*1000);
+    }
+    else if (Do(TTY) || Do(STAT))
+       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+    else
+       PT = openproc(flags);
+    va_end(ap);
+    buff = (proc_t *) 1;
+    while (n<len && buff) {     /* read table: (i) already allocated chunks */
+       if (tab[n]->cmdline) {
+           free((void*)*tab[n]->cmdline);
+           tab[n]->cmdline = NULL;
+       }
+        buff = readproc(PT, tab[n]);
+       if (buff) n++;
+    }
+    if (buff) {
+       do {               /* (ii) not yet allocated chunks */
+           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+           buff = readproc(PT, NULL);            /* final null to terminate */
+           if(buff) tab[n]=buff;
+           len++;
+           n++;
+       } while (buff);                   /* stop when NULL reached */
+       tab[n-1] = xcalloc(NULL, sizeof (proc_t));
+       tab[n-1]->pid=-1;                /* Mark end of Table */
+    } else {
+       if (n == len) {
+           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
+           tab[n] = xcalloc(NULL, sizeof (proc_t));
+           len++;
+       }
+       tab[n]->pid=-1;    /* Use this instead of NULL when not at the end of */
+    }                   /* the allocated space */
+    closeproc(PT);
+    return tab;
+}
diff --git a/oldtop.h b/oldtop.h
new file mode 100644 (file)
index 0000000..e7cc2e2
--- /dev/null
+++ b/oldtop.h
@@ -0,0 +1,228 @@
+/*
+ * top.h header file 1996/05/18, 
+ *
+ * function prototypes, global data definitions and string constants.
+ */
+
+static proc_t** readproctab2(int flags, proc_t** tab, ...);
+static void parse_options(char *Options, int secure);
+static void get_options(void);
+static void error_end(int rno);
+static void end(int signo);
+static void stop(int signo);
+static void window_size(int signo);
+static int make_header(void);
+static char *getstr(void);
+static int getsig(void);
+static float getfloat(void);
+static int time_sort(proc_t **P, proc_t **Q);
+static int pcpu_sort(proc_t **P, proc_t **Q);
+static int mem_sort(proc_t **P, proc_t **Q);
+static int age_sort(proc_t **P, proc_t **Q);
+static void show_fields(void);
+static void change_order(void);
+static void change_fields(void);
+static void show_task_info(proc_t *task);
+static void show_procs(void);
+static float get_elapsed_time(void);
+static void show_meminfo(void);
+static void do_stats(proc_t** p, float elapsed_time, int pass);
+static void do_key(char c);
+
+
+/* configurable field display support */
+
+static int pflags[30];
+static int Numfields;
+
+
+       /* Name of the config file (in $HOME)  */
+#ifndef RCFILE
+#define RCFILE         ".toprc"
+#endif
+
+#ifndef SYS_TOPRC
+#define SYS_TOPRC      "/etc/toprc"
+#endif
+
+#define MAXLINES 2048
+#define MAXNAMELEN 1024
+
+/* this is what procps top does by default, so let's do this, if nothing is
+ * specified
+ */
+#ifndef DEFAULT_SHOW
+/*                       0         1         2         3 */
+/*                       0123456789012345678901234567890 */
+#define DEFAULT_SHOW    "AbcDgHIjklMnoTP|qrsuzyV{EFWX"
+#endif
+static char Fields[256] = "";
+
+
+/* This structure stores some critical information from one frame to
+   the next. mostly used for sorting. Added cumulative and resident fields. */
+struct save_hist {
+    int ticks;
+    int pid;
+    int pcpu;
+    int utime;
+    int stime;
+};
+
+       /* The original terminal attributes. */
+static struct termios Savetty;
+       /* The new terminal attributes. */
+static struct termios Rawtty;
+       /* Cached termcap entries. */
+static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr;
+       /* Current window size.  Note that it is legal to set Display_procs
+          larger than can fit; if the window is later resized, all will be ok.
+          In other words: Display_procs is the specified max number of
+          processes to display (zero for infinite), and Maxlines is the actual
+          number. */
+static int Lines, Cols, Maxlines, Display_procs;
+       /* Maximum length to display of the command line of a process. */
+static unsigned Maxcmd;
+
+       /* Controls how long we sleep between screen updates.  Accurate to
+          microseconds. */
+static float Sleeptime = 5;
+       /* for opening/closing the system map */
+static int psdbsucc = 0;
+       /* Mode flags. */
+static int Irixmode = 1;
+static int Secure = 0;
+static int Cumulative = 0;
+static int Noidle = 0;
+
+static int CPU_states = 0;
+static char CurrUser[BUFSIZ];
+
+static int CL_pg_shift = (PAGE_SHIFT - 10);
+static int CL_wchan_nout = -1;
+
+static int show_stats = 1;    /* show status summary */
+static int show_memory = 1;   /* show memory summary */
+static int show_loadav = 1;   /* show load average and uptime */
+static int show_cmd = 1;      /* show command name instead of commandline */
+
+static pid_t monpids[520]; /* randomly chosen value */
+static const int monpids_max = sizeof(monpids)/sizeof(pid_t);
+static int monpids_index = 0;
+
+static int Loops = -1;        /* number of iterations. -1 loops forever */
+static int Batch = 0;         /* batch mode. Collect no input, dumb output */
+
+/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */
+enum {
+    S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE
+};
+/* default sorting by CPU% */ 
+static int sort_type = S_PCPU;
+
+/* flags for each possible field. At the moment up to 30 are supported */
+enum {
+    P_PID, P_PPID, P_EUID, P_EUSER,
+    P_PCPU, P_PMEM, P_TTY, P_PRI,
+    P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ,
+    P_SIZE, P_TRS, P_SWAP, P_SHARE,
+    P_A, P_WP, P_DT, P_RSS,
+    P_WCHAN, P_STAT, P_TIME, P_COMMAND,
+    P_LCPU, P_FLAGS, P_END
+};
+/* corresponding headers */
+static char *headers[] =
+{
+    "  PID ", " PPID ", " UID ",
+    "USER     ", "%CPU ", "%MEM ",
+    "TTY      ", "PRI ", " NI ",
+    "PAGEIN ", "TSIZE ", "DSIZE ",
+    " SIZE ", " TRS ", "SWAP ",
+    "SHARE ", "  A ", " WP ",
+    "  D ", " RSS ", "WCHAN     ",
+    "STAT ", "  TIME ", "COMMAND",
+    "LC ",
+    "   FLAGS "
+};
+/* corresponding field desciptions */
+static char *headers2[] =
+{
+    "Process Id", "Parent Process Id", "User Id",
+    "User Name", "CPU Usage", "Memory Usage",
+    "Controlling tty", "Priority", "Nice Value",
+    "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)",
+    "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb",
+    "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages",
+    "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function",
+    "Process Status", "CPU Time", "Command",
+    "Last used CPU (expect this to change regularly)",
+    "Task Flags (see linux/sched.h)"
+};
+
+       /* The header printed at the top of the process list.*/
+static char Header[MAXLINES];
+
+       /* The response to the interactive 'h' command. */
+#define HELP_SCREEN "\
+Interactive commands are:\n\
+\n\
+space\tUpdate display\n\
+^L\tRedraw the screen\n\
+fF\tadd and remove fields\n\
+oO\tChange order of displayed fields\n\
+h or ?\tPrint this list\n\
+S\tToggle cumulative mode\n\
+i\tToggle display of idle proceses\n\
+I\tToggle between Irix and Solaris views (SMP-only)\n\
+c\tToggle display of command name/line\n\
+l\tToggle display of load average\n\
+m\tToggle display of memory information\n\
+t\tToggle display of summary information\n\
+k\tKill a task (with any signal)\n\
+r\tRenice a task\n\
+N\tSort by pid (Numerically)\n\
+A\tSort by age\n\
+P\tSort by CPU usage\n\
+M\tSort by resident memory usage\n\
+T\tSort by time / cumulative time\n\
+u\tShow only a specific user\n\
+n or #\tSet the number of process to show\n\
+s\tSet the delay in seconds between updates\n\
+W\tWrite configuration file ~/.toprc\n\
+q\tQuit"
+#define SECURE_HELP_SCREEN "\
+Interactive commands available in secure mode are:\n\
+\n\
+space\tUpdate display\n\
+^L\tRedraw the screen\n\
+fF\tadd and remove fields\n\
+h or ?\tPrint this list\n\
+S\tToggle cumulative mode\n\
+i\tToggle display of idle proceses\n\
+c\tToggle display of command name/line\n\
+l\tToggle display of load average\n\
+m\tToggle display of memory information\n\
+t\tToggle display of summary information\n\
+n or #\tSet the number of process to show\n\
+u\tShow only a specific user\n\
+oO\tChange order of displayed fields\n\
+W\tWrite configuration file ~/.toprc\n\
+q\tQuit"
+
+       /* Number of lines needed to display the header information. */
+static int header_lines;
+
+/* ############## Some Macro definitions for screen handling ######### */
+       /* String to use in error messages. */
+#define PROGNAME "top"
+       /* Clear the screen. */
+#define clear_screen() \
+           printf("%s", cl)
+       /* Show an error in the context of the spiffy full-screen display. */
+#define SHOWMESSAGE(x) do {                    \
+           printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr);       \
+           printf x;                                   \
+           printf ("%s",me);                           \
+           fflush(stdout);                             \
+           sleep(2);                                   \
+       } while (0)
diff --git a/top.c b/top.c
index 2faaf0cfbc40c857f28b4b2a17bcf05bd0f2c8d0..95ad28936546a50b16bb105960c3a598c8b79220 100644 (file)
--- a/top.c
+++ b/top.c
+/* top.c - Source file:         show Linux processes */
 /*
- * top.c              - show top CPU processes
- *
- * Copyright (c) 1992 Branko Lankester
- * Copyright (c) 1992 Roger Binns
- * Copyright (c) 1997 Michael K. Johnson
- *
- * Snarfed and HEAVILY modified in december 1992 for procps
- * by Michael K. Johnson, johnsonm@sunsite.unc.edu.
- *
- * Modified Michael K. Johnson's ps to make it a top program.
- * Also borrowed elements of Roger Binns kmem based top program.
- * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com)
- * 1/93
- *
- * Modified by Michael K. Johnson to be more efficient in cpu use
- * 2/21/93
- *
- * Changed top line to use uptime for the load average.  Also
- * added SIGTSTP handling.  J. Cowley, 19 Mar 1993.
- *
- * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu)
- * 1994/04/02.  Secure mode added.  "d" option added.  Argument parsing
- * improved.  Switched order of tick display to user, system, nice, idle,
- * because it makes more sense that way.  Style regularized (to K&R,
- * more or less).  Cleaned up much throughout.  Added cumulative mode.
- * Help screen improved.
- *
- * Fixed kill buglet brought to my attention by Rob Hooft.
- * Problem was mixing of stdio and read()/write().  Added
- * getnum() to solve problem.
- * 12/30/93 Michael K. Johnson
- *
- * Added toggling output of idle processes via 'i' key.
- * 3/29/94 Gregory K. Nickonov
- *
- * Fixed buglet where rawmode wasn't getting restored.
- * Added defaults for signal to send and nice value to use.
- * 5/4/94 Jon Tombs.
- *
- * Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
- * Merged previous changes to 0.8 into 0.95.
- * Allowed the use of symbolic names (e.g., "HUP") for signal input.
- * Rewrote getnum() into getstr(), getint(), getsig(), etc.
- * 
- * Modified 1995  Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> 
- * added kmem top functionality (configurable fields)
- * configurable order of process display
- * Added options for dis/enabling uptime, statistics, and memory info.
- * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields)
- *
- * Modified 1996/05/18 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
- * Use of new interface and general cleanup. The code should be far more
- * readable than before.
- *
- * Modified 1996/06/25 Zygo Blaxell <zblaxell@ultratech.net>
- * Added field scaling code for programs that run more than two hours or
- * take up more than 100 megs.  We have lots of both on our production line.
- *
- * Modified 1998/02/21 Kirk Bauer <kirk@kaybee.org>
- * Added the 'u' option to display only a selected user... plus it will
- * take into account that not all 20 top processes are actually shown,
- * so it can fit more onto the screen.  I think this may help the
- * 'don't show idle' mode, but I'm not sure.
- *
- * Modified 1997/07/27 & 1999/01/27 Tim Janik <timj@gtk.org>
- * added `-p' option to display specific process ids.
- * process sorting is by default disabled in this case.
- * added `N' and `A' keys to sort the tasks Numerically by pid or
- * sort them by Age (newest first).
- *
- * Modified 1999/10/22 Tim Janik <timj@gtk.org>
- * miscellaneous minor fixes, including "usage: ..." output for
- * unrecognized options.
- *
- * Modified 2000/02/07 Jakub Jelinek <jakub@redhat.com>
- * Only load System.map when we are going to display WCHAN.
- * Show possible error messages from that load using SHOWMESSAGE.
- *
- * Modified 2000/07/10 Michael K. Johnson <johnsonm@redhat.com>
- * Integrated a patch to display SMP information.
+ * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
+ *    All rights reserved.
+ * 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
  */
-
+#include <asm/param.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <curses.h>
 #include <errno.h>
-#include <sys/types.h>
+#include <signal.h>
+#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <unistd.h>
 #include <string.h>
-#include <fcntl.h>
-#include <libintl.h>
-#include <time.h>
-#include <sys/ioctl.h>
-#include <pwd.h>
-#include <termcap.h>
+#include <term.h>
 #include <termios.h>
-#include <signal.h>
-#include <sys/time.h>
-#include <sys/resource.h>
-#include <ctype.h>
-#include <setjmp.h>
-#include <stdarg.h>
-#include <sys/param.h>
-#include <locale.h>
-
-#include "proc/sysinfo.h"
+#include <time.h>
+#include <unistd.h>
+#include <values.h>
+   /*
+      I am listing precisely why each header is needed because of the
+      construction of libproc -- separate header files may not always be
+      available and function names are not normalized.  We have avoided
+      some library routine(s) as overkill and have subsumed some others.
+   */
+        /* need: 1 define + dev_to_tty */
+#include "proc/devname.h"
+        /* need: (ksym.c) open_psdb_message, wchan, close_psdb (redhat only) */
 #include "proc/procps.h"
-#include "proc/whattime.h"
-#include "proc/sig.h"
-#include "proc/version.h"
+        /* need: 2 types + openproc, readproc, closeproc */
 #include "proc/readproc.h"
+#ifdef UGH_ITS_4_RH
+        /* need: get_signal2 */
+#include "proc/signals.h"
+#else
+        /* need: signal_name_to_number */
+#include "proc/sig.h"
+#endif
+        /* need: status */
 #include "proc/status.h"
-#include "proc/devname.h"
-#include "proc/compare.h"
+        /* need: meminfo stuff */
+#include "proc/sysinfo.h"
+        /* need: procps_version */
+#include "proc/version.h"
+        /* need: sprint_uptime */
+#include "proc/whattime.h"
 
-#define PUTP(x) (tputs(x,1,putchar))
-#define BAD_INPUT -30
+#include "top.h"
+
+
+/*######  Miscellaneous global stuff  ####################################*/
+
+        /* The original and new terminal attributes */
+static struct termios Savedtty,
+                      Rawtty;
+static int  Ttychanged = 0;
+
+        /* Program names used in error messages and 'rc' file names */
+static char *Myname,
+            *Myrealname;
+
+        /* The Name of the config file(s), dynamically constructed */
+static char  RCfile     [OURPATHSZ],
+             RCfile_Sys [SMLBUFSIZ];
+
+        /* The run-time acquired page size */
+static int  Page_size;
+
+        /* SMP and Irix/Solaris mode support */
+static int  Cpu_tot,
+           *Cpu_map;
+
+        /* Specific process id monitoring support */
+static pid_t  Monpids [MONPIDMAX] = { 0 };
+static int    Monpidsidx = 0;
+
+        /* A postponed error message */
+static char  Msg_delayed [SMLBUFSIZ];
+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) */
+
+        /* 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 */
+            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;
+
+        /* These are our gosh darn 'Fields' !
+           They MUST be kept in sync with pflags !! */
+static FTAB_t  Fieldstab[] = {
+/*   head           fmts     width   scale   sort    desc
+     -----------    -------  ------  ------  ------  ---------------------- */
+   { "  PID ",      "%5d ",     -1,     -1,  S_PID,  "Process Id"           },
+   { " PPID ",      "%5d ",     -1,     -1,     -1,  "Parent Process Pid"   },
+   { " UID ",       "%4d ",     -1,     -1,     -1,  "User Id"              },
+   { "USER     ",   "%-8.8s ",  -1,     -1,  S_USR,  "User Name"            },
+   { "GROUP    ",   "%-8.8s ",  -1,     -1,     -1,  "Group Name"           },
+   { "TTY      ",   "%-8.8s ",   8,     -1,  S_TTY,  "Controlling Tty"      },
+   { " PR ",        "%3ld ",    -1,     -1,     -1,  "Priority"             },
+   { " NI ",        "%3ld ",    -1,     -1,     -1,  "Nice value"           },
+   { "#C ",         "%2d ",     -1,     -1,     -1,  "Last used cpu (SMP)"  },
+   { "%CPU ",       "%#4.1f ",  -1,     -1,  S_CPU,  "CPU usage"            },
+   { "  TIME ",     "%6.6s ",    6,     -1,  S_TME,  "CPU Time"             },
+   { "   TIME+  ",  "%9.9s ",    9,     -1,  S_TME,  "CPU Time, hundredths" },
+   { "%MEM ",       "%#4.1f ",  -1,     -1,  S_MEM,  "Memory usage (RES)"   },
+   { " VIRT ",      "%5.5s ",    5,  SK_Kb,     -1,  "Virtual Image (kb)"   },
+   { "SWAP ",       "%4.4s ",    4,  SK_Kb,     -1,  "Swapped size (kb)"    },
+   { " RES ",       "%4.4s ",    4,  SK_Kb,  S_MEM,  "Resident size (kb)"   },
+   { "CODE ",       "%4.4s ",    4,  SK_Kb,     -1,  "Code size (kb)"       },
+   { "DATA ",       "%4.4s ",    4,  SK_Kb,     -1,  "Data+Stack size (kb)" },
+   { " 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"    },
+   { "STA ",        "%3.3s ",   -1,     -1,     -1,  "Process Status"       },
+   /** 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
+   { "Flags    ",   "%08lX ",   -1,     -1,     -1,  "Task Flags <sched.h>" }
+#else
+   { "Flags    ",   "%08lx ",   -1,     -1,     -1,  "Task Flags <sched.h>" }
+#endif
+};
+
+        /* 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] = "",
+             Cap_clr_eol    [CAPBUFSIZ] = "",
+             Cap_clr_eos    [CAPBUFSIZ] = "",
+             Cap_clr_scr    [CAPBUFSIZ] = "",
+             Cap_curs_norm  [CAPBUFSIZ] = "",
+             Cap_curs_huge  [CAPBUFSIZ] = "",
+             Cap_home       [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
+
+#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
+
+\f
+/*######  Sort callbacks  ################################################*/
+
+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 (!(*P)->cmdline) return SORT_lt;
+      if (!(*Q)->cmdline) return SORT_gt;
+      if ( 0 > strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) )
+         return SORT_lt;
+      if ( 0 < strncmp((*P)->cmdline[0], (*Q)->cmdline[0], (unsigned)Max_cmd) )
+         return SORT_gt;
+   } else {
+      /* this part also handles the compare if both are kernel threads */
+      if ( 0 > strcmp((*P)->cmd, (*Q)->cmd) ) return SORT_lt;
+      if ( 0 < strcmp((*P)->cmd, (*Q)->cmd) ) return SORT_gt;
+   }
+   return 0;
+}
 
-#include "top.h"  /* new header for top specific things */
 
-static int *cpu_mapping;
-static int nr_cpu;
+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;
+   return 0;
+}
 
-/*#######################################################################
- *####  Startup routines: parse_options, get_options,      ##############
- *####                    setup_terminal and main          ##############
- *#######################################################################
- */
 
-      /*
-       * parse the options string as read from the config file(s).
-       * if top is in secure mode, disallow changing of the delay time between
-       * screen updates.
-       */
-static void parse_options(char *Options, int secure)
+static int sort_mem (proc_t **P, proc_t **Q)
 {
-    int i;
-    for (i = 0; i < strlen(Options); i++) {
-       switch (Options[i]) {
-         case '2':
-         case '3':
-         case '4':
-         case '5':
-         case '6':
-         case '7':
-         case '8':
-         case '9':
-           if (!secure)
-               Sleeptime = (float) Options[i] - '0';
-           break;
-         case 'S':
-           Cumulative = 1;
-           headers[22][1] = 'C';
-           break;
-         case 's':
-           Secure = 1;
-           break;
-         case 'i':
-           Noidle = 1;
-           break;
-         case 'm':
-           show_memory = 0;
-           header_lines -= 2;
-           break;
-         case 'M':
-           sort_type = S_MEM;
-           reset_sort_options();
-           register_sort_function( -1, (cmp_t)mem_sort);
-           break;
-         case 'l':
-           show_loadav = 0;
-           header_lines -= 1;
-           break;
-         case 'P':
-           sort_type = S_PCPU;
-           reset_sort_options();
-           register_sort_function( -1, (cmp_t)pcpu_sort);
-           break;
-         case 'N':
-           sort_type = S_NONE;
-           reset_sort_options();
-           break;
-         case 'A':
-           sort_type = S_AGE;
-           reset_sort_options();
-           register_sort_function( -1, (cmp_t)age_sort);
-           break;
-         case 't':
-           show_stats = 0;
-           header_lines -= 2;
-           break;
-         case 'T':
-           sort_type = S_TIME;
-           reset_sort_options();
-           register_sort_function( -1, (cmp_t)time_sort);
-           break;
-         case 'c':
-           show_cmd = 0;
-           break;
-         case '\n':
-           break;
-         case 'I': 
-           Irixmode = 0;
-           break;
-         default:
-           fprintf(stderr, "Wrong configuration option %c\n", i);
-           exit(1);
-           break;
-       }
-    }
+   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;
 }
 
-/* 
- * Read the configuration file(s). There are two files, once SYS_TOPRC 
- * which should only contain the secure switch and a sleeptime
- * value iff ordinary users are to use top in secure mode only.
- * 
- * The other file is $HOME/RCFILE. 
- * The configuration file should contain two lines (any of which may be
- *  empty). The first line specifies the fields that are to be displayed
- * in the order you want them to. Uppercase letters specify fields 
- * displayed by default, lowercase letters specify fields not shown by
- * default. The order of the letters in this line corresponds to the 
- * order of the displayed fileds.
- *
- * all Options but 'q' can be read from this config file
- * The delay time option syntax differs from the commandline syntax:
- *   only integer values between 2 and 9 seconds are recognized
- *   (this is for standard configuration, so I think this should do).
- *
- * usually this file is not edited by hand, but written from top using
- * the 'W' command. 
- */
 
-static void get_options(void)
+static int sort_pid (proc_t **P, proc_t **Q)
 {
-    FILE *fp;
-    char *pt;
-    char *rcfile = NULL;        /* path to rc file... */
-    char *home = NULL;          /* path of user's home directory... */
-    size_t home_length = 0;     /* length of path... */
-    char Options[256] = "";
-    int i;
-
-    nr_cpu = sysconf (_SC_NPROCESSORS_ONLN);
-    if (nr_cpu < 1) nr_cpu = 1;
-    cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu);
-    /* read cpuname */
-    for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i;
-    header_lines = 6 + nr_cpu;
-    fp = fopen(SYS_TOPRC, "r");
-    if (fp != NULL) {
-       fgets(Options, 254, fp);
-       fclose(fp);
-    }
-    parse_options(Options, 0);
-    strcpy(Options, "");
-
-    if ( (home = getenv("HOME")) != NULL) {
-          home_length = strlen(home);
-    }
-
-    if ( (rcfile = malloc(home_length + strlen(RCFILE) + 2))) {
-        if (home != NULL) {
-            strcpy(rcfile, home);
-            strcat(rcfile, "/");
-        }
-        strcat(rcfile, RCFILE);
-        fp = fopen(rcfile, "r");
-        if (fp == NULL) {
-            strcpy(Fields, DEFAULT_SHOW);
-        } else {
-            if (fgets(Fields, 254, fp) != NULL) {
-                pt = strchr(Fields, '\n');
-                if (pt) *pt = 0;
-            }
-            fgets(Options, 254, fp);
-            fclose(fp);
-        }
+   if ( (*P)->pid < (*Q)->pid ) return SORT_lt;
+   if ( (*P)->pid > (*Q)->pid ) return SORT_gt;
+   return 0;
+}
+
 
-        free(rcfile);
-    }    
-    parse_options(Options, getuid()? Secure : 0);
+static int sort_tme (proc_t **P, proc_t **Q)
+{
+   if (Show_ctimes) {
+      if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+        < ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return SORT_lt;
+      if ( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime)
+        > ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return SORT_gt;
+   } else {
+      if ( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+         return SORT_lt;
+      if ( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+         return SORT_gt;
+   }
+   return 0;
 }
 
-/*
- * Set up the terminal attributes.
- */
-static void setup_terminal(void)
+
+static int sort_tty (proc_t **P, proc_t **Q)
 {
-    char *termtype;
-    struct termios newtty;
-    if (!Batch)
-       termtype = getenv("TERM");
-    else 
-       termtype = "dumb";
-    if (!termtype) {
-       /* In theory, $TERM should never not be set, but in practice,
-          some gettys don't.  Fortunately, vt100 is nearly always
-          correct (or pretty close). */
-       termtype = "VT100";
-       /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */
-       /* exit(1); */
-    }
-
-    /*
-     * Get termcap entries and window size.
-     */
-    if(tgetent(NULL, termtype) != 1) {
-       fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n",
-               termtype);
-       exit(1);
-    }
-
-    cm = tgetstr("cm", 0);
-    top_clrtobot = tgetstr("cd", 0);
-    cl = tgetstr("cl", 0);
-    top_clrtoeol = tgetstr("ce", 0);
-    ho = tgetstr("ho", 0);
-    md = tgetstr("md", 0);
-    mr = tgetstr("mr", 0);
-    me = tgetstr("me", 0);
-
-
-    if (Batch) return; /* the rest doesn't apply to batch mode */
-    if (tcgetattr(0, &Savetty) == -1) {
-        perror(PROGNAME ": tcgetattr() failed");
-       error_end(errno);
-    }
-    newtty = Savetty;
-    newtty.c_lflag &= ~ICANON;
-    newtty.c_lflag &= ~ECHO;
-    newtty.c_cc[VMIN] = 1;
-    newtty.c_cc[VTIME] = 0;
-    if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) {
-       printf("cannot put tty into raw mode\n");
-       error_end(1);
-    }
-    tcgetattr(0, &Rawtty);
+   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);
 }
 
-static int parseint(const char *src, const char *err)
+
+static int sort_usr (proc_t **P, proc_t **Q)
 {
-    char *endp;
-    int num;
-    int len;
-    num = strtol(src, &endp, 0);
-    if (*endp == '\0') return num;
-    /* also accept prefixes of: infinite, infinity, maximum, all */
-    len = strlen(src);
-    if(len<1) goto fail;
-    if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX;
-    if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX;
-    if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX;
-    if(len<4 && !strncmp(src,"all"     ,len)) return INT_MAX;
-fail:
-    fprintf(stderr, err, src);
-    exit(1);
+   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);
 }
 
-static double parseflt(const char *src, const char *err)
+\f
+/*######  Tiny useful routine(s)  ########################################*/
+
+        /*
+         * This routine isolates ALL user input and ensures that we
+         * wont be mixing I/O from stdio and low-level read() requests */
+static int chin (int ech, char *buf, unsigned cnt)
 {
-    char *endp;
-    double num;
-    int len;
-    num = strtod(src, &endp);
-    if (*endp == '\0') return num;
-    /* also accept prefixes of: infinite, infinity, maximum, all */
-    len = strlen(src);
-    if(len<1) goto fail;
-    if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX;
-    if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX;
-    if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX;
-    if(len<4 && !strncmp(src,"all"     ,len)) return (double)INT_MAX;
-fail:
-    fprintf(stderr, err, src);
-    exit(1);
+   int rc;
+
+   fflush(stdout);
+   if (!ech)
+      rc = read(STDIN_FILENO, buf, cnt);
+   else {
+      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+      rc = read(STDIN_FILENO, buf, cnt);
+      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Rawtty);
+   }
+      /* may be the beginning of a lengthy escape sequence  */
+   tcflush(STDIN_FILENO, TCIFLUSH);
+   return rc;                   /* note: we do NOT produce a vaid 'string' */
 }
 
-int main(int argc, char **argv)
+
+        /*
+         * This routine simply formats whatever the caller wants and
+         * returns a pointer to the resulting 'const char' string... */
+static const char *fmtmk (const char *fmts, ...)
 {
-    /* For select(2). */
-    struct timeval tv;
-    fd_set in;
-    /* For parsing arguments. */
-    char *cp;
-    /* The key read in. */
-    char c;
-
-    struct sigaction sact;
-
-    setlocale(LC_ALL, "");
-    get_options();
-    
-    /* set to PCPU sorting */
-    register_sort_function( -1, (cmp_t)pcpu_sort);
-    
-    /*
-     * Parse arguments.
-     */
-    (void)argc;
-    argv++;
-    while (*argv) {
-       cp = *argv++;
-       while (*cp) {
-           switch (*cp) {
-             case 'd':
-               if (cp[1]) {
-                   Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n");
-                   goto breakargv;
-               } else if (*argv) { /* last char in an argv, use next as arg */
-                   Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n");
-                   goto breakargv;
-               } else {
-                   fprintf(stderr, "-d requires an argument\n");
-                   exit(1);
-               }
-               break;
-             case 'n':
-               if (cp[1]) {
-                   Loops = parseint(++cp, PROGNAME ": Bad value %s'\n");
-                   goto breakargv;
-               } else if (*argv) { /* last char in an argv, use next as arg */
-                   Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n");
-                   goto breakargv;
-               }
-               break;
-                                       
-             case 'q':
-               if (!getuid())
-                   /* set priority to -10 in order to stay above kswapd */
-                   if (setpriority(PRIO_PROCESS, getpid(), -10)) {
-                       /* We check this just for paranoia.  It's not
-                          fatal, and shouldn't happen. */
-                       perror(PROGNAME ": setpriority() failed");
-                   }
-               Sleeptime = 0;
-               break;
-             case 'p':
-               if (monpids_index >= monpids_max) {
-                   fprintf(stderr, PROGNAME ": More than %u process ids specified\n",
-                           monpids_max);
-                   exit(1);
-               }
-               if (cp[1]) {
-                   if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 ||
-                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
-                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
-                       exit(1);
-                   }
-               } else if (*argv) { /* last char in an argv, use next as arg */
-                   if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 ||
-                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
-                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
-                       exit(1);
-                   }
-               } else {
-                   fprintf(stderr, "-p requires an argument\n");
-                   exit(1);
-               }
-               if (!monpids[monpids_index])
-                   monpids[monpids_index] = getpid();
-               /* default to no sorting when monitoring process ids */
-               if (!monpids_index++) {
-                   sort_type = S_NONE;
-                   reset_sort_options();
-               }
-               cp = "_";
-               break;
-             case 'b':
-               Batch = 1;
-               break;
-             case 'c':
-               show_cmd = !show_cmd;
-               break;
-             case 'S':
-               Cumulative = 1;
-               break;
-             case 'i':
-               Noidle = 1;
-               break;
-             case 's':
-                 Secure = 1;
-                 break;
-             case 'C': 
-                 CPU_states = 1;
-                 break;
-             case '-':
-               break;          /* Just ignore it */
-             case 'v':
-             case 'V':
-               fprintf(stdout, "top (%s)\n", procps_version);
-               exit(0);
-             case 'h': 
-               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
-               exit(0);
-             default:
-               fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp);
-               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
-               exit(1);
-           }
-           cp++;
-       }
-    breakargv:
-    }
-    
-    if (nr_cpu > 1 && CPU_states)
-      header_lines++;
-
-    meminfo();  /* need kb_main_total value filled in */
-
-    setup_terminal();
-    window_size(0);
-    /*
-     * Set up signal handlers.
-     */
-    sact.sa_handler = end;
-    sact.sa_flags = 0;
-    sigemptyset(&sact.sa_mask);
-    sigaction(SIGHUP, &sact, NULL);
-    sigaction(SIGINT, &sact, NULL);
-    sigaction(SIGQUIT, &sact, NULL);
-    sact.sa_handler = stop;
-    sact.sa_flags = SA_RESTART;
-    sigaction(SIGTSTP, &sact, NULL);
-    sact.sa_handler = window_size;
-    sigaction(SIGWINCH, &sact, NULL);
-    sigaction(SIGCONT, &sact, NULL);
-
-    /* loop, collecting process info and sleeping */
-    while (1) {
-       if (Loops > 0)
-               Loops--;
-       /* display the tasks */
-       show_procs();
-       /* sleep & wait for keyboard input */
-       if (Loops == 0)
-           end(0);
-        if (!Batch)
-        {
-               tv.tv_sec = Sleeptime;
-               tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000;
-               FD_ZERO(&in);
-               FD_SET(0, &in);
-               if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1)
-                       do_key(c);
-        } else {
-          sleep(Sleeptime);
-       }
-    }
+   static char buf[BIGBUFSIZ];          /* with help stuff, our buffer */
+   va_list va;                          /* requirements exceed 1k */
+
+   va_start(va, fmts);
+   vsprintf(buf, fmts, va);
+   va_end(va);
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(fmtmk, buf);
+#endif
+   return (const char *)buf;
 }
 
-/*#######################################################################
- *#### Signal handled routines: error_end, end, stop, window_size     ###
- *#### Small utilities: make_header, getstr, getint, getfloat, getsig ###
- *#######################################################################
- */
+
+        /*
+         * What need be said... */
+static char *strim (int sp, char *str)
+{
+   const char *ws = "\b\f\n\r\t\v";
+   char *p;
+
+   /* this guy was originally designed just to trim the rc file lines and
+      any 'open_psdb_message' result which arrived with an inappropriate
+      newline (thanks to 'sysmap_mmap') -- but when tabs (^I) were found
+      in some proc cmdlines, a choice was offered twix space or null... */
+   if (sp)
+      while ((p = strpbrk(str, ws))) *p = ' ';
+   else
+      if ((p = strpbrk(str, ws))) *p = 0;
+   return str;
+}
 
 
-       /*
-        *  end when exiting with an error.
-        */
-static void error_end(int rno)
+        /*
+         * This guy just facilitates Batch and protects against dumb ttys. */
+static char *tg2 (int x, int y)
 {
-    if (!Batch)
-       tcsetattr(0, TCSAFLUSH, &Savetty);
-    PUTP(tgoto(cm, 0, Lines - 1));
-    fputs("\r\n", stdout);
-    exit(rno);
+   return Cap_can_goto ? tgoto(cursor_address, x, y) : (char *)"\n";
 }
-/*
-        * Normal end of execution.
-        */
-static void end(int signo)
+
+\f
+/*######  Misc Color/Highlighting support  ###############################*/
+
+        /*
+         * Make the appropriate caps/color strings and set some
+         * lengths which are used to distinguish twix the displayed
+         * 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)
 {
-    (void)signo;
-    if (!Batch)
-       tcsetattr(0, TCSAFLUSH, &Savetty);
-    PUTP(tgoto(cm, 0, Lines - 1));
-    fputs("\r\n", stdout);
-    exit(0);
+   /* 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... */
+   if (Batch) return;
+
+      /* these are the unchangeable puppies, so we only do 'em once */
+   if (!capsdone) {
+      strcpy(Cap_bold, tIF(enter_bold_mode));
+      strcpy(Cap_clr_eol, tIF(clr_eol));
+      strcpy(Cap_clr_eos, tIF(clr_eos));
+      strcpy(Cap_clr_scr, tIF(clear_screen));
+      strcpy(Cap_curs_huge, tIF(cursor_visible));
+      strcpy(Cap_curs_norm, tIF(cursor_normal));
+      strcpy(Cap_home, tIF(cursor_home));
+      strcpy(Cap_norm, tIF(exit_attribute_mode));
+      strcpy(Cap_reverse, tIF(enter_reverse_mode));
+      sprintf(Caps_off, "%s%s", Cap_norm, tIF(orig_pair));
+      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));
+   } 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);
+   }
+
+   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);
+
+#undef tIF
 }
 
-/*
-        * SIGTSTP catcher.
-        */
-static void stop(int signo)
+
+        /*
+         * 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 */
+static void msg_save (const char *fmts, ...)
 {
-    (void)signo;
-    /* Reset terminal. */
-    if (!Batch)
-       tcsetattr(0, TCSAFLUSH, &Savetty);
-    PUTP(tgoto(cm, 0, Lines - 3));
-    fflush(stdout);
-    raise(SIGSTOP);
-    /* Later... */
-    if (!Batch)
-       tcsetattr (0, TCSAFLUSH, &Rawtty);
+   char tmp[SMLBUFSIZ];
+   va_list va;
+
+   va_start(va, fmts);
+   vsprintf(tmp, fmts, va);
+   va_end(va);
+      /* we'll add some extra attention grabbers to whatever this is */
+   sprintf(Msg_delayed, "\a***  %s  ***", strim(0, tmp));
+   Msg_awaiting = 1;
 }
 
-/*
-       * Reads the window size and clear the window.  This is called on setup,
-       * and also catches SIGWINCHs, and adjusts Maxlines.  Basically, this is
-       * the central place for window size stuff.
-       */
-static void window_size(int signo)
+
+        /*
+         * Show an error message (caller may include a '\a' for sound) */
+static void show_msg (const char *str)
 {
-    struct winsize ws;
-    (void)signo;
-    if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){
-       Cols = ws.ws_col;
-       Lines = ws.ws_row;
-    }else{
-       Cols = tgetnum("co");
-       Lines = tgetnum("li");
-    }
-    if (!Batch)
-        clear_screen();
-    /*
-     * calculate header size, length of cmdline field ...
-     */
-     Numfields = make_header();
+   printf("%s%s %s %s%s"
+      , tg2(0, MSG_line)
+      , Msg_color
+      , str
+      , Caps_off
+      , Cap_clr_eol);
+   fflush(stdout);
+   sleep(MSG_SLEEP);
+   Msg_awaiting = 0;
 }
-/*
-       * this prints a possible message from open_psdb_message
-       */
-static void top_message(const char *format, ...) {
-    va_list arg;
-    int n;
-    char buffer[512];
-
-    va_start (arg, format);
-    n = vsnprintf (buffer, 512, format, arg);
-    va_end (arg);
-    if (n > -1 && n < 512)
-       SHOWMESSAGE(("%s", buffer));
+
+
+        /*
+         * Show an input prompt + larger cursor */
+static void show_pmt (const char *str)
+{
+   printf("%s%s%s: %s%s"
+      , tg2(0, MSG_line)
+      , Pmt_color
+      , str
+      , Cap_curs_huge
+      , Caps_off);
+   fflush(stdout);
 }
 
-/*
-       * this adjusts the lines needed for the header to the current value
-       */
-static int make_header(void)
+
+        /*
+         * Show lines with specially formatted elements, but only output
+         * what will fit within the current screen width.
+         *    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)
+         *    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.
+         *
+         * Caution:
+         *    This routine treats all non-delimiter bytes as displayable
+         *    data subject to our screen width marching orders.  If callers
+         *    embed non-display data like tabs or terminfo strings in our
+         *    glob, a line will truncate incorrectly at best.  Worse case
+         *    would be truncation of an embedded tty escape sequence.
+         *
+         *    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. */
+static void show_special (const char *glob)
 {
-    int i, j;
-
-    j = 0;
-    for (i = 0; i < strlen(Fields); i++) {
-       if (Fields[i] < 'a') {   
-           pflags[j++] = Fields[i] - 'A';
-           if (Fields[i] == 'U' && CL_wchan_nout == -1) {
-               CL_wchan_nout = 0;
-               /* for correct handling of WCHAN fields, we have to do distingu
-                * between kernel versions */
-               /* get kernel symbol table, if needed */
-               if (open_psdb_message(NULL, top_message)) {
-                   CL_wchan_nout = 1;
-               } else {
-                   psdbsucc = 1;
-               }
-           }
-       }
-    }
-    strcpy(Header, "");
-    for (i = 0; i < j; i++)
-       strcat(Header, headers[pflags[i]]);
-    /* readjust window size ... */
-    Maxcmd = Cols - strlen(Header) + 7;
-    Maxlines = Display_procs ? Display_procs : Lines - header_lines;
-    if (Maxlines > Lines - header_lines)
-       Maxlines = Lines - header_lines;
-    return (j);
+   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           */
+   char tmp[BIGBUFSIZ], *cap, *lin_end, *sub_beg, *sub_end;
+   int room;
+
+      /* handle multiple lines passed in a bunch */
+   while ((lin_end = strchr(glob, '\n'))) {
+
+         /* 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;
+      sub_beg = sub_end = tmp;
+
+      while (*sub_beg) {
+         switch (*sub_end) {
+            case '\00' :                /* no end delim, captab makes normal */
+               *(sub_end + 1) = '\0';   /* extend str end, then fall through */
+            case '\01' ... '\07' :
+               cap = 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 */
+               ++sub_end;
+         }
+
+         if (0 >= room) break;          /* skip substrings that won't fit */
+      } /* end: while 'subtrings' */
+
+      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'),
+      it probably means caller wants to retain cursor position on this final
+      line -- ok then, we'll just do our 'fit-to-screen' thingy... */
+   if (strlen(glob)) printf("%.*s", Screen_cols, 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
+}
 
-/*
- * Get a string from the user; the base of getint(), et al.  This really
- * ought to handle long input lines and errors better.  NB: The pointer
- * returned is a statically allocated buffer, so don't expect it to
- * persist between calls.
- */
-static char *getstr(void)
+\f
+/*######  Small utility routines  ########################################*/
+
+        /*
+         * Get a string from the user */
+static char *ask_str (const char *prompt)
 {
-    static char line[BUFSIZ];  /* BUFSIZ from <stdio.h>; arbitrary */
-    int i = 0;
+   static char buf[GETBUFSIZ];
+
+   show_pmt(prompt);
+   memset(buf, '\0', sizeof(buf));
+   chin(1, buf, sizeof(buf) - 1);
+   putp(Cap_curs_norm);
 
-    /* Must make sure that buffered IO doesn't kill us. */
-    fflush(stdout);
-    fflush(stdin);             /* Not POSIX but ok */
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(ask_str, buf);
+#endif
+   return strim(0, buf);
+}
 
-    do {
-       read(STDIN_FILENO, &line[i], 1);
-    } while (line[i++] != '\n' && i < sizeof(line));
-    line[--i] = 0;
 
-    return (line);
+        /*
+         * Get a float from the user */
+static float get_float (const char *prompt)
+{
+   char *line;
+   float f;
+
+   if (!(*(line = ask_str(prompt)))) return -1;
+      /* note: we're not allowing negative floats */
+   if (strcspn(line, ".1234567890")) {
+      show_msg("\aNot valid");
+      return -1;
+   }
+   sscanf(line, "%f", &f);
+   return f;
 }
 
 
-/*
- * Get an integer from the user.  Display an error message and
- * return BAD_INPUT if it's invalid; else return the number.
- */
-static int getint(void)
+        /*
+         * Get an integer from the user */
+static int get_int (const char *prompt)
 {
-    char *line;
-    int i;
-    int r;
+   char *line;
+   int n;
+
+   if (!(*(line = ask_str(prompt)))) return -1;
+      /* note: we've got to allow negative ints (renice)  */
+   if (strcspn(line, "-1234567890")) {
+      show_msg("\aNot valid");
+      return -1;
+   }
+   sscanf(line, "%d", &n);
+   return n;
+}
 
-    line = getstr();
 
-    for (i = 0; line[i]; i++) {
-       if (!isdigit(line[i]) && line[i] != '-') {
-           SHOWMESSAGE(("That's not a number!"));
-           return (BAD_INPUT);
-       }
-    }
+        /*
+         * 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
+}
 
-    /* An empty line is a legal error (hah!). */
-    if (!line[0])
-       return (BAD_INPUT);
 
-    sscanf(line, "%d", &r);
-    return (r);
+        /*
+         * Do some scaling stuff.
+         * We'll interpret 'num' as one of the following types and
+         * try to format it to fit 'width'.
+         *    SK_no (0) it's a byte count
+         *    SK_Kb (1) it's kilobytes
+         *    SK_Mb (2) it's megabytes
+         *    SK_Gb (3) it's gigabytes  */
+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
+   static char nextup[] =  { 'K', 'M', 'G', 0 };
+#else
+   static char nextup[] =  { 'k', 'm', 'g', 0 };
+#endif
+   static char buf[TNYBUFSIZ];
+   double *dp;
+   char *up;
+
+      /* try an unscaled version first... */
+   sprintf(buf, "%d", num);
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(scale_num, buf);
+#endif
+   if (strlen(buf) <= width)
+      return buf;
+
+      /* now try successively higher types until it fits */
+   for (up = nextup + type, dp = scale; *dp; ++dp, ++up) {
+         /* the most accurate version */
+      sprintf(buf, "%.1f%c", num / *dp, *up);
+      if (strlen(buf) <= width)
+         return buf;
+         /* the integer version */
+      sprintf(buf, "%d%c", (int)(num / *dp), *up);
+      if (strlen(buf) <= width)
+         return buf;
+   }
+      /* well shoot, this outta' fit... */
+   return (char *)"?";
 }
 
 
-/*
* Get a float from the user.  Just like getint().
- */
-static float getfloat(void)
+        /*
        * Do some scaling stuff.
        * Format 'tics' to fit 'width' */
+static char *scale_tics (TICS_t tics, const unsigned width)
 {
-    char *line;
-    int i;
-    float r;
-    char *savelocale;
-
-    line = getstr();
-
-    for (i = 0; line[i]; i++) {
-       if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') {
-           SHOWMESSAGE(("That's not a float!"));
-           return (BAD_INPUT);
-       }
-    }
-
-    /* An empty line is a legal error (hah!). */
-    if (!line[0])
-       return (BAD_INPUT);
-
-    savelocale = setlocale(LC_NUMERIC, NULL);
-    setlocale(LC_NUMERIC, "C");
-    sscanf(line, "%f", &r); 
-    setlocale(LC_NUMERIC, savelocale);
-    return (r);
+   static struct {
+      unsigned    div;
+      const char *fmt;
+   } ttab[] = {
+     /* minutes        hours          days            weeks */
+#ifdef UPCASE_SCALE
+      { 60, "%uM" }, { 60, "%uH" }, { 24, "%uD" }, {  7, "%uW" }
+#else
+      { 60, "%um" }, { 60, "%uh" }, { 24, "%ud" }, {  7, "%uw" }
+#endif
+   };
+   static char buf[TNYBUFSIZ];
+   unsigned i, t;
+
+      /* try successively higher units until it fits */
+   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;
+
+      /* try successively: minutes; hours; days; weeks */
+   for (i = 0; i < MAXtbl(ttab); i++) {
+      t /= ttab[i].div;
+      sprintf(buf, ttab[i].fmt, t);
+      if (strlen(buf) <= width)
+         return buf;
+   };
+      /* well shoot, this outta' fit... */
+   return (char *)"?";
 }
 
 
-/*
-        * Get a signal number or name from the user.  Return the number, or -1
-        * on error.
-        */
-static int getsig(void)
+        /*
+         * Calculate and return the elapsed time since the last update
+         * which is then used in % CPU calc's. */
+static float time_elapsed (void)
 {
-    char *line;
+    static struct timeval oldtimev;
+    struct timeval timev;
+    struct timezone timez;
+    float et;
+
+    gettimeofday(&timev, &timez);
+    et = (timev.tv_sec - oldtimev.tv_sec)
+       + (float)(timev.tv_usec - oldtimev.tv_usec) / 1000000.0;
+    oldtimev.tv_sec = timev.tv_sec;
+    oldtimev.tv_usec = timev.tv_usec;
+    return et;
+}
+
+\f
+/*######  Exit and Signal handled routines  ##############################*/
 
-    /* This is easy. */
-    line = getstr();
-    return signal_name_to_number(line);
+        /*
+         * The usual program end --
+         * called only by functions in this section. */
+static void bye_bye (int eno, const char *str)
+{
+#ifdef UGH_ITS_4_RH
+   if (PSDBopen)
+      close_psdb();
+#endif
+   if (!Batch)
+      tcsetattr(STDIN_FILENO, TCSAFLUSH, &Savedtty);
+   printf("%s%s\n", tg2(0, Screen_rows), Cap_curs_norm);
+
+   if (str) {
+     if (eno) perror(str);
+     else fputs(str, stderr);
+   }
+#ifdef TRAK_MAXCAPS
+   fprintf(stderr,
+      "\nTRAK_MAXCAPS report:"
+      "\n\tBasic lengths:"
+      "\n\t   Cap_home = %d, Cap_bold = %d, Cap_norm = %d, Cap_reverse = %d"
+      "\n\t   Cap_clr_eol = %d, Cap_clr_eos = %d, Cap_clr_scr = %d"
+      "\n\t   Cap_curs_norm = %d, Cap_curs_huge = %d"
+      "\n\t   Caps_off = %d"
+      "\n\tColor lengths:"
+      "\n\t   Sum_color = %d, Msg_color = %d, Pmt_color = %d, Hdr_color = %d"
+      "\n\t   Row_color_norm = %d, Len_row_norm = '%d'"
+      "\n\t   Row_color_high = %d, Len_row_high = '%d'"
+      "\n\tMax_pads = %d, Min_pads = %d"
+      "\n\tMax_rbuf = %d, Min_rbuf = %d"
+      "\n"
+      , strlen(Cap_home), strlen(Cap_bold), strlen(Cap_norm), strlen(Cap_reverse)
+      , strlen(Cap_clr_eol), strlen(Cap_clr_eos), strlen(Cap_clr_scr)
+      , strlen(Cap_curs_norm), strlen(Cap_curs_huge)
+      , strlen(Caps_off)
+      , strlen(Sum_color), strlen(Msg_color), strlen(Pmt_color), strlen(Hdr_color)
+      , strlen(Row_color_norm), Len_row_norm
+      , strlen(Row_color_high), Len_row_high
+      , Max_pads, Min_pads
+      , Max_rbuf, Min_rbuf);
+#endif
+#ifdef TRAK_MAXBUFS
+   fprintf(stderr,
+      "\nTRAK_MAXBUFS report:"
+      "\n\tused      size\tfunction, buffer"
+      "\n\t----      ----\t-----------------------"
+      ARPTBUF(fmtmk, buf)
+      ARPTBUF(show_special, tmp)
+      ARPTBUF(ask_str, buf)
+      ARPTBUF(scale_num, buf)
+      ARPTBUF(scale_tics, buf)
+      ARPTBUF(std_err, buf)
+      ARPTBUF(frame_states, tmp)
+      ARPTBUF(mkcol, tmp)
+      ARPTBUF(show_a_task, cbuf)
+      ARPTBUF(show_a_task, rbuf)
+      ARPTBUF(rcfiles_read, fbuf)
+      ARPTBUF(rcfiles_read, RCfile)
+      ARPTBUF(rcfiles_read, RCfile_Sys)
+      ARPTBUF(do_key, ColUsername)
+      ARPTBUF(mkheadings, CurFields)
+      ARPTBUF(mkheadings, ColHeadings)
+      ARPTBUF(main, not_really_tmp)
+      "\n"
+      , AUSEBUF(fmtmk, buf), ASIZBUF(fmtmk, buf)
+      , AUSEBUF(show_special, tmp), ASIZBUF(show_special, tmp)
+      , AUSEBUF(ask_str, buf), ASIZBUF(ask_str, buf)
+      , AUSEBUF(scale_num, buf), ASIZBUF(scale_num, buf)
+      , AUSEBUF(scale_tics, buf), ASIZBUF(scale_tics, buf)
+      , AUSEBUF(std_err, buf), ASIZBUF(std_err, buf)
+      , AUSEBUF(frame_states, tmp), ASIZBUF(frame_states, tmp)
+      , AUSEBUF(mkcol, tmp), ASIZBUF(mkcol, tmp)
+      , AUSEBUF(show_a_task, cbuf), ASIZBUF(show_a_task, cbuf)
+      , AUSEBUF(show_a_task, rbuf), ASIZBUF(show_a_task, rbuf)
+      , AUSEBUF(rcfiles_read, fbuf), ASIZBUF(rcfiles_read, fbuf)
+      , AUSEBUF(rcfiles_read, RCfile) , ASIZBUF(rcfiles_read, RCfile)
+      , AUSEBUF(rcfiles_read, RCfile_Sys) , ASIZBUF(rcfiles_read, RCfile_Sys)
+      , AUSEBUF(do_key, ColUsername), ASIZBUF(do_key, ColUsername)
+      , AUSEBUF(mkheadings, CurFields), ASIZBUF(mkheadings, CurFields)
+      , AUSEBUF(mkheadings, ColHeadings), ASIZBUF(mkheadings, ColHeadings)
+      , AUSEBUF(main, not_really_tmp), ASIZBUF(main, not_really_tmp));
+#endif
+
+#if defined(TRAK_MAXCAPS) || defined(TRAK_MAXBUFS)
+   fprintf(stderr,
+      "\nbye_bye's Summary report:"
+      "\n\tprogram names:"
+      "\n\t   Myrealname = %s, Myname = %s"
+      "\n\tterminal = '%s'"
+      "\n\t   device = %s, ncurses = v%s"
+      "\n\t   max_colors = %d, max_pairs = %d"
+      "\n\t   Cap_can_goto = %s"
+      "\n\tScreen_cols = %d, Screen_rows = %d"
+      "\n\tNumFields = %d, HSum_lines = %d"
+      "\n\tMax_lines = %d, Max_cmd = %d, Max_tasks = %d"
+      "\n\tPage_size = %d"
+      "\n"
+      , Myrealname, Myname
+#ifdef PRETENDNOCAP
+      , "dumb"
+#else
+      , termname()
+#endif
+      , ttyname(STDOUT_FILENO), NCURSES_VERSION
+      , max_colors, max_pairs
+      , Cap_can_goto ? "yes" : "No!"
+      , Screen_cols, Screen_rows
+      , NumFields, HSum_lines
+      , Max_lines, Max_cmd, Max_tasks
+      , Page_size);
+#endif
+
+   if (str && !eno) eno = 1;
+   exit(eno);
 }
 
-/*#######################################################################
- *####  Routine for sorting on used time, resident memory and %CPU  #####
- *####  It would be easy to include full sorting capability as in   #####
- *####  ps, but I think there is no real use for something that     #####
- *####  complicated. Using register_sort_function or parse_sort_opt #####
- *####  you just have to do the natural thing and it will work.     #####
- *#######################################################################
- */
 
-static int time_sort (proc_t **P, proc_t **Q)
+        /*
+         * Standard error handler to normalize the look of all err o/p */
+static void std_err (const char *str)
 {
-    if (Cumulative) {
-       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < 
-           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
-           return -1;
-       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) >
-           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
-           return 1;
-    } else {
-       if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
-           return -1;
-       if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
-           return 1;
-    }
-    return 0;
+   static char buf[SMLBUFSIZ];
+
+   fflush(stdout);
+      /* we'll use our own buffer so callers can still use fmtmk() */
+   sprintf(buf, "\t%s: %s\n", Myname, str);
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(std_err, buf);
+#endif
+   if (!Ttychanged) {
+      fprintf(stderr, buf);
+      exit(1);
+   }
+      /* not to worry, he'll change our exit code to 1 due to 'buf' */
+   bye_bye(0, buf);
 }
 
-static int pcpu_sort (proc_t **P, proc_t **Q)
+
+        /*
+         * Normal end of execution.
+         * catches:
+         *    SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT and SIGTERM */
+static void stop (int dont_care_sig)
 {
-    if( (*P)->pcpu < (*Q)->pcpu )      return -1;
-    if( (*P)->pcpu > (*Q)->pcpu )      return 1;
-    return 0;
+   bye_bye(0, NULL);
 }
 
-static int mem_sort (proc_t **P, proc_t **Q)
+
+        /*
+         * Suspend ourself.
+         * catches:
+         *    SIGTSTP, SIGTTIN and SIGTTOU */
+static void suspend (int dont_care_sig)
 {
-    if( (*P)->vm_rss < (*Q)->vm_rss )      return -1;
-    if( (*P)->vm_rss > (*Q)->vm_rss )      return 1;  
-    return 0;
+        /* 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 age_sort (proc_t **P, proc_t **Q)
+
+        /*
+         * Set the screen dimensions and call the real workhorse.
+         * catches:
+         *    SIGWINCH and SIGCONT */
+static void window_resize (int dont_care_sig)
 {
-    if( (*P)->start_time < (*Q)->start_time )      return -1;
-    if( (*P)->start_time > (*Q)->start_time )      return 1;
-    return 0;
+   struct winsize w;
+
+   Screen_cols = columns;
+   Screen_rows = lines;
+   if (-1 != (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w))) {
+      Screen_cols = w.ws_col;
+      Screen_rows = w.ws_row;
+   }
+      /* he'll calculate header size, length of command field, etc ... */
+   mkheadings();
 }
 
-/*#######################################################################
- *####  Routines handling the field selection/ordering screens:  ########
- *####    show_fields, change_order, change_fields               ########
- *#######################################################################
- */
+\f
+/*######  Startup routines  ##############################################*/
 
         /*
-        * Display the specification line of all fields. Upper case indicates
-        * a displayed field, display order is according to the order of the 
-        * letters. A short description of each field is shown as well.
-        * The description of a displayed field is marked by a leading 
-        * asterisk (*).
-        */
-static void show_fields(void)
+         * Parse command line arguments
+         * (and force ol' main into a much needed diet)
+         * Note: it's assumed that the rc file(s) have already been read
+         *       and our job is to see if any of those options are to be
+         *       overridden -- we'll force some on and negate others in our
+         *       best effort to honor the user's wishes... */
+static void parse_argvs (char **argv)
 {
-    int i, row, col;
-    char *p;
-
-    clear_screen();
-    PUTP(tgoto(cm, 3, 0));
-    printf("Current Field Order: %s\n", Fields);
-    for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) {
-       row = i % (Lines - 3) + 3;
-       col = i / (Lines - 3) * 40;
-       PUTP(tgoto(cm, col, row));
-       for (p = headers[i]; *p == ' '; ++p);
-       printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A',
-              p, headers2[i]);
-    }
+   /* difference(s) from traditional top:
+      -q (zero delay time) eliminated as redundant, use -d0
+      -p (pid monitoring) allows comma delimited list
+      no deprecated/illegal use of 'goto' with 'breakargv:'
+      bunched args are actually handled properly and none are ignored
+      (we tolerate No whitespace and No switches -- maybe too tolerant) */
+   const char *usage = " -hv | -bcisS -d delay -n iterations -p pid [,pid ...]\n";
+   float tmp_delay = MAXFLOAT;
+   char *p;
+
+   ++argv;
+   while (*argv) {
+      char *cp = *(argv++);
+
+      while (*cp) {
+         switch (*cp) {
+            case '\0':
+            case '-':
+               break;
+            case 'b':
+               Batch = 1;
+               break;
+            case 'c':
+               Show_cmdlin = !Show_cmdlin;
+               break;
+            case 'd':
+               if (cp[1]) ++cp;
+               else if (*argv) cp = *argv++;
+               else std_err("-d requires argument");
+                  /* a negative delay will be dealt with shortly... */
+               if (1 != sscanf(cp, "%f", &tmp_delay))
+                  std_err(fmtmk("bad delay '%s'", cp));
+               break;
+            case 'h': case 'H':
+            case 'v': case 'V':
+               std_err(fmtmk("\t%s\nusage:\t%s%s"
+                  , procps_version, Myname, usage));
+            case 'i':
+               Show_idleps = !Show_idleps;
+               break;
+            case 'n':
+               if (cp[1]) cp++;
+               else if (*argv) cp = *argv++;
+               else std_err("-n requires argument");
+               if (1 != sscanf(cp, "%d", &Loops) || 0 > Loops)
+                  std_err(fmtmk("bad iterations arg '%s'", cp));
+               break;
+            case 'p':
+               if (cp[1]) cp++;
+               else if (*argv) cp = *argv++;
+               else std_err("-p requires argument");
+               do {
+                  if (Monpidsidx >= MONPIDMAX)
+                     std_err(fmtmk("pid limit (%d) exceeded", MONPIDMAX));
+                  if (1 != sscanf(cp, "%d", &Monpids[Monpidsidx])
+                  || 0 > Monpids[Monpidsidx])
+                     std_err(fmtmk("bad pid '%s'", cp));
+                  if (!Monpids[Monpidsidx])
+                     Monpids[Monpidsidx] = getpid();
+                  Monpidsidx++;
+                  if (!(p = strchr(cp, ',')))
+                     break;
+                  cp = ++p;
+               } while (*cp);
+               break;
+            case 's':
+               Secure_mode = 1;
+               break;
+            case 'S':
+               Show_ctimes = !Show_ctimes;
+               break;
+            default :
+               std_err(fmtmk("unknown argument '%c'\nusage:\t%s%s"
+                  , *cp, Myname, usage));
+
+         } /* end: switch */
+            /* advance cp and jump over any numerical args used above */
+         cp += strspn(++cp, "-.1234567890 ");
+
+      } /* end: while *cp */
+   } /* end: while *argv */
+
+      /* fixup delay time, maybe... */
+   if (MAXFLOAT != tmp_delay) {
+      if (Secure_mode || 0 > tmp_delay)
+         msg_save("Delay time Not changed");
+      else
+         Delay_time = tmp_delay;
+   }
+
+      /* set the default sort type, maybe... */
+   if (-1 == Sort_type || Monpidsidx) {
+      Sort_type = S_PID;
+      Sort_func = (QSORT_t)sort_pid;
+   }
 }
 
-/*
-        * change order of displayed fields
-        */
-static void change_order(void)
+
+        /*
+         * Parse the options string as read from line two of the "local"
+         * configuration file. */
+static void parse_rc (char *opts)
 {
-    char c, ch, *p;
-    int i;
-
-    show_fields();
-    for (;;) {
-       PUTP(tgoto(cm, 0, 0));
-       PUTP(top_clrtoeol);
-       PUTP(tgoto(cm, 3, 0));
-       PUTP(mr);
-       printf("Current Field Order: %s", Fields);
-       PUTP(me);
-       putchar('\n');
-       PUTP(tgoto(cm, 0, 1));
-       printf("Upper case characters move a field to the left, lower case to the right");
-       fflush(stdout);
-       if (!Batch) { /* should always be true, but... */
-           tcsetattr(0, TCSAFLUSH, &Rawtty);
-           read(0, &c, 1);
-           tcsetattr(0, TCSAFLUSH, &Savetty);
-       }
-       i = toupper(c) - 'A';
-       if ((p = strchr(Fields, i + 'A')) != NULL) {
-           if (isupper(c))
-               p--;
-           if ((p[1] != '\0') && (p >= Fields)) {
-               ch = p[0];
-               p[0] = p[1];
-               p[1] = ch;
-           }
-       } else if ((p = strchr(Fields, i + 'a')) != NULL) {
-           if (isupper(c))
-               p--;
-           if ((p[1] != '\0') && (p >= Fields)) {
-               ch = p[0];
-               p[0] = p[1];
-               p[1] = ch;
-           }
-       } else {
-           break;
-       }
-    }
-    Numfields = make_header();
+   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]));
+      }
+   }
 }
-/*
-        * toggle displayed fields
-        */
-static void change_fields(void)
+
+
+        /*
+         * 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)
 {
-    int i, changed = 0;
-    int row, col;
-    char c, *p;
-    char tmp[2] = " ";
-
-    show_fields();
-    for (;;) {
-       PUTP(tgoto(cm, 0, 0));
-       PUTP(top_clrtoeol);
-       PUTP(tgoto(cm, 3, 0));
-       PUTP(mr);
-       printf("Current Field Order: %s", Fields);
-       PUTP(me);
-       putchar('\n');
-       PUTP(tgoto(cm, 0, 1));
-       if (!Batch) { /* should always be true, but... */
-           printf("Toggle fields with a-x, any other key to return: ");
-           fflush(stdout);
-           tcsetattr(0, TCSAFLUSH, &Rawtty);
-           read(0, &c, 1);
-           tcsetattr(0, TCSAFLUSH, &Savetty);
-       }
-       i = toupper(c) - 'A';
-       if (i >= 0 && i < sizeof headers / sizeof headers[0]) {
-           row = i % (Lines - 3) + 3;
-           col = i / (Lines - 3) * 40;
-           PUTP(tgoto(cm, col, row));
-           if ((p = strchr(Fields, i + 'A')) != NULL) {        /* deselect Field */
-               *p = i + 'a';
-               putchar(' ');
-           } else if ((p = strchr(Fields, i + 'a')) != NULL) {         /* select previously */
-               *p = i + 'A';   /* deselected field */
-               putchar('*');
-           } else {            /* select new field */
-               tmp[0] = i + 'A';
-               strcat(Fields, tmp);
-               putchar('*');
-           }
-           changed = 1;
-           fflush(stdout);
-       } else
-           break;
-    }
-    if (changed)
-       Numfields = make_header();
+   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;
 }
 
-/* Do the scaling stuff: interprets time in seconds, formats it to
- * fit width, and returns pointer to static char*.
- */
-static char *scale_time(int t,int width) 
+
+        /*
        * Set up the terminal attributes */
+static void terminal_set (void)
 {
-       static char buf[100];
-
-       /* Try successively higher units until it fits */
-
-       sprintf(buf,"%d:%02d",t/60,t%60);       /* minutes:seconds */
-       if (strlen(buf)<=width) 
-               return buf;
-
-       t/=60;  /* minutes */
-       sprintf(buf,"%dm",t);
-       if (strlen(buf)<=width) 
-               return buf;
-
-       t/=60;  /* hours */
-       sprintf(buf,"%dh",t);
-       if (strlen(buf)<=width) 
-               return buf;
-
-       t/=24;  /* days */
-       sprintf(buf,"%dd",t);
-       if (strlen(buf)<=width) 
-               return buf;
-       
-       t/=7;   /* weeks */
-       sprintf(buf,"%dw",t);
-       return buf;     /* this is our last try; 
-                               if it still doesn't fit, too bad. */
-
-       /* FIXME: if someone has a 16-way SMP running over a year... */
+   struct termios newtty;
+
+      /* first the ncurses part... */
+#ifdef PRETENDNOCAP
+   setupterm((char *)"dumb", STDOUT_FILENO, NULL);
+#else
+   setupterm(NULL, STDOUT_FILENO, NULL);
+#endif
+      /* now our part... */
+   if (!Batch) {
+      if (-1 == tcgetattr(STDIN_FILENO, &Savedtty))
+         std_err("tcgetattr() failed");
+      capsmk();
+      newtty = Savedtty;
+      newtty.c_lflag &= ~ICANON;
+      newtty.c_lflag &= ~ECHO;
+      newtty.c_cc[VMIN] = 1;
+      newtty.c_cc[VTIME] = 0;
+
+      Ttychanged = 1;
+      if (-1 == tcsetattr(STDIN_FILENO, TCSAFLUSH, &newtty)) {
+         putp(Cap_clr_scr);
+         std_err(fmtmk("Failed tty set: %s", strerror(errno)));
+      }
+      tcgetattr(STDIN_FILENO, &Rawtty);
+      putp(Cap_clr_scr);
+      fflush(stdout);
+   }
 }
 
-/*   scale_k(k,width,unit)  - interprets k as a count, formats to fit width.
-                            if unit is 0, k is a byte count; 1 is a kilobyte
-                           count; 2 for megabytes; 3 for gigabytes.
-*/
+\f
+/*######  Field Selection/Ordering routines  #############################*/
 
-static char *scale_k(int k,int width,int unit) 
+        /*
+         * Display the current fields and their order.
+         * Upper case indicates a displayed field, display order is
+         * according to the order of the letters.
+         *
+         * A short description of each field is shown as well and is
+         * marked by a leading asterisk (*) if currently displayed.
+         *
+         * After all fields have been displayed, some extra explanatory
+         * text is then output */
+static void display_fields (void)
 {
-               /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
-       static double scale[]={1024,1024*1024,1024*1024*1024,0};
-               /* kilo, mega, giga, tera */
-       static char unitletters[]={'K','M','G','T',0};
-       static char buf[100];
-       char *up;
-       double *dp;
-
-       /* Try successively higher units until it fits */
-
-       sprintf(buf,"%d",k);
-       if (strlen(buf)<=width) 
-               return buf;
-
-       for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) {
-               sprintf(buf,"%.1f%c",k / *dp,*up);
-               if (strlen(buf)<=width) 
-                       return buf;
-               sprintf(buf,"%d%c",(int)(k / *dp),*up);
-               if (strlen(buf)<=width) 
-                       return buf;
-       }
-
-       /* Give up; give them what we got on our shortest attempt */
-       return buf;
+   const char *p, *x;
+   int i, cmax = Screen_cols / 2, rmax = Screen_rows - 3;
+
+   /* we're relying on our callers to first clear the screen --
+      thus 'fields_toggle' can avoid screen flicker since he's
+      too lazy to handle his own asterisk (*) logic */
+   putp(Cap_bold);
+   for (i = 0; i < MAXtbl(Fieldstab); ++i) {
+         /* advance past any leading spaces */
+      for (p = Fieldstab[i].head; ' ' == *p; ++p)
+         ;
+      printf("%s%c %c: %-10s = %s"
+         , tg2((i / rmax) * cmax, (i % rmax) + 3)
+         , strchr(CurFields, i + 'A') ? '*' : ' '
+         , i + 'A'
+         , p
+         , Fieldstab[i].desc);
+   }
+   putp(Row_color_norm);
+   x = FIELDS_xtra;
+   while ((p = strchr(x, '\n'))) {
+      ++i;
+      printf("%s%.*s"
+         , tg2((i / rmax) * cmax, (i % rmax) + 3)
+         , p - x, x);
+      x = ++p;
+   }
+   putp(Caps_off);
 }
 
-/*
- *#######################################################################
- *####  Routines handling the main top screen:                   ########
- *####    show_task_info, show_procs, show_memory, do_stats      ########
- *#######################################################################
- */
-       /*
-        * Displays infos for a single task
-        */
-static void show_task_info(proc_t *task)
+
+        /*
+         * Change order of displayed fields. */
+static void fields_reorder (void)
 {
-    int i,j;
-    unsigned int t;
-    char *cmdptr;
-    char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p;
-
-    for (i = 0; i < Numfields; i++) {
-       tmp[0] = 0;
-       switch (pflags[i]) {
-         case P_PID:
-           sprintf(tmp, "%5d ", task->pid);
-           break;
-         case P_PPID:
-           sprintf(tmp, "%5d ", task->ppid);
-           break;
-         case P_EUID:
-           sprintf(tmp, "%4d ", task->euid);
-           break;
-         case P_EUSER:
-           sprintf(tmp, "%-8.8s ", task->euser);
-           break;
-         case P_PCPU:
-           sprintf(tmp, "%4.1f ", (float)task->pcpu / 10);
-           break;
-         case P_LCPU:
-           sprintf(tmp, "%2d ", task->processor);
-           break;
-         case P_PMEM: {
-              unsigned pmem;
-             pmem = task->vm_rss * 1000ULL / kb_main_total;
-             if (pmem > 999) pmem = 999;
-             sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U);
-           }
-           break;
-         case P_TTY: {
-             char outbuf[9];
-             dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV);
-             sprintf(tmp, "%-8.8s ", outbuf);
-           }
-           break;
-         case P_PRI:
-           sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0));
-           break;
-         case P_NICE:
-           sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0));
-           break;
-         case P_PAGEIN:
-           sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0));
-           break;
-         case P_TSIZ:
-           sprintf(tmp, "%5.5s ",
-               scale_k(((task->end_code - task->start_code) / 1024), 5, 1));
-           break;
-         case P_DSIZ:
-           sprintf(tmp, "%5.5s ",
-               scale_k(((task->vsize - task->end_code) / 1024), 5, 1));
-           break;
-         case P_SIZE:
-           sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1));
-           break;
-         case P_TRS:
-           sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1));
-           break;
-         case P_SWAP:
-           sprintf(tmp, "%4.4s ",
-               scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1));
-           break;
-         case P_SHARE:
-           sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1));
-           break;
-         case P_A:
-           sprintf(tmp, "%3.3s ", "NYI");
-           break;
-         case P_WP:
-           sprintf(tmp, "%3.3s ", "NYI");
-           break;
-         case P_DT:
-           sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0));
-           break;
-         case P_RSS:   /* rss, not resident (which includes IO memory) */
-           sprintf(tmp, "%4.4s ",
-               scale_k((task->rss << CL_pg_shift), 4, 1));
-           break;
-         case P_WCHAN:
-           if (!CL_wchan_nout)
-               sprintf(tmp, "%-9.9s ", wchan(task->wchan));
-           else
-               sprintf(tmp, "%-9lx", task->wchan);
-           break;
-         case P_STAT:
-           sprintf(tmp, "%-4.4s ", status(task));
-           break;
-         case P_TIME:
-           t = (task->utime + task->stime) / Hertz;
-           if (Cumulative)
-               t += (task->cutime + task->cstime) / Hertz;
-           sprintf(tmp, "%6.6s ", scale_time(t,6));
-           break;
-         case P_COMMAND:
-           if (!show_cmd && task->cmdline && *(task->cmdline)) {
-               j=0;
-               while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){
-/* #if 0 */ /* This is useless? FIXME */
-                   if (j > 0)
-                       strcat(tmp3, " ");
-/* #endif */
-                   strncat(tmp3, (task->cmdline)[j], 1000);
-                   j++; 
-               }
-               cmdptr = tmp3;
-           } else {
-               cmdptr = task->cmd;
-           }
-           if (strlen(cmdptr) > Maxcmd)
-               cmdptr[Maxcmd - 1] = 0;
-           sprintf(tmp, "%s", cmdptr);
-           tmp3[0]=0;
-           break;
-         case P_FLAGS:
-           sprintf(tmp, "%8lx ", task->flags);
-           break;
-       }
-       strcat(tmp2, tmp);
-    }
-    if (strlen(tmp2) > Cols - 1)
-       tmp2[Cols - 1] = 0;
-
-    /* take care of cases like:
-       perl -e 'foo
-          bar foo bar
-          foo
-          # end of perl script'
-    */
-    for (p=tmp2;*p;++p)
-        if (!isgraph(*p))
-            *p=' ';
-
-    printf("\n%s", tmp2);
-    PUTP(top_clrtoeol);
+   static char prompt[] =
+      "Upper case characters move field left, lower case right";
+   char c, *p;
+   int i;
+
+   printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+   display_fields();
+   do {
+      show_special(fmtmk(FIELDS_current
+         , Cap_home, Myname, CurFields, prompt));
+      chin(0, &c, 1);
+      i = toupper(c) - 'A';
+      if (i < 0 || i >= MAXtbl(Fieldstab))
+         break;
+      if (((p = strchr(CurFields, i + 'A')))
+      || ((p = strchr(CurFields, i + 'a')))) {
+         if (isupper(c)) p--;
+         if (('\0' != p[1]) && (p >= CurFields)) {
+            c    = p[0];
+            p[0] = p[1];
+            p[1] = c;
+         }
+      }
+   } while (1);
+   putp(Cap_curs_norm);
+   mkheadings();
 }
 
-/*
- * This is the real program!  Read process info and display it.
- * One could differentiate options of readproctable2, perhaps it
- * would be useful to support the PROC_UID and PROC_TTY
- * as command line options.
- */
-static void show_procs(void)
+
+        /*
+         * Toggle displayed fields. */
+static void fields_toggle (void)
 {
-    static proc_t **p_table=NULL;
-    static int proc_flags;
-    int count;
-    int ActualLines;
-    float elapsed_time;
-    static int first=0;
-
-    if (first==0) {
-       proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT;
-       if (monpids_index)
-           proc_flags |= PROC_PID;
-       p_table=readproctab2(proc_flags, p_table, monpids);
-       elapsed_time = get_elapsed_time();
-       do_stats(p_table, elapsed_time, 0);
-       sleep(1);
-       first=1;
-    }
-    if (first && Batch)
-           fputs("\n\n",stdout);
-    /* Display the load averages. */
-    PUTP(ho);
-    PUTP(md);
-    if (show_loadav) {
-       printf("%s", sprint_uptime());
-       PUTP(top_clrtoeol);
-       putchar('\n');
-    }
-    p_table=readproctab2(proc_flags, p_table, monpids);
-    /* Immediately find out the elapsed time for the frame. */
-    elapsed_time = get_elapsed_time();
-    /* Display the system stats, calculate percent CPU time
-     * and sort the list. */
-    do_stats(p_table, elapsed_time,1);
-    /* Display the memory and swap space usage. */
-    show_meminfo();
-    if (strlen(Header) + 2 > Cols)
-       Header[Cols - 2] = 0;
-    PUTP(mr);
-    fputs(Header, stdout);
-    PUTP(top_clrtoeol);
-    PUTP(me);
-
-    /*
-     * Finally!  Loop through to find the top task, and display it.
-     * Lather, rinse, repeat.
-     */
-    count = 0;
-    ActualLines = 0;
-    while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) {
-       char Stat;
-
-       Stat = p_table[count]->state;
-
-       if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) &&
-            ( (CurrUser[0] == '\0') ||
-             (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
-
-           /*
-            * Show task info.
-            */
-           show_task_info(p_table[count]);
-           if (!Batch)
-               ActualLines++;
-       }
-       count++;
-    }
-    PUTP(top_clrtobot);
-    PUTP(tgoto(cm, 0, header_lines - 2));
-    fflush(stdout);
+   static char prompt[] =
+      "Toggle fields with a-x, type any other key to return";
+   char c, *p;
+   int i;
+
+   printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+   do {
+      display_fields();
+      show_special(fmtmk(FIELDS_current
+         , Cap_home, Myname, CurFields, prompt));
+      chin(0, &c, 1);
+      i = toupper(c) - 'A';
+      if (i < 0 || i >= MAXtbl(Fieldstab))
+         break;
+      if ((p = strchr(CurFields, i + 'A')))
+         *p = i + 'a';
+      else if ((p = strchr(CurFields, i + 'a')))
+         *p = i + 'A';
+   } while (1);
+   putp(Cap_curs_norm);
+   mkheadings();
 }
 
+\f
+/*######  Library Alternatives  ##########################################*/
 
-/*
* Finds the current time (in microseconds) and calculates the time
- * elapsed since the last update. This is essential for computing
- * percent CPU usage.
- */
-static float get_elapsed_time(void)
+        /*
        * 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)
 {
-    struct timeval t;
-    static struct timeval oldtime;
-    struct timezone timez;
-    float elapsed_time;
-
-    gettimeofday(&t, &timez);
-    elapsed_time = (t.tv_sec - oldtime.tv_sec)
-       + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
-    oldtime.tv_sec  = t.tv_sec;
-    oldtime.tv_usec = t.tv_usec;
-    return (elapsed_time);
+   void * p;
+
+   if (!numb) ++numb;
+   if (!(p = calloc(1, numb)))
+      std_err(fmtmk(alloc_msg, numb));
+   return p;
 }
 
-/*
- * Reads the memory info and displays it.  Returns the total memory
- * available, for use in percent memory usage calculations.
- */
-static void show_meminfo(void)
+
+static void *alloc_r (void *q, unsigned numb)
 {
-    meminfo(); /* read+parse /proc/meminfo */
-    if (show_memory) {
-       printf(
-           "Mem:  %8dK total, %8dK used, %8dK free, %8dK buffers",
-           kb_main_total,
-           kb_main_used,
-           kb_main_free,
-           kb_main_buffers
-       );
-       PUTP(top_clrtoeol);
-       putchar('\n');
-       printf(
-           "Swap: %8dK total, %8dK used, %8dK free, %8dK cached",
-           kb_swap_total,
-           kb_swap_used,
-           kb_swap_free,
-           kb_main_cached
-       );
-       PUTP(top_clrtoeol);
-       putchar('\n');
-    }
-    PUTP(me);
-    PUTP(top_clrtoeol);
-    putchar('\n');
+   void *p;
+
+   if (!numb) ++numb;
+   if (!(p = realloc(q, numb)))
+      std_err(fmtmk(alloc_msg, numb));
+   return p;
 }
 
 
-/*
- * Calculates the number of tasks in each state (running, sleeping, etc.).
- * Calculates the CPU time in each state (system, user, nice, etc).
- * Calculates percent cpu usage for each task.
- */
-static void do_stats(proc_t** p, float elapsed_time, int pass)
+        /*
+         * 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)
 {
-    proc_t *this;
-    int arrindex, total_time, cpumap, i, n = 0;
-    int sleeping = 0, stopped = 0, zombie = 0, running = 0;
-    double system_ticks, user_ticks, nice_ticks, idle_ticks;
-    static int prev_count = 0;
-    int systime, usrtime;
-     /* start with one page as a reasonable allocate size */
-     static int save_history_size =
-         sizeof(long)*1024 / sizeof(struct save_hist);
-     static struct save_hist *save_history;
-     struct save_hist *New_save_hist;
-     if (!save_history)
-       save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
-     New_save_hist  = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
-
-    /*
-     * Make a pass through the data to get stats.
-     */
-    arrindex = 0;
-    while (p[n]->pid != -1) {
-       this = p[n];
-       switch (this->state) {
-         case 'S':
-         case 'D':
-           sleeping++;
-           break;
-         case 'T':
-           stopped++;
-           break;
-         case 'Z':
-           zombie++;
-           break;
-         case 'R':
-           running++;
-           break;
-         default:
-           /* Don't know how to handle this one. */
-           break;
-        }
-
-       /*
-        * Calculate time in this process.  Time is sum of user time
-        * (usrtime) plus system time (systime).
-        */
-       total_time = this->utime + this->stime;
-        if (arrindex >= save_history_size) {
-            save_history_size *= 2;
-            save_history  = xrealloc(save_history, sizeof(struct save_hist)*save_history_size);
-            New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size);
-        }
-       New_save_hist[arrindex].ticks = total_time;
-       New_save_hist[arrindex].pid = this->pid;
-       systime = this->stime;
-       usrtime = this->utime;
-       New_save_hist[arrindex].stime = systime;
-       New_save_hist[arrindex].utime = usrtime;
-
-       /* find matching entry from previous pass */
-       for (i = 0; i < prev_count; i++) {
-           if (save_history[i].pid == this->pid) {
-               total_time -= save_history[i].ticks;
-               systime -= save_history[i].stime;
-               usrtime -= save_history[i].utime;
-
-               i = prev_count;
-           }
-       }
-
-       /*
-        * Calculate percent cpu time for this task.
-        */
-       this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time;
-       if (this->pcpu > 999)
-           this->pcpu = 999;
-
-       arrindex++;
-       n++;
-    }
-
-    /*
-     * Display stats.
-     */
-    if (pass > 0 && show_stats) {
-       printf("%d processes: %d sleeping, %d running, %d zombie, "
-              "%d stopped",
-              n, sleeping, running, zombie, stopped);
-       PUTP(top_clrtoeol);
-       putchar('\n');
-       four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks);
-       printf("CPU states:"
-           " %# 5.1f%% user, %# 5.1f%% system,"
-           " %# 5.1f%% nice, %# 5.1f%% idle",
-            user_ticks,
-            system_ticks,
-            nice_ticks,
-            idle_ticks
-       );
-       PUTP(top_clrtoeol);
-       putchar('\n');
-    }
-    /*
-     * Save this frame's information.
-     */
-    for (i = 0; i < n; i++) {
-       /* copy the relevant info for the next pass */
-       save_history[i].pid = New_save_hist[i].pid;
-       save_history[i].ticks = New_save_hist[i].ticks;
-       save_history[i].stime = New_save_hist[i].stime;
-       save_history[i].utime = New_save_hist[i].utime;
-    }
-    free(New_save_hist);
-
-    prev_count = n;
-    qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+#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;
+   }
+
+      /* 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
+/*######  Main screen routines  ##########################################*/
 
-/*
- * Process keyboard input during the main loop
- */
-static void do_key(char c)
+        /*
+         * 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)
 {
-    int numinput, i;
-    char rcfile[MAXNAMELEN];
-    FILE *fp;
-
-    /*
-     * First the commands which don't require a terminal mode switch.
-     */
-    if (c == 'q')
-       end(0);
-    else if (c == ' ')
-        return;
-    else if (c == 12) {
-       clear_screen();
-       return;
-    } else if (c == 'I') {
-       Irixmode=(Irixmode) ? 0 : 1;
-       return;
-    }
-
-    /*
-     * Switch the terminal to normal mode.  (Will the original
-     * attributes always be normal?  Does it matter?  I suppose the
-     * shell will be set up the way the user wants it.)
-     */
-    if (!Batch) tcsetattr(0, TCSANOW, &Savetty);
-
-    /*
-     * Handle the rest of the commands.
-     */
-    switch (c) {
-      case '?':
-      case 'h':
-       PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr);
-       printf("Proc-Top Revision 1.2");
-       PUTP(me); putchar('\n');
-       printf("Secure mode ");
-       PUTP(md);
-       fputs(Secure ? "on" : "off", stdout);
-       PUTP(me);
-       fputs("; cumulative mode ", stdout);
-       PUTP(md);
-       fputs(Cumulative ? "on" : "off", stdout);
-       PUTP(me);
-       fputs("; noidle mode ", stdout);
-       PUTP(md);
-       fputs(Noidle ? "on" : "off", stdout);
-       PUTP(me);
-       fputs("\n\n", stdout);
-       printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
-       if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
-       (void) getchar();
-       break;
+#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;
+
+      case 'C':
+         Sort_type = S_CMD;
+         Sort_func = (QSORT_t)sort_cmd;
+         break;
+
+      case 'E':
+         Sort_type = S_USR;
+         Sort_func = (QSORT_t)sort_usr;
+         break;
+
+      case 'f':
+      case 'F':
+         fields_toggle();
+         break;
+
       case 'i':
-       Noidle = !Noidle;
-       SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
-       break;
-      case 'u':
-       SHOWMESSAGE(("Which User (Blank for All): "));
-       strcpy(CurrUser,getstr());
-       break;
+         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)
-           SHOWMESSAGE(("\aCan't kill in secure mode"));
-       else {
-           int pid, signo;
-           PUTP(md);
-           SHOWMESSAGE(("PID to kill: "));
-           pid = getint();
-           if (pid == BAD_INPUT)
-               break;
-           PUTP(top_clrtoeol);
-           SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid));
-           PUTP(me);
-           signo = getsig();
-           /* FIXME: -1 may mean an unknown signal */
-           if (signo == -1)
-               signo = SIGTERM;
-           if (kill(pid, signo))
-               SHOWMESSAGE(("\aKill of PID %d with %d failed: %s",
-                            pid, signo, strerror(errno)));
-       }
-       break;
+         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':
-       SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off"));
-       if (show_loadav) {
-           show_loadav = 0;
-           header_lines--;
-       } else {
-           show_loadav = 1;
-           header_lines++;
-       }
-       Numfields = make_header();
-       break;
+         HSum_loadav = !HSum_loadav;
+         mkheadings();
+         break;
+
       case 'm':
-       SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off"));
-       if (show_memory) {
-           show_memory = 0;
-           header_lines -= 2;
-       } else {
-           show_memory = 1;
-           header_lines += 2;
-       }
-       Numfields = make_header();
-       break;
+         HSum_memory = !HSum_memory;
+         mkheadings();
+         break;
+
       case 'M':
-        SHOWMESSAGE(("Sort by memory usage"));
-       sort_type = S_MEM;
-       reset_sort_options();
-       register_sort_function(-1, (cmp_t)mem_sort);
-       break;
+         Sort_type = S_MEM;
+         Sort_func = (QSORT_t)sort_mem;
+         break;
+
       case 'n':
       case '#':
-       printf("Processes to display (0 for unlimited): ");
-       numinput = getint();
-       if (numinput != -1) {
-           Display_procs = numinput;
-           window_size(0);
-       }
-       break;
-      case 'r':
-       if (Secure)
-           SHOWMESSAGE(("\aCan't renice in secure mode"));
-       else {
-           int pid, val;
-
-           printf("PID to renice: ");
-           pid = getint();
-           if (pid == BAD_INPUT)
-               break;
-           PUTP(tgoto(cm, 0, header_lines - 2));
-           PUTP(top_clrtoeol);
-           printf("Renice PID %d to value: ", pid);
-           val = getint();
-           if (val == BAD_INPUT)
-               val = 10;
-           if (setpriority(PRIO_PROCESS, pid, val))
-               SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s",
-                            pid, val, strerror(errno)));
-       }
-       break;
+      {  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();
+         break;
+
       case 'P':
-        SHOWMESSAGE(("Sort by CPU usage"));
-       sort_type = S_PCPU;
-       reset_sort_options();
-       register_sort_function(-1, (cmp_t)pcpu_sort);
-       break;
-      case 'A':
-       SHOWMESSAGE(("Sort by age"));
-       sort_type = S_AGE;
-       reset_sort_options();
-       register_sort_function(-1, (cmp_t)age_sort);
-       break;
-      case 'N':
-       SHOWMESSAGE(("Sort numerically by pid"));
-       sort_type = S_NONE;
-       reset_sort_options();
-       break;
-    case 'c':
-        show_cmd = !show_cmd;
-       SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line"));
-       break;
-      case 'S':
-       Cumulative = !Cumulative;
-       SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off"));
-       if (Cumulative)
-           headers[22][1] = 'C';
-       else
-           headers[22][1] = ' ';
-       Numfields = make_header();
-       break;
+         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':
-       if (Secure)
-           SHOWMESSAGE(("\aCan't change delay in secure mode"));
-       else {
-           double tmp;
-           printf("Delay between updates: ");
-           tmp = getfloat();
-           if (!(tmp < 0))
-               Sleeptime = tmp;
-       }
-       break;
+      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':
-       SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off"));
-       if (show_stats) {
-           show_stats = 0;
-           header_lines -= 2;
-       } else {
-           show_stats = 1;
-           header_lines += 2;
-       }
-       Numfields = make_header();
-       break;
+         HSum_states = !HSum_states;
+         mkheadings();
+         break;
+
       case 'T':
-       SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : ""));
-       sort_type = S_TIME;
-       reset_sort_options();
-       register_sort_function( -1, (cmp_t)time_sort);  
-       break;
-      case 'f':
-      case 'F':
-       change_fields();
-       break;
-      case 'o':
-      case 'O':
-       change_order();
-       break;
+         Sort_type = S_TME;
+         Sort_func = (QSORT_t)sort_tme;
+         break;
+
+      case 'u':
+         strcpy(ColUsername, ask_str("Which User (Blank for All)"));
+#ifdef TRAK_MAXBUFS
+         MAXBUFS(do_key, ColUsername);
+#endif
+         break;
+
+      case 'U':
+         Sort_type = S_CPU;
+         Sort_func = (QSORT_t)sort_cpu;
+         break;
+
       case 'W':
-       if (Secure)
-           SHOWMESSAGE(("\aCan't write configuration in secure mode"));
-       else {
-           if (getenv("HOME")) {
-               strcpy(rcfile, getenv("HOME"));
-             strcat(rcfile, "/");
-              strcat(rcfile, RCFILE);
-              fp = fopen(rcfile, "w");
-              if (fp != NULL) {
-                  fprintf(fp, "%s\n", Fields);
-                 i = (int) Sleeptime;
-                  if (i < 2)
-                      i = 2;
-                  if (i > 9)
-                      i = 9;
-                  fprintf(fp, "%d", i);
-                  if (Secure)
-                      fprintf(fp, "%c", 's');
-                  if (Cumulative)
-                      fprintf(fp, "%c", 'S');
-                  if (!show_cmd)
-                             fprintf(fp, "%c", 'c');
-                  if (Noidle)
-                      fprintf(fp, "%c", 'i');
-                  if (!show_memory)
-                      fprintf(fp, "%c", 'm');
-                  if (!show_loadav)
-                             fprintf(fp, "%c", 'l');
-                  if (!show_stats)
-                      fprintf(fp, "%c", 't');
-                  if (!Irixmode)
-                      fprintf(fp, "%c", 'I');
-                  fprintf(fp, "\n");
-                 fclose(fp);
-                 SHOWMESSAGE(("Wrote configuration to %s", rcfile));
-              } else {
-                  SHOWMESSAGE(("Couldn't open %s", rcfile));
-              }
-            } else {
-                SHOWMESSAGE(("Couldn't get $HOME -- not saving"));
-            }
-          }
-        break;
+      {  FILE *fp = fopen(RCfile, "w");
+
+         if (fp) {
+            fprintf(fp, "%s\t\t# Fields displayed and ordering\n"
+               , CurFields);
+            fprintf(fp, "%s%s%s%s%s%s%s%s%s%s%s%s%s%c"
+                        "\t\t\t\t# Misc options (spelled poorly)\n"
+               , Show_cmdlin ? "c" : "", Sort_normal ? "R" : ""
+               , HSum_loadav ? "l" : "", Show_hirows ? "y" : ""
+               , HSum_memory ? "m" : "", Show_hicols ? "x" : ""
+               , Show_idleps ? "i" : "", Show_ctimes ? "S" : ""
+               , Irix_mode   ? "I" : "", HSum_states ? "t" : ""
+               , Show_hibold ? "b" : "", Show_colors ? "z" : ""
+               , Show_cpusum ? "1" : "", Sort_type);
+            fprintf(fp, "%d\t\t\t\t\t# Number of tasks shown\n"
+               , Max_tasks);
+            fprintf(fp, "%.1f\t\t\t\t\t# Delay between updates\n"
+               , Delay_time);
+            fprintf(fp, "%u,%u,%u\t\t\t\t\t# Base, Msgs & Head colors\n"
+               , Base_color, Msgs_color, Head_color);
+            fclose(fp);
+            show_msg(fmtmk("Wrote configuration to '%s'", RCfile));
+         } else
+            show_msg(fmtmk("\aFailed '%s' open: %s", RCfile, strerror(errno)));
+      }
+         break;
+
+      case 'x':
+         Show_hicols = !Show_hicols;
+         capsmk();
+         break;
+
+      case 'y':
+         Show_hirows = !Show_hirows;
+         capsmk();
+         break;
+
+      case 'Y':
+         Sort_type = S_TTY;
+         Sort_func = (QSORT_t)sort_tty;
+         break;
+
+      case 'z':
+         Show_colors = !Show_colors;
+         capsmk();
+         break;
+
+      case 'Z':
+         tweak_colors();
+         break;
+
+      case '?':
+      case 'h':
+      {  char ch;
+
+         printf("%s%s", Cap_clr_scr, Cap_curs_huge);
+            /* this string is well above ISO C89's minimum requirements! */
+         show_special(fmtmk(HELP_data
+            , Myname, procps_version
+            , Secure_mode ? "On" : "Off", Show_ctimes ? "On" : "Off", Delay_time
+            , Secure_mode ? "" : HELP_unsecured));
+         chin(0, &ch, 1);
+         putp(Cap_curs_norm);
+            /* our help screen currently provides for three 'immediate' keys,
+               two of which conflict with the main process display keys */
+         switch (ch) {
+            case 'j' :
+               strcpy(CurFields, JOB_FIELDS);
+               ch = 'T';        /* force sort on time */
+               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);
+         break;
+
+      case '1':
+         Show_cpusum = !Show_cpusum;
+         mkheadings();
+         break;
+
+      case '\n':          /* just ignore it */
+         break;
+
       default:
-       SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c));
-    }
-
-    /*
-     * Return to raw mode.
-     */
-    if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
-    return;
+         show_msg("\aUnknown command -- try 'h' for help");
+   }
+
+#undef kbdCTRL_L
+#undef smERROR
 }
 
 
-/*#####################################################################
- *#######   A readproctable function that uses already allocated  #####
- *#######   table entries.                                        #####
- *#####################################################################
- */
-#define Do(x) (flags & PROC_ ## x)
-
-static proc_t** readproctab2(int flags, proc_t** tab, ...) {
-    PROCTAB* PT = NULL;
-    static proc_t *buff;
-    int n = 0;
-    static int len = 0;
-    va_list ap;
-
-    va_start(ap, tab);         /* pass through args to openproc */
-    if (Do(UID)) {
-       /* temporary variables to ensure that va_arg() instances
-        * are called in the right order
-        */
-       uid_t* u;
-       int i;
-
-       u = va_arg(ap, uid_t*);
-       i = va_arg(ap, int);
-       PT = openproc(flags, u, i);
-    }
-    else if (Do(PID)) {
-       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
-       /* work around a bug in openproc() */
-       PT->procfs = NULL;
-       /* share some process time, since we skipped opendir("/proc") */
-       usleep (50*1000);
-    }
-    else if (Do(TTY) || Do(STAT))
-       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
-    else
-       PT = openproc(flags);
-    va_end(ap);
-    buff = (proc_t *) 1;
-    while (n<len && buff) {     /* read table: (i) already allocated chunks */
-       if (tab[n]->cmdline) {
-           free((void*)*tab[n]->cmdline);
-           tab[n]->cmdline = NULL;
-       }
-        buff = readproc(PT, tab[n]);
-       if (buff) n++;
-    }
-    if (buff) {
-       do {               /* (ii) not yet allocated chunks */
-           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
-           buff = readproc(PT, NULL);            /* final null to terminate */
-           if(buff) tab[n]=buff;
-           len++;
-           n++;
-       } while (buff);                   /* stop when NULL reached */
-       tab[n-1] = xcalloc(NULL, sizeof (proc_t));
-       tab[n-1]->pid=-1;                /* Mark end of Table */
-    } else {
-       if (n == len) {
-           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
-           tab[n] = xcalloc(NULL, sizeof (proc_t));
-           len++;
-       }
-       tab[n]->pid=-1;    /* Use this instead of NULL when not at the end of */
-    }                   /* the allocated space */
-    closeproc(PT);
-    return tab;
+#ifdef UGH_ITS_4_RH
+        /*
+         * Obtain memory information and display it.
+         * Return the total memory available as a page count which is
+         * then used in % memory calc's. */
+static unsigned frame_memory (void)
+{
+   /* don't be mislead by the proc/sysinfo subscripts, they're just poorly
+      chosen names for enumerations apparently designed to make source
+      lines as imbalanced and as long as possible */
+   unsigned long long **memarray;
+
+   if (!(memarray = meminfo()))
+      std_err("Failed /proc/meminfo read");
+
+   if (HSum_memory) {
+      show_special(fmtmk(MEMORY_line1
+         , BYTES_2K(memarray[meminfo_main][meminfo_total])
+         , BYTES_2K(memarray[meminfo_main][meminfo_used])
+         , BYTES_2K(memarray[meminfo_main][meminfo_free])
+         , BYTES_2K(memarray[meminfo_main][meminfo_buffers])));
+
+      show_special(fmtmk(MEMORY_line2
+         , BYTES_2K(memarray[meminfo_swap][meminfo_total])
+         , BYTES_2K(memarray[meminfo_swap][meminfo_used])
+         , BYTES_2K(memarray[meminfo_swap][meminfo_free])
+         , BYTES_2K(memarray[meminfo_total][meminfo_cached])));
+   }
+
+   return PAGE_CNT(memarray[meminfo_main][meminfo_total]);
+}
+
+#else
+
+        /*
+         * Obtain memory information and display it. */
+static void frame_memory (void)
+{
+   meminfo();
+   if (HSum_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));
+   }
+}
+#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)
+{
+        /* 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");
+
+   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);
+   tot_frme = u_frme + s_frme + n_frme + i_frme;
+   if (1 > tot_frme) tot_frme = 1;
+
+      /* display some kinda' cpu state percentages
+         (who or what is explained by the passed prefix) */
+   show_special(fmtmk(STATES_line2
+      , pfx
+      , (float)u_frme * 100 / tot_frme
+      , (float)s_frme * 100 / tot_frme
+      , (float)n_frme * 100 / tot_frme
+      , (float)i_frme * 100 / tot_frme));
+
+      /* remember for next time around */
+   cpu->u = u_tics;
+   cpu->s = s_tics;
+   cpu->n = n_tics;
+   cpu->i = i_tics;
+
+#undef TRIMz
+}
+
+
+        /*
+         * 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) */
+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 CPUS_t   *smpcpu;
+   HIST_t          *hist_new;
+   unsigned         total, running, sleeping, stopped, zombie;
+   float            etime;
+   int              i;
+
+   if (!hist_sav) {
+      hist_tot = 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 */
+      smpcpu = alloc_c((1 + Cpu_tot) * sizeof(CPUS_t));
+      showsav = Show_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) {
+      TICS_t tics;
+      proc_t *this = p[total];
+
+      switch (this->state) {
+         case 'S':
+         case 'D':
+            sleeping++;
+            break;
+         case 'T':
+            stopped++;
+            break;
+         case 'Z':
+            zombie++;
+            break;
+         case 'R':
+            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! */
+      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++) {
+         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;
+
+      total++;
+   } /* end: while 'p[total]->pid' */
+
+   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;
+      }
+
+         /* display Task states */
+      show_special(fmtmk(STATES_line1
+         , total, running, sleeping, stopped, zombie));
+
+         /* 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)
+            /* retrieve and display just the 1st /proc/stat line */
+         frame_smp(fp, CPU_FMTS_JUST1, &smpcpu[Cpu_tot], "Cpu(s) state:");
+      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);
+         }
+      }
+
+      fclose(fp);
+   } /* end: if 'show' */
+
+      /* save this frame's information */
+   hist_tot = total;
+   memcpy(hist_sav, hist_new, hist_siz);
+   free(hist_new);
+      /* finally, sort the processes on whatever... */
+   qsort(p, total, sizeof(proc_t *), (QSORT_t)Sort_func);
+}
+
+
+        /*
+         * 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;
+
+   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;
+      }
+   }
+   va_end(va);
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(mkcol, tmp);
+#endif
+}
+
+
+        /*
+         * Displays information for a single task. */
+#ifdef UGH_ITS_4_RH
+static void show_a_task (proc_t *task, unsigned mempgs)
+#else
+static void show_a_task (proc_t *task)
+#endif
+{
+   /* 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;
+
+   pad = 0;
+   rbuf[0] = '\0';
+
+   for (i = 0; i < NumFields; i++) {
+      char cbuf[COLBUFSIZ];
+      unsigned f, s, w;
+
+      cbuf[0] = '\0';
+      f = PFlags[i];
+      s = Fieldstab[f].scale;
+      w = Fieldstab[f].width;
+
+      switch (f) {
+         case P_CMD:
+         {  char *cmdptr, cmdnam[ROWBUFSIZ];
+
+            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);
+         }
+            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;
+
+            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];
+
+            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));
+#else
+               MKCOL(f, task->state, &pad, cbuf
+                  , fmtmk("x%08lx", (long)task->wchan));
+#endif
+            else
+               MKCOL(f, task->state, &pad, cbuf, wchan(task->wchan));
+            break;
+
+        } /* end: switch 'PFlags[i]' */
+#ifdef TRAK_MAXBUFS
+        MAXBUFS(show_a_task, cbuf);
+#endif
+        strcat(rbuf, cbuf);
+
+   } /* end: for 'NumFields' */
+#ifdef TRAK_MAXBUFS
+   MAXBUFS(show_a_task, rbuf);
+#endif
+
+   /* 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);
+
+#ifdef TRAK_MAXCAPS
+   if (pad > Max_pads) Max_pads = pad;
+   if (pad < Min_pads) Min_pads = pad;
+      /* now that we have TRAK_MAXBUFS, the next two duplicate some effort */
+   if ((int)strlen(rbuf) > Max_rbuf) Max_rbuf = (int)strlen(rbuf);
+   if ((int)strlen(rbuf) < Min_rbuf) Min_rbuf = (int)strlen(rbuf);
+#endif
+
+#undef MKCOL
+}
+
+
+        /*
+         * Read all process info and display it. */
+static void show_everything (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);
+      frame_states(p_table, 0);
+      sleep(1);
+   } else
+      putp(Batch ? "\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 Memory and Swap space usage */
+#ifdef UGH_ITS_4_RH
+   mempgs = frame_memory();
+#else
+   frame_memory();
+#endif
+
+  /*
+   ** 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]);
+#endif
+         if (!Batch) ++nline;
+      }
+      ++ntask;
+   }
+
+   printf("%s%s%s", Cap_clr_eos, tg2(0, MSG_line), Cap_clr_eol);
+   fflush(stdout);
+}
+
+\f
+/*######  Entry point  ###################################################*/
+
+int main (int dont_care_argc, char **argv)
+{
+   char not_really_tmp[OURPATHSZ];
+   int i;
+
+      /* 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
+   }
+
+      /* setup some important system stuff... */
+   Page_size = getpagesize();
+   Cpu_tot = sysconf(_SC_NPROCESSORS_ONLN);
+   if (1 > Cpu_tot) Cpu_tot = 1;
+   Cpu_map = alloc_r(NULL, sizeof(int) * Cpu_tot);
+   for (i = 0; i < Cpu_tot; i++)
+      Cpu_map[i] = i;
+
+   rcfiles_read();
+   parse_argvs(argv);
+   terminal_set();
+   window_resize(0);
+
+      /* set up signal handlers */
+   signal(SIGALRM,  stop);
+   signal(SIGHUP,   stop);
+   signal(SIGINT,   stop);
+   signal(SIGPIPE,  stop);
+   signal(SIGQUIT,  stop);
+   signal(SIGTERM,  stop);
+   signal(SIGTSTP,  suspend);
+   signal(SIGTTIN,  suspend);
+   signal(SIGTTOU,  suspend);
+   signal(SIGCONT,  window_resize);
+   signal(SIGWINCH, window_resize);
+
+      /* loop, collecting process info and sleeping */
+   do {
+      struct timeval tv;
+      fd_set fs;
+      char c;
+
+      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;
+         tv.tv_usec = (Delay_time - (int)Delay_time) * 1000000;
+         FD_ZERO(&fs);
+         FD_SET(STDIN_FILENO, &fs);
+         if (0 < select(STDIN_FILENO+1, &fs, NULL, NULL, &tv)
+         &&  0 < chin(0, &c, 1))
+            do_key((unsigned)c);
+      }
+   } while (1);
+
+   return 0;
 }
diff --git a/top.h b/top.h
index e7cc2e2ef14ee678a487a8382dd56c78f7815371..84f3a772d064e7ef83f9a9ffd456e860ea9b1eed 100644 (file)
--- a/top.h
+++ b/top.h
+/* top.h - Header file:         show Linux processes */
 /*
- * top.h header file 1996/05/18, 
- *
- * function prototypes, global data definitions and string constants.
+ * Copyright (c) 2002 - James C. Warner, <warnerjc@worldnet.att.net>
+ *    All rights reserved.
+ * 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Library General Public License for more details.
  */
+#ifndef _Itop
+#define _Itop
 
-static proc_t** readproctab2(int flags, proc_t** tab, ...);
-static void parse_options(char *Options, int secure);
-static void get_options(void);
-static void error_end(int rno);
-static void end(int signo);
-static void stop(int signo);
-static void window_size(int signo);
-static int make_header(void);
-static char *getstr(void);
-static int getsig(void);
-static float getfloat(void);
-static int time_sort(proc_t **P, proc_t **Q);
-static int pcpu_sort(proc_t **P, proc_t **Q);
-static int mem_sort(proc_t **P, proc_t **Q);
-static int age_sort(proc_t **P, proc_t **Q);
-static void show_fields(void);
-static void change_order(void);
-static void change_fields(void);
-static void show_task_info(proc_t *task);
-static void show_procs(void);
-static float get_elapsed_time(void);
-static void show_meminfo(void);
-static void do_stats(proc_t** p, float elapsed_time, int pass);
-static void do_key(char c);
-
-
-/* configurable field display support */
-
-static int pflags[30];
-static int Numfields;
-
-
-       /* Name of the config file (in $HOME)  */
-#ifndef RCFILE
-#define RCFILE         ".toprc"
-#endif
+        /* Determines for whom we're destined, debian or !#@*#! redhat ----- */
+//#define UGH_ITS_4_RH            /* use the redhat libproc conventions      */
 
-#ifndef SYS_TOPRC
-#define SYS_TOPRC      "/etc/toprc"
-#endif
+        /* 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 MAXLINES 2048
-#define MAXNAMELEN 1024
+//#define COLOR_SLEEPY            /* purples and blues (sissy colors)        */
+//#define COLOR_CONSOL            /* for linux consoles (white on black)     */
 
-/* this is what procps top does by default, so let's do this, if nothing is
- * specified
- */
-#ifndef DEFAULT_SHOW
-/*                       0         1         2         3 */
-/*                       0123456789012345678901234567890 */
-#define DEFAULT_SHOW    "AbcDgHIjklMnoTP|qrsuzyV{EFWX"
-#endif
-static char Fields[256] = "";
+        /* 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 PRETENDNOCAP            /* use a terminal without essential caps   */
 
-/* This structure stores some critical information from one frame to
-   the next. mostly used for sorting. Added cumulative and resident fields. */
-struct save_hist {
-    int ticks;
-    int pid;
-    int pcpu;
-    int utime;
-    int stime;
-};
 
-       /* The original terminal attributes. */
-static struct termios Savetty;
-       /* The new terminal attributes. */
-static struct termios Rawtty;
-       /* Cached termcap entries. */
-static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr;
-       /* Current window size.  Note that it is legal to set Display_procs
-          larger than can fit; if the window is later resized, all will be ok.
-          In other words: Display_procs is the specified max number of
-          processes to display (zero for infinite), and Maxlines is the actual
-          number. */
-static int Lines, Cols, Maxlines, Display_procs;
-       /* Maximum length to display of the command line of a process. */
-static unsigned Maxcmd;
-
-       /* Controls how long we sleep between screen updates.  Accurate to
-          microseconds. */
-static float Sleeptime = 5;
-       /* for opening/closing the system map */
-static int psdbsucc = 0;
-       /* Mode flags. */
-static int Irixmode = 1;
-static int Secure = 0;
-static int Cumulative = 0;
-static int Noidle = 0;
-
-static int CPU_states = 0;
-static char CurrUser[BUFSIZ];
-
-static int CL_pg_shift = (PAGE_SHIFT - 10);
-static int CL_wchan_nout = -1;
-
-static int show_stats = 1;    /* show status summary */
-static int show_memory = 1;   /* show memory summary */
-static int show_loadav = 1;   /* show load average and uptime */
-static int show_cmd = 1;      /* show command name instead of commandline */
-
-static pid_t monpids[520]; /* randomly chosen value */
-static const int monpids_max = sizeof(monpids)/sizeof(pid_t);
-static int monpids_index = 0;
-
-static int Loops = -1;        /* number of iterations. -1 loops forever */
-static int Batch = 0;         /* batch mode. Collect no input, dumb output */
-
-/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */
-enum {
-    S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE
-};
-/* default sorting by CPU% */ 
-static int sort_type = S_PCPU;
-
-/* flags for each possible field. At the moment up to 30 are supported */
-enum {
-    P_PID, P_PPID, P_EUID, P_EUSER,
-    P_PCPU, P_PMEM, P_TTY, P_PRI,
-    P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ,
-    P_SIZE, P_TRS, P_SWAP, P_SHARE,
-    P_A, P_WP, P_DT, P_RSS,
-    P_WCHAN, P_STAT, P_TIME, P_COMMAND,
-    P_LCPU, P_FLAGS, P_END
+/*######  Some Typedef's and Enum's  #####################################*/
+
+        /* This typedef attempts to ensure consistent 'ticks' handling. */
+typedef unsigned long TICS_t;
+
+        /* This structure consolidates the information that's used
+           in a variety of display roles. */
+typedef struct {
+   const char *head;    /* name for column headings + toggle/reorder fields */
+   const char *fmts;    /* sprintf format string for field display */
+   const int   width;   /* field width, if applicable */
+   const int   scale;   /* scale_num type, if applicable */
+   const int   sort;    /* sort type, if applicable (used soley by mkcol) */
+   const char *desc;    /* description for toggle/reorder fields */
+} FTAB_t;
+
+        /* This structure stores one piece of critical 'history'
+           information from one frame to the next -- we don't calc
+           and save data that goes unused like the old top! */
+typedef struct {
+   int    pid;
+   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 */
+typedef struct {
+   TICS_t u,    /* ticks count as represented in /proc/stat */
+          n,    /* (not in the order of our display) */
+          s,
+          i;
+} CPUS_t;
+
+        /* Sorted columns support. */
+typedef int (*QSORT_t)(const void *, const void *);
+enum sort {
+   S_CMD = 'C', S_MEM = 'M', S_TME = 'T', S_PID = 'P', S_TTY = 'Y',
+   S_CPU = 'U', S_USR = 'E'
 };
-/* corresponding headers */
-static char *headers[] =
-{
-    "  PID ", " PPID ", " UID ",
-    "USER     ", "%CPU ", "%MEM ",
-    "TTY      ", "PRI ", " NI ",
-    "PAGEIN ", "TSIZE ", "DSIZE ",
-    " SIZE ", " TRS ", "SWAP ",
-    "SHARE ", "  A ", " WP ",
-    "  D ", " RSS ", "WCHAN     ",
-    "STAT ", "  TIME ", "COMMAND",
-    "LC ",
-    "   FLAGS "
+
+        /* The scaling 'type' used with scale_num() -- this is how
+           the passed number is interpreted should scaling be necessary */
+enum scale_num {
+   SK_no, SK_Kb, SK_Mb, SK_Gb
 };
-/* corresponding field desciptions */
-static char *headers2[] =
-{
-    "Process Id", "Parent Process Id", "User Id",
-    "User Name", "CPU Usage", "Memory Usage",
-    "Controlling tty", "Priority", "Nice Value",
-    "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)",
-    "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb",
-    "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages",
-    "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function",
-    "Process Status", "CPU Time", "Command",
-    "Last used CPU (expect this to change regularly)",
-    "Task Flags (see linux/sched.h)"
+
+        /* Flags for each possible field.
+           At the moment 32 are supported [ see PFlags/PFLAGSSIZ ] */
+enum pflag {
+   P_PID, P_PPID, P_UID, P_USER, P_GROUP, P_TTY,
+   P_PR, P_NI,
+   P_NCPU, P_CPU, P_TIME, P_TIME2,
+   P_MEM, P_VIRT, P_SWAP, P_RES, P_CODE, P_DATA, P_SHR,
+   P_FAULT, P_DIRTY,
+   P_STA, P_CMD, P_WCHAN, P_FLAGS
 };
 
-       /* The header printed at the top of the process list.*/
-static char Header[MAXLINES];
-
-       /* The response to the interactive 'h' command. */
-#define HELP_SCREEN "\
-Interactive commands are:\n\
-\n\
-space\tUpdate display\n\
-^L\tRedraw the screen\n\
-fF\tadd and remove fields\n\
-oO\tChange order of displayed fields\n\
-h or ?\tPrint this list\n\
-S\tToggle cumulative mode\n\
-i\tToggle display of idle proceses\n\
-I\tToggle between Irix and Solaris views (SMP-only)\n\
-c\tToggle display of command name/line\n\
-l\tToggle display of load average\n\
-m\tToggle display of memory information\n\
-t\tToggle display of summary information\n\
-k\tKill a task (with any signal)\n\
-r\tRenice a task\n\
-N\tSort by pid (Numerically)\n\
-A\tSort by age\n\
-P\tSort by CPU usage\n\
-M\tSort by resident memory usage\n\
-T\tSort by time / cumulative time\n\
-u\tShow only a specific user\n\
-n or #\tSet the number of process to show\n\
-s\tSet the delay in seconds between updates\n\
-W\tWrite configuration file ~/.toprc\n\
-q\tQuit"
-#define SECURE_HELP_SCREEN "\
-Interactive commands available in secure mode are:\n\
-\n\
-space\tUpdate display\n\
-^L\tRedraw the screen\n\
-fF\tadd and remove fields\n\
-h or ?\tPrint this list\n\
-S\tToggle cumulative mode\n\
-i\tToggle display of idle proceses\n\
-c\tToggle display of command name/line\n\
-l\tToggle display of load average\n\
-m\tToggle display of memory information\n\
-t\tToggle display of summary information\n\
-n or #\tSet the number of process to show\n\
-u\tShow only a specific user\n\
-oO\tChange order of displayed fields\n\
-W\tWrite configuration file ~/.toprc\n\
-q\tQuit"
-
-       /* Number of lines needed to display the header information. */
-static int header_lines;
-
-/* ############## Some Macro definitions for screen handling ######### */
-       /* String to use in error messages. */
-#define PROGNAME "top"
-       /* Clear the screen. */
-#define clear_screen() \
-           printf("%s", cl)
-       /* Show an error in the context of the spiffy full-screen display. */
-#define SHOWMESSAGE(x) do {                    \
-           printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr);       \
-           printf x;                                   \
-           printf ("%s",me);                           \
-           fflush(stdout);                             \
-           sleep(2);                                   \
-       } while (0)
+
+/*######  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
+
+
+/*######  Display Support *Data*  ########################################*/
+
+        /* 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 */
+#define JOB_FIELDS  "ABWdefikqrstuxyLJMGHVNOPC"
+#define MEM_FIELDS  "AMNOPQRSTUWbcdefiklxyVGHJ"
+#define USR_FIELDS  "CDEFABWghiknopqrstuxyLJMV"
+
+
+        /* These are the possible fscanf formats used in /proc/stat
+           reads during history processing. */
+#define CPU_FMTS_MULTI  "cpu%*d %lu %lu %lu %lu\n"
+#define CPU_FMTS_JUST1  "cpu %lu %lu %lu %lu\n"
+
+
+        /* Summary Lines specially formatted string(s) --
+           see 'show_special' for syntax details + other cautions. */
+#define LOADAV_line   "%s -\03%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
+#define MEMORY_line1  "Mem: \03" \
+   " %8uK \02total,\03 %8uK \02used,\03" \
+   " %8uK \02free,\03 %8uK \02buffers\03\n"
+#define MEMORY_line2  "Swap:\03" \
+   " %8uK \02total,\03 %8uK \02used,\03" \
+   " %8uK \02free,\03 %8uK \02cached\03\n"
+#else
+#define MEMORY_line1  "Mem: \03" \
+   " %8uk \02total,\03 %8uk \02used,\03" \
+   " %8uk \02free,\03 %8uk \02buffers\03\n"
+#define MEMORY_line2  "Swap:\03" \
+   " %8uk \02total,\03 %8uk \02used,\03" \
+   " %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 \
+   "%s%s's \01Help for color mapping\02 - %s\n" \
+   "\n" \
+   "   color -\03 08:41:24 up 1 day,  6:07,  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" \
+   "Select \01target\02 as upper case letter:\n" \
+   "   B\02 = Base color,\01  H\02 = Column Headings,\01  M\02 = Messages/Prompts\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 " \
+   ""
+
+
+        /* 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" \
+   "\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 " \
+   ""
+
+        /* 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" \
+   ""
+
+
+        /* 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 " \
+   ""
+
+        /* Some extra explanatory text which accompanies the Fields display.
+           note: the newlines cannot actually be used, they just serve as
+                 substring delimiters for the 'display_fields' routine. */
+#define FIELDS_xtra \
+   "Flags field:\n" \
+   "  0x00000001  PF_ALIGNWARN\n" \
+   "  0x00000002  PF_STARTING\n" \
+   "  0x00000004  PF_EXITING\n" \
+   "  0x00000040  PF_FORKNOEXEC\n" \
+   "  0x00000100  PF_SUPERPRIV\n" \
+   "  0x00000200  PF_DUMPCORE\n" \
+   "  0x00000400  PF_SIGNALED\n" \
+   "  0x00000800  PF_MEMALLOC\n" \
+   "  0x00040000  PF_KERNTHREAD (2.5)\n" \
+   "  0x00100000  PF_USEDFPU (thru 2.4)\n" \
+   "  0x00400000  PF_ATOMICALLOC\n" \
+   "\n" \
+   "Memory notes:\n" \
+   "  VIRT = SWAP + RES\n" \
+   "  RES  = CODE + DATA\n" \
+   ""
+\f
+/*######  Some Prototypes (ha!)  #########################################*/
+
+   /* None of these are necessary when the source file is properly
+    * organized -- they're here for documentation purposes !
+    * Note also that functions are alphabetical within a group to aid
+    * source code navigation, which often influences choice of identifers. */
+/*------  Sort callbacks  ------------------------------------------------*/
+//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);
+/*------  Tiny useful routine(s)  ----------------------------------------*/
+//atic int         chin (int ech, char *buf, unsigned cnt);
+//atic const char *fmtmk (const char *fmts, ...);
+//atic char       *strim (int sp, char *str);
+//atic char       *tg2 (int x, int y);
+/*------  Misc Color/Highlighting support  -------------------------------*/
+//atic void        capsmk (void);
+//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);
+//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);
+/*------  Startup routines  ----------------------------------------------*/
+//atic void        parse_argvs (char **argv);
+//atic void        parse_rc (char *opts);
+//atic void        rcfiles_read (void);
+//atic void        terminal_set (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);
+//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);
+/*------  Entry point  ---------------------------------------------------*/
+//     int         main (int dont_care_argc, char **argv);
+
+
+        /* just sanity check(s)... */
+#if USRNAMSIZ < GETBUFSIZ
+ #error "Jeeze, USRNAMSIZ Must NOT be less than GETBUFSIZ !"
+#endif
+
+#endif /* _Itop */
+\f