bin/127633: [patch] Update top(1) to 3.8b1

Edwin Groothuis edwin at mavetju.org
Thu Sep 25 15:30:04 UTC 2008


>Number:         127633
>Category:       bin
>Synopsis:       [patch] Update top(1) to 3.8b1
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Thu Sep 25 15:30:03 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Edwin Groothuis
>Release:        FreeBSD 7.1-PRERELEASE i386
>Organization:
-
>Environment:
System: FreeBSD k7.mavetju 7.1-PRERELEASE FreeBSD 7.1-PRERELEASE #0 r183246M: Mon Sep 22 15:32:35 EST 2008 edwin at k7.mavetju:/mnt/ad8/obj/usr/home/edwin/svn/stable-7/sys/GENERIC i386


>Description:
>How-To-Repeat:
>Fix:


Number of lines changed in the FreeBSD CVS repository: 12921
Number of lines changed from the top 3.8b1 code: 1502


There are three patches:

- The first patch is for base/usr.bin/top.
- The second patch is for base/contrib/top.
- The third patch is for against the clean 3.8b1 code.


Suggested approach:

- Import stock 3.8b1 code in base/vendor/top/dist and tag it.
- Merge into base/head/contrib/top and base/head/contrib/usr.bin/top.
  This will give a lot of conflicts etc.
- Remove all files in base/head/contrib/top and resolve all issues.
- Unpack 3.8b1 code in base/head/contrib/top.
- Apply third patch against base/head/contrib/top and
  base/head/usr.bin/top.
- Commit everything.


In src/usr.bin/top:

Index: machine.c
===================================================================
--- machine.c	(revision 183246)
+++ machine.c	(working copy)
@@ -1,1380 +1,1810 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+/*
  * top - a top users display for Unix
  *
- * SYNOPSIS:  For FreeBSD-2.x and later
+ * SYNOPSIS:  For FreeBSD 5.x, 6.x, 7.x, 8.x
  *
  * DESCRIPTION:
  * Originally written for BSD4.4 system by Christos Zoulas.
  * Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
  * Order support hacked in from top-3.5beta6/machine/m_aix41.c
- *   by Monte Mitzelfelt (for latest top see http://www.groupsys.com/topinfo/)
+ *   by Monte Mitzelfelt
+ * Ported to FreeBSD 5.x and higher by William LeFebvre
+ * Updates for the import in FreeBSD 7 by Edwin Groothuis.
  *
- * This is the machine-dependent module for FreeBSD 2.2
- * Works for:
- *	FreeBSD 2.2.x, 3.x, 4.x, and probably FreeBSD 2.1.x
- *
- * LIBS: -lkvm
- *
  * AUTHOR:  Christos Zoulas <christos at ee.cornell.edu>
  *          Steven Wallace  <swallace at freebsd.org>
  *          Wolfram Schneider <wosch at FreeBSD.org>
- *          Thomas Moestl <tmoestl at gmx.net>
- *
- * $FreeBSD$
  */
 
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/signal.h>
 #include <sys/param.h>
+
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <nlist.h>
+#include <math.h>
+#include <kvm.h>
+#include <paths.h>
+#include <pwd.h>
 #include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/dkstat.h>
 #include <sys/file.h>
+#include <sys/time.h>
 #include <sys/proc.h>
+#include <sys/user.h>
+#include <sys/vmmeter.h>
 #include <sys/resource.h>
 #include <sys/rtprio.h>
-#include <sys/signal.h>
-#include <sys/sysctl.h>
-#include <sys/time.h>
-#include <sys/user.h>
-#include <sys/vmmeter.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 
-#include <kvm.h>
-#include <math.h>
-#include <nlist.h>
-#include <paths.h>
-#include <pwd.h>
-#include <stdio.h>
+/* Swap */
 #include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-#include <vis.h>
+#include <sys/conf.h>
 
+#include <osreldate.h> /* for changes in kernel structures */
+
 #include "top.h"
 #include "machine.h"
-#include "screen.h"
 #include "utils.h"
-#include "layout.h"
+#include "username.h"
+#include "hash.h"
+#include "display.h"
 
-#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var))
-#define	SMPUNAMELEN	13
-#define	UPUNAMELEN	15
+extern char* printable __P((char *));
+int swapmode __P((int *retavail, int *retfree));
+static int smpmode;
+static int namelength;
 
-extern struct process_select ps;
-extern char* printable(char *);
-static int smpmode;
-enum displaymodes displaymode;
-#ifdef TOP_USERNAME_LEN
-static int namelength = TOP_USERNAME_LEN;
-#else
-static int namelength = 8;
+/*
+ * Versions prior to 5.x do not track threads in kinfo_proc, so we
+ * simply do not display any information about them.
+ * Versions 5.x, 6.x, and 7.x track threads but the data reported
+ * as runtime for each thread is actually per-process and is just
+ * duplicated across all threads.  It would be very wrong to show
+ * this data individually for each thread.  Therefore we will show
+ * a THR column (number of threads) but not provide any sort of
+ * per-thread display.  We distinguish between these three ways of
+ * handling threads as follows:  HAS_THREADS indicates that the
+ * system has and tracks kernel threads (a THR column will appear
+ * in the display).  HAS_SHOWTHREADS indicates that the system 
+ * reports correct per-thread information and we will provide a
+ * per-thread display (the 'H' and 't' command) upon request.
+ * HAS_SHOWTHREADS implies HAS_THREADS.
+ */
+
+/* HAS_THREADS for anything 5.x and up */
+#if OSMAJOR >= 5
+#define HAS_THREADS
 #endif
-static int cmdlengthdelta;
 
-/* Prototypes for top internals */
-void quit(int);
+/* HAS_SHOWTHREADS for anything 8.x and up */
+#if OSMAJOR >=8
+#define HAS_SHOWTHREADS
+#endif
 
 /* get_process_info passes back a handle.  This is what it looks like: */
 
-struct handle {
-	struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
-	int remaining;			/* number of pointers remaining */
+struct handle
+{
+    struct kinfo_proc **next_proc;	/* points to next valid proc pointer */
+    int remaining;		/* number of pointers remaining */
 };
 
 /* declarations for load_avg */
 #include "loadavg.h"
 
-/* define what weighted cpu is.  */
-#define weighted_cpu(pct, pp) ((pp)->ki_swtime == 0 ? 0.0 : \
-			 ((pct) / (1.0 - exp((pp)->ki_swtime * logcpu))))
-
-/* what we consider to be process size: */
-#define PROCSIZE(pp) ((pp)->ki_size / 1024)
-
-#define RU(pp)	(&(pp)->ki_rusage)
-#define RUTOT(pp) \
-	(RU(pp)->ru_inblock + RU(pp)->ru_oublock + RU(pp)->ru_majflt)
-
-
-/* definitions for indices in the nlist array */
-
 /*
- *  These definitions control the format of the per-process area
+ * Macros to access process information:
+ * In versions 4.x and earlier the kinfo_proc structure was a collection of
+ * substructures (kp_proc and kp_eproc).  Starting with 5.0 kinfo_proc was
+ * redesigned and "flattene" so that most of the information was available
+ * in a single structure.  We use macros to access the various types of
+ * information and define these macros according to the OS revision.  The
+ * names PP, EP, and VP are due to the fact that information was originally
+ * contained in the different substructures.  We retain these names in the
+ * code for backward compatibility.  These macros use ANSI concatenation.
+ * PP: proc
+ * EP: extented proc
+ * VP: vm (virtual memory information)
+ * PRUID: Real uid
+ * RP: rusage
+ * PPCPU: where we store calculated cpu% data
+ * SPPTR: where we store pointer to extra calculated data
+ * SP: access to the extra calculated data pointed to by SPPTR
  */
+#if OSMAJOR <= 4
+#define PP(pp, field) ((pp)->kp_proc . p_##field)
+#define EP(pp, field) ((pp)->kp_eproc . e_##field)
+#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field)
+#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid)
+#else
+#define PP(pp, field) ((pp)->ki_##field)
+#define EP(pp, field) ((pp)->ki_##field)
+#define VP(pp, field) ((pp)->ki_##field)
+#define PRUID(pp) ((pp)->ki_ruid)
+#define RP(pp, field) ((pp)->ki_rusage.ru_##field)
+#define PPCPU(pp) ((pp)->ki_sparelongs[0])
+#define SPPTR(pp) ((pp)->ki_spareptrs[0])
+#define SP(pp, field) (((struct save_proc *)((pp)->ki_spareptrs[0]))->sp_##field)
+#endif
 
-static char io_header[] =
-    "  PID%s %-*.*s   VCSW  IVCSW   READ  WRITE  FAULT  TOTAL PERCENT COMMAND";
+/* what we consider to be process size: */
+#if OSMAJOR <= 4
+#define PROCSIZE(pp) (VP((pp), map.size) / 1024)
+#else
+#define PROCSIZE(pp) (((pp)->ki_size) / 1024)
+#endif
 
-#define io_Proc_format \
-    "%5d%s %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %6.2f%% %.*s"
+/* calculate a per-second rate using milliseconds */
+#define per_second(n, msec)   (((n) * 1000) / (msec))
 
-static char smp_header_thr[] =
-    "  PID%s %-*.*s  THR PRI NICE   SIZE    RES STATE  C   TIME %6s COMMAND";
-static char smp_header[] =
-    "  PID%s %-*.*s "   "PRI NICE   SIZE    RES STATE  C   TIME %6s COMMAND";
-
-#define smp_Proc_format \
-    "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s %1x%7s %5.2f%% %.*s"
-
-static char up_header_thr[] =
-    "  PID%s %-*.*s  THR PRI NICE   SIZE    RES STATE    TIME %6s COMMAND";
-static char up_header[] =
-    "  PID%s %-*.*s "   "PRI NICE   SIZE    RES STATE    TIME %6s COMMAND";
-
-#define up_Proc_format \
-    "%5d%s %-*.*s %s%3d %4s%7s %6s %-6.6s%.0d%7s %5.2f%% %.*s"
-
-
 /* process state names for the "STATE" column of the display */
 /* the extra nulls in the string "run" are for adding a slash and
    the processor number when needed */
 
-char *state_abbrev[] = {
-	"", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK"
+char *state_abbrev[] =
+{
+    "?", "START", "RUN", "SLEEP", "STOP", "ZOMB", "WAIT", "LOCK"
 };
+#define NUM_STATES 8
 
-
+/* kernel access */
 static kvm_t *kd;
 
-/* values that we stash away in _init and use in later routines */
+/* these are for dealing with sysctl-based data */
+#define MAXMIBLEN 8
+struct sysctl_mib {
+    char *name;
+    int mib[MAXMIBLEN];
+    size_t miblen;
+};
+static struct sysctl_mib mibs[] = {
+    { "vm.stats.sys.v_swtch" },
+#define V_SWTCH 0
+    { "vm.stats.sys.v_trap" },
+#define V_TRAP 1
+    { "vm.stats.sys.v_intr" },
+#define V_INTR 2
+    { "vm.stats.sys.v_soft" },
+#define V_SOFT 3
+    { "vm.stats.vm.v_forks" },
+#define V_FORKS 4
+    { "vm.stats.vm.v_vforks" },
+#define V_VFORKS 5
+    { "vm.stats.vm.v_rforks" },
+#define V_RFORKS 6
+    { "vm.stats.vm.v_vm_faults" },
+#define V_VM_FAULTS 7
+    { "vm.stats.vm.v_swapin" },
+#define V_SWAPIN 8
+    { "vm.stats.vm.v_swapout" },
+#define V_SWAPOUT 9
+    { "vm.stats.vm.v_tfree" },
+#define V_TFREE 10
+    { "vm.stats.vm.v_vnodein" },
+#define V_VNODEIN 11
+    { "vm.stats.vm.v_vnodeout" },
+#define V_VNODEOUT 12
+    { "vm.stats.vm.v_active_count" },
+#define V_ACTIVE_COUNT 13
+    { "vm.stats.vm.v_inactive_count" },
+#define V_INACTIVE_COUNT 14
+    { "vm.stats.vm.v_wire_count" },
+#define V_WIRE_COUNT 15
+    { "vm.stats.vm.v_cache_count" },
+#define V_CACHE_COUNT 16
+    { "vm.stats.vm.v_free_count" },
+#define V_FREE_COUNT 17
+    { "vm.stats.vm.v_swappgsin" },
+#define V_SWAPPGSIN 18
+    { "vm.stats.vm.v_swappgsout" },
+#define V_SWAPPGSOUT 19
+    { "vfs.bufspace" },
+#define VFS_BUFSPACE 20
+    { "kern.cp_time" },
+#define K_CP_TIME 21
+    { "kern.cp_times" },
+#define K_CP_TIMES 22
+#ifdef HAS_SHOWTHREADS
+    { "kern.proc.all" },
+#else
+    { "kern.proc.proc" },
+#endif
+#define K_PROC 23
+#define K_LASTPID 24
+    { "kern.lastpid" },
+    { NULL }
+};
+    
+/* Local pointer */
+static struct statics *statics;
 
-static double logcpu;
-
-/* these are retrieved from the kernel in _init */
-
-static load_avg  ccpu;
-
-/* these are used in the get_ functions */
-
-static int lastpid;
-
 /* these are for calculating cpu state percentages */
 
-static long cp_time[CPUSTATES];
-static long cp_old[CPUSTATES];
-static long cp_diff[CPUSTATES];
+static long *cp_time;	/* num_cpus * CPUSTATES */
+static long *cp_old;	/* num_cpus * CPUSTATES */
+static long *cp_diff;	/* num_cpus * CPUSTATES */
 
 /* these are for detailing the process states */
 
 int process_states[8];
 char *procstatenames[] = {
-	"", " starting, ", " running, ", " sleeping, ", " stopped, ",
-	" zombie, ", " waiting, ", " lock, ",
-	NULL
+    "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ",
+    " waiting, ", " locked, ",
+    NULL
 };
 
 /* these are for detailing the cpu states */
 
-int cpu_states[CPUSTATES];
+int *cpu_states;
 char *cpustatenames[] = {
-	"user", "nice", "system", "interrupt", "idle", NULL
+    "user", "nice", "system", "interrupt", "idle", NULL
 };
 
+/* these are for detailing the kernel information */
+
+int kernel_stats[9];
+char *kernelnames[] = {
+    " ctxsw, ", " trap, ", " intr, ", " soft, ", " fork, ",
+    " flt, ", " pgin, ", " pgout, ", " fr",
+    NULL
+};
+
 /* these are for detailing the memory statistics */
 
-int memory_stats[7];
+long memory_stats[7];
 char *memorynames[] = {
-	"K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ",
-	"K Free", NULL
+    "K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
+    NULL
 };
 
-int swap_stats[7];
+long swap_stats[7];
 char *swapnames[] = {
-	"K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
-	NULL
+/*   0           1            2           3            4       5 */
+    "K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
+    NULL
 };
 
 
-/* these are for keeping track of the proc array */
+/*
+ * pbase points to the array that holds the kinfo_proc structures.  pref
+ * (pronounced p-ref) points to an array of kinfo_proc pointers and is where
+ * we build up a list of processes we wish to display.  Both pbase and pref are
+ * potentially resized on every call to get_process_info.  psize is the number
+ * of procs for which we currently have space allocated.  pref_len is the number
+ * of valid pointers in pref (this is used by proc_owner).  We start psize off
+ * at -1 to ensure that space gets allocated on the first call to
+ * get_process_info.
+ */
 
-static int nproc;
-static int onproc = -1;
+static int psize = -1;
 static int pref_len;
-static struct kinfo_proc *pbase;
-static struct kinfo_proc **pref;
-static struct kinfo_proc *previous_procs;
-static struct kinfo_proc **previous_pref;
-static int previous_proc_count = 0;
-static int previous_proc_count_max = 0;
+static struct kinfo_proc *pbase = NULL;
+static struct kinfo_proc **pref = NULL;
 
-/* total number of io operations */
-static long total_inblock;
-static long total_oublock;
-static long total_majflt;
+/* this structure retains information from the proc array between samples */
+struct save_proc {
+    pid_t sp_pid;
+    u_int64_t sp_runtime;
+    long sp_vcsw;
+    long sp_ivcsw;
+    long sp_inblock;
+    long sp_oublock;
+    long sp_majflt;
+    long sp_totalio;
+    long sp_old_nvcsw;
+    long sp_old_nivcsw;
+    long sp_old_inblock;
+    long sp_old_oublock;
+    long sp_old_majflt;
+};
+hash_table *procs;
 
+struct proc_field {
+    char *name;
+    int width;
+    int rjust;
+    int min_screenwidth;
+    int (*format)(char *, int, struct kinfo_proc *);
+};
+
 /* these are for getting the memory statistics */
 
+static int pagesize;		/* kept from getpagesize */
 static int pageshift;		/* log base 2 of the pagesize */
 
 /* define pagetok in terms of pageshift */
 
 #define pagetok(size) ((size) << pageshift)
 
-/* useful externals */
-long percentages();
+/* things that we track between updates */
+static u_int ctxsws = 0;
+static u_int traps = 0;
+static u_int intrs = 0;
+static u_int softs = 0;
+static u_int64_t forks = 0;
+static u_int pfaults;
+static u_int pagein;
+static u_int pageout;
+static u_int tfreed;
+static int swappgsin = -1;
+static int swappgsout = -1;
+extern struct timeval timeout;
+static struct timeval lasttime = { 0, 0 };
+static long elapsed_time;
+static long elapsed_msecs;
 
-#ifdef ORDER
-/*
- * Sorting orders.  The first element is the default.
- */
+/* things that we track during an update */
+static long total_io;
+static int show_fullcmd;
+static struct handle handle;
+static int username_length;
+static int show_usernames;
+static int display_mode;
+static int *display_fields;
+#ifdef HAS_SHOWTHREADS
+static int show_threads = 0;
+#endif
+
+
+/* sorting orders. first is default */
 char *ordernames[] = {
-	"cpu", "size", "res", "time", "pri", "threads",
-	"total", "read", "write", "fault", "vcsw", "ivcsw",
-	"jid", NULL
+    "cpu", "size", "res", "time", "pri", "io", "pid", "jid", NULL
 };
-#endif
 
-/* Per-cpu time states */
-static int maxcpu;
-static int maxid;
-static int ncpus;
-static u_long cpumask;
-static long *times;
-static long *pcpu_cp_time;
-static long *pcpu_cp_old;
-static long *pcpu_cp_diff;
-static int *pcpu_cpu_states;
+/* compare routines */
+int proc_compare(), compare_size(), compare_res(), compare_time(),
+    compare_prio(), compare_io(), compare_pid(), compare_jid();
 
-static int compare_jid(const void *a, const void *b);
-static int compare_pid(const void *a, const void *b);
-static const char *format_nice(const struct kinfo_proc *pp);
-static void getsysctl(const char *name, void *ptr, size_t len);
-static int swapmode(int *retavail, int *retfree);
+int (*proc_compares[])() = {
+    proc_compare,
+    compare_size,
+    compare_res,
+    compare_time,
+    compare_prio,
+    compare_io,
+    compare_pid,
+    compare_jid,
+    NULL
+};
 
+/* swap related calculations */
+
+static int mib_swapinfo[16];
+static int *mib_swapinfo_idx;
+static int mib_swapinfo_size = 0;
+
+void
+swap_init()
+
+{
+    size_t m;
+
+    m = sizeof(mib_swapinfo) / sizeof(mib_swapinfo[0]);
+    if (sysctlnametomib("vm.swap_info", mib_swapinfo, &m) != -1)
+    {
+	mib_swapinfo_size = m + 1;
+	mib_swapinfo_idx = &(mib_swapinfo[m]);
+    }
+}
+
 int
-machine_init(struct statics *statics, char do_unames)
+swap_getdata(long long *retavail, long long *retfree)
+
 {
-	int pagesize;
-	size_t modelen;
-	struct passwd *pw;
+    int n;
+    size_t size;
+    long long total = 0;
+    long long used = 0;
+    struct xswdev xsw;
 
-	modelen = sizeof(smpmode);
-	if ((sysctlbyname("machdep.smp_active", &smpmode, &modelen,
-	    NULL, 0) != 0 &&
-	    sysctlbyname("kern.smp.active", &smpmode, &modelen,
-	    NULL, 0) != 0) ||
-	    modelen != sizeof(smpmode))
-		smpmode = 0;
+    n = 0;
+    if (mib_swapinfo_size > 0)
+    {
+	*mib_swapinfo_idx = 0;
+	while (size = sizeof(xsw),
+	       sysctl(mib_swapinfo, mib_swapinfo_size, &xsw, &size, NULL, 0) != -1)
+	{
+	    dprintf("swap_getdata: swaparea %d: nblks %d, used %d\n",
+		    n, xsw.xsw_nblks, xsw.xsw_used);
+	    total += (long long)xsw.xsw_nblks;
+	    used += (long long)xsw.xsw_used;
+	    *mib_swapinfo_idx = ++n;
+	}
 
-	if (do_unames) {
-	    while ((pw = getpwent()) != NULL) {
-		if (strlen(pw->pw_name) > namelength)
-			namelength = strlen(pw->pw_name);
-	    }
+	*retavail = pagetok(total);
+	*retfree = pagetok(total) - pagetok(used);
+
+	if (total > 0)
+	{
+	    n = (int)((double)used * 100.0 / (double)total);
 	}
-	if (smpmode && namelength > SMPUNAMELEN)
-		namelength = SMPUNAMELEN;
-	else if (namelength > UPUNAMELEN)
-		namelength = UPUNAMELEN;
+	else
+	{
+	    n = 0;
+	}
+    }
+    else
+    {
+	*retavail = 0;
+	*retfree = 0;
+    }
 
-	kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open");
-	if (kd == NULL)
-		return (-1);
+    dprintf("swap_getdata: avail %lld, free %lld, %d%%\n",
+	    *retavail, *retfree, n);
+    return(n);
+}
 
-	GETSYSCTL("kern.ccpu", ccpu);
+/*
+ *  getkval(offset, ptr, size) - get a value out of the kernel.
+ *	"offset" is the byte offset into the kernel for the desired value,
+ *  	"ptr" points to a buffer into which the value is retrieved,
+ *  	"size" is the size of the buffer (and the object to retrieve).
+ *      Return 0 on success, -1 on any kind of failure.
+ */
+#ifdef notmore
+static int
+getkval(unsigned long offset, int *ptr, int size)
 
-	/* this is used in calculating WCPU -- calculate it ahead of time */
-	logcpu = log(loaddouble(ccpu));
+{
+    if (kd != NULL)
+    {
+	if (kvm_read(kd, offset, (char *) ptr, size) == size)
+	{
+	    return(0);
+	}
+    }
+    return(-1);
+}
+#endif
 
-	pbase = NULL;
-	pref = NULL;
-	nproc = 0;
-	onproc = -1;
+int
+get_sysctl_mibs()
 
-	/* get the page size and calculate pageshift from it */
-	pagesize = getpagesize();
-	pageshift = 0;
-	while (pagesize > 1) {
-		pageshift++;
-		pagesize >>= 1;
+{
+    struct sysctl_mib *mp;
+    size_t len;
+
+    mp = mibs;
+    while (mp->name != NULL)
+    {
+	len = MAXMIBLEN;
+	if (sysctlnametomib(mp->name, mp->mib, &len) == -1)
+	{
+	    message_error(" sysctlnametomib: %s", strerror(errno));
+	    return -1;
 	}
+	mp->miblen = len;
+	mp++;
+    }
+    return 0;
+}
 
-	/* we only need the amount of log(2)1024 for our conversion */
-	pageshift -= LOG1024;
+int
+get_sysctl(int idx, void *v, size_t l)
 
-	/* fill in the statics information */
-	statics->procstate_names = procstatenames;
-	statics->cpustate_names = cpustatenames;
-	statics->memory_names = memorynames;
-	statics->swap_names = swapnames;
-#ifdef ORDER
-	statics->order_names = ordernames;
+{
+    struct sysctl_mib *m;
+    size_t len;
+
+    m = &(mibs[idx]);
+    len = l;
+    if (sysctl(m->mib, m->miblen, v, &len, NULL, 0) == -1)
+    {
+	message_error(" sysctl: %s", strerror(errno));
+	return -1;
+    }
+    return len;
+}
+
+size_t
+get_sysctlsize(int idx)
+
+{
+    size_t len;
+    struct sysctl_mib *m;
+
+    m = &(mibs[idx]);
+    if (sysctl(m->mib, m->miblen, NULL, &len, NULL, 0) == -1)
+    {
+	message_error(" sysctl (size): %s", strerror(errno));
+	len = 0;
+    }
+    return len;
+}
+    
+int
+fmt_pid(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6d", PP(pp, pid));
+}
+    
+int
+fmt_jid(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%3d", pp->ki_jid);
+}
+
+int
+fmt_username(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%-*.*s",
+		    username_length, username_length, username(PRUID(pp)));
+}
+
+int
+fmt_uid(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6d", PRUID(pp));
+}
+
+int
+fmt_thr(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%3d", PP(pp, numthreads));
+}
+
+int
+fmt_pri(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+#if OSMAJOR <= 4
+    return snprintf(buf, sz, "%3d", PP(pp, priority));
+#else
+    return snprintf(buf, sz, "%3d", PP(pp, pri.pri_level));
 #endif
+}
 
-	/* Adjust display based on ncpus */
-	if (pcpu_stats) {
-		int i, j, empty;
-		size_t size;
+int
+fmt_nice(char *buf, int sz, struct kinfo_proc *pp)
 
-		cpumask = 0;
-		ncpus = 0;
-		GETSYSCTL("kern.smp.maxcpus", maxcpu);
-		size = sizeof(long) * maxcpu * CPUSTATES;
-		times = malloc(size);
-		if (times == NULL)
-			err(1, "malloc %zd bytes", size);
-		if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1)
-			err(1, "sysctlbyname kern.cp_times");
-		pcpu_cp_time = calloc(1, size);
-		maxid = (size / CPUSTATES / sizeof(long)) - 1;
-		for (i = 0; i <= maxid; i++) {
-			empty = 1;
-			for (j = 0; empty && j < CPUSTATES; j++) {
-				if (times[i * CPUSTATES + j] != 0)
-					empty = 0;
-			}
-			if (!empty) {
-				cpumask |= (1ul << i);
-				ncpus++;
-			}
-		}
+{
+    return snprintf(buf, sz, "%4d", PP(pp, nice) - NZERO);
+}
 
-		if (ncpus > 1) {
-			y_mem += ncpus - 1;	/* 3 */
-			y_swap += ncpus - 1;	/* 4 */
-			y_idlecursor += ncpus - 1; /* 5 */
-			y_message += ncpus - 1;	/* 5 */
-			y_header += ncpus - 1;	/* 6 */
-			y_procs += ncpus - 1;	/* 7 */
-			Header_lines += ncpus - 1; /* 7 */
-		}
-		size = sizeof(long) * ncpus * CPUSTATES;
-		pcpu_cp_old = calloc(1, size);
-		pcpu_cp_diff = calloc(1, size);
-		pcpu_cpu_states = calloc(1, size);
-		statics->ncpus = ncpus;
-	} else {
-		statics->ncpus = 1;
-	}
+int
+fmt_size(char *buf, int sz, struct kinfo_proc *pp)
 
-	/* all done! */
-	return (0);
+{
+    return snprintf(buf, sz, "%5s", format_k(PROCSIZE(pp)));
 }
 
-char *
-format_header(char *uname_field)
+int
+fmt_res(char *buf, int sz, struct kinfo_proc *pp)
+
 {
-	static char Header[128];
-	const char *prehead;
+    return snprintf(buf, sz, "%5s", format_k(pagetok(VP(pp, rssize))));
+}
 
-	switch (displaymode) {
-	case DISP_CPU:
-		/*
-		 * The logic of picking the right header format seems reverse
-		 * here because we only want to display a THR column when
-		 * "thread mode" is off (and threads are not listed as
-		 * separate lines).
-		 */
-		prehead = smpmode ?
-		    (ps.thread ? smp_header : smp_header_thr) :
-		    (ps.thread ? up_header : up_header_thr);
-		snprintf(Header, sizeof(Header), prehead,
-		    ps.jail ? " JID" : "",
-		    namelength, namelength, uname_field,
-		    ps.wcpu ? "WCPU" : "CPU");
-		break;
-	case DISP_IO:
-		prehead = io_header;
-		snprintf(Header, sizeof(Header), prehead,
-		    ps.jail ? " JID" : "",
-		    namelength, namelength, uname_field);
-		break;
+int
+fmt_state(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    int state;
+    char status[16];
+
+    state = PP(pp, stat);
+    switch(state)
+    {
+    case SRUN:
+	if (smpmode && PP(pp, oncpu) != 0xff)
+	    sprintf(status, "CPU%d", PP(pp, oncpu));
+	else
+	    strcpy(status, "RUN");
+	break;
+
+    case SSLEEP:
+	if (EP(pp, wmesg) != NULL) {
+	    sprintf(status, "%.6s", EP(pp, wmesg));
+	    break;
 	}
-	cmdlengthdelta = strlen(Header) - 7;
-	return (Header);
+	/* fall through */
+    default:
+	if (state >= 0 && state < NUM_STATES)
+	    sprintf(status, "%.6s", state_abbrev[(unsigned char) state]);
+	else
+	    sprintf(status, "?%-5d", state);
+	break;
+    }
+
+    return snprintf(buf, sz, "%-6.6s", status);
 }
 
-static int swappgsin = -1;
-static int swappgsout = -1;
-extern struct timeval timeout;
+int
+fmt_flags(char *buf, int sz, struct kinfo_proc *pp)
 
+{
+    long flag;
+    char chrs[12];
+    char *p;
 
-void
-get_system_info(struct system_info *si)
+    flag = PP(pp, flag);
+    p = chrs;
+    if (PP(pp, nice) < NZERO)
+	*p++ = '<';
+    else if (PP(pp, nice) > NZERO)
+	*p++ = 'N';
+    if (flag & P_TRACED)
+	*p++ = 'X';
+    if (flag & P_WEXIT && PP(pp, stat) != SZOMB)
+	*p++ = 'E';
+    if (flag & P_PPWAIT)
+	*p++ = 'V';
+    if (flag & P_SYSTEM || PP(pp, lock) > 0)
+	*p++ = 'L';
+    if (PP(pp, kiflag) & KI_SLEADER)
+	*p++ = 's';
+    if (flag & P_CONTROLT)
+	*p++ = '+';
+    if (flag & P_JAILED)
+	*p++ = 'J';
+    *p = '\0';
+
+    return snprintf(buf, sz, "%-3.3s", chrs);
+}
+
+int
+fmt_c(char *buf, int sz, struct kinfo_proc *pp)
+
 {
-	long total;
-	struct loadavg sysload;
-	int mib[2];
-	struct timeval boottime;
-	size_t bt_size;
-	int i, j;
-	size_t size;
+    return snprintf(buf, sz, "%1x", PP(pp, lastcpu));
+}
 
-	/* get the cp_time array */
-	if (pcpu_stats) {
-		size = (maxid + 1) * CPUSTATES * sizeof(long);
-		if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1)
-			err(1, "sysctlbyname kern.cp_times");
-	} else {
-		GETSYSCTL("kern.cp_time", cp_time);
-	}
-	GETSYSCTL("vm.loadavg", sysload);
-	GETSYSCTL("kern.lastpid", lastpid);
+int
+fmt_time(char *buf, int sz, struct kinfo_proc *pp)
 
-	/* convert load averages to doubles */
-	for (i = 0; i < 3; i++)
-		si->load_avg[i] = (double)sysload.ldavg[i] / sysload.fscale;
+{
+    return snprintf(buf, sz, "%6s",
+		    format_time((PP(pp, runtime) + 500000) / 1000000));
+}
 
-	if (pcpu_stats) {
-		for (i = j = 0; i <= maxid; i++) {
-			if ((cpumask & (1ul << i)) == 0)
-				continue;
-			/* convert cp_time counts to percentages */
-			percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES],
-			    &pcpu_cp_time[j * CPUSTATES],
-			    &pcpu_cp_old[j * CPUSTATES],
-			    &pcpu_cp_diff[j * CPUSTATES]);
-			j++;
-		}
-	} else {
-		/* convert cp_time counts to percentages */
-		percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
-	}
+int
+fmt_cpu(char *buf, int sz, struct kinfo_proc *pp)
 
-	/* sum memory & swap statistics */
-	{
-		static unsigned int swap_delay = 0;
-		static int swapavail = 0;
-		static int swapfree = 0;
-		static int bufspace = 0;
-		static int nspgsin, nspgsout;
+{
+    return snprintf(buf, sz, "%5.2f%%", (double)PPCPU(pp) / 100.0);
+}
 
-		GETSYSCTL("vfs.bufspace", bufspace);
-		GETSYSCTL("vm.stats.vm.v_active_count", memory_stats[0]);
-		GETSYSCTL("vm.stats.vm.v_inactive_count", memory_stats[1]);
-		GETSYSCTL("vm.stats.vm.v_wire_count", memory_stats[2]);
-		GETSYSCTL("vm.stats.vm.v_cache_count", memory_stats[3]);
-		GETSYSCTL("vm.stats.vm.v_free_count", memory_stats[5]);
-		GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin);
-		GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout);
-		/* convert memory stats to Kbytes */
-		memory_stats[0] = pagetok(memory_stats[0]);
-		memory_stats[1] = pagetok(memory_stats[1]);
-		memory_stats[2] = pagetok(memory_stats[2]);
-		memory_stats[3] = pagetok(memory_stats[3]);
-		memory_stats[4] = bufspace / 1024;
-		memory_stats[5] = pagetok(memory_stats[5]);
-		memory_stats[6] = -1;
+int
+fmt_command(char *buf, int sz, struct kinfo_proc *pp)
 
-		/* first interval */
-		if (swappgsin < 0) {
-			swap_stats[4] = 0;
-			swap_stats[5] = 0;
-		}
+{
+    int inmem;
+    char cmd[MAX_COLS];
+    char *bufp;
+    char **args;
+    int argc;
 
-		/* compute differences between old and new swap statistic */
-		else {
-			swap_stats[4] = pagetok(((nspgsin - swappgsin)));
-			swap_stats[5] = pagetok(((nspgsout - swappgsout)));
-		}
+#if OSMAJOR <= 4
+    inmem = (PP(pp, flag) & P_INMEM);
+#else
+    inmem = (PP(pp, sflag) & PS_INMEM);
+#endif
 
-		swappgsin = nspgsin;
-		swappgsout = nspgsout;
+    if (show_fullcmd && inmem)
+    {
+        /* get the pargs structure */
+        if ((args = kvm_getargv(kd, pp, sz)) != NULL)
+        {
+	    /* successfull retrieval: now convert nulls in to spaces */
+	    bufp = cmd;
+	    cmd[0] = '\0';
+	    argc = 0;
+	    while (args[argc] != NULL)
+	    {
+		if (cmd[0] != '\0')
+		    strcat(cmd, " ");
+		strcat(cmd, args[argc++]);
+	    }
 
-		/* call CPU heavy swapmode() only for changes */
-		if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0) {
-			swap_stats[3] = swapmode(&swapavail, &swapfree);
-			swap_stats[0] = swapavail;
-			swap_stats[1] = swapavail - swapfree;
-			swap_stats[2] = swapfree;
-		}
-		swap_delay = 1;
-		swap_stats[6] = -1;
-	}
+	    /* format cmd as our answer */
+	    return snprintf(buf, sz, "%s", cmd);
+        }
+    }
 
-	/* set arrays and strings */
-	if (pcpu_stats) {
-		si->cpustates = pcpu_cpu_states;
-		si->ncpus = ncpus;
-	} else {
-		si->cpustates = cpu_states;
-		si->ncpus = 1;
-	}
-	si->memory = memory_stats;
-	si->swap = swap_stats;
+    /* for anything else we just display comm */
+    return snprintf(buf, sz, inmem ? "%s" : "<%s>", printable(PP(pp, comm)));
+}
 
+int
+fmt_vcsw(char *buf, int sz, struct kinfo_proc *pp)
 
-	if (lastpid > 0) {
-		si->last_pid = lastpid;
-	} else {
-		si->last_pid = -1;
-	}
+{
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, vcsw), elapsed_msecs));
+}
 
-	/*
-	 * Print how long system has been up.
-	 * (Found by looking getting "boottime" from the kernel)
-	 */
-	mib[0] = CTL_KERN;
-	mib[1] = KERN_BOOTTIME;
-	bt_size = sizeof(boottime);
-	if (sysctl(mib, 2, &boottime, &bt_size, NULL, 0) != -1 &&
-	    boottime.tv_sec != 0) {
-		si->boottime = boottime;
-	} else {
-		si->boottime.tv_sec = -1;
-	}
+int
+fmt_ivcsw(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, ivcsw), elapsed_msecs));
 }
 
-#define NOPROC	((void *)-1)
+int
+fmt_read(char *buf, int sz, struct kinfo_proc *pp)
 
-/*
- * We need to compare data from the old process entry with the new
- * process entry.
- * To facilitate doing this quickly we stash a pointer in the kinfo_proc
- * structure to cache the mapping.  We also use a negative cache pointer
- * of NOPROC to avoid duplicate lookups.
- * XXX: this could be done when the actual processes are fetched, we do
- * it here out of laziness.
- */
-const struct kinfo_proc *
-get_old_proc(struct kinfo_proc *pp)
 {
-	struct kinfo_proc **oldpp, *oldp;
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, inblock), elapsed_msecs));
+}
 
-	/*
-	 * If this is the first fetch of the kinfo_procs then we don't have
-	 * any previous entries.
-	 */
-	if (previous_proc_count == 0)
-		return (NULL);
-	/* negative cache? */
-	if (pp->ki_udata == NOPROC)
-		return (NULL);
-	/* cached? */
-	if (pp->ki_udata != NULL)
-		return (pp->ki_udata);
-	/*
-	 * Not cached,
-	 * 1) look up based on pid.
-	 * 2) compare process start.
-	 * If we fail here, then setup a negative cache entry, otherwise
-	 * cache it.
-	 */
-	oldpp = bsearch(&pp, previous_pref, previous_proc_count,
-	    sizeof(*previous_pref), compare_pid);
-	if (oldpp == NULL) {
-		pp->ki_udata = NOPROC;
-		return (NULL);
-	}
-	oldp = *oldpp;
-	if (bcmp(&oldp->ki_start, &pp->ki_start, sizeof(pp->ki_start)) != 0) {
-		pp->ki_udata = NOPROC;
-		return (NULL);
-	}
-	pp->ki_udata = oldp;
-	return (oldp);
+int
+fmt_write(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, oublock), elapsed_msecs));
 }
 
-/*
- * Return the total amount of IO done in blocks in/out and faults.
- * store the values individually in the pointers passed in.
- */
-long
-get_io_stats(struct kinfo_proc *pp, long *inp, long *oup, long *flp,
-    long *vcsw, long *ivcsw)
+int
+fmt_fault(char *buf, int sz, struct kinfo_proc *pp)
+
 {
-	const struct kinfo_proc *oldp;
-	static struct kinfo_proc dummy;
-	long ret;
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, majflt), elapsed_msecs));
+}
 
-	oldp = get_old_proc(pp);
-	if (oldp == NULL) {
-		bzero(&dummy, sizeof(dummy));
-		oldp = &dummy;
-	}
-	*inp = RU(pp)->ru_inblock - RU(oldp)->ru_inblock;
-	*oup = RU(pp)->ru_oublock - RU(oldp)->ru_oublock;
-	*flp = RU(pp)->ru_majflt - RU(oldp)->ru_majflt;
-	*vcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw;
-	*ivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw;
-	ret =
-	    (RU(pp)->ru_inblock - RU(oldp)->ru_inblock) +
-	    (RU(pp)->ru_oublock - RU(oldp)->ru_oublock) +
-	    (RU(pp)->ru_majflt - RU(oldp)->ru_majflt);
-	return (ret);
+int
+fmt_iototal(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6ld", per_second(SP(pp, totalio), elapsed_msecs));
 }
 
+int
+fmt_iopct(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%6.2f%%", (SP(pp, totalio) * 100.) / total_io);
+}
+
+
 /*
- * Return the total number of block in/out and faults by a process.
+ * The order of proc_fields and proc_field must match!
  */
-long
-get_io_total(struct kinfo_proc *pp)
+
+enum proc_fields {
+	FIELD_PID = 0, FIELD_JID, FIELD_USERNAME, FIELD_UID,
+	FIELD_THR, FIELD_PRI, FIELD_NICE, FIELD_SIZE, FIELD_RES,
+	FIELD_STATE, FIELD_FLG, FIELD_C, FIELD_TIME, FIELD_CPU,
+	FIELD_WCPU, FIELD_COMMAND, FIELD_VCSW, FIELD_IVCSW, FIELD_READ,
+	FIELD_WRITE, FIELD_FAULT, FIELD_TOTAL, FIELD_PERCENT
+
+};
+
+#define MAX_FIELDS 25
+struct proc_field proc_field[MAX_FIELDS] = {
+    { "PID", 6, 1, 0, fmt_pid },
+    { "JID", 3, 1, 0, fmt_jid },
+    { "USERNAME", 8, 0, 0, fmt_username },
+    { "UID", 6, 1, 0, fmt_uid },
+    { "THR", 3, 1, 0, fmt_thr },
+    { "PRI", 3, 1, 0, fmt_pri },
+    { "NICE", 4, 1, 0, fmt_nice },
+    { "SIZE", 5, 1, 0, fmt_size },
+    { "RES", 5, 1, 0, fmt_res },
+    { "STATE", 6, 0, 0, fmt_state },
+    { "FLG", 3, 0, 84, fmt_flags },
+    { "C", 1, 0, 0, fmt_c },
+    { "TIME", 6, 1, 0, fmt_time },
+    { "CPU", 6, 1, 0, fmt_cpu },
+    { "WCPU", 6, 1, 0, fmt_cpu },
+    { "COMMAND", 7, 0, 0, fmt_command },
+    { "VCSW", 6, 1, 0, fmt_vcsw },
+    { "IVCSW", 6, 1, 0, fmt_ivcsw },
+    { "READ", 6, 1, 0, fmt_read },
+    { "WRITE", 6, 1, 0, fmt_write },
+    { "FAULT", 6, 1, 0, fmt_fault },
+    { "TOTAL", 6, 1, 0, fmt_iototal },
+    { "PERCENT", 7, 1, 0, fmt_iopct },
+    { NULL, 0, 0, 0, NULL }
+};
+
+static int mode0_display[MAX_FIELDS];
+static int mode1_display[MAX_FIELDS];
+
+int
+field_index(char *col)
+
 {
-	long dummy;
+    struct proc_field *fp;
+    int i = 0;
 
-	return (get_io_stats(pp, &dummy, &dummy, &dummy, &dummy, &dummy));
+    fp = proc_field;
+    while (fp->name != NULL)
+    {
+	if (strcmp(col, fp->name) == 0)
+	{
+	    return i;
+	}
+	fp++;
+	i++;
+    }
+
+    return -1;
 }
 
-static struct handle handle;
+void
+field_subst(int *fp, int old, int new)
 
-caddr_t
-get_process_info(struct system_info *si, struct process_select *sel,
-    int (*compare)(const void *, const void *))
 {
-	int i;
-	int total_procs;
-	long p_io;
-	long p_inblock, p_oublock, p_majflt, p_vcsw, p_ivcsw;
-	int active_procs;
-	struct kinfo_proc **prefp;
-	struct kinfo_proc *pp;
-	struct kinfo_proc *prev_pp = NULL;
+    while (*fp != -1)
+    {
+	if (*fp == old)
+	{
+	    *fp = new;
+	}
+	fp++;
+    }
+}
 
-	/* these are copied out of sel for speed */
-	int show_idle;
-	int show_self;
-	int show_system;
-	int show_uid;
-	int show_command;
+int
+machine_init(struct statics *_statics)
 
-	/*
-	 * Save the previous process info.
-	 */
-	if (previous_proc_count_max < nproc) {
-		free(previous_procs);
-		previous_procs = malloc(nproc * sizeof(*previous_procs));
-		free(previous_pref);
-		previous_pref = malloc(nproc * sizeof(*previous_pref));
-		if (previous_procs == NULL || previous_pref == NULL) {
-			(void) fprintf(stderr, "top: Out of memory.\n");
-			quit(23);
-		}
-		previous_proc_count_max = nproc;
-	}
-	if (nproc) {
-		for (i = 0; i < nproc; i++)
-			previous_pref[i] = &previous_procs[i];
-		bcopy(pbase, previous_procs, nproc * sizeof(*previous_procs));
-		qsort(previous_pref, nproc, sizeof(*previous_pref),
-		    compare_pid);
-	}
-	previous_proc_count = nproc;
+{
+    int i = 0;
+    size_t len;
+    int *ip;
 
-	pbase = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
-	if (nproc > onproc)
-		pref = realloc(pref, sizeof(*pref) * (onproc = nproc));
-	if (pref == NULL || pbase == NULL) {
-		(void) fprintf(stderr, "top: Out of memory.\n");
-		quit(23);
-	}
-	/* get a pointer to the states summary array */
-	si->procstates = process_states;
+    statics = _statics;	/* Keep a local copy */
 
-	/* set up flags which define what we are going to select */
-	show_idle = sel->idle;
-	show_self = sel->self == -1;
-	show_system = sel->system;
-	show_uid = sel->uid != -1;
-	show_command = sel->command != NULL;
+    struct timeval boottime;
 
-	/* count up process states and get pointers to interesting procs */
-	total_procs = 0;
-	active_procs = 0;
-	total_inblock = 0;
-	total_oublock = 0;
-	total_majflt = 0;
-	memset((char *)process_states, 0, sizeof(process_states));
-	prefp = pref;
-	for (pp = pbase, i = 0; i < nproc; pp++, i++) {
+    len = sizeof(statics->ncpus);
+    if (sysctlbyname("kern.smp.cpus", &(statics->ncpus), &len, NULL, 0) < 0 ||
+	len != sizeof(statics->ncpus))
+	statics->ncpus = 1;
+    smpmode = statics->ncpus != 1;
 
-		if (pp->ki_stat == 0)
-			/* not in use */
-			continue;
+    len = sizeof(statics->maxcpus);
+    if (sysctlbyname("kern.smp.maxcpus", &(statics->maxcpus), &len, NULL, 0) < 0 ||
+	len != sizeof(statics->maxcpus))
+	statics->maxcpus = 1;
 
-		if (!show_self && pp->ki_pid == sel->self)
-			/* skip self */
-			continue;
+    cp_time = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cp_old  = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cp_diff = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cpu_states = (int *)calloc(statics->maxcpus * CPUSTATES, sizeof(int));
 
-		if (!show_system && (pp->ki_flag & P_SYSTEM))
-			/* skip system process */
-			continue;
+    /* kvm_open the active kernel: its okay if this fails */
+    kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
 
-		p_io = get_io_stats(pp, &p_inblock, &p_oublock, &p_majflt,
-		    &p_vcsw, &p_ivcsw);
-		total_inblock += p_inblock;
-		total_oublock += p_oublock;
-		total_majflt += p_majflt;
-		total_procs++;
-		process_states[pp->ki_stat]++;
+    /* get boot time */
+    len = sizeof(boottime);
+    if (sysctlbyname("kern.boottime", &boottime, &len, NULL, 0) == -1)
+    {
+	/* we have no boottime to report */
+	boottime.tv_sec = -1;
+    }
 
-		if (pp->ki_stat == SZOMB)
-			/* skip zombies */
-			continue;
+    pbase = NULL;
+    pref = NULL;
 
-		if (displaymode == DISP_CPU && !show_idle &&
-		    (pp->ki_pctcpu == 0 ||
-		     pp->ki_stat == SSTOP || pp->ki_stat == SIDL))
-			/* skip idle or non-running processes */
-			continue;
+    /* get the page size with "getpagesize" and calculate pageshift from it */
+    i = pagesize = getpagesize();
+    pageshift = 0;
+    while (i > 1)
+    {
+	pageshift++;
+	i >>= 1;
+    }
 
-		if (displaymode == DISP_IO && !show_idle && p_io == 0)
-			/* skip processes that aren't doing I/O */
-			continue;
+    /* translate sysctl paths to mibs for faster access */
+    get_sysctl_mibs();
 
-		if (show_uid && pp->ki_ruid != (uid_t)sel->uid)
-			/* skip proc. that don't belong to the selected UID */
-			continue;
+    /* initialize swap stuff */
+    swap_init();
 
-		/*
-		 * When not showing threads, take the first thread
-		 * for output and add the fields that we can from
-		 * the rest of the process's threads rather than
-		 * using the system's mostly-broken KERN_PROC_PROC.
-		 */
-		if (sel->thread || prev_pp == NULL ||
-		    prev_pp->ki_pid != pp->ki_pid) {
-			*prefp++ = pp;
-			active_procs++;
-			prev_pp = pp;
-		} else {
-			prev_pp->ki_pctcpu += pp->ki_pctcpu;
-			prev_pp->ki_runtime += pp->ki_runtime;
-		}
-	}
+    /* create the hash table that remembers proc data */
+    procs = hash_create(2039);
 
-	/* if requested, sort the "interesting" processes */
-	if (compare != NULL)
-		qsort(pref, active_procs, sizeof(*pref), compare);
+    /* we only need the amount of log(2)1024 for our conversion */
+    pageshift -= LOG1024;
 
-	/* remember active and total counts */
-	si->p_total = total_procs;
-	si->p_active = pref_len = active_procs;
+    /* fill in the statics information */
+    statics->procstate_names = procstatenames;
+    statics->cpustate_names = cpustatenames;
+    statics->memory_names = memorynames;
+    statics->kernel_names = kernelnames;
+    statics->boottime = boottime.tv_sec;
+    statics->swap_names = swapnames;
+    statics->order_names = ordernames;
+    statics->flags.warmup = 0;
+    statics->modemax = 2;
+#ifdef HAS_SHOWTHREADS
+    statics->flags.threads = 1;
+#endif
 
-	/* pass back a handle */
-	handle.next_proc = pref;
-	handle.remaining = active_procs;
-	return ((caddr_t)&handle);
+    /* we need kvm descriptor in order to show full commands */
+    statics->flags.fullcmds = kd != NULL;
+
+    /* set up the display indices for mode0 (DISP_CPU) */
+    ip = mode0_display;
+    *ip++ = field_index("PID");
+    *ip++ = field_index("JID");
+    *ip++ = field_index("USERNAME");
+#ifdef HAS_THREADS
+    *ip++ = field_index("THR");
+#endif
+    *ip++ = field_index("PRI");
+    *ip++ = field_index("NICE");
+    *ip++ = field_index("SIZE");
+    *ip++ = field_index("RES");
+    *ip++ = field_index("STATE");
+    *ip++ = field_index("FLG");
+    *ip++ = field_index("C");
+    *ip++ = field_index("TIME");
+    *ip++ = field_index("CPU");
+    *ip++ = field_index("COMMAND");
+    *ip = -1;
+
+    /* set up the display indices for mode1 (DISP_IO) */
+    ip = mode1_display;
+    *ip++ = field_index("PID");
+    *ip++ = field_index("JID");
+    *ip++ = field_index("USERNAME");
+    *ip++ = field_index("VCSW");
+    *ip++ = field_index("IVCSW");
+    *ip++ = field_index("READ");
+    *ip++ = field_index("WRITE");
+    *ip++ = field_index("FAULT");
+    *ip++ = field_index("TOTAL");
+    *ip++ = field_index("PERCENT");
+    *ip++ = field_index("COMMAND");
+    *ip = -1;
+
+    /* all done! */
+    return(0);
 }
 
-static char fmt[128];	/* static area where result is built */
+char *format_header(char *uname_field)
 
-char *
-format_next_process(caddr_t handle, char *(*get_userid)(int), int flags)
 {
-	struct kinfo_proc *pp;
-	const struct kinfo_proc *oldp;
-	long cputime;
-	double pct;
-	struct handle *hp;
-	char status[16];
-	int state;
-	struct rusage ru, *rup;
-	long p_tot, s_tot;
-	char *proc_fmt, thr_buf[6], jid_buf[6];
-	char *cmdbuf = NULL;
-	char **args;
+    return "";
+}
 
-	/* find and remember the next proc structure */
-	hp = (struct handle *)handle;
-	pp = *(hp->next_proc++);
-	hp->remaining--;
+void
+get_vm_sum(struct vmmeter *sum)
 
-	/* get the process's command name */
-	if ((pp->ki_flag & P_INMEM) == 0) {
-		/*
-		 * Print swapped processes as <pname>
-		 */
-		size_t len;
+{
+#define GET_VM_STAT(v, s)  (void)get_sysctl(v, &(sum->s), sizeof(sum->s))
 
-		len = strlen(pp->ki_comm);
-		if (len > sizeof(pp->ki_comm) - 3)
-			len = sizeof(pp->ki_comm) - 3;
-		memmove(pp->ki_comm + 1, pp->ki_comm, len);
-		pp->ki_comm[0] = '<';
-		pp->ki_comm[len + 1] = '>';
-		pp->ki_comm[len + 2] = '\0';
-	}
+    GET_VM_STAT(V_SWTCH, v_swtch);
+    GET_VM_STAT(V_TRAP, v_trap);
+    GET_VM_STAT(V_INTR, v_intr);
+    GET_VM_STAT(V_SOFT, v_soft);
+    GET_VM_STAT(V_VFORKS, v_vforks);
+    GET_VM_STAT(V_FORKS, v_forks);
+    GET_VM_STAT(V_RFORKS, v_rforks);
+    GET_VM_STAT(V_VM_FAULTS, v_vm_faults);
+    GET_VM_STAT(V_SWAPIN, v_swapin);
+    GET_VM_STAT(V_SWAPOUT, v_swapout);
+    GET_VM_STAT(V_TFREE, v_tfree);
+    GET_VM_STAT(V_VNODEIN, v_vnodein);
+    GET_VM_STAT(V_VNODEOUT, v_vnodeout);
+    GET_VM_STAT(V_ACTIVE_COUNT, v_active_count);
+    GET_VM_STAT(V_INACTIVE_COUNT, v_inactive_count);
+    GET_VM_STAT(V_WIRE_COUNT, v_wire_count);
+    GET_VM_STAT(V_CACHE_COUNT, v_cache_count);
+    GET_VM_STAT(V_FREE_COUNT, v_free_count);
+    GET_VM_STAT(V_SWAPPGSIN, v_swappgsin);
+    GET_VM_STAT(V_SWAPPGSOUT, v_swappgsout);
+}
 
-	/*
-	 * Convert the process's runtime from microseconds to seconds.  This
-	 * time includes the interrupt time although that is not wanted here.
-	 * ps(1) is similarly sloppy.
-	 */
-	cputime = (pp->ki_runtime + 500000) / 1000000;
+void
+get_system_info(struct system_info *si)
 
-	/* calculate the base for cpu percentages */
-	pct = pctdouble(pp->ki_pctcpu);
+{
+    struct timeval thistime;
+    struct timeval timediff;
+    int i;
 
-	/* generate "STATE" field */
-	switch (state = pp->ki_stat) {
-	case SRUN:
-		if (smpmode && pp->ki_oncpu != 0xff)
-			sprintf(status, "CPU%d", pp->ki_oncpu);
-		else
-			strcpy(status, "RUN");
-		break;
-	case SLOCK:
-		if (pp->ki_kiflag & KI_LOCKBLOCK) {
-			sprintf(status, "*%.6s", pp->ki_lockname);
-			break;
-		}
-		/* fall through */
-	case SSLEEP:
-		if (pp->ki_wmesg != NULL) {
-			sprintf(status, "%.6s", pp->ki_wmesg);
-			break;
-		}
-		/* FALLTHROUGH */
-	default:
+    /* timestamp and time difference */
+    gettimeofday(&thistime, 0);
+    timersub(&thistime, &lasttime, &timediff);
+    elapsed_time = timediff.tv_sec * 1000000 + timediff.tv_usec;
+    elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
 
-		if (state >= 0 &&
-		    state < sizeof(state_abbrev) / sizeof(*state_abbrev))
-			sprintf(status, "%.6s", state_abbrev[state]);
-		else
-			sprintf(status, "?%5d", state);
-		break;
+    /* get the load averages */
+    if (getloadavg(si->load_avg, NUM_AVERAGES) == -1)
+    {
+	/* failed: fill in with zeroes */
+	(void) memset(si->load_avg, 0, sizeof(si->load_avg));
+    }
+
+    /* get the cp_times array ... */
+    if (statics->ncpus == 1) {
+	(void)get_sysctl(K_CP_TIME, cp_time, sizeof(long) * CPUSTATES);
+    } else {
+	(void)get_sysctl(K_CP_TIMES, cp_time, sizeof(long) * CPUSTATES * statics->maxcpus);
+    }
+
+    /* ... and convert cp_time counts to percentages */
+    for (i = 0; i < statics->ncpus; i++) {
+	percentages(CPUSTATES,
+	    &cpu_states[i * CPUSTATES], &cp_time[i * CPUSTATES],
+	    &cp_old[i * CPUSTATES], &cp_diff[i * CPUSTATES]);
+    }
+
+    /* sum memory & swap statistics */
+    {
+	struct vmmeter sum;
+	static unsigned int swap_delay = 0;
+	static long long swapavail = 0;
+	static long long swapfree = 0;
+	static int bufspace = 0;
+
+	get_vm_sum(&sum);
+
+	/* get bufspace */
+	bufspace = 0;
+	(void) get_sysctl(VFS_BUFSPACE, &bufspace, sizeof(bufspace));
+
+	/* kernel stats */
+	dprintf("kernel: swtch %d, trap %d, intr %d, soft %d, vforks %d\n",
+		sum.v_swtch, sum.v_trap, sum.v_intr, sum.v_soft, sum.v_vforks);
+	kernel_stats[0] = per_second(sum.v_swtch - ctxsws, elapsed_msecs);
+	kernel_stats[1] = per_second(sum.v_trap - traps, elapsed_msecs);
+	kernel_stats[2] = per_second(sum.v_intr - intrs, elapsed_msecs);
+	kernel_stats[3] = per_second(sum.v_soft - softs, elapsed_msecs);
+	kernel_stats[4] = per_second(sum.v_vforks + sum.v_forks +
+				     sum.v_rforks - forks, elapsed_msecs);
+	kernel_stats[5] = per_second(sum.v_vm_faults - pfaults, elapsed_msecs);
+	kernel_stats[6] = per_second(sum.v_swapin + sum.v_vnodein - pagein, elapsed_msecs);
+	kernel_stats[7] = per_second(sum.v_swapout + sum.v_vnodeout - pageout, elapsed_msecs);
+	kernel_stats[8] = per_second(sum.v_tfree - tfreed, elapsed_msecs);
+	ctxsws = sum.v_swtch;
+	traps = sum.v_trap;
+	intrs = sum.v_intr;
+	softs = sum.v_soft;
+	forks = (u_int64_t)sum.v_vforks + sum.v_forks + sum.v_rforks;
+	pfaults = sum.v_vm_faults;
+	pagein = sum.v_swapin + sum.v_vnodein;
+	pageout = sum.v_swapout + sum.v_vnodeout;
+	tfreed = sum.v_tfree;
+
+	/* convert memory stats to Kbytes */
+	memory_stats[0] = pagetok(sum.v_active_count);
+	memory_stats[1] = pagetok(sum.v_inactive_count);
+	memory_stats[2] = pagetok(sum.v_wire_count);
+	memory_stats[3] = pagetok(sum.v_cache_count);
+	memory_stats[4] = bufspace / 1024;
+	memory_stats[5] = pagetok(sum.v_free_count);
+	memory_stats[6] = -1;
+
+	/* first interval */
+        if (swappgsin < 0)
+	{
+	    swap_stats[4] = 0;
+	    swap_stats[5] = 0;
+	} 
+
+	/* compute differences between old and new swap statistic */
+	else
+	{
+	    swap_stats[4] = pagetok(sum.v_swappgsin - swappgsin);
+	    swap_stats[5] = pagetok(sum.v_swappgsout - swappgsout);
 	}
 
-	cmdbuf = (char *)malloc(cmdlengthdelta + 1);
-	if (cmdbuf == NULL) {
-		warn("malloc(%d)", cmdlengthdelta + 1);
-		return NULL;
+        swappgsin = sum.v_swappgsin;
+	swappgsout = sum.v_swappgsout;
+
+	/* call CPU heavy swap_getdata() only for changes */
+        if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0)
+	{
+	    swap_stats[3] = swap_getdata(&swapavail, &swapfree);
+	    swap_stats[0] = swapavail;
+	    swap_stats[1] = swapavail - swapfree;
+	    swap_stats[2] = swapfree;
 	}
+        swap_delay = 1;
+	swap_stats[6] = -1;
+    }
 
-	if (!(flags & FMT_SHOWARGS)) {
-		if (ps.thread && pp->ki_flag & P_HADTHREADS &&
-		    pp->ki_ocomm[0]) {
-			snprintf(cmdbuf, cmdlengthdelta, "{%s}", pp->ki_ocomm);
-		} else {
-			snprintf(cmdbuf, cmdlengthdelta, "%s", pp->ki_comm);
-		}
-	} else {
-		if (pp->ki_flag & P_SYSTEM ||
-		    pp->ki_args == NULL ||
-		    (args = kvm_getargv(kd, pp, cmdlengthdelta)) == NULL ||
-		    !(*args)) {
-			if (ps.thread && pp->ki_flag & P_HADTHREADS &&
-		    	pp->ki_ocomm[0]) {
-				snprintf(cmdbuf, cmdlengthdelta,
-				    "{%s}", pp->ki_ocomm);
-			} else {
-				snprintf(cmdbuf, cmdlengthdelta,
-				    "[%s]", pp->ki_comm);
-			}
-		} else {
-			char *src, *dst, *argbuf;
-			char *cmd;
-			size_t argbuflen;
-			size_t len;
+    /* set arrays and strings */
+    si->cpustates = cpu_states;
+    si->kernel = kernel_stats;
+    si->memory = memory_stats;
+    si->swap = swap_stats;
 
-			argbuflen = cmdlengthdelta * 4;
-			argbuf = (char *)malloc(argbuflen + 1);
-			if (argbuf == NULL) {
-				warn("malloc(%d)", argbuflen + 1);
-				free(cmdbuf);
-				return NULL;
-			}
+    get_sysctl(K_LASTPID, &si->last_pid, sizeof(si->last_pid));
+    lasttime = thistime;
+}
 
-			dst = argbuf;
+caddr_t
+get_process_info(struct system_info *si,
+			 struct process_select *sel,
+			 int compare_index)
 
-			/* Extract cmd name from argv */
-			cmd = strrchr(*args, '/');
-			if (cmd == NULL)
-				cmd = *args;
-			else
-				cmd++;
+{
+    int i;
+    int total_procs;
+    int active_procs;
+    struct kinfo_proc **prefp;
+    struct kinfo_proc *pp;
+    struct kinfo_proc *prev_pp = NULL;
+    struct save_proc *savep;
+    long proc_io;
+    pid_t pid;
+    size_t size;
+    int nproc;
 
-			for (; (src = *args++) != NULL; ) {
-				if (*src == '\0')
-					continue;
-				len = (argbuflen - (dst - argbuf) - 1) / 4;
-				strvisx(dst, src,
-				    strlen(src) < len ? strlen(src) : len,
-				    VIS_NL | VIS_CSTYLE);
-				while (*dst != '\0')
-					dst++;
-				if ((argbuflen - (dst - argbuf) - 1) / 4 > 0)
-					*dst++ = ' '; /* add delimiting space */
-			}
-			if (dst != argbuf && dst[-1] == ' ')
-				dst--;
-			*dst = '\0';
+    /* these are copied out of sel for speed */
+    int show_idle;
+    int show_self;
+    int show_system;
+    int show_uid;
+    char *show_command;
 
-			if (strcmp(cmd, pp->ki_comm) != 0 )
-				snprintf(cmdbuf, cmdlengthdelta,
-				    "%s (%s)",argbuf,  pp->ki_comm);
-			else
-				strlcpy(cmdbuf, argbuf, cmdlengthdelta);
+    /* get proc table size and give it a boost */
+    nproc = (int)get_sysctlsize(K_PROC) / sizeof(struct kinfo_proc);
+    nproc += nproc >> 4;
+    size = nproc * sizeof(struct kinfo_proc);
+    dprintf("get_process_info: nproc %d, psize %d, size %d\n", nproc, psize, size);
 
-			free(argbuf);
-		}
+    /* make sure we have enough space allocated */
+    if (nproc > psize)
+    {
+	/* reallocate both pbase and pref */
+	pbase = (struct kinfo_proc *)realloc(pbase, size);
+	pref  = (struct kinfo_proc **)realloc(pref,
+		    sizeof(struct kinfo_proc *) * nproc);
+	psize = nproc;
+    }
+
+    /* make sure we got the space we asked for */
+    if (pref == NULL || pbase == NULL)
+    {
+	/* abandon all hope */
+	message_error(" Out of memory!");
+	nproc = psize = 0;
+	si->p_total = 0;
+	si->p_active = 0;
+	return NULL;
+    }
+
+    /* get all process information (threads, too) */
+    if (size > 0)
+    {
+	nproc = get_sysctl(K_PROC, pbase, size);
+	if (nproc == -1)
+	{
+	    nproc = 0;
 	}
-
-	if (ps.jail == 0) 
-		jid_buf[0] = '\0';
 	else
-		snprintf(jid_buf, sizeof(jid_buf), " %*d",
-		    sizeof(jid_buf) - 3, pp->ki_jid);
+	{
+	    nproc /= sizeof(struct kinfo_proc);
+	}
+    }
 
-	if (displaymode == DISP_IO) {
-		oldp = get_old_proc(pp);
-		if (oldp != NULL) {
-			ru.ru_inblock = RU(pp)->ru_inblock -
-			    RU(oldp)->ru_inblock;
-			ru.ru_oublock = RU(pp)->ru_oublock -
-			    RU(oldp)->ru_oublock;
-			ru.ru_majflt = RU(pp)->ru_majflt - RU(oldp)->ru_majflt;
-			ru.ru_nvcsw = RU(pp)->ru_nvcsw - RU(oldp)->ru_nvcsw;
-			ru.ru_nivcsw = RU(pp)->ru_nivcsw - RU(oldp)->ru_nivcsw;
-			rup = &ru;
-		} else {
-			rup = RU(pp);
+    /* get a pointer to the states summary array */
+    si->procstates = process_states;
+
+    /* set up flags which define what we are going to select */
+    show_idle = sel->idle;
+    show_self = 0;
+    show_system = sel->system;
+    show_uid = sel->uid != -1;
+    show_fullcmd = sel->fullcmd;
+    show_command = sel->command;
+    show_usernames = sel->usernames;
+    display_mode = sel->mode;
+#ifdef HAS_SHOWTHREADS
+    show_threads = sel->threads;
+#endif
+
+    /* count up process states and get pointers to interesting procs */
+    total_procs = 0;
+    active_procs = 0;
+    total_io = 0;
+    memset((char *)process_states, 0, sizeof(process_states));
+    prefp = pref;
+    for (pp = pbase, i = 0; i < nproc; pp++, i++)
+    {
+	/*
+	 *  Place pointers to each valid proc structure in pref[].
+	 *  Process slots that are actually in use have a non-zero
+	 *  status field.  Processes with P_SYSTEM set are system
+	 *  processes---these get ignored unless show_sysprocs is set.
+	 */
+	pid = PP(pp, pid);
+	if (PP(pp, stat) != 0)
+	{
+#ifdef HAS_SHOWTHREADS
+	    int is_thread;
+	    lwpid_t tid;
+
+	    /* get thread id */
+	    tid = PP(pp, tid);
+
+	    /* is this just a thread? */
+	    is_thread = (prev_pp != NULL && PP(prev_pp, pid) == pid);
+
+	    /* count this process and its state */
+	    /* only count threads if we are showing them */
+	    if (show_threads || !is_thread)
+	    {
+		total_procs++;
+		process_states[(unsigned char) PP(pp, stat)]++;
+	    }
+
+	    /* grab old data from hash */
+	    if ((savep = hash_lookup_lwpid(procs, tid)) != NULL)
+	    {
+		/* verify that this is not a new or different thread */
+		/* (freebsd reuses thread ids fairly quickly) */
+		/* pids must match and time can't have gone backwards */
+		if (pid != savep->sp_pid || PP(pp, runtime) < savep->sp_runtime)
+		{
+		    /* not the same thread -- reuse the save_proc structure */
+		    memset(savep, 0, sizeof(struct save_proc));
+		    savep->sp_pid = pid;
 		}
-		p_tot = rup->ru_inblock + rup->ru_oublock + rup->ru_majflt;
-		s_tot = total_inblock + total_oublock + total_majflt;
+	    }
+	    else
+	    {
+		/* havent seen this one before */
+		savep = (struct save_proc *)calloc(1, sizeof(struct save_proc));
+		savep->sp_pid = pid;
+		hash_add_lwpid(procs, tid, savep);
+	    }
 
-		sprintf(fmt, io_Proc_format,
-		    pp->ki_pid,
-		    jid_buf,
-		    namelength, namelength, (*get_userid)(pp->ki_ruid),
-		    rup->ru_nvcsw,
-		    rup->ru_nivcsw,
-		    rup->ru_inblock,
-		    rup->ru_oublock,
-		    rup->ru_majflt,
-		    p_tot,
-		    s_tot == 0 ? 0.0 : (p_tot * 100.0 / s_tot),
-		    screen_width > cmdlengthdelta ?
-		    screen_width - cmdlengthdelta : 0,
-		    printable(cmdbuf));
+#else /* !HAS_SHOWTHREADS */
+	    total_procs++;
+	    process_states[(unsigned char) PP(pp, stat)]++;
 
-		free(cmdbuf);
+	    /* grab old data from hash */
+	    if ((savep = hash_lookup_pid(procs, pid)) == NULL)
+	    {
+		/* havent seen this one before */
+		savep = (struct save_proc *)calloc(1, sizeof(struct save_proc));
+		savep->sp_pid = pid;
+		hash_add_pid(procs, pid, savep);
+	    }
+#endif
 
-		return (fmt);
+	    /* save the pointer to the sp struct */
+	    SPPTR(pp) = (void *)savep;
+
+	    /* calculate %cpu */
+	    PPCPU(pp) = ((PP(pp, runtime) - savep->sp_runtime) * 10000) /
+		elapsed_time;
+	    dprintf("%d (%d): runtime %lld, saved_pid %d, saved_runtime %lld, elapsed_time %d, ppcpu %d\n",
+		    pid, PP(pp, tid), PP(pp, runtime), savep->sp_pid, savep->sp_runtime,
+		    elapsed_time, PPCPU(pp));
+
+	    /* calculate io differences */
+	    proc_io = 0;
+	    savep->sp_vcsw = (RP(pp, nvcsw) - savep->sp_old_nvcsw);
+	    savep->sp_ivcsw = (RP(pp, nivcsw) - savep->sp_old_nivcsw);
+	    proc_io += (savep->sp_inblock = (RP(pp, inblock) - savep->sp_old_inblock));
+	    proc_io += (savep->sp_oublock = (RP(pp, oublock) - savep->sp_old_oublock));
+	    proc_io += (savep->sp_majflt = (RP(pp, majflt) - savep->sp_old_majflt));
+	    total_io += proc_io;
+	    savep->sp_totalio = proc_io;
+
+	    /* save data for next time */
+	    savep->sp_runtime = PP(pp, runtime);
+	    savep->sp_old_nvcsw = RP(pp, nvcsw);
+	    savep->sp_old_nivcsw = RP(pp, nivcsw);
+	    savep->sp_old_inblock = RP(pp, inblock);
+	    savep->sp_old_oublock = RP(pp, oublock);
+	    savep->sp_old_majflt = RP(pp, majflt);
+
+	    /* is this one selected for viewing? */
+	    if ((PP(pp, stat) != SZOMB) &&
+		(show_system || ((PP(pp, flag) & P_SYSTEM) == 0)) &&
+		(show_idle || (PP(pp, pctcpu) != 0) || 
+		 (PP(pp, stat) == SRUN)) &&
+		(!show_uid || PRUID(pp) == (uid_t)sel->uid) &&
+		(show_command == NULL ||
+		 strcasestr(PP(pp, comm), show_command) != NULL))
+	    {
+#ifdef HAS_SHOWTHREADS
+		/* yes, but make sure it isn't just a thread */
+		if (show_threads || !is_thread)
+		{
+		    /* we will be showing this thread */
+		    *prefp++ = pp;
+		    active_procs++;
+		}
+		else
+		{
+		    /* we will not be showing this thread, but we need to roll
+		       up its cpu usage in to its process */
+		    PP(prev_pp, pctcpu) += PP(pp, pctcpu);
+		}
+#else /* !HAS_SHOWTHREADS */
+		/* we will be showing this process */
+		*prefp++ = pp;
+		active_procs++;
+#endif
+	    }
+	    prev_pp = pp;
 	}
+    }
 
-	/* format this entry */
-	proc_fmt = smpmode ? smp_Proc_format : up_Proc_format;
-	if (ps.thread != 0)
-		thr_buf[0] = '\0';
-	else
-		snprintf(thr_buf, sizeof(thr_buf), "%*d ",
-		    sizeof(thr_buf) - 2, pp->ki_numthreads);
+    dprintf("total_io: %d\n", total_io);
+    if (total_io == 0) total_io = 1;
 
-	sprintf(fmt, proc_fmt,
-	    pp->ki_pid,
-	    jid_buf,
-	    namelength, namelength, (*get_userid)(pp->ki_ruid),
-	    thr_buf,
-	    pp->ki_pri.pri_level - PZERO,
-	    format_nice(pp),
-	    format_k2(PROCSIZE(pp)),
-	    format_k2(pagetok(pp->ki_rssize)),
-	    status,
-	    smpmode ? pp->ki_lastcpu : 0,
-	    format_time(cputime),
-	    ps.wcpu ? 100.0 * weighted_cpu(pct, pp) : 100.0 * pct,
-	    screen_width > cmdlengthdelta ? screen_width - cmdlengthdelta : 0,
-	    printable(cmdbuf));
+    /* if requested, sort the "interesting" processes */
+    if (active_procs > 1)
+    {
+	qsort((char *)pref, active_procs, sizeof(struct kinfo_proc *),
+	      proc_compares[compare_index]);
+    }
 
-	free(cmdbuf);
+    /* remember active and total counts */
+    si->p_total = total_procs;
+    si->p_active = pref_len = active_procs;
 
-	/* return the result */
-	return (fmt);
+    /* pass back a handle */
+    handle.next_proc = pref;
+    handle.remaining = active_procs;
+    return((caddr_t)&handle);
 }
 
-static void
-getsysctl(const char *name, void *ptr, size_t len)
+static char p_header[MAX_COLS];
+
+char *
+format_process_header(struct process_select *sel, caddr_t handle, int count)
+
 {
-	size_t nlen = len;
+    int cols;
+    int n;
+    int w;
+    char *p;
+    int *fi;
+    struct kinfo_proc **kip;
+    struct proc_field *fp;
 
-	if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
-		fprintf(stderr, "top: sysctl(%s...) failed: %s\n", name,
-		    strerror(errno));
-		quit(23);
+    /* check for null handle */
+    if (handle == NULL)
+    {
+	return("");
+    }
+
+    /* remember how many columns there are on the display */
+    cols = display_columns();
+
+    /* mode & threads dictate format */
+    fi = display_fields =
+	sel->mode == DISP_CPU ?
+	mode0_display :
+	mode1_display;
+
+    /* set CPU vs Weighted CPU */
+    if (sel->wcpu) {
+	field_subst(fi, FIELD_CPU, FIELD_WCPU);
+    } else {
+	field_subst(fi, FIELD_WCPU, FIELD_CPU);
+    }
+
+    /* set username field correctly */
+    if (!sel->usernames)
+    {
+	/* display uids */
+	field_subst(fi, FIELD_USERNAME, FIELD_UID);
+    }
+    else
+    {
+	/* display usernames */
+	field_subst(fi, FIELD_UID, FIELD_USERNAME);
+
+	/* we also need to determine the longest username for column width */
+	/* calculate namelength from first "count" processes */
+	kip = ((struct handle *)handle)->next_proc;
+	n = ((struct handle *)handle)->remaining;
+	if (n > count)
+	    n = count;
+	namelength = 0;
+	while (n-- > 0)
+	{
+	    w = strlen(username(PRUID(*kip)));
+	    if (w > namelength) namelength = w;
+	    kip++;
 	}
-	if (nlen != len) {
-		fprintf(stderr, "top: sysctl(%s...) expected %lu, got %lu\n",
-		    name, (unsigned long)len, (unsigned long)nlen);
-		quit(23);
+	dprintf("format_process_header: namelength %d\n", namelength);
+
+	/* place it in bounds */
+	if (namelength < 8)
+	{
+	    namelength = 8;
 	}
-}
 
-static const char *
-format_nice(const struct kinfo_proc *pp)
-{
-	const char *fifo, *kthread;
-	int rtpri;
-	static char nicebuf[4 + 1];
+	/* set the column width */
+	proc_field[FIELD_USERNAME].width = username_length = namelength;
+    }
 
-	fifo = PRI_NEED_RR(pp->ki_pri.pri_class) ? "" : "F";
-	kthread = (pp->ki_flag & P_KTHREAD) ? "k" : "";
-	switch (PRI_BASE(pp->ki_pri.pri_class)) {
-	case PRI_ITHD:
-		return ("-");
-	case PRI_REALTIME:
-		/*
-		 * XXX: the kernel doesn't tell us the original rtprio and
-		 * doesn't really know what it was, so to recover it we
-		 * must be more chummy with the implementation than the
-		 * implementation is with itself.  pri_user gives a
-		 * constant "base" priority, but is only initialized
-		 * properly for user threads.  pri_native gives what the
-		 * kernel calls the "base" priority, but it isn't constant
-		 * since it is changed by priority propagation.  pri_native
-		 * also isn't properly initialized for all threads, but it
-		 * is properly initialized for kernel realtime and idletime
-		 * threads.  Thus we use pri_user for the base priority of
-		 * user threads (it is always correct) and pri_native for
-		 * the base priority of kernel realtime and idletime threads
-		 * (there is nothing better, and it is usually correct).
-		 *
-		 * The field width and thus the buffer are too small for
-		 * values like "kr31F", but such values shouldn't occur,
-		 * and if they do then the tailing "F" is not displayed.
-		 */
-		rtpri = ((pp->ki_flag & P_KTHREAD) ? pp->ki_pri.pri_native :
-		    pp->ki_pri.pri_user) - PRI_MIN_REALTIME;
-		snprintf(nicebuf, sizeof(nicebuf), "%sr%d%s",
-		    kthread, rtpri, fifo);
-		break;
-	case PRI_TIMESHARE:
-		if (pp->ki_flag & P_KTHREAD)
-			return ("-");
-		snprintf(nicebuf, sizeof(nicebuf), "%d", pp->ki_nice - NZERO);
-		break;
-	case PRI_IDLE:
-		/* XXX: as above. */
-		rtpri = ((pp->ki_flag & P_KTHREAD) ? pp->ki_pri.pri_native :
-		    pp->ki_pri.pri_user) - PRI_MIN_IDLE;
-		snprintf(nicebuf, sizeof(nicebuf), "%si%d%s",
-		    kthread, rtpri, fifo);
-		break;
-	default:
-		return ("?");
+    /* walk thru fields and construct header */
+    /* are we worried about overflow??? */
+    p = p_header;
+    while (*fi != -1)
+    {
+	if ((*fi == FIELD_JID && sel->jail == 0) ||
+	    (*fi == FIELD_C && smpmode == 0) ||
+	    (*fi == FIELD_THR && sel->threads != 0)) {
+	    fi++;
+	    continue;
 	}
-	return (nicebuf);
+
+	fp = &(proc_field[*fi++]);
+
+	if (fp->min_screenwidth <= cols)
+	{
+	    p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name);
+	    *p++ = ' ';
+	}
+    }
+    *--p = '\0';
+
+    return p_header;
 }
 
-/* comparison routines for qsort */
+static char fmt[MAX_COLS];		/* static area where result is built */
 
-static int
-compare_pid(const void *p1, const void *p2)
+char *
+format_next_process(struct process_select *sel, caddr_t handle, char *(*get_userid)(int))
+
 {
-	const struct kinfo_proc * const *pp1 = p1;
-	const struct kinfo_proc * const *pp2 = p2;
+    struct kinfo_proc *pp;
+    struct handle *hp;
+    struct proc_field *fp;
+    int *fi;
+    int cols;
+    char *p;
+    int len;
+    int x;
 
-	if ((*pp2)->ki_pid < 0 || (*pp1)->ki_pid < 0)
-		abort();
+    /* find and remember the next proc structure */
+    hp = (struct handle *)handle;
+    pp = *(hp->next_proc++);
+    hp->remaining--;
+    
+    /* mode & threads dictate format */
+    fi = display_fields;
 
-	return ((*pp1)->ki_pid - (*pp2)->ki_pid);
+    /* screen width is a consideration, too */
+    cols = display_columns();
+
+    /* build output by field */
+    p = fmt;
+    len = MAX_COLS;
+    while (*fi != -1)
+    {
+	if ((*fi == FIELD_JID && sel->jail == 0) ||
+	    (*fi == FIELD_C && smpmode == 0) ||
+	    (*fi == FIELD_THR && sel->threads != 0)) {
+		fi++;
+		continue;
+	}
+
+	fp = &(proc_field[*fi++]);
+	if (len > 0 && fp->min_screenwidth <= cols)
+	{
+	    x = (*(fp->format))(p, len, pp);
+	    if (x >= len)
+	    {
+		dprintf("format_next_process: formatter overflow: x %d, len %d, p %08x => %08x, fmt %08x - %08x\n",
+			x, len, p, p + len, fmt, fmt + sizeof(fmt));
+		p += len;
+		len = 0;
+	    }
+	    else
+	    {
+		p += x;
+		*p++ = ' ';
+		len -= x + 1;
+	    }
+	}
+    }
+    *--p = '\0';
+
+    /* return the result */
+    return(fmt);
 }
 
+/* comparison routines for qsort */
+
 /*
  *  proc_compare - comparison function for "qsort"
  *	Compares the resource consumption of two processes using five
- *	distinct keys.  The keys (in descending order of importance) are:
- *	percent cpu, cpu ticks, state, resident set size, total virtual
- *	memory usage.  The process states are ordered as follows (from least
- *	to most important):  WAIT, zombie, sleep, stop, start, run.  The
- *	array declaration below maps a process state index into a number
- *	that reflects this ordering.
+ *  	distinct keys.  The keys (in descending order of importance) are:
+ *  	percent cpu, cpu ticks, state, resident set size, total virtual
+ *  	memory usage.  The process states are ordered as follows (from least
+ *  	to most important):  WAIT, zombie, sleep, stop, start, run.  The
+ *  	array declaration below maps a process state index into a number
+ *  	that reflects this ordering.
  */
 
-static int sorted_state[] = {
-	0,	/* not used		*/
-	3,	/* sleep		*/
-	1,	/* ABANDONED (WAIT)	*/
-	6,	/* run			*/
-	5,	/* start		*/
-	2,	/* zombie		*/
-	4	/* stop			*/
+static unsigned char sorted_state[] =
+{
+    0,	/* not used		*/
+    3,	/* sleep		*/
+    1,	/* ABANDONED (WAIT)	*/
+    6,	/* run			*/
+    5,	/* start		*/
+    2,	/* zombie		*/
+    4	/* stop			*/
 };
+ 
 
+#define ORDERKEY_PCTCPU \
+  if (lresult = (long) PPCPU(p2) - (long) PPCPU(p1), \
+     (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
 
-#define ORDERKEY_PCTCPU(a, b) do { \
-	long diff; \
-	if (ps.wcpu) \
-		diff = floor(1.0E6 * weighted_cpu(pctdouble((b)->ki_pctcpu), \
-		    (b))) - \
-		    floor(1.0E6 * weighted_cpu(pctdouble((a)->ki_pctcpu), \
-		    (a))); \
-	else \
-		diff = (long)(b)->ki_pctcpu - (long)(a)->ki_pctcpu; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_CPTICKS \
+  if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \
+                PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0)
 
-#define ORDERKEY_CPTICKS(a, b) do { \
-	int64_t diff = (int64_t)(b)->ki_runtime - (int64_t)(a)->ki_runtime; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_STATE \
+  if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \
+                sorted_state[(unsigned char) PP(p1, stat)]) == 0)
 
-#define ORDERKEY_STATE(a, b) do { \
-	int diff = sorted_state[(b)->ki_stat] - sorted_state[(a)->ki_stat]; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#if OSMAJOR <= 4
+#define ORDERKEY_PRIO \
+  if ((result = PP(p2, priority) - PP(p1, priority)) == 0)
+#else
+#define ORDERKEY_PRIO \
+  if ((result = PP(p2, pri.pri_level) - PP(p1, pri.pri_level)) == 0)
+#endif
 
-#define ORDERKEY_PRIO(a, b) do { \
-	int diff = (int)(b)->ki_pri.pri_level - (int)(a)->ki_pri.pri_level; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_RSSIZE \
+  if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0) 
 
-#define	ORDERKEY_THREADS(a, b) do { \
-	int diff = (int)(b)->ki_numthreads - (int)(a)->ki_numthreads; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_MEM \
+  if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 )
 
-#define ORDERKEY_RSSIZE(a, b) do { \
-	long diff = (long)(b)->ki_rssize - (long)(a)->ki_rssize; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_IO \
+  if ( (result = SP(p2, totalio) - SP(p1, totalio)) == 0)
 
-#define ORDERKEY_MEM(a, b) do { \
-	long diff = (long)PROCSIZE((b)) - (long)PROCSIZE((a)); \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_PID \
+  if ( (result = PP(p1, pid) - PP(p2, pid)) == 0)
 
-#define ORDERKEY_JID(a, b) do { \
-	int diff = (int)(b)->ki_jid - (int)(a)->ki_jid; \
-	if (diff != 0) \
-		return (diff > 0 ? 1 : -1); \
-} while (0)
+#define ORDERKEY_JID \
+  if ( (result = PP(p2, jid) - PP(p1, jid)) == 0)
 
 /* compare_cpu - the comparison function for sorting by cpu percentage */
 
 int
-#ifdef ORDER
-compare_cpu(void *arg1, void *arg2)
-#else
-proc_compare(void *arg1, void *arg2)
-#endif
+proc_compare(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
 
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (0);
+    ORDERKEY_PCTCPU
+    ORDERKEY_CPTICKS
+    ORDERKEY_STATE
+    ORDERKEY_PRIO
+    ORDERKEY_RSSIZE
+    ORDERKEY_MEM
+    ;
+
+    return(result);
 }
 
-#ifdef ORDER
-/* "cpu" compare routines */
-int compare_size(), compare_res(), compare_time(), compare_prio(),
-    compare_threads();
-
-/*
- * "io" compare routines.  Context switches aren't i/o, but are displayed
- * on the "io" display.
- */
-int compare_iototal(), compare_ioread(), compare_iowrite(), compare_iofault(),
-    compare_vcsw(), compare_ivcsw();
-
-int (*compares[])() = {
-	compare_cpu,
-	compare_size,
-	compare_res,
-	compare_time,
-	compare_prio,
-	compare_threads,
-	compare_iototal,
-	compare_ioread,
-	compare_iowrite,
-	compare_iofault,
-	compare_vcsw,
-	compare_ivcsw,
-	compare_jid,
-	NULL
-};
-
 /* compare_size - the comparison function for sorting by total memory usage */
 
 int
-compare_size(void *arg1, void *arg2)
+compare_size(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
 
-	ORDERKEY_MEM(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (0);
+    ORDERKEY_MEM
+    ORDERKEY_RSSIZE
+    ORDERKEY_PCTCPU
+    ORDERKEY_CPTICKS
+    ORDERKEY_STATE
+    ORDERKEY_PRIO
+    ;
+
+    return(result);
 }
 
 /* compare_res - the comparison function for sorting by resident set size */
 
 int
-compare_res(void *arg1, void *arg2)
+compare_res(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
 
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (0);
+    ORDERKEY_RSSIZE
+    ORDERKEY_MEM
+    ORDERKEY_PCTCPU
+    ORDERKEY_CPTICKS
+    ORDERKEY_STATE
+    ORDERKEY_PRIO
+    ;
+
+    return(result);
 }
 
 /* compare_time - the comparison function for sorting by total cpu time */
 
 int
-compare_time(void *arg1, void *arg2)
+compare_time(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
+  
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
+    ORDERKEY_CPTICKS
+    ORDERKEY_PCTCPU
+    ORDERKEY_STATE
+    ORDERKEY_PRIO
+    ORDERKEY_RSSIZE
+    ORDERKEY_MEM
+    ;
 
-	return (0);
-}
-
+      return(result);
+  }
+  
 /* compare_prio - the comparison function for sorting by priority */
 
 int
-compare_prio(void *arg1, void *arg2)
+compare_prio(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
 
-	ORDERKEY_PRIO(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (0);
+    ORDERKEY_PRIO
+    ORDERKEY_CPTICKS
+    ORDERKEY_PCTCPU
+    ORDERKEY_STATE
+    ORDERKEY_RSSIZE
+    ORDERKEY_MEM
+    ;
+
+    return(result);
 }
 
-/* compare_threads - the comparison function for sorting by threads */
+/* compare_io - the comparison function for sorting by io count */
+
 int
-compare_threads(void *arg1, void *arg2)
+compare_io(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+    pctcpu lresult;
 
-	ORDERKEY_THREADS(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (0);
-}
+    ORDERKEY_IO
+    ORDERKEY_PCTCPU
+    ORDERKEY_CPTICKS
+    ORDERKEY_STATE
+    ORDERKEY_PRIO
+    ORDERKEY_RSSIZE
+    ORDERKEY_MEM
+    ;
 
-/* compare_jid - the comparison function for sorting by jid */
-static int
-compare_jid(const void *arg1, const void *arg2)
-{
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-
-	ORDERKEY_JID(p1, p2);
-	ORDERKEY_PCTCPU(p1, p2);
-	ORDERKEY_CPTICKS(p1, p2);
-	ORDERKEY_STATE(p1, p2);
-	ORDERKEY_PRIO(p1, p2);
-	ORDERKEY_RSSIZE(p1, p2);
-	ORDERKEY_MEM(p1, p2);
-
-	return (0);
+    return(result);
 }
-#endif /* ORDER */
 
-/* assorted comparison functions for sorting by i/o */
+/* compare_pid - the comparison function for sorting by process id */
 
 int
-#ifdef ORDER
-compare_iototal(void *arg1, void *arg2)
-#else
-io_compare(void *arg1, void *arg2)
-#endif
+compare_pid(struct proc **pp1, struct proc **pp2)
+
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
 
-	return (get_io_total(p2) - get_io_total(p1));
-}
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-#ifdef ORDER
-int
-compare_ioread(void *arg1, void *arg2)
-{
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-	long dummy, inp1, inp2;
+    ORDERKEY_PID
+    ;
 
-	(void) get_io_stats(p1, &inp1, &dummy, &dummy, &dummy, &dummy);
-	(void) get_io_stats(p2, &inp2, &dummy, &dummy, &dummy, &dummy);
-
-	return (inp2 - inp1);
+    return(result);
 }
 
-int
-compare_iowrite(void *arg1, void *arg2)
-{
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-	long dummy, oup1, oup2;
+/* compare_jid - the comparison function for sorting by jail id */
 
-	(void) get_io_stats(p1, &dummy, &oup1, &dummy, &dummy, &dummy);
-	(void) get_io_stats(p2, &dummy, &oup2, &dummy, &dummy, &dummy);
-
-	return (oup2 - oup1);
-}
-
 int
-compare_iofault(void *arg1, void *arg2)
-{
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-	long dummy, flp1, flp2;
+compare_jid(struct proc **pp1, struct proc **pp2)
 
-	(void) get_io_stats(p1, &dummy, &dummy, &flp1, &dummy, &dummy);
-	(void) get_io_stats(p2, &dummy, &dummy, &flp2, &dummy, &dummy);
-
-	return (flp2 - flp1);
-}
-
-int
-compare_vcsw(void *arg1, void *arg2)
 {
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-	long dummy, flp1, flp2;
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
 
-	(void) get_io_stats(p1, &dummy, &dummy, &dummy, &flp1, &dummy);
-	(void) get_io_stats(p2, &dummy, &dummy, &dummy, &flp2, &dummy);
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
 
-	return (flp2 - flp1);
-}
+    ORDERKEY_JID
+    ;
 
-int
-compare_ivcsw(void *arg1, void *arg2)
-{
-	struct kinfo_proc *p1 = *(struct kinfo_proc **)arg1;
-	struct kinfo_proc *p2 = *(struct kinfo_proc **)arg2;
-	long dummy, flp1, flp2;
-
-	(void) get_io_stats(p1, &dummy, &dummy, &dummy, &dummy, &flp1);
-	(void) get_io_stats(p2, &dummy, &dummy, &dummy, &dummy, &flp2);
-
-	return (flp2 - flp1);
+    return(result);
 }
-#endif /* ORDER */
 
 /*
  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
@@ -1388,40 +1818,22 @@
 
 int
 proc_owner(int pid)
+
 {
-	int cnt;
-	struct kinfo_proc **prefp;
-	struct kinfo_proc *pp;
+    int cnt;
+    struct kinfo_proc **prefp;
+    struct kinfo_proc *pp;
 
-	prefp = pref;
-	cnt = pref_len;
-	while (--cnt >= 0) {
-		pp = *prefp++;
-		if (pp->ki_pid == (pid_t)pid)
-			return ((int)pp->ki_ruid);
+    prefp = pref;
+    cnt = pref_len;
+    while (--cnt >= 0)
+    {
+	pp = *prefp++;	
+	if (PP(pp, pid) == (pid_t)pid)
+	{
+	    return((int)PRUID(pp));
 	}
-	return (-1);
+    }
+    return(-1);
 }
 
-static int
-swapmode(int *retavail, int *retfree)
-{
-	int n;
-	int pagesize = getpagesize();
-	struct kvm_swap swapary[1];
-
-	*retavail = 0;
-	*retfree = 0;
-
-#define CONVERT(v)	((quad_t)(v) * pagesize / 1024)
-
-	n = kvm_getswapinfo(kd, swapary, 1, 0);
-	if (n < 0 || swapary[0].ksw_total == 0)
-		return (0);
-
-	*retavail = CONVERT(swapary[0].ksw_total);
-	*retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);
-
-	n = (int)(swapary[0].ksw_used * 100.0 / swapary[0].ksw_total);
-	return (n);
-}
Index: Makefile
===================================================================
--- Makefile	(revision 183246)
+++ Makefile	(working copy)
@@ -5,10 +5,10 @@
 
 PROG=	top
 SRCS=	commands.c display.c machine.c screen.c top.c \
-	username.c utils.c version.c
-SRCS+=	sigdesc.h top.local.h
-CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
-CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I.
+	username.c utils.c version.c color.c hash.c
+SRCS+=	sigdesc.h config.h
+CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -DSIGWINCH -DHAS_SHOWTHREADS
+CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. -Wall -g
 
 #
 # The table size should be a prime number approximately twice as
@@ -40,8 +40,28 @@
 	-e's,%random%,1,g' \
 	${.IMPSRC} > ${.TARGET}
 
+CLEANFILES+= config.h
+config.h: config.h.in
+	@${ECHO} Making config.h from config.h.in
+	@sed \
+		-e 's/@DEFAULT_TOPN@/30/' \
+		-e 's/@DEFAULT_DELAY@/2/' \
+		-e 's/@HAVE_GETOPT_LONG@/1/' \
+		-e 's/@ENABLE_KILL@/1/' \
+		< config.h.in > config.h
+
+CLEANFILES+= top.1.local
+top.1.local: top.1.in
+	@${ECHO} Making top.1.local from top.1.in
+	@sed \
+		-e 's/@DEFAULT_TOPN@/30/' \
+		-e 's/@DEFAULT_DELAY@/2/' \
+		-e 's/@HAVE_GETOPT_LONG@/1/' \
+		-e 's/@ENABLE_KILL@/1/' \
+		< ${TOPDIR}/top.1.in > top.1.local
+
 CLEANFILES+= top.1
-top.1: top.x top.local.1
+top.1: top.1.local machine.man
 	cat ${.ALLSRC} > ${.TARGET}
 
 .include <bsd.prog.mk>



In src/contrib/top:

Index: utils.c
===================================================================
--- utils.c	(revision 183246)
+++ utils.c	(working copy)
@@ -1,27 +1,77 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
- *
- * $FreeBSD$
  */
 
 /*
  *  This file contains various handy utilities used by top.
  */
 
+#include "os.h"
+#include <ctype.h>
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#else
+#undef DEBUG
+#endif
 #include "top.h"
-#include "os.h"
+#include "utils.h"
 
-int atoiwi(str)
+static int
+alldigits(char *s)
 
-char *str;
+{
+    int ch;
 
+    while ((ch = *s++) != '\0')
+    {
+	if (!isdigit(ch))
+	{
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+int
+atoiwi(char *str)
+
 {
     register int len;
 
@@ -34,13 +84,13 @@
 	{
 	    return(Infinity);
 	}
-	else if (str[0] == '-')
+	else if (alldigits(str))
 	{
-	    return(Invalid);
+	    return(atoi(str));
 	}
 	else
 	{
-	    return(atoi(str));
+	    return(Invalid);
 	}
     }
     return(0);
@@ -59,10 +109,9 @@
 				 * digits.
 				 */
 
-char *itoa(val)
+char *
+itoa(int val)
 
-register int val;
-
 {
     register char *ptr;
     static char buffer[16];	/* result is built here */
@@ -90,18 +139,22 @@
  *	a front end to a more general routine for efficiency.
  */
 
-char *itoa7(val)
+char *
+itoa_w(int val, int w)
 
-register int val;
-
 {
-    register char *ptr;
+    char *ptr;
+    char *eptr;
     static char buffer[16];	/* result is built here */
     				/* 16 is sufficient since the largest number
 				   we will ever convert will be 2^32-1,
 				   which is 10 digits. */
 
-    ptr = buffer + sizeof(buffer);
+    if (w > 15)
+    {
+	w = 15;
+    }
+    eptr = ptr = buffer + sizeof(buffer);
     *--ptr = '\0';
     if (val == 0)
     {
@@ -112,25 +165,36 @@
 	*--ptr = (val % 10) + '0';
 	val /= 10;
     }
-    while (ptr > buffer + sizeof(buffer) - 7)
+    while (ptr >= eptr - w)
     {
 	*--ptr = ' ';
     }
     return(ptr);
 }
 
+char *
+itoa7(int val)
+
+{
+    return itoa_w(val, 7);
+}
+
 /*
  *  digits(val) - return number of decimal digits in val.  Only works for
- *	positive numbers.  If val <= 0 then digits(val) == 0.
+ *	positive numbers.  If val < 0 then digits(val) == 0, but
+ *      digits(0) == 1.
  */
 
-int digits(val)
+int
+digits(int val)
 
-int val;
-
 {
     register int cnt = 0;
 
+    if (val == 0)
+    {
+	return 1;
+    }
     while (val > 0)
     {
 	cnt++;
@@ -140,29 +204,80 @@
 }
 
 /*
- *  strecpy(to, from) - copy string "from" into "to" and return a pointer
+ *  printable(char *str) - make the string pointed to by "str" into one that is
+ *	printable (i.e.: all ascii), by converting all non-printable
+ *	characters into '?'.  Replacements are done in place and a pointer
+ *	to the original buffer is returned.
+ */
+
+char *
+printable(char *str)
+
+{
+    register char *ptr;
+    register int ch;
+
+    ptr = str;
+    while ((ch = *ptr) != '\0')
+    {
+	if (!isprint(ch))
+	{
+	    *ptr = '?';
+	}
+	ptr++;
+    }
+    return(str);
+}
+
+/*
+ *  strcpyend(to, from) - copy string "from" into "to" and return a pointer
  *	to the END of the string "to".
  */
 
-char *strecpy(to, from)
+char *
+strcpyend(char *to, char *from)
 
-register char *to;
-register char *from;
-
 {
     while ((*to++ = *from++) != '\0');
     return(--to);
 }
 
 /*
+ * char *
+ * homogenize(char *str)
+ *
+ * Remove unwanted characters from "str" and make everything lower case.
+ * Newly allocated string is returned: the original is not altered.
+ */
+
+char *homogenize(char *str)
+
+{
+    char *ans;
+    char *fr;
+    char *to;
+    int ch;
+
+    to = fr = ans = strdup(str);
+    while ((ch = *fr++) != '\0')
+    {
+	if (isalnum(ch))
+	{
+	    *to++ = tolower(ch);
+	}
+    }
+
+    *to = '\0';
+    return ans;
+}
+
+/*
  * string_index(string, array) - find string in array and return index
  */
 
-int string_index(string, array)
+int
+string_index(char *string, char **array)
 
-char *string;
-char **array;
-
 {
     register int i = 0;
 
@@ -179,17 +294,55 @@
 }
 
 /*
+ * char *string_list(char **strings)
+ *
+ * Create a comma-separated list of the strings in the NULL-terminated
+ * "strings".  Returned string is malloc-ed and should be freed when the
+ * caller is done.  Note that this is not an efficient function.
+ */
+
+char *string_list(char **strings)
+
+{
+    int cnt = 0;
+    char **pp;
+    char *p;
+    char *result = NULL;
+    char *resp = NULL;
+
+    pp = strings;
+    while ((p = *pp++) != NULL)
+    {
+	cnt += strlen(p) + 2;
+    }
+
+    if (cnt > 0)
+    {
+	resp = result = (char *)malloc(cnt);
+	pp = strings;
+	while ((p = *pp++) != NULL)
+	{
+	    resp = strcpyend(resp, p);
+	    if (*pp != NULL)
+	    {
+		resp = strcpyend(resp, ", ");
+	    }
+	}
+    }
+
+    return result;
+}
+
+/*
  * argparse(line, cntp) - parse arguments in string "line", separating them
  *	out into an argv-like array, and setting *cntp to the number of
  *	arguments encountered.  This is a simple parser that doesn't understand
  *	squat about quotes.
  */
 
-char **argparse(line, cntp)
+char **
+argparse(char *line, int *cntp)
 
-char *line;
-int *cntp;
-
 {
     register char *from;
     register char *to;
@@ -270,14 +423,9 @@
  *	useful on BSD mchines for calculating cpu state percentages.
  */
 
-long percentages(cnt, out, new, old, diffs)
+long
+percentages(int cnt, int *out, long *new, long *old, long *diffs)
 
-int cnt;
-int *out;
-register long *new;
-register long *old;
-long *diffs;
-
 {
     register int i;
     register long change;
@@ -312,11 +460,11 @@
     half_total = total_change / 2l;
 
     /* Do not divide by 0. Causes Floating point exception */
-    if(total_change) {
-        for (i = 0; i < cnt; i++)
-        {
-          *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
-        }
+    if (total_change != 0) {
+	for (i = 0; i < cnt; i++)
+	{
+	    *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
+	}
     }
 
     /* return the total in case the caller wants to use it */
@@ -337,18 +485,16 @@
 /* externs referenced by errmsg */
 
 #ifndef HAVE_STRERROR
-#ifndef SYS_ERRLIST_DECLARED
-#define SYS_ERRLIST_DECLARED
+#if !HAVE_DECL_SYS_ERRLIST
 extern char *sys_errlist[];
 #endif
 
 extern int sys_nerr;
 #endif
 
-char *errmsg(errnum)
+char *
+errmsg(int errnum)
 
-int errnum;
-
 {
 #ifdef HAVE_STRERROR
     char *msg = strerror(errnum);
@@ -359,12 +505,43 @@
 #else
     if (errnum > 0 && errnum < sys_nerr)
     {
-	return((char *)sys_errlist[errnum]);
+	return((char *)(sys_errlist[errnum]));
     }
 #endif
     return("No error");
 }
 
+/* format_percent(v) - format a double as a percentage in a manner that
+ *		does not exceed 5 characters (excluding any trailing
+ *		percent sign).  Since it is possible for the value
+ *		to exceed 100%, we format such values with no fractional
+ *		component to fit within the 5 characters.
+ */
+
+char *
+format_percent(double v)
+
+{
+    static char result[10];
+
+    /* enumerate the possibilities */
+    if (v < 0 || v >= 100000.)
+    {
+	/* we dont want to try extreme values */
+	strcpy(result, "  ???");
+    }
+    else if (v > 99.99)
+    {
+	sprintf(result, "%5.0f", v);
+    }
+    else
+    {
+	sprintf(result, "%5.2f", v);
+    }
+
+    return result;
+}
+
 /* format_time(seconds) - format number of seconds into a suitable
  *		display that will fit within 6 characters.  Note that this
  *		routine builds its string in a static area.  If it needs
@@ -381,14 +558,10 @@
    exceed 9999.9, we use "???".
  */
 
-char *format_time(seconds)
+char *
+format_time(long seconds)
 
-long seconds;
-
 {
-    register int value;
-    register int digit;
-    register char *ptr;
     static char result[10];
 
     /* sanity protection */
@@ -411,8 +584,7 @@
     {
 	/* standard method produces MMM:SS */
 	/* we avoid printf as must as possible to make this quick */
-	sprintf(result, "%3ld:%02ld",
-	    (long)(seconds / 60), (long)(seconds % 60));
+	sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l);
     }
     return(result);
 }
@@ -442,18 +614,16 @@
 
 #define NUM_STRINGS 8
 
-char *format_k(amt)
+char *
+format_k(long amt)
 
-int amt;
-
 {
     static char retarray[NUM_STRINGS][16];
     static int index = 0;
-    register char *p;
     register char *ret;
     register char tag = 'K';
 
-    p = ret = retarray[index];
+    ret = retarray[index];
     index = (index + 1) % NUM_STRINGS;
 
     if (amt >= 10000)
@@ -467,41 +637,117 @@
 	}
     }
 
-    p = strecpy(p, itoa(amt));
-    *p++ = tag;
-    *p = '\0';
+    snprintf(ret, sizeof(retarray[index])-1, "%ld%c", amt, tag);
 
     return(ret);
 }
 
-char *format_k2(amt)
+/*
+ * Time keeping functions.  
+ */
 
-int amt;
+static struct timeval lasttime = { 0, 0 };
+static unsigned int elapsed_msecs = 0;
 
+void
+time_get(struct timeval *tv)
+
 {
-    static char retarray[NUM_STRINGS][16];
-    static int index = 0;
-    register char *p;
-    register char *ret;
-    register char tag = 'K';
+    /* get the current time */
+#ifdef HAVE_GETTIMEOFDAY
+    gettimeofday(tv, NULL);
+#else
+    tv->tv_sec = (long)time(NULL);
+    tv->tv_usec = 0;
+#endif
+}
 
-    p = ret = retarray[index];
-    index = (index + 1) % NUM_STRINGS;
+void
+time_mark(struct timeval *tv)
 
-    if (amt >= 100000)
+{
+    struct timeval thistime;
+    struct timeval timediff;
+
+    /* if the caller didnt provide one then use our own */
+    if (tv == NULL)
     {
-	amt = (amt + 512) / 1024;
-	tag = 'M';
-	if (amt >= 100000)
-	{
-	    amt = (amt + 512) / 1024;
-	    tag = 'G';
-	}
+	tv = &thistime;
     }
 
-    p = strecpy(p, itoa(amt));
-    *p++ = tag;
-    *p = '\0';
+    /* get the current time */
+#ifdef HAVE_GETTIMEOFDAY
+    gettimeofday(tv, NULL);
+#else
+    tv->tv_sec = (long)time(NULL);
+    tv->tv_usec = 0;
+#endif
 
-    return(ret);
+    /* calculate the difference */
+    timediff.tv_sec = tv->tv_sec - lasttime.tv_sec;
+    timediff.tv_usec = tv->tv_usec - lasttime.tv_usec;
+    if (timediff.tv_usec < 0) {
+	timediff.tv_sec--;
+	timediff.tv_usec += 1000000;
+    }
+
+    /* convert to milliseconds */
+    elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000;
+    if (elapsed_msecs == 0)
+    {
+	elapsed_msecs = 1;
+    }
+
+    /* save for next time */
+    lasttime = *tv;
 }
+
+unsigned int
+time_elapsed()
+
+{
+    return elapsed_msecs;
+}
+
+unsigned int
+diff_per_second(unsigned int x, unsigned int y)
+
+{
+    return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs;
+}
+
+static int debug_on = 0;
+
+#ifdef DEBUG
+FILE *debugfile;
+#endif
+
+void
+debug_set(int i)
+
+{
+    debug_on = i;
+#ifdef DEBUG
+    debugfile = fopen("/tmp/top.debug", "w");
+#endif
+}
+
+#ifdef DEBUG
+void
+xdprintf(char *fmt, ...)
+
+{
+    va_list argp;
+
+    va_start(argp, fmt);
+
+    if (debug_on)
+    {
+	vfprintf(debugfile, fmt, argp);
+	fflush(debugfile);
+    }
+
+    va_end(argp);
+}
+#endif
+
Index: Porting
===================================================================
--- Porting	(revision 183246)
+++ Porting	(working copy)
@@ -3,8 +3,6 @@
 This is still a preliminary document.  Suggestions for improvement are
 most welcome.
 
-My address is now "wnl at groupsys.com".
-
 Before you embark on a port, please send me a mail message telling me
 what platform you are porting top to.  There are three reasons for
 this: (1) I may already have a port, (2) module naming needs to be
@@ -163,3 +161,69 @@
 discuss the details.  I want to keep such changes as general as
 possible.
 
+--------
+
+Changes were made to the module interface between 3.5 and 3.6.  Here are
+the changes that need to be made to port a 3.5 module to 3.6:
+
+The array that stores memory statistics and is passed back in the system
+information structure as "memory" must now be an array of (signed) longs.
+This was done to more easily accomodate systems that have gigabytes of
+memory.  Since the numbers are supposed to be kilobytes, a long can still
+represent up to 2 terabytes.  Look for "int memory_stats[X]" (where "X"
+is some arbitrary number) and change it to "long memory_stats[X]".  If
+the module support reporting swap information on a separate line, then
+its "swap_stats" array also needs to be an array of longs.
+
+The argument to proc_owner should be an int, as in "int pid".  When it is
+used in proc_owner it should be cast as necessary.  Many operating systems
+will require it to be cast to a pid_t before being compared to the appropriate
+element in the proc structure.
+
+In the function format_next_process, the last argument in the main call
+to sprintf is the string that contains the command for the process.
+Make sure that this last argument is enclosed in a call to "printable".
+For example:  "printable(MPP(pp, p_comm))".
+
+The third argument to "get_process_info" needs to be changed to an integer,
+typically "int compare_index".  The call to qsort in get_process_info may
+be guarded by "if (compare != NULL)".  If it is, remove the if statement.
+
+The other changes to get_process_info depends on whether or not the module
+supports multiple sort orders.
+
+To support multiple keys:
+
+Create an array int (*proc_compares[])() and assign to it the list of
+comparison functions, NULL terminated.  For example:
+
+int (*proc_compares[])() = {
+    compare_cpu,
+    compare_size,
+    compare_res,
+    compare_time,
+    NULL };
+
+In get_process_info there is a call to qsort which uses one of the
+functions in proc_compares.  It should be changed so that its fourth
+argument is "proc_compares[compare_index]".
+
+If the module contains the function "proc_compare", it should be removed.
+
+There should also be a NULL-terminated array of strings which list the names
+for the sort keys, for example:
+
+char *ordernames[] = 
+{"cpu", "size", "res", "time", NULL};
+
+To indicate that this module supports multiple sort keys, add the following
+line in machine_init:
+
+	statics->order_names = ordernames;
+
+If there is no support for multiple keys:
+
+Leave statics->order_names alone and call the comparison function of
+your choice in get_process_info, ignoring the third argument.
+
+
Index: commands.c
===================================================================
--- commands.c	(revision 183246)
+++ commands.c	(working copy)
@@ -1,14 +1,42 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ *  $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
- *
- * $FreeBSD$
  */
 
 /*
@@ -21,144 +49,51 @@
 #include "os.h"
 #include <ctype.h>
 #include <signal.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <color.h>
 #include <errno.h>
-#include <sys/time.h>
+#ifdef HAVE_SYS_RESOURCE_H
 #include <sys/resource.h>
+#endif
 
+#if defined(HAVE_DECL_SYS_SIGLIST) & defined(HAVE_STRCASECMP)
+#define USE_SYS_SIGLIST
+#endif
+
+#ifdef USE_SYS_SIGLIST
+extern const char * const sys_siglist[];
+extern const char * const sys_signame[];
+#else
 #include "sigdesc.h"		/* generated automatically */
+#endif
 #include "top.h"
+#include "machine.h"
+#include "globalstate.h"
 #include "boolean.h"
+#include "color.h"
+#include "commands.h"
+#include "display.h"
+#include "screen.h"
+#include "username.h"
 #include "utils.h"
+#include "version.h"
 
 extern int  errno;
 
 extern char *copyright;
 
-/* imported from screen.c */
-extern int overstrike;
+typedef struct command {
+    int ch;
+    int (*cmd_func)(globalstate *);
+    char *help;
+} command;
 
-int err_compar();
-char *err_string();
-
 /*
- *  show_help() - display the help screen; invoked in response to
- *		either 'h' or '?'.
- */
-
-show_help()
-
-{
-    printf("Top version %s, %s\n", version_string(), copyright);
-    fputs("\n\n\
-A top users display for Unix\n\
-\n\
-These single-character commands are available:\n\
-\n\
-^L      - redraw screen\n\
-q       - quit\n\
-h or ?  - help; show this text\n", stdout);
-
-    /* not all commands are availalbe with overstrike terminals */
-    if (overstrike)
-    {
-	fputs("\n\
-Other commands are also available, but this terminal is not\n\
-sophisticated enough to handle those commands gracefully.\n\n", stdout);
-    }
-    else
-    {
-	fputs("\
-C       - toggle the displaying of weighted CPU percentage\n\
-d       - change number of displays to show\n\
-e       - list errors generated by last \"kill\" or \"renice\" command\n\
-H       - toggle the displaying of threads\n\
-i or I  - toggle the displaying of idle processes\n\
-j       - toggle the displaying of jail ID\n\
-k       - kill processes; send a signal to a list of processes\n\
-m       - toggle the display between 'cpu' and 'io' modes\n\
-n or #  - change number of processes to display\n", stdout);
-#ifdef ORDER
-	if (displaymode == DISP_CPU)
-		fputs("\
-o       - specify sort order (pri, size, res, cpu, time, threads)\n", stdout);
-	else
-		fputs("\
-o       - specify sort order (vcsw, ivcsw, read, write, fault, total)\n", stdout);
-#endif
-	fputs("\
-r       - renice a process\n\
-s       - change number of seconds to delay between updates\n\
-S       - toggle the displaying of system processes\n\
-a       - toggle the displaying of process titles\n\
-t       - toggle the display of this process\n\
-u       - display processes for only one user (+ selects all users)\n\
-\n\
-\n", stdout);
-    }
-}
-
-/*
- *  Utility routines that help with some of the commands.
- */
-
-char *next_field(str)
-
-register char *str;
-
-{
-    if ((str = strchr(str, ' ')) == NULL)
-    {
-	return(NULL);
-    }
-    *str = '\0';
-    while (*++str == ' ') /* loop */;
-
-    /* if there is nothing left of the string, return NULL */
-    /* This fix is dedicated to Greg Earle */
-    return(*str == '\0' ? NULL : str);
-}
-
-scanint(str, intp)
-
-char *str;
-int  *intp;
-
-{
-    register int val = 0;
-    register char ch;
-
-    /* if there is nothing left of the string, flag it as an error */
-    /* This fix is dedicated to Greg Earle */
-    if (*str == '\0')
-    {
-	return(-1);
-    }
-
-    while ((ch = *str++) != '\0')
-    {
-	if (isdigit(ch))
-	{
-	    val = val * 10 + (ch - '0');
-	}
-	else if (isspace(ch))
-	{
-	    break;
-	}
-	else
-	{
-	    return(-1);
-	}
-    }
-    *intp = val;
-    return(0);
-}
-
-/*
  *  Some of the commands make system calls that could generate errors.
  *  These errors are collected up in an array of structures for later
  *  contemplation and display.  Such routines return a string containing an
- *  error message, or NULL if no errors occurred.  The next few routines are
- *  for manipulating and displaying these errors.  We need an upper limit on
+ *  error message, or NULL if no errors occurred.  We need an upper limit on
  *  the number of errors, so we arbitrarily choose 20.
  */
 
@@ -172,97 +107,44 @@
 
 static struct errs errs[ERRMAX];
 static int errcnt;
-static char *err_toomany = " too many errors occurred";
-static char *err_listem = 
-	" Many errors occurred.  Press `e' to display the list of errors.";
 
 /* These macros get used to reset and log the errors */
 #define ERR_RESET   errcnt = 0
-#define ERROR(p, e) if (errcnt >= ERRMAX) \
+#define ERROR(p, e) if (errcnt < ERRMAX) \
 		    { \
-			return(err_toomany); \
-		    } \
-		    else \
-		    { \
 			errs[errcnt].arg = (p); \
 			errs[errcnt++].errnum = (e); \
 		    }
 
 /*
- *  err_string() - return an appropriate error string.  This is what the
- *	command will return for displaying.  If no errors were logged, then
- *	return NULL.  The maximum length of the error string is defined by
- *	"STRMAX".
+ *  err_compar(p1, p2) - comparison routine used by "qsort"
+ *	for sorting errors.
  */
 
-#define STRMAX 80
+int
+err_compar(const void *p1, const void *p2)
 
-char *err_string()
-
 {
-    register struct errs *errp;
-    register int  cnt = 0;
-    register int  first = Yes;
-    register int  currerr = -1;
-    int stringlen;		/* characters still available in "string" */
-    static char string[STRMAX];
+    register int result;
 
-    /* if there are no errors, return NULL */
-    if (errcnt == 0)
+    if ((result = ((struct errs *)p1)->errnum -
+	 ((struct errs *)p2)->errnum) == 0)
     {
-	return(NULL);
+	return(strcmp(((struct errs *)p1)->arg,
+		      ((struct errs *)p2)->arg));
     }
-
-    /* sort the errors */
-    qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
-
-    /* need a space at the front of the error string */
-    string[0] = ' ';
-    string[1] = '\0';
-    stringlen = STRMAX - 2;
-
-    /* loop thru the sorted list, building an error string */
-    while (cnt < errcnt)
-    {
-	errp = &(errs[cnt++]);
-	if (errp->errnum != currerr)
-	{
-	    if (currerr != -1)
-	    {
-		if ((stringlen = str_adderr(string, stringlen, currerr)) < 2)
-		{
-		    return(err_listem);
-		}
-		(void) strcat(string, "; ");	  /* we know there's more */
-	    }
-	    currerr = errp->errnum;
-	    first = Yes;
-	}
-	if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) ==0)
-	{
-	    return(err_listem);
-	}
-	first = No;
-    }
-
-    /* add final message */
-    stringlen = str_adderr(string, stringlen, currerr);
-
-    /* return the error string */
-    return(stringlen == 0 ? err_listem : string);
+    return(result);
 }
 
 /*
  *  str_adderr(str, len, err) - add an explanation of error "err" to
- *	the string "str".
+ *	the string "str" without overflowing length "len".  return
+ *      number of characters remaining in str, or 0 if overflowed.
  */
 
-str_adderr(str, len, err)
+int
+str_adderr(char *str, int len, int err)
 
-char *str;
-int len;
-int err;
-
 {
     register char *msg;
     register int  msglen;
@@ -280,17 +162,15 @@
 
 /*
  *  str_addarg(str, len, arg, first) - add the string argument "arg" to
- *	the string "str".  This is the first in the group when "first"
- *	is set (indicating that a comma should NOT be added to the front).
+ *	the string "str" without overflowing length "len".  This is the
+ *      first in the group when "first" is set (indicating that a comma
+ *      should NOT be added to the front).  Return number of characters
+ *      remaining in str, or 0 if overflowed.
  */
 
-str_addarg(str, len, arg, first)
+int
+str_addarg(char *str, int len, char *arg, int first)
 
-char *str;
-int  len;
-char *arg;
-int  first;
-
 {
     register int arglen;
 
@@ -312,28 +192,139 @@
 }
 
 /*
- *  err_compar(p1, p2) - comparison routine used by "qsort"
- *	for sorting errors.
+ * void err_string()
+ *
+ * Use message_error to log errors in the errs array.  This function 
+ * will combine identical errors to make the message short, but if
+ * there is more than one type of error it will call message_error
+ * for each one.
  */
 
-err_compar(p1, p2)
+#define STRMAX 80
 
-register struct errs *p1, *p2;
+void
+err_string()
 
 {
-    register int result;
+    register struct errs *errp;
+    register int  cnt = 0;
+    register int  first = Yes;
+    register int  currerr = -1;
+    int stringlen = 0;		/* characters still available in "string" */
+    char string[STRMAX];
 
-    if ((result = p1->errnum - p2->errnum) == 0)
+    /* if there are no errors, our job is easy */
+    if (errcnt == 0)
     {
-	return(strcmp(p1->arg, p2->arg));
+	return;
     }
-    return(result);
+
+    /* sort the errors */
+    qsort((char *)errs, errcnt, sizeof(struct errs), err_compar);
+
+    /* initialize the buffer (probably not necessary) */
+    string[0] = '\0';
+    stringlen = STRMAX - 1;
+
+    /* loop thru the sorted list, logging errors */
+    while (cnt < errcnt)
+    {
+	/* point to the current error */
+	errp = &(errs[cnt++]);
+
+	/* note that on overflow "stringlen" will become 0 and all
+	   subsequent calls to str_addarg or str_adderr will return 0 */
+
+	/* if the error number is different then add the error string */
+	if (errp->errnum != currerr)
+	{
+	    if (currerr != -1)
+	    {
+		/* add error string and log the error */
+		stringlen = str_adderr(string, stringlen, currerr);
+		message_error(" %s", string);
+
+	    }
+	    /* reset the buffer */
+	    string[0] = '\0';
+	    stringlen = STRMAX - 1;
+
+	    /* move to next error num */
+	    currerr = errp->errnum;
+	    first = Yes;
+	}
+
+	/* add this arg */
+	stringlen = str_addarg(string, stringlen, errp->arg, first);
+	first = No;
+    }
+
+    /* add final message */
+    stringlen = str_adderr(string, stringlen, currerr);
+
+    /* write the error string */
+    message_error(" %s", string);
 }
 
 /*
+ *  Utility routines that help with some of the commands.
+ */
+
+char *
+next_field(char *str)
+
+
+{
+    if ((str = strchr(str, ' ')) == NULL)
+    {
+	return(NULL);
+    }
+    *str = '\0';
+    while (*++str == ' ') /* loop */;
+
+    /* if there is nothing left of the string, return NULL */
+    /* This fix is dedicated to Greg Earle */
+    return(*str == '\0' ? NULL : str);
+}
+
+int
+scanint(char *str, int *intp)
+
+{
+    register int val = 0;
+    register int ch;
+
+    /* if there is nothing left of the string, flag it as an error */
+    /* This fix is dedicated to Greg Earle */
+    if (*str == '\0')
+    {
+	return(-1);
+    }
+
+    while ((ch = *str++) != '\0')
+    {
+	if (isdigit(ch))
+	{
+	    val = val * 10 + (ch - '0');
+	}
+	else if (isspace(ch))
+	{
+	    break;
+	}
+	else
+	{
+	    return(-1);
+	}
+    }
+    *intp = val;
+    return(0);
+}
+
+/*
  *  error_count() - return the number of errors currently logged.
  */
 
+int
 error_count()
 
 {
@@ -344,6 +335,7 @@
  *  show_errors() - display on stdout the current log of errors.
  */
 
+void
 show_errors()
 
 {
@@ -364,16 +356,18 @@
  *		command does; invoked in response to 'k'.
  */
 
-char *kill_procs(str)
+void
+kill_procs(char *str)
 
-char *str;
-
 {
     register char *nptr;
     int signum = SIGTERM;	/* default */
     int procnum;
+    int uid;
+    int owner;
+#ifndef USE_SYS_SIGLIST
     struct sigdesc *sigp;
-    int uid;
+#endif
 
     /* reset error array */
     ERR_RESET;
@@ -382,30 +376,51 @@
     uid = getuid();
 
     /* skip over leading white space */
-    while (isspace(*str)) str++;
+    while (isspace((int)*str)) str++;
 
     if (str[0] == '-')
     {
 	/* explicit signal specified */
 	if ((nptr = next_field(str)) == NULL)
 	{
-	    return(" kill: no processes specified");
+	    message_error(" kill: no processes specified");
+	    return;
 	}
 
-	if (isdigit(str[1]))
+	str++;
+	if (isdigit((int)str[0]))
 	{
-	    (void) scanint(str + 1, &signum);
+	    (void) scanint(str, &signum);
 	    if (signum <= 0 || signum >= NSIG)
 	    {
-		return(" invalid signal number");
+		message_error(" kill: invalid signal number");
+		return;
 	    }
 	}
 	else 
 	{
 	    /* translate the name into a number */
+#ifdef USE_SYS_SIGLIST
+	    for (signum = 1; signum < NSIG; signum++)
+	    {
+		if (strcasecmp(sys_signame[signum], str) == 0)
+		{
+		    break;
+		}
+	    }
+	    if (signum == NSIG)
+	    {
+		message_error(" kill: bad signal name");
+		return;
+	    }
+#else
 	    for (sigp = sigdesc; sigp->name != NULL; sigp++)
 	    {
-		if (strcmp(sigp->name, str + 1) == 0)
+#ifdef HAVE_STRCASECMP
+		if (strcasecmp(sigp->name, str) == 0)
+#else
+		if (strcmp(sigp->name, str) == 0)
+#endif
 		{
 		    signum = sigp->number;
 		    break;
@@ -415,8 +430,10 @@
 	    /* was it ever found */
 	    if (sigp->name == NULL)
 	    {
-		return(" bad signal name");
+		message_error(" kill: bad signal name");
+		return;
 	    }
+#endif
 	}
 	/* put the new pointer in place */
 	str = nptr;
@@ -432,9 +449,10 @@
 	else
 	{
 	    /* check process owner if we're not root */
-	    if (uid && (uid != proc_owner(procnum)))
+	    owner = proc_owner(procnum);
+	    if (uid && (uid != owner))
 	    {
-		ERROR(str, EACCES);
+		ERROR(str, owner == -1 ? ESRCH : EACCES);
 	    }
 	    /* go in for the kill */
 	    else if (kill(procnum, signum) == -1)
@@ -445,8 +463,8 @@
 	}
     } while ((str = next_field(str)) != NULL);
 
-    /* return appropriate error string */
-    return(err_string());
+    /* process errors */
+    err_string();
 }
 
 /*
@@ -454,10 +472,9 @@
  *		"renice" command does; invoked in response to 'r'.
  */
 
-char *renice_procs(str)
+void
+renice_procs(char *str)
 
-char *str;
-
 {
     register char negate;
     int prio;
@@ -487,16 +504,17 @@
     /* check for validity */
     if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
     {
-	return(" bad priority value");
+	message_error(" renice: bad priority value");
     }
 #endif
 
     /* move to the first process number */
     if ((str = next_field(str)) == NULL)
     {
-	return(" no processes specified");
+	message_error(" remice: no processes specified");
     }
 
+#ifdef HAVE_SETPRIORITY
     /* loop thru the process numbers, renicing each one */
     do
     {
@@ -515,8 +533,543 @@
 	    ERROR(str, errno);
 	}
     } while ((str = next_field(str)) != NULL);
+    err_string();
+#else
+    message_error(" renice operation not supported");
+#endif
+}
 
-    /* return appropriate error string */
-    return(err_string());
+/* COMMAND ROUTINES */
+
+/*
+ * Each command routine is called by command_process and is passed a
+ * pointer to the current global state.  Command routines are free
+ * to change anything in the global state, although changes to the
+ * statics structure are discouraged.  Whatever a command routine
+ * returns will be returned by command_process.
+ */
+
+void
+cmd_quit(globalstate *gstate)
+
+{
+    quit(EX_OK);
+    /*NOTREACHED*/
 }
 
+int
+cmd_update(globalstate *gstate)
+
+{
+    /* go home for visual feedback */
+    screen_home();
+    fflush(stdout);
+    message_expire();
+    return CMD_REFRESH;
+}
+
+int
+cmd_redraw(globalstate *gstate)
+
+{
+    gstate->fulldraw = Yes;
+    return CMD_REFRESH;
+}
+
+int
+cmd_color(globalstate *gstate)
+
+{
+    gstate->use_color = color_activate(-1);
+    gstate->fulldraw = Yes;
+    return CMD_REFRESH;
+}
+
+int
+cmd_number(globalstate *gstate)
+
+{
+    int newval;
+    char tmpbuf[20];
+
+    message_prompt("Number of processes to show: ");
+    newval = readline(tmpbuf, 8, Yes);
+    if (newval > -1)
+    {
+	if (newval > gstate->max_topn)
+	{
+	    message_error(" This terminal can only display %d processes",
+			  gstate->max_topn);
+	}
+
+	if (newval == 0)
+	{
+	    /* inhibit the header */
+	    display_header(No);
+	}
+
+	else if (gstate->topn == 0)
+	{
+	    display_header(Yes);
+	}
+
+	gstate->topn = newval;
+    }
+    return CMD_REFRESH;
+}
+
+int
+cmd_delay(globalstate *gstate)
+
+{
+    int newval;
+    char tmpbuf[20];
+
+    message_prompt("Seconds to delay: ");
+    if ((newval = readline(tmpbuf, 8, Yes)) > -1)
+    {
+	if ((gstate->delay = newval) == 0 && getuid() != 0)
+	{
+	    gstate->delay = 1;
+	}
+    }
+    return CMD_REFRESH;
+}
+
+int
+cmd_idle(globalstate *gstate)
+
+{
+    gstate->pselect.idle = !gstate->pselect.idle;
+    message_error(" %sisplaying idle processes.",
+		  gstate->pselect.idle ? "D" : "Not d");
+    return CMD_REFRESH;
+}
+
+int
+cmd_weightedcpu(globalstate *gstate)
+
+{
+    gstate->pselect.wcpu = !gstate->pselect.wcpu;
+    message_error(" Displaying %sCPU.",
+		  gstate->pselect.wcpu ? "W" : "");
+    return CMD_REFRESH;
+}
+
+int
+cmd_jails(globalstate *gstate)
+
+{
+    gstate->pselect.jail = !gstate->pselect.jail;
+    message_error(" %sisplaying jail ID.",
+		  gstate->pselect.jail ? "D" : "Not d");
+    return CMD_REFRESH;
+}
+
+int
+cmd_thisprocess(globalstate *gstate)
+
+{
+    gstate->pselect.self = !gstate->pselect.self;
+    message_error(" %sisplaying self.",
+		  gstate->pselect.self ? "D" : "Not d");
+    return CMD_REFRESH;
+}
+
+int
+cmd_displays(globalstate *gstate)
+
+{
+    int i;
+    char tmpbuf[20];
+
+    message_prompt("Displays to show (currently %s): ",
+		   gstate->displays == -1 ? "infinite" :
+		   itoa(gstate->displays));
+
+    if ((i = readline(tmpbuf, 10, Yes)) > 0)
+    {
+	gstate->displays = i;
+    }
+    else if (i == 0)
+    {
+	quit(EX_OK);
+	/*NOTREACHED*/
+    }
+    return CMD_OK;
+}
+
+int
+cmd_cmdline(globalstate *gstate)
+
+{
+    if (gstate->statics->flags.fullcmds)
+    {
+	gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
+	message_error(" %sisplaying full command lines.",
+		      gstate->pselect.fullcmd ? "D" : "Not d");
+	return CMD_REFRESH;
+    }
+    message_error(" Full command display not supported.");
+    return CMD_OK;
+}
+
+int
+cmd_order(globalstate *gstate)
+
+{
+    char tmpbuf[MAX_COLS];
+    int i;
+
+    if (gstate->statics->order_names != NULL)
+    {
+	message_prompt("Column to sort: ");
+	if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+	{
+	    if ((i = string_index(tmpbuf, gstate->statics->order_names)) == -1)
+	    {
+		message_error(" Sort order \"%s\" not recognized", tmpbuf);
+	    }
+	    else
+	    {
+		gstate->order_index = i;
+		return CMD_REFRESH;
+	    }
+	}
+    }
+    return CMD_OK;
+}
+
+int
+cmd_order_x(globalstate *gstate, char *name, ...)
+
+{
+    va_list ap;
+    char *p;
+    char **names;
+    int i;
+
+    names = gstate->statics->order_names;
+    if (names != NULL)
+    {
+	if ((i = string_index(name, names)) == -1)
+	{
+	    /* check the alternate list */
+	    va_start(ap, name);
+	    p = va_arg(ap, char *);
+	    while (p != NULL)
+	    {
+		if ((i = string_index(p, names)) != -1)
+		{
+		    gstate->order_index = i;
+		    return CMD_REFRESH;
+		}
+		p = va_arg(ap, char *);
+	    }
+	    message_error(" Sort order not recognized");
+	}
+	else
+	{
+	    gstate->order_index = i;
+	    return CMD_REFRESH;
+	}
+    }
+    return CMD_OK;
+}
+
+int
+cmd_order_cpu(globalstate *gstate)
+
+{
+    return cmd_order_x(gstate, "cpu", NULL);
+}
+
+int
+cmd_order_pid(globalstate *gstate)
+
+{
+    return cmd_order_x(gstate, "pid", NULL);
+}
+
+int
+cmd_order_mem(globalstate *gstate)
+
+{
+    return cmd_order_x(gstate, "mem", "size", NULL);
+}
+
+int
+cmd_order_time(globalstate *gstate)
+
+{
+    return cmd_order_x(gstate, "time");
+}
+
+#ifdef ENABLE_KILL
+
+int
+cmd_kill(globalstate *gstate)
+
+{
+    char tmpbuf[MAX_COLS];
+
+    message_prompt_plain("kill ");
+    if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+    {
+	kill_procs(tmpbuf);
+    }
+    return CMD_OK;
+}
+	    
+int
+cmd_renice(globalstate *gstate)
+
+{
+    char tmpbuf[MAX_COLS];
+
+    message_prompt_plain("renice ");
+    if (readline(tmpbuf, sizeof(tmpbuf), No) > 0)
+    {
+	renice_procs(tmpbuf);
+    }
+    return CMD_OK;
+}
+
+#endif
+
+int
+cmd_user(globalstate *gstate)
+
+{
+    char linebuf[MAX_COLS];
+    int i;
+    int ret = CMD_OK;
+
+    message_prompt("Username to show: ");
+    if (readline(linebuf, sizeof(linebuf), No) > 0)
+    {
+	if (linebuf[0] == '+' &&
+	    linebuf[1] == '\0')
+	{
+	    gstate->pselect.uid = -1;
+	    ret = CMD_REFRESH;
+	}
+	else if ((i = userid(linebuf)) == -1)
+	{
+	    message_error(" %s: unknown user", linebuf);
+	}
+	else
+	{
+	    gstate->pselect.uid = i;
+	    ret = CMD_REFRESH;
+	}
+    }
+    return ret;
+}
+
+int
+cmd_command(globalstate *gstate)
+
+{
+    char linebuf[MAX_COLS];
+
+    if (gstate->pselect.command != NULL)
+    {
+	free(gstate->pselect.command);
+	gstate->pselect.command = NULL;
+    }
+
+    message_prompt("Command to show: ");
+    if (readline(linebuf, sizeof(linebuf), No) > 0)
+    {
+	if (linebuf[0] != '\0')
+	{
+	    gstate->pselect.command = strdup(linebuf);
+	}
+    }
+    return CMD_REFRESH;
+}
+
+int
+cmd_useruid(globalstate *gstate)
+
+{
+    gstate->pselect.usernames = !gstate->pselect.usernames;
+    display_header(2);
+    return CMD_REFRESH;
+}
+
+int
+cmd_mode(globalstate *gstate)
+
+{
+    if (gstate->statics->modemax <= 1)
+    {
+	return CMD_NA;
+    }
+    gstate->pselect.mode = (gstate->pselect.mode + 1) % gstate->statics->modemax;
+    display_header(2);
+    return CMD_REFRESH;
+}
+
+int
+cmd_system(globalstate *gstate)
+
+{
+    gstate->pselect.system = !gstate->pselect.system;
+    display_header(2);
+    message_error(" %showing system processes.",
+		  gstate->pselect.system ? "S" : "Not s");
+    return CMD_REFRESH;
+}
+
+int
+cmd_threads(globalstate *gstate)
+
+{
+    if (gstate->statics->flags.threads)
+    {
+	gstate->pselect.threads = !gstate->pselect.threads;
+	display_header(2);
+	message_error(" Displaying threads as %s.",
+		      gstate->pselect.threads ? "seperately" : "a count");
+	return CMD_REFRESH;
+    }
+    message_error(" Thread support not enabled.");
+    return CMD_NA;
+}
+
+/* forward reference for cmd_help, as it needs to see the command_table */
+int cmd_help(globalstate *gstate);
+
+/* command table */
+command command_table[] = {
+    { '\014', cmd_redraw, "redraw screen" },
+    { ' ', cmd_update, "update screen" },
+    { '?', cmd_help, "help; show this text" },
+    { 'h', cmd_help, NULL },
+    { 'C', cmd_weightedcpu, "toggle the displaying of weighted CPU percentage" },
+    { 'E', cmd_color, "toggle the use of color" },
+    { 'H', cmd_threads, "toggle the display of individual threads" },
+    { 'j', cmd_jails, "toggle the displaying of jail ID" },
+    { 'M', cmd_order_mem, "sort by memory usage" },
+    { 'N', cmd_order_pid, "sort by process id" },
+    { 'P', cmd_order_cpu, "sort by CPU usage" },
+    { 'S', cmd_system, "toggle the display of system processes" },
+    { 'T', cmd_order_time, "sort by CPU time" },
+    { 'U', cmd_useruid, "toggle the display of usernames or uids" },
+    { 'c', cmd_command, "display processes by command name" },
+    { 'd', cmd_displays, "change number of displays to show" },
+    { 'a', cmd_cmdline, "toggle the display of full command paths" },
+    { 'i', cmd_idle, "toggle the displaying of idle processes" },
+    { 'I', cmd_idle, NULL },
+#ifdef ENABLE_KILL
+    { 'k', cmd_kill, "kill processes; send a signal to a list of processes" },
+#endif
+    { 'm', cmd_mode, "toggle between 'cpu' and 'io' modes" },
+    { 'n', cmd_number, "change number of processes to display" },
+    { '#', cmd_number, NULL },
+    { 'o', cmd_order, "specify sort order (see below)" },
+    { 'q', (int (*)(globalstate *))cmd_quit, "quit" },
+#ifdef ENABLE_KILL
+    { 'r', cmd_renice, "renice a process" },
+#endif
+    { 's', cmd_delay, "change number of seconds to delay between updates" },
+    { 't', cmd_thisprocess, "toggle the display of this process" },
+    { 'u', cmd_user, "display processes for only one user (+ selects all users)" },
+    { '\0', NULL, NULL },
+};
+
+int
+cmd_help(globalstate *gstate)
+
+{
+    command *c;
+    char buf[12];
+    char *p;
+    char *help;
+
+    display_pagerstart();
+
+    display_pager("Top version %s, %s\n", version_string(), copyright);
+    display_pager("Platform module: %s\n\n", MODULE);
+    display_pager("A top users display for Unix\n\n");
+    display_pager("These single-character commands are available:\n\n");
+
+    c = command_table;
+    while (c->cmd_func != NULL)
+    {
+	/* skip null help strings */
+	if ((help = c->help) == NULL)
+	{
+	    continue;
+	}
+
+	/* translate character in to something readable */
+	if (c->ch < ' ')
+	{
+	    buf[0] = '^';
+	    buf[1] = c->ch + '@';
+	    buf[2] = '\0';
+	}
+	else if (c->ch == ' ')
+	{
+	    strcpy(buf, "<sp>");
+	}
+	else
+	{
+	    buf[0] = c->ch;
+	    buf[1] = '\0';
+	}
+
+	/* if the next command is the same, fold them onto one line */
+	if ((c+1)->cmd_func == c->cmd_func)
+	{
+	    strcat(buf, " or ");
+	    p = buf + strlen(buf);
+	    *p++ = (c+1)->ch;
+	    *p = '\0';
+	    c++;
+	}
+
+	display_pager("%-7s - %s\n", buf, help);
+	c++;
+    }
+
+    display_pager("\nNot all commands are available on all systems.\n\n");
+    display_pager("Available sort orders: %s\n", gstate->order_namelist);
+    display_pagerend();
+    gstate->fulldraw = Yes;
+    return CMD_REFRESH;
+}
+
+/*
+ * int command_process(globalstate *gstate, int cmd)
+ *
+ * Process the single-character command "cmd".  The global state may
+ * be modified by the command to alter the output.  Returns CMD_ERROR
+ * if there was a serious error that requires an immediate exit, CMD_OK
+ * to indicate success, CMD_REFRESH to indicate that the screen needs
+ * to be refreshed immediately, CMD_UNKNOWN when the command is not known,
+ * and CMD_NA when the command is not available.  Error messages for
+ * CMD_NA and CMD_UNKNOWN must be handled by the caller.
+ */
+
+int
+command_process(globalstate *gstate, int cmd)
+
+{
+    command *c;
+
+    c = command_table;
+    while (c->cmd_func != NULL)
+    {
+	if (c->ch == cmd)
+	{
+	    return (c->cmd_func)(gstate);
+	}
+	c++;
+    }
+
+    return CMD_UNKNOWN;
+}
Index: top.c
===================================================================
--- top.c	(revision 183246)
+++ top.c	(working copy)
@@ -1,1161 +1,1025 @@
-char *copyright =
-    "Copyright (c) 1984 through 1996, William LeFebvre";
-
 /*
- *  Top users/processes display for Unix
- *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
- *  Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
- *  Copyright (c) 1996, William LeFebvre, Group sys Consulting
- *
- * $FreeBSD$
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 /*
- *  See the file "Changes" for information on version-to-version changes.
+ * $FreeBSD$
  */
 
-/*
- *  This file contains "main" and other high-level routines.
- */
+char *copyright =
+    "Copyright (c) 1984 through 2008, William LeFebvre";
 
 /*
- * The following preprocessor variables, when defined, are used to
- * distinguish between different Unix implementations:
- *
- *	SIGHOLD  - use SVR4 sighold function when defined
- *	SIGRELSE - use SVR4 sigrelse function when defined
- *	FD_SET   - macros FD_SET and FD_ZERO are used when defined
+ * Changes to other files that we can do at the same time:
+ * screen.c:init_termcap: get rid of the "interactive" argument and have it
+ *      pass back something meaningful (such as success/failure/error).
  */
 
 #include "os.h"
 #include <errno.h>
 #include <signal.h>
-#include <setjmp.h>
 #include <ctype.h>
-#include <sys/time.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
 
+#ifdef HAVE_SYS_UTSNAME_H
+#include <sys/utsname.h>
+#endif
+
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+
+/* definitions */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO 0
+#endif
+
+/* determine which type of signal functions to use */
+/* cant have sigaction without sigprocmask */
+#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK)
+#undef HAVE_SIGACTION
+#endif
+/* always use sigaction when it is available */
+#ifdef HAVE_SIGACTION
+#undef HAVE_SIGHOLD
+#else
+/* use sighold/sigrelse, otherwise use old fashioned BSD signals */
+#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
+#define BSD_SIGNALS
+#endif
+#endif
+
+/* if FD_SET and friends aren't present, then fake something up */
+#ifndef FD_SET
+typedef int fd_set;
+#define FD_ZERO(x)     (*(x) = 0)
+#define FD_SET(f, x)   (*(x) = 1<<f)
+#endif
+
 /* includes specific to top */
-#include "display.h"		/* interface to display package */
-#include "screen.h"		/* interface to screen package */
+
 #include "top.h"
-#include "top.local.h"
-#include "boolean.h"
 #include "machine.h"
+#include "globalstate.h"
+#include "commands.h"
+#include "display.h"
+#include "screen.h"
+#include "boolean.h"
+#include "username.h"
 #include "utils.h"
+#include "version.h"
+#ifdef ENABLE_COLOR
+#include "color.h"
+#endif
 
-/* Size of the stdio buffer given to stdout */
-#define Buffersize	2048
+/* definitions */
+#define BUFFERSIZE 4096
+#define JMP_RESUME 1
+#define JMP_RESIZE 2
 
-/* The buffer that stdio will use */
-char stdoutbuf[Buffersize];
-
-/* build Signal masks */
-#define Smask(s)	(1 << ((s) - 1))
-
-/* for getopt: */
+/* externs for getopt: */
 extern int  optind;
 extern char *optarg;
 
-/* imported from screen.c */
-extern int overstrike;
+/* statics */
+static char stdoutbuf[BUFFERSIZE];
+static jmp_buf jmp_int;
 
-static int fmt_flags = 0;
+/* globals */
+char *myname = "top";
 int pcpu_stats = No;
 
-/* signal handling routines */
-sigret_t leave();
-sigret_t onalrm();
-sigret_t tstop();
-#ifdef SIGWINCH
-sigret_t winch();
-#endif
+void
+quit(int status)
 
-volatile sig_atomic_t leaveflag;
-volatile sig_atomic_t tstopflag;
-volatile sig_atomic_t winchflag;
+{
+    screen_end();
+    chdir("/tmp");
+    exit(status);
+    /* NOTREACHED */
+}
 
-/* internal routines */
-void quit();
+/*
+ *  signal handlers
+ */
 
-/* values which need to be accessed by signal handlers */
-static int max_topn;		/* maximum displayable processes */
+void
+set_signal(int sig, RETSIGTYPE (*handler)(int))
 
-/* miscellaneous things */
-struct process_select ps;
-char *myname = "top";
-jmp_buf jmp_int;
+{
+#ifdef HAVE_SIGACTION
+    struct sigaction action;
 
-/* routines that don't return int */
+    action.sa_handler = handler;
+    action.sa_flags = 0;
+    (void) sigaction(sig, &action, NULL);
+#else
+    (void) signal(sig, handler);
+#endif
+}
 
-char *username();
-char *ctime();
-char *kill_procs();
-char *renice_procs();
+void
+release_signal(int sig)
 
-#ifdef ORDER
-extern int (*compares[])();
-#else
-extern int proc_compare();
-extern int io_compare();
+{
+#ifdef HAVE_SIGACTION
+    sigset_t set;
+    sigemptyset(&set);
+    sigaddset(&set, sig);
+    sigprocmask(SIG_UNBLOCK, &set, NULL);
 #endif
-time_t time();
 
-caddr_t get_process_info();
+#ifdef HAVE_SIGHOLD
+    sigrelse(sig);
+#endif
 
-/* different routines for displaying the user's identification */
-/* (values assigned to get_userid) */
-char *username();
-char *itoa7();
+#ifdef BSD_SIGNALS
+    (void) sigsetmask(sigblock(0) & ~(sigmask(sig)));
+#endif
+}
 
-/* display routines that need to be predeclared */
-int i_loadave();
-int u_loadave();
-int i_procstates();
-int u_procstates();
-int i_cpustates();
-int u_cpustates();
-int i_memory();
-int u_memory();
-int i_swap();
-int u_swap();
-int i_message();
-int u_message();
-int i_header();
-int u_header();
-int i_process();
-int u_process();
+RETSIGTYPE
+sig_leave(int i)	/* exit under normal conditions -- INT handler */
 
-/* pointers to display routines */
-int (*d_loadave)() = i_loadave;
-int (*d_procstates)() = i_procstates;
-int (*d_cpustates)() = i_cpustates;
-int (*d_memory)() = i_memory;
-int (*d_swap)() = i_swap;
-int (*d_message)() = i_message;
-int (*d_header)() = i_header;
-int (*d_process)() = i_process;
+{
+    screen_end();
+    exit(EX_OK);
+}
 
+RETSIGTYPE
+sig_tstop(int i)	/* SIGTSTP handler */
 
-main(argc, argv)
+{
+    /* move to the lower left */
+    screen_end();
+    fflush(stdout);
 
-int  argc;
-char *argv[];
+    /* default the signal handler action */
+    set_signal(SIGTSTP, SIG_DFL);
 
+    /* unblock the TSTP signal */
+    release_signal(SIGTSTP);
+
+    /* send ourselves a TSTP to stop the process */
+    (void) kill(0, SIGTSTP);
+
+    /* reset the signal handler */
+    set_signal(SIGTSTP, sig_tstop);
+
+    /* reinit screen */
+    screen_reinit();
+
+    /* jump back to a known place in the main loop */
+    longjmp(jmp_int, JMP_RESUME);
+
+    /* NOTREACHED */
+}
+
+#ifdef SIGWINCH
+RETSIGTYPE
+sig_winch(int i)		/* SIGWINCH handler */
+
 {
-    register int i;
-    register int active_procs;
-    register int change;
+    /* reascertain the screen dimensions */
+    screen_getsize();
 
-    struct system_info system_info;
-    struct statics statics;
-    caddr_t processes;
+    /* jump back to a known place in the main loop */
+    longjmp(jmp_int, JMP_RESIZE);
+}
+#endif
 
-    static char tempbuf1[50];
-    static char tempbuf2[50];
-    int old_sigmask;		/* only used for BSD-style signals */
-    int topn = Default_TOPN;
-    int delay = Default_DELAY;
-    int displays = 0;		/* indicates unspecified */
-    int sel_ret = 0;
-    time_t curr_time;
-    char *(*get_userid)() = username;
-    char *uname_field = "USERNAME";
-    char *header_text;
-    char *env_top;
-    char **preset_argv;
-    int  preset_argc = 0;
-    char **av;
-    int  ac;
-    char dostates = No;
-    char do_unames = Yes;
-    char interactive = Maybe;
-    char warnings = 0;
-#if Default_TOPN == Infinity
-    char topn_specified = No;
+#ifdef HAVE_SIGACTION
+static sigset_t signalset;
 #endif
-    char ch;
-    char *iptr;
-    char no_command = 1;
-    struct timeval timeout;
-#ifdef ORDER
-    char *order_name = NULL;
-    int order_index = 0;
+
+void *
+hold_signals()
+
+{
+#ifdef HAVE_SIGACTION
+    sigemptyset(&signalset);
+    sigaddset(&signalset, SIGINT);
+    sigaddset(&signalset, SIGQUIT);
+    sigaddset(&signalset, SIGTSTP);
+#ifdef SIGWINCH
+    sigaddset(&signalset, SIGWINCH);
 #endif
-#ifndef FD_SET
-    /* FD_SET and friends are not present:  fake it */
-    typedef int fd_set;
-#define FD_ZERO(x)     (*(x) = 0)
-#define FD_SET(f, x)   (*(x) = 1<<f)
+    sigprocmask(SIG_BLOCK, &signalset, NULL);
+    return (void *)(&signalset);
 #endif
-    fd_set readfds;
 
-#ifdef ORDER
-    static char command_chars[] = "\f qh?en#sdkriIutHmSCajo";
-#else
-    static char command_chars[] = "\f qh?en#sdkriIutHmSCaj";
+#ifdef HAVE_SIGHOLD
+    sighold(SIGINT);
+    sighold(SIGQUIT);
+    sighold(SIGTSTP);
+#ifdef SIGWINCH
+    sighold(SIGWINCH);
+    return NULL;
 #endif
-/* these defines enumerate the "strchr"s of the commands in command_chars */
-#define CMD_redraw	0
-#define CMD_update	1
-#define CMD_quit	2
-#define CMD_help1	3
-#define CMD_help2	4
-#define CMD_OSLIMIT	4    /* terminals with OS can only handle commands */
-#define CMD_errors	5    /* less than or equal to CMD_OSLIMIT	   */
-#define CMD_number1	6
-#define CMD_number2	7
-#define CMD_delay	8
-#define CMD_displays	9
-#define CMD_kill	10
-#define CMD_renice	11
-#define CMD_idletog     12
-#define CMD_idletog2    13
-#define CMD_user	14
-#define CMD_selftog	15
-#define CMD_thrtog	16
-#define CMD_viewtog	17
-#define CMD_viewsys	18
-#define	CMD_wcputog	19
-#define	CMD_showargs	20
-#define	CMD_jidtog	21
-#ifdef ORDER
-#define CMD_order       22
 #endif
 
-    /* set the buffer for stdout */
-#ifdef DEBUG
-    extern FILE *debug;
-    debug = fopen("debug.run", "w");
-    setbuffer(stdout, NULL, 0);
+#ifdef BSD_SIGNALS
+    int mask;
+#ifdef SIGWINCH
+    mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
+		    sigmask(SIGTSTP) | sigmask(SIGWINCH));
 #else
-    setbuffer(stdout, stdoutbuf, Buffersize);
+    mask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) | sigmask(SIGTSTP));
+    return (void *)mask;
 #endif
+#endif
 
-    /* get our name */
-    if (argc > 0)
-    {
-	if ((myname = strrchr(argv[0], '/')) == 0)
-	{
-	    myname = argv[0];
-	}
-	else
-	{
-	    myname++;
-	}
-    }
+}
 
-    /* initialize some selection options */
-    ps.idle    = Yes;
-    ps.self    = -1;
-    ps.system  = No;
-    ps.uid     = -1;
-    ps.thread  = No;
-    ps.wcpu    = 1;
-    ps.jail    = No;
-    ps.command = NULL;
+void
+set_signals()
 
-    /* get preset options from the environment */
-    if ((env_top = getenv("TOP")) != NULL)
-    {
-	av = preset_argv = argparse(env_top, &preset_argc);
-	ac = preset_argc;
+{
+    (void) set_signal(SIGINT, sig_leave);
+    (void) set_signal(SIGQUIT, sig_leave);
+    (void) set_signal(SIGTSTP, sig_tstop);
+#ifdef SIGWINCH
+    (void) set_signal(SIGWINCH, sig_winch);
+#endif
+}
 
-	/* set the dummy argument to an explanatory message, in case
-	   getopt encounters a bad argument */
-	preset_argv[0] = "while processing environment";
-    }
+void
+release_signals(void *parm)
 
-    /* process options */
-    do {
-	/* if we're done doing the presets, then process the real arguments */
-	if (preset_argc == 0)
-	{
-	    ac = argc;
-	    av = argv;
+{
+#ifdef HAVE_SIGACTION
+    sigprocmask(SIG_UNBLOCK, (sigset_t *)parm, NULL);
+#endif
 
-	    /* this should keep getopt happy... */
-	    optind = 1;
-	}
+#ifdef HAVE_SIGHOLD
+    sigrelse(SIGINT);
+    sigrelse(SIGQUIT);
+    sigrelse(SIGTSTP);
+#ifdef SIGWINCH
+    sigrelse(SIGWINCH);
+#endif
+#endif
 
-	while ((i = getopt(ac, av, "CSIHPabijnquvs:d:U:m:o:t")) != EOF)
-	{
-	    switch(i)
-	    {
-	      case 'v':			/* show version number */
-		fprintf(stderr, "%s: version %s\n",
-			myname, version_string());
-		exit(1);
-		break;
+#ifdef BSD_SIGNALS
+    (void) sigsetmask((int)parm);
+#endif
+}
 
-	      case 'u':			/* toggle uid/username display */
-		do_unames = !do_unames;
-		break;
+/*
+ * void do_arguments(globalstate *gstate, int ac, char **av)
+ *
+ * Arguments processing.  gstate points to the global state,
+ * ac and av are the arguments to process.  This can be called
+ * multiple times with different sets of arguments.
+ */
 
-	      case 'U':			/* display only username's processes */
-		if ((ps.uid = userid(optarg)) == -1)
-		{
-		    fprintf(stderr, "%s: unknown user\n", optarg);
-		    exit(1);
-		}
-		break;
+#ifdef HAVE_GETOPT_LONG
+static struct option longopts[] = {
+    { "color", no_argument, NULL, 'C' },
+    { "debug", no_argument, NULL, 'D' },
+    { "system-procs", no_argument, NULL, 'S' },
+    { "idle-procs", no_argument, NULL, 'I' },
+    { "tag-names", no_argument, NULL, 'T' },
+    { "all", no_argument, NULL, 'a' },
+    { "batch", no_argument, NULL, 'b' },
+    { "full-commands", no_argument, NULL, 'c' },
+    { "interactive", no_argument, NULL, 'i' },
+    { "quick", no_argument, NULL, 'q' },
+    { "threads", no_argument, NULL, 't' },
+    { "uids", no_argument, NULL, 'u' },
+    { "version", no_argument, NULL, 'v' },
+    { "delay", required_argument, NULL, 's' },
+    { "displays", required_argument, NULL, 'd' },
+    { "user", required_argument, NULL, 'U' },
+    { "sort-order", required_argument, NULL, 'o' },
+    { "display-mode", required_argument, NULL, 'm' },
+    { NULL, 0, NULL, 0 },
+};
+#endif
 
-	      case 'S':			/* show system processes */
-		ps.system = !ps.system;
-		break;
 
-	      case 'I':                   /* show idle processes */
-		ps.idle = !ps.idle;
-		break;
+void
+do_arguments(globalstate *gstate, int ac, char **av)
 
-	      case 'i':			/* go interactive regardless */
-		interactive = Yes;
-		break;
+{
+    int i;
 
-	      case 'n':			/* batch, or non-interactive */
-	      case 'b':
-		interactive = No;
-		break;
+    /* this appears to keep getopt happy */
+    optind = 1;
 
-	      case 'a':
-		fmt_flags ^= FMT_SHOWARGS;
-		break;
-
-	      case 'd':			/* number of displays to show */
-		if ((i = atoiwi(optarg)) == Invalid || i == 0)
-		{
-		    fprintf(stderr,
-			"%s: warning: display count should be positive -- option ignored\n",
-			myname);
-		    warnings++;
-		}
-		else
-		{
-		    displays = i;
-		}
-		break;
-
-	      case 's':
-		if ((delay = atoi(optarg)) < 0 || (delay == 0 && getuid() != 0))
-		{
-		    fprintf(stderr,
-			"%s: warning: seconds delay should be positive -- using default\n",
-			myname);
-		    delay = Default_DELAY;
-		    warnings++;
-		}
-		break;
-
-	      case 'q':		/* be quick about it */
-		/* only allow this if user is really root */
-		if (getuid() == 0)
-		{
-		    /* be very un-nice! */
-		    (void) nice(-20);
-		}
-		else
-		{
-		    fprintf(stderr,
-			"%s: warning: `-q' option can only be used by root\n",
-			myname);
-		    warnings++;
-		}
-		break;
-
-	      case 'm':		/* select display mode */
-		if (strcmp(optarg, "io") == 0) {
-			displaymode = DISP_IO;
-		} else if (strcmp(optarg, "cpu") == 0) {
-			displaymode = DISP_CPU;
-		} else {
-			fprintf(stderr,
-			"%s: warning: `-m' option can only take args "
-			"'io' or 'cpu'\n",
-			myname);
-			exit(1);
-		}
-		break;
-
-	      case 'o':		/* select sort order */
-#ifdef ORDER
-		order_name = optarg;
+#ifdef HAVE_GETOPT_LONG
+    while ((i = getopt_long(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P", longopts, NULL)) != -1)
 #else
-		fprintf(stderr,
-			"%s: this platform does not support arbitrary ordering.  Sorry.\n",
-			myname);
-		warnings++;
+    while ((i = getopt(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P")) != EOF)
 #endif
-		break;
+    {
+	switch(i)
+	{
+#ifdef ENABLE_COLOR
+	case 'E':
+	    gstate->use_color = !gstate->use_color;
+	    break;
+#endif
 
-	      case 't':
-		ps.self = (ps.self == -1) ? getpid() : -1;
-		break;
+	case 'D':
+	    debug_set(1);
+	    break;
 
-	      case 'C':
-		ps.wcpu = !ps.wcpu;
-		break;
+	case 'v':
+	    fprintf(stderr, "%s: version %s\n", myname, version_string());
+	    exit(EX_OK);
+	    break;
 
-	      case 'H':
-		ps.thread = !ps.thread;
-		break;
+	case 'b':
+	case 'n':
+	    gstate->interactive = No;
+	    break;
 
-	      case 'j':
-		ps.jail = !ps.jail;
-		break;
+	case 'a':
+	    gstate->displays = Infinity;
+	    gstate->topn = Infinity;
+	    break;
 
-	      case 'P':
-		pcpu_stats = Yes;
-		break;
+	case 'i':
+	    gstate->interactive = Yes;
+	    break;
 
-	      default:
-		fprintf(stderr,
-"Top version %s\n"
-"Usage: %s [-abCHIijnPqStuv] [-d count] [-m io | cpu] [-o field] [-s time]\n"
-"       [-U username] [number]\n",
-			version_string(), myname);
-		exit(1);
+	case 'o':
+	    gstate->order_name = optarg;
+	    break;
+
+	case 'd':
+	    i = atoiwi(optarg);
+	    if (i == Invalid || i == 0)
+	    {
+		message_error(" Bad display count");
 	    }
-	}
+	    else
+	    {
+		gstate->displays = i;
+	    }
+	    break;
 
-	/* get count of top processes to display (if any) */
-	if (optind < ac)
-	{
-	    if ((topn = atoiwi(av[optind])) == Invalid)
+	case 's':
+	    i = atoi(optarg);
+	    if (i < 0 || (i == 0 && getuid() != 0))
 	    {
-		fprintf(stderr,
-			"%s: warning: process display count should be non-negative -- using default\n",
-			myname);
-		warnings++;
+		message_error(" Bad seconds delay");
 	    }
-#if Default_TOPN == Infinity
-            else
+	    else
 	    {
-		topn_specified = Yes;
+		gstate->delay = i;
 	    }
-#endif
-	}
+	    break;
 
-	/* tricky:  remember old value of preset_argc & set preset_argc = 0 */
-	i = preset_argc;
-	preset_argc = 0;
+	case 'u':
+	    gstate->show_usernames = !gstate->show_usernames;
+	    break;
 
-    /* repeat only if we really did the preset arguments */
-    } while (i != 0);
+	case 'U':
+	    i = userid(optarg);
+	    if (i == -1)
+	    {
+		message_error(" Unknown user '%s'", optarg);
+	    }
+	    else
+	    {
+		gstate->pselect.uid = i;
+	    }
+	    break;
 
-    /* set constants for username/uid display correctly */
-    if (!do_unames)
-    {
-	uname_field = "   UID  ";
-	get_userid = itoa7;
-    }
+	case 'm':
+	    if (strcmp(optarg, "io") == 0) {
+		gstate->pselect.mode = DISP_IO;
+	    } else if (strcmp(optarg, "cpu") == 0) {
+		gstate->pselect.mode = DISP_CPU;
+	    } else {
+		fprintf(stderr,
+		    "%s: warning: `-m' option can only take args "
+		    "'io' or 'cpu'\n",
+		    myname);
+		    exit(1);
+	    }
+	    break;
 
-    /* initialize the kernel memory interface */
-    if (machine_init(&statics, do_unames) == -1)
-    {
-	exit(1);
-    }
+	case 'H':
+	    gstate->pselect.threads = !gstate->pselect.threads;
+	    break;
 
-#ifdef ORDER
-    /* determine sorting order index, if necessary */
-    if (order_name != NULL)
-    {
-	if ((order_index = string_index(order_name, statics.order_names)) == -1)
-	{
-	    char **pp;
+	case 'C':
+	    gstate->pselect.wcpu = !gstate->pselect.wcpu;
+	    break;
 
-	    fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
-		    myname, order_name);
-	    fprintf(stderr, "\tTry one of these:");
-	    pp = statics.order_names;
-	    while (*pp != NULL)
+	case 'j':
+	    gstate->pselect.jail = !gstate->pselect.jail;
+	    break;
+
+	case 'S':
+	    gstate->pselect.system = !gstate->pselect.system;
+	    break;
+
+	case 'I':
+	    gstate->pselect.idle = !gstate->pselect.idle;
+	    break;
+
+#ifdef ENABLE_COLOR
+	case 'T':
+	    gstate->show_tags = 1;
+	    break;
+#endif
+
+	case 'c':
+	    gstate->pselect.fullcmd = !gstate->pselect.fullcmd;
+	    break;
+
+	case 't':
+	    gstate->pselect.self = !gstate->pselect.self;
+	    break;
+
+	case 'q':		/* be quick about it */
+	    /* only allow this if user is really root */
+	    if (getuid() == 0)
 	    {
-		fprintf(stderr, " %s", *pp++);
+		/* be very un-nice! */
+		(void) nice(-20);
 	    }
-	    fputc('\n', stderr);
-	    exit(1);
+	    else
+	    {
+		message_error(" Option -q can only be used by root");
+	    }
+	    break;
+
+	case 'P':
+	    pcpu_stats = Yes;
+	    break;
+
+	default:
+	    fprintf(stderr,
+"Top version %s\n"
+"Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
+		    version_string(), myname);
+	    exit(EX_USAGE);
 	}
     }
-#endif
 
-#ifdef no_initialization_needed
-    /* initialize the hashing stuff */
-    if (do_unames)
+    /* get count of top processes to display */
+    if (optind < ac && *av[optind])
     {
-	init_hash();
+	if ((i = atoiwi(av[optind])) == Invalid)
+	{
+	    message_error(" Process count not a number");
+	}
+	else
+	{
+	    gstate->topn = i;
+	}
     }
-#endif
+}
 
-    /* initialize termcap */
-    init_termcap(interactive);
+void
+do_display(globalstate *gstate)
 
-    /* get the string to use for the process area header */
-    header_text = format_header(uname_field);
+{
+    int active_procs;
+    int i;
+    time_t curr_time;
+    caddr_t processes;
+    struct system_info system_info;
+    char *hdr;
 
-    /* initialize display interface */
-    if ((max_topn = display_init(&statics)) == -1)
-    {
-	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
-	exit(4);
-    }
-    
-    /* print warning if user requested more processes than we can display */
-    if (topn > max_topn)
-    {
-	fprintf(stderr,
-		"%s: warning: this terminal can only display %d processes.\n",
-		myname, max_topn);
-	warnings++;
-    }
+    /* get the time */
+    time_mark(&(gstate->now));
+    curr_time = (time_t)(gstate->now.tv_sec);
 
-    /* adjust for topn == Infinity */
-    if (topn == Infinity)
-    {
-	/*
-	 *  For smart terminals, infinity really means everything that can
-	 *  be displayed, or Largest.
-	 *  On dumb terminals, infinity means every process in the system!
-	 *  We only really want to do that if it was explicitly specified.
-	 *  This is always the case when "Default_TOPN != Infinity".  But if
-	 *  topn wasn't explicitly specified and we are on a dumb terminal
-	 *  and the default is Infinity, then (and only then) we use
-	 *  "Nominal_TOPN" instead.
-	 */
-#if Default_TOPN == Infinity
-	topn = smart_terminal ? Largest :
-		    (topn_specified ? Largest : Nominal_TOPN);
-#else
-	topn = Largest;
-#endif
-    }
+    /* get the current stats */
+    get_system_info(&system_info);
 
-    /* set header display accordingly */
-    display_header(topn > 0);
+    /* get the current processes */
+    processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index);
 
-    /* determine interactive state */
-    if (interactive == Maybe)
+    /* determine number of processes to actually display */
+    if (gstate->topn > 0)
     {
-	interactive = smart_terminal;
+	/* this number will be the smallest of:  active processes,
+	   number user requested, number current screen accomodates */
+	active_procs = system_info.P_ACTIVE;
+	if (active_procs > gstate->topn)
+	{
+	    active_procs = gstate->topn;
+	}
+	if (active_procs > gstate->max_topn)
+	{
+	    active_procs = gstate->max_topn;
+	}
     }
-
-    /* if # of displays not specified, fill it in */
-    if (displays == 0)
+    else
     {
-	displays = smart_terminal ? Infinity : 1;
+	/* dont show any */
+	active_procs = 0;
     }
 
-    /* hold interrupt signals while setting up the screen and the handlers */
-#ifdef SIGHOLD
-    sighold(SIGINT);
-    sighold(SIGQUIT);
-    sighold(SIGTSTP);
+#ifdef HAVE_FORMAT_PROCESS_HEADER
+    /* get the process header to use */
+    hdr = format_process_header(&(gstate->pselect), processes, active_procs);
 #else
-    old_sigmask = sigblock(Smask(SIGINT) | Smask(SIGQUIT) | Smask(SIGTSTP));
+    hdr = gstate->header_text;
 #endif
-    init_screen();
-    (void) signal(SIGINT, leave);
-    (void) signal(SIGQUIT, leave);
-    (void) signal(SIGTSTP, tstop);
-#ifdef SIGWINCH
-    (void) signal(SIGWINCH, winch);
-#endif
-#ifdef SIGRELSE
-    sigrelse(SIGINT);
-    sigrelse(SIGQUIT);
-    sigrelse(SIGTSTP);
-#else
-    (void) sigsetmask(old_sigmask);
-#endif
-    if (warnings)
-    {
-	fputs("....", stderr);
-	fflush(stderr);			/* why must I do this? */
-	sleep((unsigned)(3 * warnings));
-	fputc('\n', stderr);
-    }
 
-restart:
-
-    /*
-     *  main loop -- repeat while display count is positive or while it
-     *		indicates infinity (by being -1)
-     */
-
-    while ((displays == -1) || (displays-- > 0))
+    /* full screen or update? */
+    if (gstate->fulldraw)
     {
-	int (*compare)();
-
-	    
-	/* get the current stats */
-	get_system_info(&system_info);
-
-#ifdef ORDER
-	compare = compares[order_index];
-#else
-	if (displaymode == DISP_CPU)
-		compare = proc_compare;
-	else
-		compare = io_compare;
-#endif
-
-	/* get the current set of processes */
-	processes =
-		get_process_info(&system_info, &ps, compare);
-
-	/* display the load averages */
-	(*d_loadave)(system_info.last_pid,
-		     system_info.load_avg);
-
-	/* display the current time */
-	/* this method of getting the time SHOULD be fairly portable */
-	time(&curr_time);
-	i_uptime(&system_info.boottime, &curr_time);
+	display_clear();
+	i_loadave(system_info.last_pid, system_info.load_avg);
+	i_uptime(&(gstate->statics->boottime), &curr_time);
 	i_timeofday(&curr_time);
-
-	/* display process state breakdown */
-	(*d_procstates)(system_info.p_total,
-			system_info.procstates);
-
-	/* display the cpu state percentage breakdown */
-	if (dostates)	/* but not the first time */
+	i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
+	if (gstate->show_cpustates)
 	{
-	    (*d_cpustates)(system_info.cpustates);
+	    i_cpustates(system_info.cpustates);
 	}
 	else
 	{
-	    /* we'll do it next time */
 	    if (smart_terminal)
 	    {
 		z_cpustates();
 	    }
-	    else
-	    {
-		putchar('\n');
-	    }
-	    dostates = Yes;
+	    gstate->show_cpustates = Yes;
 	}
+	i_kernel(system_info.kernel);
+	i_memory(system_info.memory);
+	i_swap(system_info.swap);
+	i_message(&(gstate->now));
+	i_header(hdr);
+	for (i = 0; i < active_procs; i++)
+	{
+	    i_process(i,
+		format_next_process(&(gstate->pselect), processes, gstate->get_userid));
+	}
+	i_endscreen();
+	if (gstate->smart_terminal)
+	{
+	    gstate->fulldraw = No;
+	}
+    }
+    else
+    {
+	u_loadave(system_info.last_pid, system_info.load_avg);
+	i_timeofday(&curr_time);
+	u_uptime(&(gstate->statics->boottime), &curr_time);
+	u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
+	u_cpustates(system_info.cpustates);
+	u_kernel(system_info.kernel);
+	u_memory(system_info.memory);
+	u_swap(system_info.swap);
+	u_message(&(gstate->now));
+	u_header(hdr);
+	for (i = 0; i < active_procs; i++)
+	{
+	    u_process(i,
+		format_next_process(&(gstate->pselect), processes, gstate->get_userid));
+	}
+	u_endscreen();
+    }
+}
 
-	/* display memory stats */
-	(*d_memory)(system_info.memory);
+#ifdef DEBUG
+void
+timeval_xdprint(char *s, struct timeval tv)
 
-	/* display swap stats */
-	(*d_swap)(system_info.swap);
+{
+    xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec);
+}
+#endif
 
-	/* handle message area */
-	(*d_message)();
+void
+do_wait(globalstate *gstate)
 
-	/* update the header area */
-	(*d_header)(header_text);
-    
-	if (topn > 0)
+{
+    struct timeval wait;
+
+    wait.tv_sec = gstate->delay;
+    wait.tv_usec = 0;
+    select(0, NULL, NULL, NULL, &wait);
+}
+
+void
+do_command(globalstate *gstate)
+
+{
+    int status;
+    struct timeval wait = {0, 0};
+    struct timeval now;
+    fd_set readfds;
+    unsigned char ch;
+    int sel_ret = 0;
+
+    /* calculate new refresh time */
+    gstate->refresh = gstate->now;
+    gstate->refresh.tv_sec += gstate->delay;
+    time_get(&now);
+
+    /* loop waiting for time to expire */
+    do {
+	/* calculate time to wait */
+	if (gstate->delay > 0)
 	{
-	    /* determine number of processes to actually display */
-	    /* this number will be the smallest of:  active processes,
-	       number user requested, number current screen accomodates */
-	    active_procs = system_info.P_ACTIVE;
-	    if (active_procs > topn)
+	    wait = gstate->refresh;
+	    wait.tv_usec -= now.tv_usec;
+	    if (wait.tv_usec < 0)
 	    {
-		active_procs = topn;
+		wait.tv_usec += 1000000;
+		wait.tv_sec--;
 	    }
-	    if (active_procs > max_topn)
+	    wait.tv_sec -= now.tv_sec;
+	}
+
+	/* set up arguments for select on stdin (0) */
+	FD_ZERO(&readfds);
+	FD_SET(STDIN_FILENO, &readfds);
+
+	/* wait for something to read or time out */
+	sel_ret = select(2, &readfds, NULL, NULL, &wait);
+	if (sel_ret < 0 && errno != EINTR)
+	    quit(0);
+	if (sel_ret > 0)
+	{
+	    /* read it */
+	    if (read(STDIN_FILENO, &ch, 1) != 1)
 	    {
-		active_procs = max_topn;
+		/* read error */
+		message_error(" Read error on stdin");
+		quit(EX_DATAERR);
+		/*NOTREACHED*/
 	    }
 
-	    /* now show the top "n" processes. */
-	    for (i = 0; i < active_procs; i++)
+	    /* mark pending messages as old */
+	    message_mark();
+
+	    /* dispatch */
+	    status = command_process(gstate, (int)ch);
+	    switch(status)
 	    {
-		(*d_process)(i, format_next_process(processes, get_userid,
-			     fmt_flags));
+	    case CMD_ERROR:
+		quit(EX_SOFTWARE);
+		/*NOTREACHED*/
+		
+	    case CMD_REFRESH:
+		return;
+
+	    case CMD_UNKNOWN:
+		message_error(" Unknown command");
+		break;
+
+	    case CMD_NA:
+		message_error(" Command not available");
 	    }
 	}
+
+	/* get new time */
+	time_get(&now);
+    } while (timercmp(&now, &(gstate->refresh), < ));
+}
+
+void
+do_minidisplay(globalstate *gstate)
+
+{
+    int real_delay;
+    struct system_info si;
+
+    /* save the real delay and substitute 1 second */
+    real_delay = gstate->delay;
+    gstate->delay = 1;
+
+    /* wait 1 second for a command */
+    time_mark(&(gstate->now));
+    do_command(gstate);
+
+    /* do a mini update that only updates the cpustates */
+    get_system_info(&si);
+    u_cpustates(si.cpustates);
+
+    /* restore the delay time */
+    gstate->delay = real_delay;
+
+    /* done */
+    i_endscreen();
+}
+
+int
+main(int argc, char *argv[])
+
+{
+    char *env_top;
+    char **preset_argv;
+    int preset_argc = 0;
+    void *mask;
+    int need_mini = 1;
+
+    struct statics statics;
+    globalstate *gstate;
+
+    /* get our name */
+    if (argc > 0)
+    {
+	if ((myname = strrchr(argv[0], '/')) == 0)
+	{
+	    myname = argv[0];
+	}
 	else
 	{
-	    i = 0;
+	    myname++;
 	}
+    }
 
-	/* do end-screen processing */
-	u_endscreen(i);
+    /* binary compatibility check */
+#ifdef HAVE_UNAME
+    {
+	struct utsname uts;
 
-	/* now, flush the output buffer */
-	if (fflush(stdout) != 0)
+	if (uname(&uts) == 0)
 	{
-	    new_message(MT_standout, " Write error on stdout");
-	    putchar('\r');
-	    quit(1);
-	    /*NOTREACHED*/
-	}
-
-	/* only do the rest if we have more displays to show */
-	if (displays)
-	{
-	    /* switch out for new display on smart terminals */
-	    if (smart_terminal)
+	    if (strcmp(uts.machine, UNAME_HARDWARE) != 0)
 	    {
-		if (overstrike)
-		{
-		    reset_display();
-		}
-		else
-		{
-		    d_loadave = u_loadave;
-		    d_procstates = u_procstates;
-		    d_cpustates = u_cpustates;
-		    d_memory = u_memory;
-		    d_swap = u_swap;
-		    d_message = u_message;
-		    d_header = u_header;
-		    d_process = u_process;
-		}
+		fprintf(stderr, "%s: incompatible hardware platform\n",
+			myname);
+		exit(EX_UNAVAILABLE);
 	    }
-    
-	    no_command = Yes;
-	    if (!interactive)
-	    {
-		/* set up alarm */
-		(void) signal(SIGALRM, onalrm);
-		(void) alarm((unsigned)delay);
-    
-		/* wait for the rest of it .... */
-		pause();
-	    }
-	    else while (no_command)
-	    {
-		/* assume valid command unless told otherwise */
-		no_command = No;
+	}
+    }
+#endif
 
-		/* set up arguments for select with timeout */
-		FD_ZERO(&readfds);
-		FD_SET(0, &readfds);		/* for standard input */
-		timeout.tv_sec  = delay;
-		timeout.tv_usec = 0;
+    /* initialization */
+    gstate = (globalstate *)calloc(1, sizeof(globalstate));
+    gstate->statics = &statics;
+    time_mark(NULL);
 
-		if (leaveflag) {
-		    end_screen();
-		    exit(0);
-		}
+    /* preset defaults for various options */
+    gstate->show_usernames = Yes;
+    gstate->topn = DEFAULT_TOPN;
+    gstate->delay = DEFAULT_DELAY;
+    gstate->fulldraw = Yes;
+    gstate->use_color = Yes;
+    gstate->interactive = Maybe;
 
-		if (tstopflag) {
-		    /* move to the lower left */
-		    end_screen();
-		    fflush(stdout);
+    /* preset defaults for process selection */
+    gstate->pselect.idle = Yes;
+    gstate->pselect.self = -1;
+    gstate->pselect.threads = No;
+    gstate->pselect.wcpu = 1;
+    gstate->pselect.jail = No;
+    gstate->pselect.system = No;
+    gstate->pselect.fullcmd = No;
+    gstate->pselect.command = NULL;
+    gstate->pselect.uid = -1;
+    gstate->pselect.mode = DISP_CPU;
 
-		    /* default the signal handler action */
-		    (void) signal(SIGTSTP, SIG_DFL);
-
-		    /* unblock the signal and send ourselves one */
-#ifdef SIGRELSE
-		    sigrelse(SIGTSTP);
+    /* use a large buffer for stdout */
+#ifdef HAVE_SETVBUF
+    setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
 #else
-		    (void) sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
+#ifdef HAVE_SETBUFFER
+    setbuffer(stdout, stdoutbuf, BUFFERSIZE);
 #endif
-		    (void) kill(0, SIGTSTP);
+#endif
 
-		    /* reset the signal handler */
-		    (void) signal(SIGTSTP, tstop);
+    /* get preset options from the environment */
+    if ((env_top = getenv("TOP")) != NULL)
+    {
+	preset_argv = argparse(env_top, &preset_argc);
+	preset_argv[0] = myname;
+	do_arguments(gstate, preset_argc, preset_argv);
+    }
 
-		    /* reinit screen */
-		    reinit_screen();
-		    reset_display();
-		    tstopflag = 0;
-		    goto restart;
-		}
+    /* process arguments */
+    do_arguments(gstate, argc, argv);
 
-		if (winchflag) {
-		    /* reascertain the screen dimensions */
-		    get_screensize();
+#ifdef ENABLE_COLOR
+    /* If colour has been turned on read in the settings. */
+    env_top = getenv("TOPCOLOURS");
+    if (!env_top)
+    {
+	env_top = getenv("TOPCOLORS");
+    }
+    /* must do something about error messages */
+    color_env_parse(env_top);
+    color_activate(gstate->use_color);
+#endif
 
-		    /* tell display to resize */
-		    max_topn = display_resize();
+    /* in order to support forward compatability, we have to ensure that
+       the entire statics structure is set to a known value before we call
+       machine_init.  This way fields that a module does not know about
+       will retain their default values */
+    memzero((void *)&statics, sizeof(statics));
+    statics.boottime = -1;
 
-		    /* reset the signal handler */
-		    (void) signal(SIGWINCH, winch);
+    /* call the platform-specific init */
+    if (machine_init(&statics) == -1)
+    {
+	exit(EX_SOFTWARE);
+    }
+    if (pcpu_stats == No)
+	statics.ncpus = 1;
 
-		    reset_display();
-		    winchflag = 0;
-		    goto restart;
-		}
+    /* create a helper list of sort order names */
+    gstate->order_namelist = string_list(statics.order_names);
 
-		/* wait for either input or the end of the delay period */
-		sel_ret = select(2, &readfds, NULL, NULL, &timeout);
-		if (sel_ret < 0 && errno != EINTR)
-		    quit(0);
-		if (sel_ret > 0)
-		{
-		    int newval;
-		    char *errmsg;
-    
-		    /* something to read -- clear the message area first */
-		    clear_message();
+    /* look up chosen sorting order */
+    if (gstate->order_name != NULL)
+    {
+	int i;
 
-		    /* now read it and convert to command strchr */
-		    /* (use "change" as a temporary to hold strchr) */
-		    if (read(0, &ch, 1) != 1)
-		    {
-			/* read error: either 0 or -1 */
-			new_message(MT_standout, " Read error on stdin");
-			putchar('\r');
-			quit(1);
-			/*NOTREACHED*/
-		    }
-		    if ((iptr = strchr(command_chars, ch)) == NULL)
-		    {
-			if (ch != '\r' && ch != '\n')
-			{
-			    /* illegal command */
-			    new_message(MT_standout, " Command not understood");
-			}
-			putchar('\r');
-			no_command = Yes;
-		    }
-		    else
-		    {
-			change = iptr - command_chars;
-			if (overstrike && change > CMD_OSLIMIT)
-			{
-			    /* error */
-			    new_message(MT_standout,
-			    " Command cannot be handled by this terminal");
-			    putchar('\r');
-			    no_command = Yes;
-			}
-			else switch(change)
-			{
-			    case CMD_redraw:	/* redraw screen */
-				reset_display();
-				break;
-    
-			    case CMD_update:	/* merely update display */
-				/* is the load average high? */
-				if (system_info.load_avg[0] > LoadMax)
-				{
-				    /* yes, go home for visual feedback */
-				    go_home();
-				    fflush(stdout);
-				}
-				break;
-	    
-			    case CMD_quit:	/* quit */
-				quit(0);
-				/*NOTREACHED*/
-				break;
-	    
-			    case CMD_help1:	/* help */
-			    case CMD_help2:
-				reset_display();
-				clear();
-				show_help();
-				standout("Hit any key to continue: ");
-				fflush(stdout);
-				(void) read(0, &ch, 1);
-				break;
-	
-			    case CMD_errors:	/* show errors */
-				if (error_count() == 0)
-				{
-				    new_message(MT_standout,
-					" Currently no errors to report.");
-				    putchar('\r');
-				    no_command = Yes;
-				}
-				else
-				{
-				    reset_display();
-				    clear();
-				    show_errors();
-				    standout("Hit any key to continue: ");
-				    fflush(stdout);
-				    (void) read(0, &ch, 1);
-				}
-				break;
-	
-			    case CMD_number1:	/* new number */
-			    case CMD_number2:
-				new_message(MT_standout,
-				    "Number of processes to show: ");
-				newval = readline(tempbuf1, 8, Yes);
-				if (newval > -1)
-				{
-				    if (newval > max_topn)
-				    {
-					new_message(MT_standout | MT_delayed,
-					  " This terminal can only display %d processes.",
-					  max_topn);
-					putchar('\r');
-				    }
+	if (statics.order_names == NULL)
+	{
+	    message_error(" This platform does not support arbitrary ordering");
+	}
+	else if ((i = string_index(gstate->order_name,
+				   statics.order_names)) == -1)
+	{
+	    message_error(" Sort order `%s' not recognized", gstate->order_name);
+	    message_error(" Recognized sort orders: %s", gstate->order_namelist);
+	}
+	else
+	{
+	    gstate->order_index = i;
+	}
+    }
 
-				    if (newval == 0)
-				    {
-					/* inhibit the header */
-					display_header(No);
-				    }
-				    else if (newval > topn && topn == 0)
-				    {
-					/* redraw the header */
-					display_header(Yes);
-					d_header = i_header;
-				    }
-				    topn = newval;
-				}
-				break;
-	    
-			    case CMD_delay:	/* new seconds delay */
-				new_message(MT_standout, "Seconds to delay: ");
-				if ((i = readline(tempbuf1, 8, Yes)) > -1)
-				{
-				    if ((delay = i) == 0 && getuid() != 0)
-				    {
-					delay = 1;
-				    }
-				}
-				clear_message();
-				break;
-	
-			    case CMD_displays:	/* change display count */
-				new_message(MT_standout,
-					"Displays to show (currently %s): ",
-					displays == -1 ? "infinite" :
-							 itoa(displays));
-				if ((i = readline(tempbuf1, 10, Yes)) > 0)
-				{
-				    displays = i;
-				}
-				else if (i == 0)
-				{
-				    quit(0);
-				}
-				clear_message();
-				break;
-    
-			    case CMD_kill:	/* kill program */
-				new_message(0, "kill ");
-				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
-				{
-				    if ((errmsg = kill_procs(tempbuf2)) != NULL)
-				    {
-					new_message(MT_standout, "%s", errmsg);
-					putchar('\r');
-					no_command = Yes;
-				    }
-				}
-				else
-				{
-				    clear_message();
-				}
-				break;
-	    
-			    case CMD_renice:	/* renice program */
-				new_message(0, "renice ");
-				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
-				{
-				    if ((errmsg = renice_procs(tempbuf2)) != NULL)
-				    {
-					new_message(MT_standout, "%s", errmsg);
-					putchar('\r');
-					no_command = Yes;
-				    }
-				}
-				else
-				{
-				    clear_message();
-				}
-				break;
+    /* initialize extensions */
+    init_username();
 
-			    case CMD_idletog:
-			    case CMD_idletog2:
-				ps.idle = !ps.idle;
-				new_message(MT_standout | MT_delayed,
-				    " %sisplaying idle processes.",
-				    ps.idle ? "D" : "Not d");
-				putchar('\r');
-				break;
+    /* initialize termcap */
+    gstate->smart_terminal = screen_readtermcap(gstate->interactive);
 
-			    case CMD_selftog:
-				ps.self = (ps.self == -1) ? getpid() : -1;
-				new_message(MT_standout | MT_delayed,
-				    " %sisplaying self.",
-				    (ps.self == -1) ? "D" : "Not d");
-				putchar('\r');
-				break;
+    /* determine interactive state */
+    if (gstate->interactive == Maybe)
+    {
+	gstate->interactive = smart_terminal;
+    }
 
-			    case CMD_user:
-				new_message(MT_standout,
-				    "Username to show: ");
-				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
-				{
-				    if (tempbuf2[0] == '+' &&
-					tempbuf2[1] == '\0')
-				    {
-					ps.uid = -1;
-				    }
-				    else if ((i = userid(tempbuf2)) == -1)
-				    {
-					new_message(MT_standout,
-					    " %s: unknown user", tempbuf2);
-					no_command = Yes;
-				    }
-				    else
-				    {
-					ps.uid = i;
-				    }
-				    putchar('\r');
-				}
-				else
-				{
-				    clear_message();
-				}
-				break;
-	    
-			    case CMD_thrtog:
-				ps.thread = !ps.thread;
-				new_message(MT_standout | MT_delayed,
-				    "Displaying threads %s",
-				    ps.thread ? "separately" : "as a count");
-				header_text = format_header(uname_field);
-				reset_display();
-				putchar('\r');
-				break;
-			    case CMD_wcputog:
-				ps.wcpu = !ps.wcpu;
-				new_message(MT_standout | MT_delayed,
-				    "Displaying %sCPU",
-				    ps.wcpu ? "W" : "");
-				header_text = format_header(uname_field);
-				reset_display();
-				putchar('\r');
-				break;
-			    case CMD_viewtog:
-				if (++displaymode == DISP_MAX)
-					displaymode = 0;
-				header_text = format_header(uname_field);
-				display_header(Yes);
-				d_header = i_header;
-				reset_display();
-				break;
-			    case CMD_viewsys:
-				ps.system = !ps.system;
-				break;
-			    case CMD_showargs:
-				fmt_flags ^= FMT_SHOWARGS;
-				break;
-#ifdef ORDER
-			    case CMD_order:
-				new_message(MT_standout,
-				    "Order to sort: ");
-				if (readline(tempbuf2, sizeof(tempbuf2), No) > 0)
-				{
-				  if ((i = string_index(tempbuf2, statics.order_names)) == -1)
-					{
-					  new_message(MT_standout,
-					      " %s: unrecognized sorting order", tempbuf2);
-					  no_command = Yes;
-				    }
-				    else
-				    {
-					order_index = i;
-				    }
-				    putchar('\r');
-				}
-				else
-				{
-				    clear_message();
-				}
-				break;
-#endif
-			    case CMD_jidtog:
-				ps.jail = !ps.jail;
-				new_message(MT_standout | MT_delayed,
-				    " %sisplaying jail ID.",
-				    ps.jail ? "D" : "Not d");
-				header_text = format_header(uname_field);
-				reset_display();
-				putchar('\r');
-				break;
-	    
-			    default:
-				new_message(MT_standout, " BAD CASE IN SWITCH!");
-				putchar('\r');
-			}
-		    }
+    /* if displays were not specified, choose an appropriate default */
+    if (gstate->displays == 0)
+    {
+	gstate->displays = gstate->smart_terminal ? Infinity: 1;
+    }
 
-		    /* flush out stuff that may have been written */
-		    fflush(stdout);
-		}
-	    }
-	}
+    /* we don't need a mini display when delay is less than 2
+       seconds or when we are not on a smart terminal */
+    if (gstate->delay <= 1 || !smart_terminal)
+    {
+	need_mini = 0;
     }
 
-#ifdef DEBUG
-    fclose(debug);
+#ifndef HAVE_FORMAT_PROCESS_HEADER
+    /* set constants for username/uid display */
+    if (gstate->show_usernames)
+    {
+	gstate->header_text = format_header("USERNAME");
+	gstate->get_userid = username;
+    }
+    else
+    {
+	gstate->header_text = format_header("   UID  ");
+	gstate->get_userid = itoa7;
+    }
 #endif
-    quit(0);
-    /*NOTREACHED*/
-}
+    gstate->pselect.usernames = gstate->show_usernames;
 
-/*
- *  reset_display() - reset all the display routine pointers so that entire
- *	screen will get redrawn.
- */
+    /* initialize display */
+    if ((gstate->max_topn = display_init(&statics)) == -1)
+    {
+	fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
+	exit(EX_OSERR);
+    }
 
-reset_display()
+    /* check for infinity and for overflowed screen */
+    gstate->topn = gstate->max_topn;
+    if (gstate->topn == Infinity)
+    {
+	gstate->topn = INT_MAX;
+    }
+    else if (gstate->topn > gstate->max_topn)
+    {
+	message_error(" This terminal can only display %d processes",
+		      gstate->max_topn);
+    }
 
-{
-    d_loadave    = i_loadave;
-    d_procstates = i_procstates;
-    d_cpustates  = i_cpustates;
-    d_memory     = i_memory;
-    d_swap       = i_swap;
-    d_message	 = i_message;
-    d_header	 = i_header;
-    d_process	 = i_process;
-}
+#ifdef ENABLE_COLOR
+    /* producing a list of color tags is easy */
+    if (gstate->show_tags)
+    {
+	color_dump(stdout);
+	exit(EX_OK);
+    }
+#endif
 
-/*
- *  signal handlers
- */
+    /* hold all signals while we initialize the screen */
+    mask = hold_signals();
+    screen_init();
 
-sigret_t leave()	/* exit under normal conditions -- INT handler */
+    /* set the signal handlers */
+    set_signals();
 
-{
-    leaveflag = 1;
-}
+    /* longjmp re-entry point */
+    /* set the jump buffer for long jumps out of signal handlers */
+    if (setjmp(jmp_int) != 0)
+    {
+	/* this is where we end up after processing sigwinch or sigtstp */
 
-sigret_t tstop(i)	/* SIGTSTP handler */
+	/* tell display to resize its buffers, and get the new length */
+	if ((gstate->max_topn = display_resize()) == -1)
+	{
+	    /* thats bad */
+	    quit(EX_OSERR);
+	    /*NOTREACHED*/
+	}
+	gstate->topn = gstate->max_topn;
 
-int i;
+	/* set up for a full redraw, and get the current line count */
+	gstate->fulldraw = Yes;
 
-{
-    tstopflag = 1;
-}
+	/* safe to release the signals now */
+	release_signals(mask);
+    }
+    else
+    {
+	/* release the signals */
+	release_signals(mask);
 
-#ifdef SIGWINCH
-sigret_t winch(i)		/* SIGWINCH handler */
+	/* some systems require a warmup */
+	/* always do a warmup for batch mode */
+	if (gstate->interactive == 0 || statics.flags.warmup)
+	{
+	    struct system_info system_info;
+	    struct timeval timeout;
 
-int i;
+	    time_mark(&(gstate->now));
+	    get_system_info(&system_info);
+	    (void)get_process_info(&system_info, &gstate->pselect, 0);
+	    timeout.tv_sec = 1;
+	    timeout.tv_usec = 0;
+	    select(0, NULL, NULL, NULL, &timeout);
 
-{
-    winchflag = 1;
-}
-#endif
+	    /* if we've warmed up, then we can show good states too */
+	    gstate->show_cpustates = Yes;
+	    need_mini = 0;
+	}
+    }
 
-void quit(status)		/* exit under duress */
+    /* main loop */
+    while ((gstate->displays == -1) || (--gstate->displays > 0))
+    {
+	do_display(gstate);
+	if (gstate->interactive)
+	{
+	    if (need_mini)
+	    {
+		do_minidisplay(gstate);
+		need_mini = 0;
+	    }
+	    do_command(gstate);
+	}
+	else
+	{
+	    do_wait(gstate);
+	}
+    }
 
-int status;
+    /* do one last display */
+    do_display(gstate);
 
-{
-    end_screen();
-    exit(status);
-    /*NOTREACHED*/
+    quit(EX_OK);
+    /* NOTREACHED */
+    return 1; /* Keep compiler quiet. */
 }
-
-sigret_t onalrm()	/* SIGALRM handler */
-
-{
-    /* this is only used in batch mode to break out of the pause() */
-    /* return; */
-}
-
Index: getopt.c
===================================================================
--- getopt.c	(revision 183246)
+++ getopt.c	(working copy)
@@ -1,4 +1,36 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  * "getopt" routine customized for top.
  */
 
@@ -43,9 +75,8 @@
 char	*optarg;
 
 int
-getopt(argc, argv, opts)
-int	argc;
-char	**argv, *opts;
+getopt(int argc, char **argv, char *opts)
+
 {
 	static int sp = 1;
 	register int c;
Index: display.c
===================================================================
--- display.c	(revision 183246)
+++ display.c	(working copy)
@@ -1,14 +1,42 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
- *
- * $FreeBSD$
  */
 
 /*
@@ -23,55 +51,104 @@
  *        In this way, those routines can be safely used on terminals that
  *        have minimal (or nonexistant) terminal capabilities.
  *
- *        The routines are called in this order:  *_loadave, i_timeofday,
- *        *_procstates, *_cpustates, *_memory, *_message, *_header,
- *        *_process, u_endscreen.
+ *        The routines should be called in this order:  *_loadave, *_uptime,
+ *        i_timeofday, *_procstates, *_cpustates, *_memory, *_swap,
+ *        *_message, *_header, *_process, *_endscreen.
  */
 
 #include "os.h"
 #include <ctype.h>
-#include <time.h>
-#include <sys/time.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
 
+#include "top.h"
+#include "machine.h"
 #include "screen.h"		/* interface to screen package */
 #include "layout.h"		/* defines for screen position layout */
 #include "display.h"
-#include "top.h"
-#include "top.local.h"
 #include "boolean.h"
-#include "machine.h"		/* we should eliminate this!!! */
 #include "utils.h"
 
-#ifdef DEBUG
-FILE *debug;
+#ifdef ENABLE_COLOR
+#include "color.h"
 #endif
 
+#define CURSOR_COST 8
+
+#define MESSAGE_DISPLAY_TIME 5
+
 /* imported from screen.c */
 extern int overstrike;
 
-static int lmpid = 0;
-static int last_hi = 0;		/* used in u_process and u_endscreen */
-static int lastline = 0;
+static int lmpid = -1;
 static int display_width = MAX_COLS;
 
-#define lineindex(l) ((l)*display_width)
+/* cursor positions of key points on the screen are maintained here */
+/* layout.h has static definitions, but we may change our minds on some
+   of the positions as we make decisions about what needs to be displayed */
 
-char *printable();
+static int x_lastpid = X_LASTPID;
+static int y_lastpid = Y_LASTPID;
+static int x_loadave = X_LOADAVE;
+static int x_loadave_nompid = X_LOADAVE_NOMPID;
+static int y_loadave = Y_LOADAVE;
+static int x_minibar = X_MINIBAR;
+static int y_minibar = Y_MINIBAR;
+static int x_uptime = X_UPTIME;
+static int y_uptime = Y_UPTIME;
+static int x_procstate = X_PROCSTATE;
+static int y_procstate = Y_PROCSTATE;
+static int x_cpustates = X_CPUSTATES;
+static int y_cpustates = Y_CPUSTATES;
+static int x_kernel = X_KERNEL;
+static int y_kernel = Y_KERNEL;
+static int x_brkdn = X_BRKDN;
+static int y_brkdn = Y_BRKDN;
+static int x_mem = X_MEM;
+static int y_mem = Y_MEM;
+static int x_swap = X_SWAP;
+static int y_swap = Y_SWAP;
+static int y_message = Y_MESSAGE;
+static int x_header = X_HEADER;
+static int y_header = Y_HEADER;
+static int x_idlecursor = X_IDLECURSOR;
+static int y_idlecursor = Y_IDLECURSOR;
+static int y_procs = Y_PROCS;
 
-/* things initialized by display_init and used thruout */
+static int Header_lines = 7;		/* XXXEG */
 
-/* buffer of proc information lines for display updating */
-char *screenbuf = NULL;
+/* buffer and colormask that describes the content of the screen */
+/* these are singly dimensioned arrays -- the row boundaries are
+   determined on the fly.
+*/
+static char *screenbuf = NULL;
+static char *colorbuf = NULL;
+static char scratchbuf[MAX_COLS];
+static int bufsize = 0;
 
+/* lineindex tells us where the beginning of a line is in the buffer */
+#define lineindex(l) ((l)*MAX_COLS)
+
+/* screen's cursor */
+static int curr_x, curr_y;
+static int curr_color;
+
+/* virtual cursor */
+static int virt_x, virt_y;
+
 static char **procstate_names;
 static char **cpustate_names;
 static char **memory_names;
 static char **swap_names;
+static char **kernel_names;
 
 static int num_procstates;
 static int num_cpustates;
 static int num_memory;
 static int num_swap;
+static int num_kernel;
 
 static int *lprocstates;
 static int *lcpustates;
@@ -83,52 +160,534 @@
 static int cpustate_total_length;
 static int cpustates_column;
 
-static enum { OFF, ON, ERASE } header_status = ON;
+static int header_status = Yes;
 
-static int string_count();
-static void summary_format();
-static void line_update();
+/* pending messages are stored in a circular buffer, where message_first
+   is the next one to display, and message_last is the last one
+   in the buffer.  Counters wrap around at MAX_MESSAGES.  The buffer is
+   empty when message_first == message_last and full when 
+   message_last + 1 == message_first.  The pointer message_current holds
+   the message currently being displayed, or "" if there is none.
+*/
+#define MAX_MESSAGES 16
+static char *message_buf[MAX_MESSAGES];
+static int message_first = 0;
+static int message_last = 0;
+static struct timeval message_time = {0, 0};
+static char *message_current = NULL;
+static int message_length = 0;
+static int message_hold = 1;
+static int message_barrier = No;
 
-int  x_lastpid =	10;
-int  y_lastpid =	0;
-int  x_loadave =	33;
-int  x_loadave_nompid =	15;
-int  y_loadave =	0;
-int  x_procstate =	0;
-int  y_procstate =	1;
-int  x_brkdn =		15;
-int  y_brkdn =		1;
-int  x_mem =		5;
-int  y_mem =		3;
-int  x_swap =		6;
-int  y_swap =		4;
-int  y_message =	5;
-int  x_header =		0;
-int  y_header =		6;
-int  x_idlecursor =	0;
-int  y_idlecursor =	5;
-int  y_procs =		7;
+#ifdef ENABLE_COLOR
+static int load_cidx[3];
+static int header_cidx;
+static int *cpustate_cidx;
+static int *memory_cidx;
+static int *swap_cidx;
+static int *kernel_cidx;
+#else
+#define memory_cidx NULL
+#define swap_cidx NULL
+#define kernel_cidx NULL
+#endif
 
-int  y_cpustates =	2;
-int  Header_lines =	7;
 
-int display_resize()
+/* internal support routines */
 
+/*
+ * static int string_count(char **pp)
+ *
+ * Pointer "pp" points to an array of string pointers, which is
+ * terminated by a NULL.  Return the number of string pointers in
+ * this array.
+ */
+
+static int
+string_count(char **pp)
+
 {
-    register int lines;
+    register int cnt = 0;
 
-    /* first, deallocate any previous buffer that may have been there */
-    if (screenbuf != NULL)
+    if (pp != NULL)
     {
-	free(screenbuf);
+	while (*pp++ != NULL)
+	{
+	    cnt++;
+	}
     }
+    return(cnt);
+}
 
+void
+display_clear()
+
+{
+    dprintf("display_clear\n");
+    screen_clear();
+    memzero(screenbuf, bufsize);
+    memzero(colorbuf, bufsize);
+    curr_x = curr_y = 0;
+}
+
+/*
+ * void display_move(int x, int y)
+ *
+ * Efficiently move the cursor to x, y.  This assumes the cursor is
+ * currently located at curr_x, curr_y, and will only use cursor
+ * addressing when it is less expensive than overstriking what's
+ * already on the screen.
+ */
+
+void
+display_move(int x, int y)
+
+{
+    char buff[128];
+    char *p;
+    char *bufp;
+    char *colorp;
+    int cnt = 0;
+    int color = curr_color;
+
+    dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
+
+    /* are we in a position to do this without cursor addressing? */
+    if (curr_y < y || (curr_y == y && curr_x <= x))
+    {
+	/* start buffering up what it would take to move there by rewriting
+	   what's on the screen */
+	cnt = CURSOR_COST;
+	p = buff;
+
+	/* one newline for every line */
+	while (cnt > 0 && curr_y < y)
+	{
+#ifdef ENABLE_COLOR
+	    if (color != 0)
+	    {
+		p = strcpyend(p, color_setstr(0));
+		color = 0;
+		cnt -= 5;
+	    }
+#endif
+	    *p++ = '\n';
+	    curr_y++;
+	    curr_x = 0;
+	    cnt--;
+	}
+
+	/* write whats in the screenbuf */
+	bufp = &screenbuf[lineindex(curr_y) + curr_x];
+	colorp = &colorbuf[lineindex(curr_y) + curr_x];
+	while (cnt > 0 && curr_x < x)
+	{
+#ifdef ENABLE_COLOR
+	    if (color != *colorp)
+	    {
+		color = *colorp;
+		p = strcpyend(p, color_setstr(color));
+		cnt -= 5;
+	    }
+#endif
+	    if ((*p = *bufp) == '\0')
+	    {
+		/* somwhere on screen we haven't been before */
+		*p = *bufp = ' ';
+	    }
+	    p++;
+	    bufp++;
+	    colorp++;
+	    curr_x++;
+	    cnt--;
+	}
+    }
+
+    /* move the cursor */
+    if (cnt > 0)
+    {
+	/* screen rewrite is cheaper */
+	*p = '\0';
+	fputs(buff, stdout);
+	curr_color = color;
+    }
+    else
+    {
+	screen_move(x, y);
+    }
+
+    /* update our position */
+    curr_x = x;
+    curr_y = y;
+}
+
+/*
+ * display_write(int x, int y, int newcolor, int eol, char *new)
+ *
+ * Optimized write to the display.  This writes characters to the
+ * screen in a way that optimizes the number of characters actually
+ * sent, by comparing what is being written to what is already on
+ * the screen (according to screenbuf and colorbuf).  The string to
+ * write is "new", the first character of "new" should appear at
+ * screen position x, y.  If x is -1 then "new" begins wherever the
+ * cursor is currently positioned.  The string is written with color
+ * "newcolor".  If "eol" is true then the remainder of the line is
+ * cleared.  It is expected that "new" will have no newlines and no
+ * escape sequences.
+ */
+
+void
+display_write(int x, int y, int newcolor, int eol, char *new)
+
+{
+    char *bufp;
+    char *colorp;
+    int ch;
+    int diff;
+
+    dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
+	    x, y, newcolor, eol, new);
+
+    /* dumb terminal handling here */
+    if (!smart_terminal)
+    {
+	if (x != -1)
+	{
+	    /* make sure we are on the right line */
+	    while (curr_y < y)
+	    {
+		putchar('\n');
+		curr_y++;
+		curr_x = 0;
+	    }
+
+	    /* make sure we are on the right column */
+	    while (curr_x < x)
+	    {
+		putchar(' ');
+		curr_x++;
+	    }
+	}
+
+	/* write */
+	fputs(new, stdout);
+	curr_x += strlen(new);
+
+	return;
+    }
+
+    /* adjust for "here" */
+    if (x == -1)
+    {
+	x = virt_x;
+	y = virt_y;
+    }
+    else
+    {
+	virt_x = x;
+	virt_y = y;
+    }
+
+    /* a pointer to where we start */
+    bufp = &screenbuf[lineindex(y) + x];
+    colorp = &colorbuf[lineindex(y) + x];
+
+    /* main loop */
+    while ((ch = *new++) != '\0')
+    {
+	/* if either character or color are different, an update is needed */
+	/* but only when the screen is wide enough */
+	if (x < display_width && (ch != *bufp || newcolor != *colorp))
+	{
+	    /* check cursor */
+	    if (y != curr_y || x != curr_x)
+	    {
+		/* have to move the cursor */
+		display_move(x, y);
+	    }
+
+	    /* write character */
+#ifdef ENABLE_COLOR
+	    if (curr_color != newcolor)
+	    {
+		fputs(color_setstr(newcolor), stdout);
+		curr_color = newcolor;
+	    }
+#endif
+	    putchar(ch);
+	    *bufp = ch;
+	    *colorp = curr_color;
+	    curr_x++;
+	}
+
+	/* move */
+	x++;
+	virt_x++;
+	bufp++;
+	colorp++;
+    }
+
+    /* eol handling */
+    if (eol && *bufp != '\0')
+    {
+	dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
+	/* make sure we are color 0 */
+#ifdef ENABLE_COLOR
+	if (curr_color != 0)
+	{
+	    fputs(color_setstr(0), stdout);
+	    curr_color = 0;
+	}
+#endif
+
+	/* make sure we are at the end */
+	if (x != curr_x || y != curr_y)
+	{
+	    screen_move(x, y);
+	    curr_x = x;
+	    curr_y = y;
+	}
+
+	/* clear to end */
+	screen_cleareol(strlen(bufp));
+
+	/* clear out whats left of this line's buffer */
+	diff = display_width - x;
+	if (diff > 0)
+	{
+	    memzero(bufp, diff);
+	    memzero(colorp, diff);
+	}
+    }
+}
+
+void
+display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...)
+
+{
+    va_list argp;
+
+    va_start(argp, fmt);
+
+    vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
+    display_write(x, y, newcolor, eol, scratchbuf);
+}
+
+void
+display_cte()
+
+{
+    int len;
+    int y;
+    char *p;
+    int need_clear = 0;
+
+    /* is there anything out there that needs to be cleared? */
+    p = &screenbuf[lineindex(virt_y) + virt_x];
+    if (*p != '\0')
+    {
+	need_clear = 1;
+    }
+    else
+    {
+	/* this line is clear, what about the rest? */
+	y = virt_y;
+	while (++y < screen_length)
+	{
+	    if (screenbuf[lineindex(y)] != '\0')
+	    {
+		need_clear = 1;
+		break;
+	    }
+	}
+    }
+
+    if (need_clear)
+    {
+	dprintf("display_cte: clearing\n");
+
+	/* we will need this later */
+	len = lineindex(virt_y) + virt_x;
+
+	/* move to x and y, then clear to end */
+	display_move(virt_x, virt_y);
+	if (!screen_cte())
+	{
+	    /* screen has no clear to end, so do it by hand */
+	    p = &screenbuf[len];
+	    len = strlen(p);
+	    if (len > 0)
+	    {
+		screen_cleareol(len);
+	    }
+	    while (++virt_y < screen_length)
+	    {
+		display_move(0, virt_y);
+		p = &screenbuf[lineindex(virt_y)];
+		len = strlen(p);
+		if (len > 0)
+		{
+		    screen_cleareol(len);
+		}
+	    }
+	}
+
+	/* clear the screenbuf */
+	memzero(&screenbuf[len], bufsize - len);
+	memzero(&colorbuf[len], bufsize - len);
+    }
+}
+
+static void
+summary_format(int x, int y, int *numbers, char **names, int *cidx)
+
+{
+    register int num;
+    register char *thisname;
+    register char *lastname = NULL;
+    register int color;
+
+    /* format each number followed by its string */
+    while ((thisname = *names++) != NULL)
+    {
+	/* get the number to format */
+	num = *numbers++;
+	color = 0;
+
+	/* display only non-zero numbers */
+	if (num != 0)
+	{
+	    /* write the previous name */
+	    if (lastname != NULL)
+	    {
+		display_write(-1, -1, 0, 0, lastname);
+	    }
+
+#ifdef ENABLE_COLOR
+	    if (cidx != NULL)
+	    {
+		/* choose a color */
+		color = color_test(*cidx++, num);
+	    }
+#endif
+
+	    /* write this number if positive */
+	    if (num > 0)
+	    {
+		display_write(x, y, color, 0, itoa(num));
+	    }
+
+	    /* defer writing this name */
+	    lastname = thisname;
+
+	    /* next iteration will not start at x, y */
+	    x = y = -1;
+	}
+    }
+
+    /* if the last string has a separator on the end, it has to be
+       written with care */
+    if (lastname != NULL)
+    {
+	if ((num = strlen(lastname)) > 1 &&
+	    lastname[num-2] == ',' && lastname[num-1] == ' ')
+	{
+	    display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
+	}
+	else
+	{
+	    display_write(-1, -1, 0, 1, lastname);
+	}
+    }
+}
+
+static void
+summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
+
+{
+    register long num;
+    register int color;
+    register char *thisname;
+    register char *lastname = NULL;
+
+    /* format each number followed by its string */
+    while ((thisname = *names++) != NULL)
+    {
+	/* get the number to format */
+	num = *numbers++;
+	color = 0;
+
+	/* display only non-zero numbers */
+	if (num != 0)
+	{
+	    /* write the previous name */
+	    if (lastname != NULL)
+	    {
+		display_write(-1, -1, 0, 0, lastname);
+	    }
+
+	    /* defer writing this name */
+	    lastname = thisname;
+
+#ifdef ENABLE_COLOR
+	    /* choose a color */
+	    color = color_test(*cidx++, num);
+#endif
+
+	    /* is this number in kilobytes? */
+	    if (thisname[0] == 'K')
+	    {
+		display_write(x, y, color, 0, format_k(num));
+		lastname++;
+	    }
+	    else
+	    {
+		display_write(x, y, color, 0, itoa((int)num));
+	    }
+
+	    /* next iteration will not start at x, y */
+	    x = y = -1;
+	}
+    }
+
+    /* if the last string has a separator on the end, it has to be
+       written with care */
+    if (lastname != NULL)
+    {
+	if ((num = strlen(lastname)) > 1 &&
+	    lastname[num-2] == ',' && lastname[num-1] == ' ')
+	{
+	    display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname);
+	}
+	else
+	{
+	    display_write(-1, -1, 0, 1, lastname);
+	}
+    }
+}
+
+/*
+ * int display_resize()
+ *
+ * Reallocate buffer space needed by the display package to accomodate
+ * a new screen size.  Must be called whenever the screen's size has
+ * changed.  Returns the number of lines available for displaying 
+ * processes or -1 if there was a problem allocating space.
+ */
+
+int
+display_resize()
+
+{
+    register int top_lines;
+    register int newsize;
+
     /* calculate the current dimensions */
     /* if operating in "dumb" mode, we only need one line */
-    lines = smart_terminal ? screen_length - Header_lines : 1;
+    top_lines = smart_terminal ? screen_length : 1;
+    if (top_lines < 0)
+	top_lines = 0;
 
-    if (lines < 0)
-	lines = 0;
     /* we don't want more than MAX_COLS columns, since the machine-dependent
        modules make static allocations based on MAX_COLS and we don't want
        to run off the end of their buffers */
@@ -138,58 +697,137 @@
 	display_width = MAX_COLS - 1;
     }
 
-    /* now, allocate space for the screen buffer */
-    screenbuf = (char *)malloc(lines * display_width);
-    if (screenbuf == (char *)NULL)
+    /* see how much space we need */
+    newsize = top_lines * (MAX_COLS + 1);
+
+    /* reallocate only if we need more than we already have */
+    if (newsize > bufsize)
     {
-	/* oops! */
-	return(-1);
+	/* deallocate any previous buffer that may have been there */
+	if (screenbuf != NULL)
+	{
+	    free(screenbuf);
+	}
+	if (colorbuf != NULL)
+	{
+	    free(colorbuf);
+	}
+
+	/* allocate space for the screen and color buffers */
+	bufsize = newsize;
+	screenbuf = (char *)calloc(bufsize, sizeof(char));
+	colorbuf = (char *)calloc(bufsize, sizeof(char));
+	if (screenbuf == NULL || colorbuf == NULL)
+	{
+	    /* oops! */
+	    return(-1);
+	}
     }
+    else
+    {
+	/* just clear them out */
+	memzero(screenbuf, bufsize);
+	memzero(colorbuf, bufsize);
+    }
 
+    /* adjust total lines on screen to lines available for procs */
+    top_lines -= y_procs;
+
     /* return number of lines available */
     /* for dumb terminals, pretend like we can show any amount */
-    return(smart_terminal ? lines : Largest);
+    return(smart_terminal ? top_lines : Largest);
 }
 
-int display_init(statics)
+int
+display_lines()
 
-struct statics *statics;
+{
+    return(smart_terminal ? screen_length : Largest);
+}
 
+int
+display_columns()
+
 {
-    register int lines;
+    return(display_width);
+}
+
+/*
+ * int display_init(struct statics *statics)
+ *
+ * Initialize the display system based on information in the statics
+ * structure.  Returns the number of lines available for displaying
+ * processes or -1 if there was an error.
+ */
+
+int
+display_init(struct statics *statics)
+
+{
+    register int top_lines;
     register char **pp;
+    register char *p;
     register int *ip;
     register int i;
 
+    /* certain things may influence the screen layout,
+       so look at those first */
+
+    /* a kernel line shifts parts of the display down */
+    kernel_names = statics->kernel_names;
+    if ((num_kernel = string_count(kernel_names)) > 0)
+    {
+	/* adjust screen placements */
+	y_mem++;
+	y_swap++;
+	y_message++;
+	y_header++;
+	y_idlecursor++;
+	y_procs++;
+    }
+
+    /* a swap line shifts parts of the display down one */
+    swap_names = statics->swap_names;
+    if ((num_swap = string_count(swap_names)) > 0)
+    {
+	/* adjust screen placements */
+	y_message++;
+	y_header++;
+	y_idlecursor++;
+	y_procs++;
+    }
+    lswap = (int *)malloc(num_swap * sizeof(int));
+    
     /* call resize to do the dirty work */
-    lines = display_resize();
+    top_lines = display_resize();
     num_cpus = statics->ncpus;
     cpustates_column = 5;	/* CPU: */
     if (num_cpus != 1)
-    cpustates_column += 2;	/* CPU 0: */
+	cpustates_column += 2;	/* CPU 0: */
     for (i = num_cpus; i > 9; i /= 10)
 	cpustates_column++;
+    y_kernel += num_cpus - 1;
+    y_mem += num_cpus - 1;
+    y_swap += num_cpus - 1;
+    y_header += num_cpus - 1;
+    y_idlecursor += num_cpus - 1;
+    y_message += num_cpus - 1;
+    y_procs += num_cpus - 1;
 
     /* only do the rest if we need to */
-    if (lines > -1)
+    if (top_lines > -1)
     {
 	/* save pointers and allocate space for names */
 	procstate_names = statics->procstate_names;
 	num_procstates = string_count(procstate_names);
-	lprocstates = (int *)malloc(num_procstates * sizeof(int));
+	lprocstates = (int *)calloc(num_procstates, sizeof(int));
 
 	cpustate_names = statics->cpustate_names;
-
-	swap_names = statics->swap_names;
-	num_swap = string_count(swap_names);
-	lswap = (int *)malloc(num_swap * sizeof(int));
 	num_cpustates = string_count(cpustate_names);
-	lcpustates = (int *)malloc(num_cpustates * sizeof(int) * num_cpus);
-	cpustate_columns = (int *)malloc(num_cpustates * sizeof(int));
-
+	lcpustates = (int *)calloc(num_cpustates, sizeof(int) * num_cpus);
+	cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
 	memory_names = statics->memory_names;
 	num_memory = string_count(memory_names);
-	lmemory = (int *)malloc(num_memory * sizeof(int));
 
 	/* calculate starting columns where needed */
 	cpustate_total_length = 0;
@@ -205,43 +843,109 @@
 	}
     }
 
-    /* return number of lines available */
-    return(lines);
+#ifdef ENABLE_COLOR
+    /* set up color tags for loadavg */
+    load_cidx[0] = color_tag("1min");
+    load_cidx[1] = color_tag("5min");
+    load_cidx[2] = color_tag("15min");
+
+    /* find header color */
+    header_cidx = color_tag("header");
+
+    /* color tags for cpu states */
+    cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int));
+    i = 0;
+    p = strcpyend(scratchbuf, "cpu.");
+    while (i < num_cpustates)
+    {
+	strcpy(p, cpustate_names[i]);
+	cpustate_cidx[i++] = color_tag(scratchbuf);
+    }
+
+    /* color tags for kernel */
+    if (num_kernel > 0)
+    {
+	kernel_cidx = (int *)malloc(num_kernel * sizeof(int));
+	i = 0;
+	p = strcpyend(scratchbuf, "kernel.");
+	while (i < num_kernel)
+	{
+	    strcpy(p, homogenize(kernel_names[i]+1));
+	    kernel_cidx[i++] = color_tag(scratchbuf);
+	}
+    }
+
+    /* color tags for memory */
+    memory_cidx = (int *)malloc(num_memory * sizeof(int));
+    i = 0;
+    p = strcpyend(scratchbuf, "memory.");
+    while (i < num_memory)
+    {
+	strcpy(p, homogenize(memory_names[i]+1));
+	memory_cidx[i++] = color_tag(scratchbuf);
+    }
+
+    /* color tags for swap */
+    if (num_swap > 0)
+    {
+	swap_cidx = (int *)malloc(num_swap * sizeof(int));
+	i = 0;
+	p = strcpyend(scratchbuf, "swap.");
+	while (i < num_swap)
+	{
+	    strcpy(p, homogenize(swap_names[i]+1));
+	    swap_cidx[i++] = color_tag(scratchbuf);
+	}
+    }
+#endif
+
+    /* return number of lines available (or error) */
+    return(top_lines);
 }
 
-i_loadave(mpid, avenrun)
+static void
+pr_loadavg(double avg, int i)
 
-int mpid;
-double *avenrun;
+{
+    int color = 0;
 
+#ifdef ENABLE_COLOR
+    color = color_test(load_cidx[i], (int)(avg * 100));
+#endif
+    display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
+		avg < 10.0 ? " %5.2f" : " %5.1f", avg);
+    display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
+}
+
+void
+i_loadave(int mpid, double *avenrun)
+
 {
     register int i;
 
-    /* i_loadave also clears the screen, since it is first */
-    clear();
-
     /* mpid == -1 implies this system doesn't have an _mpid */
     if (mpid != -1)
     {
-	printf("last pid: %5d;  ", mpid);
+	display_fmt(0, 0, 0, 0,
+		    "last pid: %5d;  load avg:", mpid);
+	x_loadave = X_LOADAVE;
     }
-
-    printf("load averages");
-
+    else
+    {
+	display_write(0, 0, 0, 0, "load averages:");
+	x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
+    }
     for (i = 0; i < 3; i++)
     {
-	printf("%c %5.2f",
-	    i == 0 ? ':' : ',',
-	    avenrun[i]);
+	pr_loadavg(avenrun[i], i);
     }
+
     lmpid = mpid;
 }
 
-u_loadave(mpid, avenrun)
+void
+u_loadave(int mpid, double *avenrun)
 
-int mpid;
-double *avenrun;
-
 {
     register int i;
 
@@ -250,136 +954,151 @@
 	/* change screen only when value has really changed */
 	if (mpid != lmpid)
 	{
-	    Move_to(x_lastpid, y_lastpid);
-	    printf("%5d", mpid);
+	    display_fmt(x_lastpid, y_lastpid, 0, 0,
+			"%5d", mpid);
 	    lmpid = mpid;
 	}
-
-	/* i remembers x coordinate to move to */
-	i = x_loadave;
     }
-    else
-    {
-	i = x_loadave_nompid;
-    }
 
-    /* move into position for load averages */
-    Move_to(i, y_loadave);
-
     /* display new load averages */
-    /* we should optimize this and only display changes */
     for (i = 0; i < 3; i++)
     {
-	printf("%s%5.2f",
-	    i == 0 ? "" : ", ",
-	    avenrun[i]);
+	pr_loadavg(avenrun[i], i);
     }
 }
 
-i_timeofday(tod)
+static char minibar_buffer[64];
+#define MINIBAR_WIDTH 20
 
-time_t *tod;
+void
+i_minibar(int (*formatter)(char *, int))
+{
+    (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
 
+    display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
+}
+
+void
+u_minibar(int (*formatter)(char *, int))
 {
-    /*
-     *  Display the current time.
-     *  "ctime" always returns a string that looks like this:
-     *  
-     *	Sun Sep 16 01:03:52 1973
-     *      012345678901234567890123
-     *	          1         2
-     *
-     *  We want indices 11 thru 18 (length 8).
-     */
+    (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
 
-    if (smart_terminal)
+    display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
+}
+
+void
+i_uptime(time_t *bt, time_t *tod)
+
+{
+    int uptime_days;
+    time_t uptime;
+    char t[10];
+    struct tm *tm;
+
+    if (*bt != -1)
     {
-	Move_to(screen_width - 8, 0);
+	uptime = *tod - *bt;
+	uptime += 30;
+	uptime_days = uptime / 86400;
+
+	tm = gmtime(&uptime);
+	strftime(t, sizeof(t), "%H:%M:%S", tm);
+
+	/*
+	 *  Display the uptime.
+	 */
+
+	x_uptime = (smart_terminal ? screen_width : 79) - 29;
+	display_fmt(x_uptime, y_uptime, 0, 0,
+		    "  up %d+%s",
+		    uptime_days, t);
     }
-    else
+}
+
+void
+u_uptime(time_t *bt, time_t *tod)
+
+{
+    i_uptime(bt, tod);
+}
+
+
+void
+i_timeofday(time_t *tod)
+
+{
+    int x;
+    char t[10];
+    struct tm *tm;
+
+    /* where on the screen do we start? */
+    x = (smart_terminal ? screen_width : 79) - 8;
+
+    /* but don't bump in to uptime */
+    if (x < x_uptime + 19)
     {
-	fputs("    ", stdout);
+	x = x_uptime + 19;
     }
-#ifdef DEBUG
-    {
-	char *foo;
-	foo = ctime(tod);
-	fputs(foo, stdout);
-    }
-#endif
-    printf("%-8.8s\n", &(ctime(tod)[11]));
-    lastline = 1;
+
+    /* display it */
+    tm = localtime(tod);
+    strftime(t, sizeof(t), "%H:%M:%S", tm);
+    display_write(x, 0, 0, 1, t);
 }
 
 static int ltotal = 0;
-static char procstates_buffer[MAX_COLS];
+static int lthreads = 0;
 
 /*
  *  *_procstates(total, brkdn, names) - print the process summary line
- *
- *  Assumptions:  cursor is at the beginning of the line on entry
- *		  lastline is valid
  */
 
-i_procstates(total, brkdn)
 
-int total;
-int *brkdn;
+void
+i_procstates(int total, int *brkdn, int threads)
 
 {
-    register int i;
-
     /* write current number of processes and remember the value */
-    printf("%d processes:", total);
+    display_fmt(0, y_procstate, 0, 0,
+		"%d %s: ", total, threads ? "threads" : "processes");
     ltotal = total;
 
-    /* put out enough spaces to get to column 15 */
-    i = digits(total);
-    while (i++ < 4)
+    /* remember where the summary starts */
+    x_procstate = virt_x;
+
+    if (total > 0)
     {
-	putchar(' ');
+	/* format and print the process state summary */
+	summary_format(-1, -1, brkdn, procstate_names, NULL);
+
+	/* save the numbers for next time */
+	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+	lthreads = threads;
     }
-
-    /* format and print the process state summary */
-    summary_format(procstates_buffer, brkdn, procstate_names);
-    fputs(procstates_buffer, stdout);
-
-    /* save the numbers for next time */
-    memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
 }
 
-u_procstates(total, brkdn)
+void
+u_procstates(int total, int *brkdn, int threads)
 
-int total;
-int *brkdn;
-
 {
-    static char new[MAX_COLS];
-    register int i;
+    /* if threads state has changed, do a full update */
+    if (lthreads != threads)
+    {
+	i_procstates(total, brkdn, threads);
+	return;
+    }
 
     /* update number of processes only if it has changed */
     if (ltotal != total)
     {
-	/* move and overwrite */
-#if (x_procstate == 0)
-	Move_to(x_procstate, y_procstate);
-#else
-	/* cursor is already there...no motion needed */
-	/* assert(lastline == 1); */
-#endif
-	printf("%d", total);
+	display_fmt(0, y_procstate, 0, 0,
+		    "%d", total);
 
 	/* if number of digits differs, rewrite the label */
 	if (digits(total) != digits(ltotal))
 	{
-	    fputs(" processes:", stdout);
-	    /* put out enough spaces to get to column 15 */
-	    i = digits(total);
-	    while (i++ < 4)
-	    {
-		putchar(' ');
-	    }
-	    /* cursor may end up right where we want it!!! */
+	    display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
+	    x_procstate = virt_x;
 	}
 
 	/* save new total */
@@ -387,11 +1106,10 @@
     }
 
     /* see if any of the state numbers has changed */
-    if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
+    if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
     {
 	/* format and update the line */
-	summary_format(new, brkdn, procstate_names);
-	line_update(procstates_buffer, new, x_brkdn, y_brkdn);
+	summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL);
 	memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
     }
 }
@@ -399,13 +1117,12 @@
 #ifdef no_more
 /*
  *  *_cpustates(states, names) - print the cpu state percentages
- *
- *  Assumptions:  cursor is on the PREVIOUS line
  */
 
 /* cpustates_tag() calculates the correct tag to use to label the line */
 
-char *cpustates_tag()
+char *
+cpustates_tag()
 
 {
     register char *use;
@@ -424,32 +1141,39 @@
 	use = long_tag;
     }
 
-    /* set cpustates_column accordingly then return result */
-    cpustates_column = strlen(use);
+    /* set x_cpustates accordingly then return result */
+    x_cpustates = strlen(use);
     return(use);
 }
 #endif
 
-i_cpustates(states)
+void
+i_cpustates(int *states)
 
-register int *states;
-
 {
-    register int i = 0;
-    register int value;
-    register char **names;
-    register char *thisname;
+    int value;
+    char **names;
+    char *thisname;
+    int *colp;
+    int color = 0;
     int cpu;
+    char scpu[10];
+#ifdef ENABLE_COLOR
+    int *cidx = cpustate_cidx;
+#endif
 
 for (cpu = 0; cpu < num_cpus; cpu++) {
+    /* initialize */
     names = cpustate_names;
+    colp = cpustate_columns;
 
-    /* print tag and bump lastline */
+    /* print tag */
     if (num_cpus == 1)
-	printf("\nCPU: ");
+	strcpy(scpu, "CPU: ");
     else
-	printf("\nCPU %d: ", cpu);
-    lastline++;
+	sprintf(scpu, "CPU %d: ", cpu);
+    display_write(0, y_cpustates + cpu, 0, 0, scpu);
+    x_cpustates = strlen(scpu) + 1;
 
     /* now walk thru the names and print the line */
     while ((thisname = *names++) != NULL)
@@ -457,14 +1181,26 @@
 	if (*thisname != '\0')
 	{
 	    /* retrieve the value and remember it */
-	    value = *states++;
+	    value = *states;
 
+#ifdef ENABLE_COLOR
+	    /* determine color number to use */
+	    color = color_test(*cidx++, value/10);
+#endif
+
 	    /* if percentage is >= 1000, print it as 100% */
-	    printf((value >= 1000 ? "%s%4.0f%% %s" : "%s%4.1f%% %s"),
-		   (i++ % num_cpustates) == 0 ? "" : ", ",
-		   ((float)value)/10.,
-		   thisname);
+	    display_fmt(
+			x_cpustates + *colp, y_cpustates + cpu,
+			color, 0,
+			(value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
+			((float)value)/10.,
+			thisname,
+			*names != NULL ? ", " : "");
+
 	}
+	/* increment */
+	colp++;
+	states++;
     }
 }
 
@@ -472,26 +1208,26 @@
     memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
 }
 
-u_cpustates(states)
+void
+u_cpustates(int *states)
 
-register int *states;
-
 {
-    register int value;
-    register char **names;
-    register char *thisname;
-    register int *lp;
-    register int *colp;
+    int value;
+    char **names;
+    char *thisname;
+    int *lp;
+    int *colp;
+    int color = 0;
     int cpu;
+#ifdef ENABLE_COLOR
+    int *cidx = cpustate_cidx;
+#endif
 
 for (cpu = 0; cpu < num_cpus; cpu++) {
+    lp = lcpustates;
+    colp = cpustate_columns;
     names = cpustate_names;
 
-    Move_to(cpustates_column, y_cpustates + cpu);
-    lastline = y_cpustates + cpu;
-    lp = lcpustates + (cpu * num_cpustates);
-    colp = cpustate_columns;
-
     /* we could be much more optimal about this */
     while ((thisname = *names++) != NULL)
     {
@@ -500,20 +1236,26 @@
 	    /* did the value change since last time? */
 	    if (*lp != *states)
 	    {
-		/* yes, move and change */
-		Move_to(cpustates_column + *colp, y_cpustates + cpu);
-		lastline = y_cpustates + cpu;
-
+		/* yes, change it */
 		/* retrieve value and remember it */
 		value = *states;
 
+#ifdef ENABLE_COLOR
+		/* determine color number to use */
+		color = color_test(*cidx, value/10);
+#endif
+
 		/* if percentage is >= 1000, print it as 100% */
-		printf((value >= 1000 ? "%4.0f" : "%4.1f"),
-		       ((double)value)/10.);
+		display_fmt(x_cpustates + *colp, y_cpustates + cpu, color, 0,
+			    (value >= 1000 ? "%4.0f" : "%4.1f"),
+			    ((double)value)/10.);
 
 		/* remember it for next time */
 		*lp = value;
 	    }
+#ifdef ENABLE_COLOR
+	    cidx++;
+#endif
 	}
 
 	/* increment and move on */
@@ -524,6 +1266,7 @@
 }
 }
 
+void
 z_cpustates()
 
 {
@@ -532,22 +1275,25 @@
     register char *thisname;
     register int *lp;
     int cpu;
+    char scpu[10];
 
 for (cpu = 0; cpu < num_cpus; cpu++) {
     names = cpustate_names;
 
-    /* show tag and bump lastline */
+    /* print tag */
     if (num_cpus == 1)
-	printf("\nCPU: ");
+	strcpy(scpu, "CPU: ");
     else
-	printf("\nCPU %d: ", cpu);
-    lastline++;
+	sprintf(scpu, "CPU %d: ", cpu);
+    display_write(0, y_cpustates + cpu, 0, 0, scpu);
+    x_cpustates = strlen(scpu) + 1;
 
     while ((thisname = *names++) != NULL)
     {
 	if (*thisname != '\0')
 	{
-	    printf("%s    %% %s", (i++ % num_cpustates) == 0 ? "" : ", ", thisname);
+	    display_fmt(-1, -1, 0, 0, "%s    %% %s", i++ == 0 ? "" : ", ",
+			thisname);
 	}
     }
 }
@@ -562,71 +1308,90 @@
 }
 
 /*
- *  *_memory(stats) - print "Memory: " followed by the memory summary string
+ *  *_kernel(stats) - print "Kernel: " followed by the kernel summary string
  *
- *  Assumptions:  cursor is on "lastline"
- *                for i_memory ONLY: cursor is on the previous line
+ *  Assumptions:  cursor is on "lastline", the previous line
  */
 
-char memory_buffer[MAX_COLS];
+void
+i_kernel(int *stats)
 
-i_memory(stats)
+{
+    if (num_kernel > 0)
+    {
+	display_write(0, y_kernel, 0, 0, "Kernel: ");
 
-int *stats;
+	/* format and print the kernel summary */
+	summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
+    }
+}
 
+void
+u_kernel(int *stats)
+
 {
-    fputs("\nMem: ", stdout);
-    lastline++;
+    if (num_kernel > 0)
+    {
+	/* format the new line */
+	summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
+    }
+}
 
+/*
+ *  *_memory(stats) - print "Memory: " followed by the memory summary string
+ *
+ *  Assumptions:  cursor is on "lastline", the previous line
+ */
+
+void
+i_memory(long *stats)
+
+{
+    display_write(0, y_mem, 0, 0, "Mem: ");
+
     /* format and print the memory summary */
-    summary_format(memory_buffer, stats, memory_names);
-    fputs(memory_buffer, stdout);
+    summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
 }
 
-u_memory(stats)
+void
+u_memory(long *stats)
 
-int *stats;
-
 {
-    static char new[MAX_COLS];
-
     /* format the new line */
-    summary_format(new, stats, memory_names);
-    line_update(memory_buffer, new, x_mem, y_mem);
+    summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
 }
 
 /*
  *  *_swap(stats) - print "Swap: " followed by the swap summary string
  *
- *  Assumptions:  cursor is on "lastline"
- *                for i_swap ONLY: cursor is on the previous line
+ *  Assumptions:  cursor is on "lastline", the previous line
+ *
+ *  These functions only print something when num_swap > 0
  */
 
-char swap_buffer[MAX_COLS];
+void
+i_swap(long *stats)
 
-i_swap(stats)
-
-int *stats;
-
 {
-    fputs("\nSwap: ", stdout);
-    lastline++;
+    if (num_swap > 0)
+    {
+	/* print the tag */
+	display_write(0, y_swap, 0, 0, "Swap: ");
 
-    /* format and print the swap summary */
-    summary_format(swap_buffer, stats, swap_names);
-    fputs(swap_buffer, stdout);
+	/* format and print the swap summary */
+	summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+    }
 }
 
-u_swap(stats)
+void
+u_swap(long *stats)
 
-int *stats;
-
 {
-    static char new[MAX_COLS];
-
-    /* format the new line */
-    summary_format(new, stats, swap_names);
-    line_update(swap_buffer, new, x_swap, y_swap);
+    if (num_swap > 0)
+    {
+	/* format the new line */
+	summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+    }
 }
 
 /*
@@ -640,39 +1405,89 @@
 
 /*
  *  i_message is funny because it gets its message asynchronously (with
- *	respect to screen updates).
+ *	respect to screen updates).  Messages are taken out of the
+ *      circular message_buf and displayed one at a time.
  */
 
-static char next_msg[MAX_COLS + 5];
-static int msglen = 0;
-/* Invariant: msglen is always the length of the message currently displayed
-   on the screen (even when next_msg doesn't contain that message). */
+void
+i_message(struct timeval *now)
 
-i_message()
+{
+    struct timeval my_now;
+    int i = 0;
 
-{
-    while (lastline < y_message)
+    dprintf("i_message(%08x)\n", now);
+
+    /* if now is NULL we have to get it ourselves */
+    if (now == NULL)
     {
-	fputc('\n', stdout);
-	lastline++;
+	time_get(&my_now);
+	now = &my_now;
     }
-    if (next_msg[0] != '\0')
+
+    /* now that we have been called, messages no longer need to be held */
+    message_hold = 0;
+
+    dprintf("i_message: now %d, message_time %d\n",
+	    now->tv_sec, message_time.tv_sec);
+
+    if (smart_terminal)
     {
-	standout(next_msg);
-	msglen = strlen(next_msg);
-	next_msg[0] = '\0';
+	/* is it time to change the message? */
+	if (timercmp(now, &message_time, > ))
+	{
+	    /* yes, free the current message */
+	    dprintf("i_message: timer expired\n");
+	    if (message_current != NULL)
+	    {
+		free(message_current);
+		message_current = NULL;
+	    }
+
+	    /* is there a new message to be displayed? */
+	    if (message_first != message_last)
+	    {
+		/* move index to next message */
+		if (++message_first == MAX_MESSAGES) message_first = 0;
+
+		/* make the next message the current one */
+		message_current = message_buf[message_first];
+
+		/* show it */
+		dprintf("i_message: showing \"%s\"\n", message_current);
+		display_move(0, y_message);
+		screen_standout(message_current);
+		i = strlen(message_current);
+
+		/* set the expiration timer */
+		message_time = *now;
+		message_time.tv_sec += MESSAGE_DISPLAY_TIME;
+
+		/* clear the rest of the line */
+		screen_cleareol(message_length - i);
+		putchar('\r');
+		message_length = i;
+	    }
+	    else
+	    {
+		/* just clear what was there before, if anything */
+		if (message_length > 0)
+		{
+		    display_move(0, y_message);
+		    screen_cleareol(message_length);
+		    putchar('\r');
+		    message_length = 0;
+		}
+	    }
+	}
     }
-    else if (msglen > 0)
-    {
-	(void) clear_eol(msglen);
-	msglen = 0;
-    }
 }
 
-u_message()
+void
+u_message(struct timeval *now)
 
 {
-    i_message();
+    i_message(now);
 }
 
 static int header_length;
@@ -710,44 +1525,39 @@
  *  Assumptions:  cursor is on the previous line and lastline is consistent
  */
 
-i_header(text)
+void
+i_header(char *text)
 
-char *text;
-
 {
     char *s;
+    int header_color = 0;
 
+#ifdef ENABLE_COLOR
+    header_color = color_test(header_cidx, 0);
+#endif
     s = trim_header(text);
     if (s != NULL)
 	text = s;
-
-    if (header_status == ON)
+    header_length = strlen(text);
+    if (header_status)
     {
-	putchar('\n');
-	fputs(text, stdout);
-	lastline++;
+	display_write(x_header, y_header, header_color, 1, text);
     }
-    else if (header_status == ERASE)
-    {
-	header_status = OFF;
-    }
     free(s);
 }
 
 /*ARGSUSED*/
-u_header(text)
+void
+u_header(char *text)
 
-char *text;		/* ignored */
-
 {
+    int header_color = 0;
 
-    if (header_status == ERASE)
-    {
-	putchar('\n');
-	lastline++;
-	clear_eol(header_length);
-	header_status = OFF;
-    }
+#ifdef ENABLE_COLOR
+    header_color = color_test(header_cidx, 0);
+#endif
+    display_write(x_header, y_header, header_color, 1,
+		  header_status ? text : "");
 }
 
 /*
@@ -756,135 +1566,53 @@
  *  Assumptions:  lastline is consistent
  */
 
-i_process(line, thisline)
+void
+i_process(int line, char *thisline)
 
-int line;
-char *thisline;
-
 {
-    register char *p;
-    register char *base;
-
-    /* make sure we are on the correct line */
-    while (lastline < y_procs + line)
-    {
-	putchar('\n');
-	lastline++;
-    }
-
     /* truncate the line to conform to our current screen width */
     thisline[display_width] = '\0';
 
     /* write the line out */
-    fputs(thisline, stdout);
+    display_write(0, y_procs + line, 0, 1, thisline);
+}
 
-    /* copy it in to our buffer */
-    base = smart_terminal ? screenbuf + lineindex(line) : screenbuf;
-    p = strecpy(base, thisline);
+void
+u_process(int line, char *new_line)
 
-    /* zero fill the rest of it */
-    memzero(p, display_width - (p - base));
+{
+    i_process(line, new_line);
 }
 
-u_process(line, newline)
+void
+i_endscreen()
 
-int line;
-char *newline;
-
 {
-    register char *optr;
-    register int screen_line = line + Header_lines;
-    register char *bufferline;
-
-    /* remember a pointer to the current line in the screen buffer */
-    bufferline = &screenbuf[lineindex(line)];
-
-    /* truncate the line to conform to our current screen width */
-    newline[display_width] = '\0';
-
-    /* is line higher than we went on the last display? */
-    if (line >= last_hi)
+    if (smart_terminal)
     {
-	/* yes, just ignore screenbuf and write it out directly */
-	/* get positioned on the correct line */
-	if (screen_line - lastline == 1)
-	{
-	    putchar('\n');
-	    lastline++;
-	}
-	else
-	{
-	    Move_to(0, screen_line);
-	    lastline = screen_line;
-	}
-
-	/* now write the line */
-	fputs(newline, stdout);
-
-	/* copy it in to the buffer */
-	optr = strecpy(bufferline, newline);
-
-	/* zero fill the rest of it */
-	memzero(optr, display_width - (optr - bufferline));
+	/* move the cursor to a pleasant place */
+	display_move(x_idlecursor, y_idlecursor);
     }
     else
     {
-	line_update(bufferline, newline, 0, line + Header_lines);
+	/* separate this display from the next with some vertical room */
+	fputs("\n\n", stdout);
     }
+    fflush(stdout);
 }
 
-u_endscreen(hi)
+void
+u_endscreen()
 
-register int hi;
-
 {
-    register int screen_line = hi + Header_lines;
-    register int i;
-
     if (smart_terminal)
     {
-	if (hi < last_hi)
-	{
-	    /* need to blank the remainder of the screen */
-	    /* but only if there is any screen left below this line */
-	    if (lastline + 1 < screen_length)
-	    {
-		/* efficiently move to the end of currently displayed info */
-		if (screen_line - lastline < 5)
-		{
-		    while (lastline < screen_line)
-		    {
-			putchar('\n');
-			lastline++;
-		    }
-		}
-		else
-		{
-		    Move_to(0, screen_line);
-		    lastline = screen_line;
-		}
+	/* clear-to-end the display */
+	display_cte();
 
-		if (clear_to_end)
-		{
-		    /* we can do this the easy way */
-		    putcap(clear_to_end);
-		}
-		else
-		{
-		    /* use clear_eol on each line */
-		    i = hi;
-		    while ((void) clear_eol(strlen(&screenbuf[lineindex(i++)])), i < last_hi)
-		    {
-			putchar('\n');
-		    }
-		}
-	    }
-	}
-	last_hi = hi;
-
 	/* move the cursor to a pleasant place */
-	Move_to(x_idlecursor, y_idlecursor);
-	lastline = y_idlecursor;
+	display_move(x_idlecursor, y_idlecursor);
+	fflush(stdout);
     }
     else
     {
@@ -893,82 +1621,244 @@
     }
 }
 
-display_header(t)
+void
+display_header(int t)
 
-int t;
+{
+    header_status = t != 0;
+}
 
+void
+message_mark()
+
 {
-    if (t)
-    {
-	header_status = ON;
-    }
-    else if (header_status == ON)
-    {
-	header_status = ERASE;
-    }
+    message_barrier = Yes;
 }
 
-/*VARARGS2*/
-new_message(type, msgfmt, a1, a2, a3)
+void
+message_expire()
 
-int type;
-char *msgfmt;
-caddr_t a1, a2, a3;
+{
+    message_time.tv_sec = 0;
+    message_time.tv_usec = 0;
+}
 
+void
+message_flush()
+
 {
-    register int i;
+    message_first = message_last;
+    message_time.tv_sec = 0;
+    message_time.tv_usec = 0;
+}
 
+/*
+ * void new_message_v(char *msgfmt, va_list ap)
+ *
+ * Display a message in the message area.  This function takes a va_list for
+ * the arguments.  Safe to call before display_init.  This function only
+ * queues a message for display, and allowed for multiple messages to be
+ * queued.  The i_message function drains the queue and actually writes the
+ * messages on the display.
+ */
+
+
+void
+new_message_v(char *msgfmt, va_list ap)
+
+{
+    int i;
+    int empty;
+    char msg[MAX_COLS];
+
+    /* if message_barrier is active, remove all pending messages */
+    if (message_barrier)
+    {
+	message_flush();
+	message_barrier = No;
+    }
+
     /* first, format the message */
-    (void) snprintf(next_msg, sizeof(next_msg), msgfmt, a1, a2, a3);
+    (void) vsnprintf(msg, sizeof(msg), msgfmt, ap);
 
-    if (msglen > 0)
+    /* where in the buffer will it go? */
+    i = message_last + 1;
+    if (i >= MAX_MESSAGES) i = 0;
+
+    /* make sure the buffer is not full */
+    if (i != message_first)
     {
-	/* message there already -- can we clear it? */
-	if (!overstrike)
+	/* insert it in to message_buf */
+	message_buf[i] = strdup(msg);
+	dprintf("new_message_v: new message inserted in slot %d\n", i);
+
+	/* remember if the buffer is empty and set the index */
+	empty = message_last == message_first;
+	message_last = i;
+
+	/* is message_buf otherwise empty and have we started displaying? */
+	if (empty && !message_hold)
 	{
-	    /* yes -- write it and clear to end */
-	    i = strlen(next_msg);
-	    if ((type & MT_delayed) == 0)
-	    {
-		type & MT_standout ? standout(next_msg) :
-		                     fputs(next_msg, stdout);
-		(void) clear_eol(msglen - i);
-		msglen = i;
-		next_msg[0] = '\0';
-	    }
+	    /* we can display the message now */
+	    i_message(NULL);
 	}
     }
-    else
+}
+
+/*
+ * void new_message(int type, char *msgfmt, ...)
+ *
+ * Display a message in the message area.  It is safe to call this function
+ * before display_init.  Messages logged before the display is drawn will be
+ * held and displayed later.
+ */
+
+void
+new_message(char *msgfmt, ...)
+
+{
+    va_list ap;
+
+    va_start(ap, msgfmt);
+    new_message_v(msgfmt, ap);
+    va_end(ap);
+}
+
+/*
+ * void message_error(char *msgfmt, ...)
+ *
+ * Put an error message in the message area.  It is safe to call this function
+ * before display_init.  Messages logged before the display is drawn will be
+ * held and displayed later.
+ */
+
+void
+message_error(char *msgfmt, ...)
+
+{
+    va_list ap;
+
+    va_start(ap, msgfmt);
+    new_message_v(msgfmt, ap);
+    fflush(stdout);
+    va_end(ap);
+}
+
+/*
+ * void message_clear()
+ *
+ * Clear message area and flush all pending messages.
+ */
+
+void
+message_clear()
+
+{
+    /* remove any existing message */
+    if (message_current != NULL)
     {
-	if ((type & MT_delayed) == 0)
-	{
-	    type & MT_standout ? standout(next_msg) : fputs(next_msg, stdout);
-	    msglen = strlen(next_msg);
-	    next_msg[0] = '\0';
-	}
+	display_move(0, y_message);
+	screen_cleareol(message_length);
+	free(message_current);
+	message_current = 0;
     }
+
+    /* flush all pending messages */
+    message_flush();
 }
 
-clear_message()
+/*
+ * void message_prompt_v(int so, char *msgfmt, va_list ap)
+ *
+ * Place a prompt in the message area.  A prompt is different from a 
+ * message as follows: it is displayed immediately, overwriting any
+ * message that may already be there, it may be highlighted in standout
+ * mode (if "so" is true), the cursor is left to rest at the end of the
+ * prompt.  This call causes all pending messages to be flushed.
+ */
 
+void
+message_prompt_v(int so, char *msgfmt, va_list ap)
+
 {
-    if (clear_eol(msglen) == 1)
+    char msg[MAX_COLS];
+    int i;
+
+    /* clear out the message buffer */
+    message_flush();
+
+    /* format the message */
+    i = vsnprintf(msg, sizeof(msg), msgfmt, ap);
+
+    /* this goes over any existing message */
+    display_move(0, y_message);
+
+    /* clear the entire line */
+    screen_cleareol(message_length);
+
+    /* show the prompt */
+    if (so)
     {
-	putchar('\r');
+	screen_standout(msg);
     }
+    else
+    {
+	fputs(msg, stdout);
+    }
+
+    /* make it all visible */
+    fflush(stdout);
+
+    /* even though we dont keep a copy of the prompt, track its length */
+    message_length = i < MAX_COLS ? i : MAX_COLS;
 }
 
-readline(buffer, size, numeric)
+/*
+ * void message_prompt(char *msgfmt, ...)
+ *
+ * Place a prompt in the message area (see message_prompt_v).
+ */
 
-char *buffer;
-int  size;
-int  numeric;
+void
+message_prompt(char *msgfmt, ...)
 
 {
+    va_list ap;
+
+    va_start(ap, msgfmt);
+    message_prompt_v(Yes, msgfmt, ap);
+    va_end(ap);
+}
+
+void
+message_prompt_plain(char *msgfmt, ...)
+
+{
+    va_list ap;
+
+    va_start(ap, msgfmt);
+    message_prompt_v(No, msgfmt, ap);
+    va_end(ap);
+}
+
+/*
+ * int readline(char *buffer, int size, int numeric)
+ *
+ * Read a line of input from the terminal.  The line is placed in
+ * "buffer" not to exceed "size".  If "numeric" is true then the input
+ * can only consist of digits.  This routine handles all character
+ * editing while keeping the terminal in cbreak mode.  If "numeric"
+ * is true then the number entered is returned.  Otherwise the number
+ * of character read in to "buffer" is returned.
+ */
+
+int
+readline(char *buffer, int size, int numeric)
+
+{
     register char *ptr = buffer;
     register char ch;
     register char cnt = 0;
-    register char maxcnt = 0;
 
     /* allow room for null terminator */
     size -= 1;
@@ -976,7 +1866,7 @@
     /* read loop */
     while ((fflush(stdout), read(0, ptr, 1) > 0))
     {
-	/* newline means we are done */
+	/* newline or return means we are done */
 	if ((ch = *ptr) == '\n' || ch == '\r')
 	{
 	    break;
@@ -985,17 +1875,39 @@
 	/* handle special editing characters */
 	if (ch == ch_kill)
 	{
-	    /* kill line -- account for overstriking */
-	    if (overstrike)
-	    {
-		msglen += maxcnt;
-	    }
-
 	    /* return null string */
 	    *buffer = '\0';
 	    putchar('\r');
 	    return(-1);
 	}
+	else if (ch == ch_werase)
+	{
+	    /* erase previous word */
+	    if (cnt <= 0)
+	    {
+		/* none to erase! */
+		putchar('\7');
+	    }
+	    else
+	    {
+		/*
+		 * First: remove all spaces till the first-non-space 
+		 * Second: remove all non-spaces till the first-space
+		 */
+		while(cnt > 0 && ptr[-1] == ' ')
+		{
+		    fputs("\b \b", stdout);
+		    ptr--;
+		    cnt--;
+		}
+		while(cnt > 0 && ptr[-1] != ' ')
+		{
+		    fputs("\b \b", stdout);
+		    ptr--;
+		    cnt--;
+		}
+	    }
+	}
 	else if (ch == ch_erase)
 	{
 	    /* erase previous character */
@@ -1012,8 +1924,8 @@
 	    }
 	}
 	/* check for character validity and buffer overflow */
-	else if (cnt == size || (numeric && !isdigit(ch)) ||
-		!isprint(ch))
+	else if (cnt == size || (numeric && !isdigit((int)ch)) ||
+		!isprint((int)ch))
 	{
 	    /* not legal */
 	    putchar('\7');
@@ -1024,273 +1936,80 @@
 	    putchar(ch);
 	    ptr++;
 	    cnt++;
-	    if (cnt > maxcnt)
-	    {
-		maxcnt = cnt;
-	    }
 	}
     }
 
     /* all done -- null terminate the string */
     *ptr = '\0';
 
-    /* account for the extra characters in the message area */
-    /* (if terminal overstrikes, remember the furthest they went) */
-    msglen += overstrike ? maxcnt : cnt;
+    /* add response length to message_length */
+    message_length += cnt;
 
     /* return either inputted number or string length */
     putchar('\r');
     return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
 }
 
-/* internal support routines */
+void
+display_pagerstart()
 
-static int string_count(pp)
-
-register char **pp;
-
 {
-    register int cnt;
-
-    cnt = 0;
-    while (*pp++ != NULL)
-    {
-	cnt++;
-    }
-    return(cnt);
+    display_clear();
 }
 
-static void summary_format(str, numbers, names)
+void
+display_pagerend()
 
-char *str;
-int *numbers;
-register char **names;
-
 {
-    register char *p;
-    register int num;
-    register char *thisname;
-    register int useM = No;
+    char ch;
 
-    /* format each number followed by its string */
-    p = str;
-    while ((thisname = *names++) != NULL)
-    {
-	/* get the number to format */
-	num = *numbers++;
-
-	/* display only non-zero numbers */
-	if (num > 0)
-	{
-	    /* is this number in kilobytes? */
-	    if (thisname[0] == 'K')
-	    {
-		/* yes: format it as a memory value */
-		p = strecpy(p, format_k(num));
-
-		/* skip over the K, since it was included by format_k */
-		p = strecpy(p, thisname+1);
-	    }
-	    else
-	    {
-		p = strecpy(p, itoa(num));
-		p = strecpy(p, thisname);
-	    }
-	}
-
-	/* ignore negative numbers, but display corresponding string */
-	else if (num < 0)
-	{
-	    p = strecpy(p, thisname);
-	}
-    }
-
-    /* if the last two characters in the string are ", ", delete them */
-    p -= 2;
-    if (p >= str && p[0] == ',' && p[1] == ' ')
-    {
-	*p = '\0';
-    }
+    screen_standout("Hit any key to continue: ");
+    fflush(stdout);
+    (void) read(0, &ch, 1);
 }
 
-static void line_update(old, new, start, line)
+void
+display_pager(char *fmt, ...)
 
-register char *old;
-register char *new;
-int start;
-int line;
-
 {
-    register int ch;
-    register int diff;
-    register int newcol = start + 1;
-    register int lastcol = start;
-    char cursor_on_line = No;
-    char *current;
+    va_list ap;
 
-    /* compare the two strings and only rewrite what has changed */
-    current = old;
-#ifdef DEBUG
-    fprintf(debug, "line_update, starting at %d\n", start);
-    fputs(old, debug);
-    fputc('\n', debug);
-    fputs(new, debug);
-    fputs("\n-\n", debug);
-#endif
+    int ch;
+    char readch;
+    char buffer[MAX_COLS];
+    char *data;
 
-    /* start things off on the right foot		    */
-    /* this is to make sure the invariants get set up right */
-    if ((ch = *new++) != *old)
+    /* format into buffer */
+    va_start(ap, fmt);
+    (void) vsnprintf(buffer, MAX_COLS, fmt, ap);
+    va_end(ap);
+    data = buffer;
+
+    while ((ch = *data++) != '\0')
     {
-	if (line - lastline == 1 && start == 0)
-	{
-	    putchar('\n');
-	}
-	else
-	{
-	    Move_to(start, line);
-	}
-	cursor_on_line = Yes;
 	putchar(ch);
-	*old = ch;
-	lastcol = 1;
-    }
-    old++;
-	
-    /*
-     *  main loop -- check each character.  If the old and new aren't the
-     *	same, then update the display.  When the distance from the
-     *	current cursor position to the new change is small enough,
-     *	the characters that belong there are written to move the
-     *	cursor over.
-     *
-     *	Invariants:
-     *	    lastcol is the column where the cursor currently is sitting
-     *		(always one beyond the end of the last mismatch).
-     */
-    do		/* yes, a do...while */
-    {
-	if ((ch = *new++) != *old)
+	if (ch == '\n')
 	{
-	    /* new character is different from old	  */
-	    /* make sure the cursor is on top of this character */
-	    diff = newcol - lastcol;
-	    if (diff > 0)
+	    if (++curr_y >= screen_length - 1)
 	    {
-		/* some motion is required--figure out which is shorter */
-		if (diff < 6 && cursor_on_line)
+		screen_standout("...More...");
+		fflush(stdout);
+		(void) read(0, &readch, 1);
+		putchar('\r');
+		switch(readch)
 		{
-		    /* overwrite old stuff--get it out of the old buffer */
-		    printf("%.*s", diff, &current[lastcol-start]);
+		case '\r':
+		case '\n':
+		    curr_y--;
+		    break;
+
+		case 'q':
+		    return;
+
+		default:
+		    curr_y = 0;
 		}
-		else
-		{
-		    /* use cursor addressing */
-		    Move_to(newcol, line);
-		    cursor_on_line = Yes;
-		}
-		/* remember where the cursor is */
-		lastcol = newcol + 1;
 	    }
-	    else
-	    {
-		/* already there, update position */
-		lastcol++;
-	    }
-		
-	    /* write what we need to */
-	    if (ch == '\0')
-	    {
-		/* at the end--terminate with a clear-to-end-of-line */
-		(void) clear_eol(strlen(old));
-	    }
-	    else
-	    {
-		/* write the new character */
-		putchar(ch);
-	    }
-	    /* put the new character in the screen buffer */
-	    *old = ch;
 	}
-	    
-	/* update working column and screen buffer pointer */
-	newcol++;
-	old++;
-	    
-    } while (ch != '\0');
-
-    /* zero out the rest of the line buffer -- MUST BE DONE! */
-    diff = display_width - newcol;
-    if (diff > 0)
-    {
-	memzero(old, diff);
     }
-
-    /* remember where the current line is */
-    if (cursor_on_line)
-    {
-	lastline = line;
-    }
 }
-
-/*
- *  printable(str) - make the string pointed to by "str" into one that is
- *	printable (i.e.: all ascii), by converting all non-printable
- *	characters into '?'.  Replacements are done in place and a pointer
- *	to the original buffer is returned.
- */
-
-char *printable(str)
-
-char *str;
-
-{
-    register char *ptr;
-    register char ch;
-
-    ptr = str;
-    while ((ch = *ptr) != '\0')
-    {
-	if (!isprint(ch))
-	{
-	    *ptr = '?';
-	}
-	ptr++;
-    }
-    return(str);
-}
-
-i_uptime(bt, tod)
-
-struct timeval* bt;
-time_t *tod;
-
-{
-    time_t uptime;
-    int days, hrs, mins, secs;
-
-    if (bt->tv_sec != -1) {
-	uptime = *tod - bt->tv_sec;
-	uptime += 30;
-	days = uptime / 86400;
-	uptime %= 86400;
-	hrs = uptime / 3600;
-	uptime %= 3600;
-	mins = uptime / 60;
-	secs = uptime % 60;
-
-	/*
-	 *  Display the uptime.
-	 */
-
-	if (smart_terminal)
-	{
-	    Move_to((screen_width - 24) - (days > 9 ? 1 : 0), 0);
-	}
-	else
-	{
-	    fputs(" ", stdout);
-	}
-	printf(" up %d+%02d:%02d:%02d", days, hrs, mins, secs);
-    }
-}
Index: utils.h
===================================================================
--- utils.h	(revision 183246)
+++ utils.h	(working copy)
@@ -1,24 +1,78 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  *  Top users/processes display for Unix
- *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
  */
 
 /* prototypes for functions found in utils.c */
 
-int atoiwi();
-char *itoa();
-char *itoa7();
-int digits();
-char *strecpy();
-char **argparse();
-long percentages();
-char *errmsg();
-char *format_time();
-char *format_k();
-char *format_k2();
+#ifndef _UTILS_H
+#define _UTILS_H
+
+int atoiwi(char *);
+char *itoa(int);
+char *itoa_w(int, int);
+char *itoa7(int);
+int digits(int);
+char *printable(char *);
+char *strcpyend(char *, char *);
+char *homogenize(char *);
+int string_index(char *, char **);
+char **argparse(char *, int *);
+long percentages(int, int *, long *, long *, long *);
+char *errmsg(int);
+char *format_percent(double);
+char *format_time(long);
+char *format_k(long);
+char *string_list(char **);
+void time_get(struct timeval *);
+void time_mark(struct timeval *);
+unsigned int time_elapsed();
+unsigned int diff_per_second(unsigned int, unsigned int);
+void debug_set(int);
+#ifdef DEBUG
+#define dprintf xdprintf
+void xdprintf(char *fmt, ...);
+#else
+#ifdef HAVE_C99_VARIADIC_MACROS
+#define dprintf(...) 
+#else
+#ifdef HAVE_GNU_VARIADIC_MACROS
+#define dprintf(x...) 
+#else
+#define dprintf if (0)
+#endif
+#endif
+#endif
+
+#endif
Index: top.h
===================================================================
--- top.h	(revision 183246)
+++ top.h	(working copy)
@@ -1,29 +1,56 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  * $FreeBSD$
  */
+
 /*
  *  Top - a top users display for Berkeley Unix
  *
  *  General (global) definitions
  */
 
-/* Current major version number */
-#define VERSION		3
+#ifndef _TOP_H_
+#define _TOP_H_
 
-/* Number of lines of header information on the standard screen */
-extern int Header_lines;	/* 7 */
+#include <sys/time.h>
 
 /* Maximum number of columns allowed for display */
-#define MAX_COLS	128
+#define MAX_COLS	255
 
 /* Log base 2 of 1024 is 10 (2^10 == 1024) */
 #define LOG1024		10
 
-char *itoa();
-char *itoa7();
-
-char *version_string();
-
 /* Special atoi routine returns either a non-negative number or one of: */
 #define Infinity	-1
 #define Invalid		-2
@@ -37,13 +64,25 @@
 
 #define NUM_AVERAGES    3
 
-enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX };
+struct ext_decl {
+    int (*f_minibar)(char *, int);
+    int (*f_display)(char *, int);
+};
 
 /*
- * Format modifiers
+ *  "Table_size" defines the size of the hash tables used to map uid to
+ *  username.  Things will work best if the number is a prime number.
+ *  We use a number that should be suitable for most installations.
  */
-#define FMT_SHOWARGS 0x00000001
+#ifndef Table_size
+#define Table_size	8191
+#endif
 
-extern enum displaymodes displaymode;
+void gettime(struct timeval *);
+void quit(int);
 
 extern int pcpu_stats;
+
+enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX };
+
+#endif /* _TOP_H_ */
Index: sigconv.awk
===================================================================
--- sigconv.awk	(revision 183246)
+++ sigconv.awk	(working copy)
@@ -1,5 +1,40 @@
+# Copyright (c) 1984 through 2008, William LeFebvre
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 
+#    * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 
+#    * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# 
+#    * Neither the name of William LeFebvre nor the names of other
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 # $FreeBSD$
 
+#
+# Awk script converts an include file with definitions for signal names
+# in to a predefined array that associates the signal numbers to the names.
+#
+
 BEGIN		{
 		    nsig = 0;
 		    j = 0;
@@ -15,41 +50,44 @@
 /^#define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $3);
+				    if (siglist[j] != "") next;
 				    str = $2;
 
 				    if (nsig < j) 
 					nsig = j;
 
-				    siglist[j] = sprintf("{ \"%s\",\t%2d },", \
+				    siglist[j] = sprintf("\"%s\",\t%2d", \
 						substr(str, 4), j);
 				}
 /^#[ \t]*define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $4);
+				    if (siglist[j] != "") next;
 				    str = $3;
 
 				    if (nsig < j)
 					nsig = j;
 
-				    siglist[j] = sprintf("{ \"%s\",\t%2d },", \
+				    siglist[j] = sprintf("\"%s\",\t%2d,", \
 						substr(str, 4), j);
 				}
 /^#[ \t]*define[ \t][ \t]*_SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $4);
+				    if (siglist[j] != "") next;
 				    str = $3;
 
 				    if (nsig < j)
 					nsig = j;
 
-				    siglist[j] = sprintf("{ \"%s\",\t%2d },", \
+				    siglist[j] = sprintf("\"%s\",\t%2d", \
 					    substr(str, 5), j);
 				}
 
 END				{
 				    for (n = 1; n <= nsig; n++) 
 					if (siglist[n] != "")
-					    printf("    %s\n", siglist[n]);
+					    printf("    { %s },\n", siglist[n]);
 
 				    printf("    { NULL,\t 0 }\n};\n");
 				}
Index: machine.h
===================================================================
--- machine.h	(revision 183246)
+++ machine.h	(working copy)
@@ -1,4 +1,36 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  * $FreeBSD$
  */
 
@@ -8,19 +40,34 @@
  *  for any specific machine.
  */
 
+#ifndef _MACHINE_H_
+#define _MACHINE_H_
+
+#include "top.h"
+
 /*
- * the statics struct is filled in by machine_init
+ * The statics struct is filled in by machine_init.  Fields marked as
+ * "optional" are not filled in by every module.
  */
 struct statics
 {
     char **procstate_names;
     char **cpustate_names;
     char **memory_names;
-    char **swap_names;
-#ifdef ORDER
-    char **order_names;
-#endif
+    char **swap_names;		/* optional */
+    char **order_names;		/* optional */
+    char **top_color_names;	/* optional */
+    char **kernel_names;	/* optional */
+    time_t boottime;		/* optional */
+    int modemax;		/* optional */
     int ncpus;
+    int maxcpus;
+    struct {
+	unsigned int fullcmds : 1;
+	unsigned int idle : 1;
+	unsigned int warmup : 1;
+	unsigned int threads : 1;
+    } flags;
 };
 
 /*
@@ -41,8 +88,9 @@
     int    P_ACTIVE;     /* number of procs considered "active" */
     int    *procstates;
     int    *cpustates;
-    int    *memory;
-    int    *swap;
+    int    *kernel;
+    long   *memory;
+    long   *swap;
     struct timeval boottime;
     int    ncpus;
 };
@@ -61,17 +109,25 @@
     int idle;		/* show idle processes */
     int self;		/* show self */
     int system;		/* show system processes */
-    int thread;		/* show threads */
+    int fullcmd;	/* show full command */
+    int usernames;      /* show usernames */
     int uid;		/* only this uid (unless uid == -1) */
+    char *command;	/* only this command (unless == NULL) */
+    int mode;		/* select display mode (0 is default) */
+    int threads;	/* show threads separately */
     int wcpu;		/* show weighted cpu */
     int jail;		/* show jail ID */
-    char *command;	/* only this command (unless == NULL) */
 };
 
 /* routines defined by the machine dependent module */
+int machine_init(struct statics *);
+void get_system_info(struct system_info *);
+caddr_t get_process_info(struct system_info *, struct process_select *, int);
+char *format_header(char *);
+char *format_next_process(struct process_select *sel, caddr_t, char *(*)(int));
+int proc_owner(int);
+#ifdef HAVE_FORMAT_PROCESS_HEADER
 
-char *format_header();
-char *format_next_process();
-
-/* non-int routines typically used by the machine dependent module */
-char *printable();
+#endif /* _MACHINE_H_ */
+char *format_process_header(struct process_select *sel, caddr_t handle, int count);
+#endif
Index: display.h
===================================================================
--- display.h	(revision 183246)
+++ display.h	(working copy)
@@ -1,7 +1,84 @@
-/* constants needed for display.c */
+/*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
 
-/* "type" argument for new_message function */
+/* interface declaration for display.c */
 
-#define  MT_standout  1
-#define  MT_delayed   2
+#ifndef _DISPLAY_H
+#define _DISPLAY_H
 
+#include "globalstate.h"
+
+void display_clear();
+int display_resize();
+int display_lines();
+int display_columns();
+int display_init(struct statics *statics);
+void i_loadave(int mpid, double *avenrun);
+void u_loadave(int mpid, double *avenrun);
+void i_minibar(int (*formatter)(char *, int));
+void u_minibar(int (*formatter)(char *, int));
+void i_uptime(time_t *bt, time_t *tod);
+void u_uptime(time_t *bt, time_t *tod);
+void i_timeofday(time_t *tod);
+void i_procstates(int total, int *brkdn, int threads);
+void u_procstates(int total, int *brkdn, int threads);
+void i_cpustates(int *states);
+void u_cpustates(int *states);
+void z_cpustates();
+void i_kernel(int *stats);
+void u_kernel(int *stats);
+void i_memory(long *stats);
+void u_memory(long *stats);
+void i_swap(long *stats);
+void u_swap(long *stats);
+void i_message(struct timeval *now);
+void u_message(struct timeval *now);
+void i_header(char *text);
+void u_header(char *text);
+void i_process(int line, char *thisline);
+void u_process(int, char *);
+void i_endscreen();
+void u_endscreen();
+void display_header(int t);
+void new_message(char *msgfmt, ...);
+void message_error(char *msgfmt, ...);
+void message_mark();
+void message_clear();
+void message_expire();
+void message_prompt(char *msgfmt, ...);
+void message_prompt_plain(char *msgfmt, ...);
+int readline(char *buffer, int size, int numeric);
+void display_pagerstart();
+void display_pagerend();
+void display_pager(char *fmt, ...);
+
+#endif
Index: screen.c
===================================================================
--- screen.c	(revision 183246)
+++ screen.c	(working copy)
@@ -1,14 +1,42 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
- *
- * $FreeBSD$
  */
 
 /*  This file contains the routines that interface to termcap and stty/gtty.
@@ -23,20 +51,52 @@
 #include "os.h"
 #include "top.h"
 
+#if HAVE_CURSES_H && HAVE_TERM_H
+#include <curses.h>
+#include <term.h>
+#else
+#if HAVE_TERMCAP_H
+#include <termcap.h>
+#else
+#if HAVE_CURSES_H
+#include <curses.h>
+#endif
+#endif
+#endif
+
+#if !HAVE_DECL_TPUTS
+int tputs(const char *, int, int (*)(int));
+#endif
+#if !HAVE_DECL_TGOTO
+char *tgoto(const char *, int, int);
+#endif
+#if !HAVE_DECL_TGETENT
+int tgetent(const char *, char *);
+#endif
+#if !HAVE_DECL_TGETFLAG
+int tgetflag(const char *);
+#endif
+#if !HAVE_DECL_TGETNUM
+int tgetnum(const char *);
+#endif
+#if !HAVE_DECL_TGETSTR
+char *tgetstr(const char *, char **);
+#endif
+
 #include <sys/ioctl.h>
 #ifdef CBREAK
 # include <sgtty.h>
-# define SGTTY
+# define USE_SGTTY
 #else
 # ifdef TCGETA
-#  define TERMIO
+#  define USE_TERMIO
 #  include <termio.h>
 # else
-#  define TERMIOS
+#  define USE_TERMIOS
 #  include <termios.h>
 # endif
 #endif
-#if defined(TERMIO) || defined(TERMIOS)
+#if defined(USE_TERMIO) || defined(USE_TERMIOS)
 # ifndef TAB3
 #  ifdef OXTABS
 #   define TAB3 OXTABS
@@ -45,44 +105,46 @@
 #  endif
 # endif
 #endif
+
 #include "screen.h"
 #include "boolean.h"
 
+#define putcap(str)     (void)((str) != NULL ? tputs(str, 1, putstdout) : 0)
+
 extern char *myname;
 
-int putstdout();
-
-int  overstrike;
-int  screen_length;
-int  screen_width;
 char ch_erase;
 char ch_kill;
+char ch_werase;
 char smart_terminal;
+int  screen_length;
+int  screen_width;
+
 char PC;
-char *tgetstr();
-char *tgoto();
-char termcap_buf[1024];
-char string_buffer[1024];
-char home[15];
-char lower_left[15];
-char *clear_line;
-char *clear_screen;
-char *clear_to_end;
-char *cursor_motion;
-char *start_standout;
-char *end_standout;
-char *terminal_init;
-char *terminal_end;
 
-#ifdef SGTTY
+static int  tc_overstrike;
+static char termcap_buf[1024];
+static char string_buffer[1024];
+static char home[15];
+static char lower_left[15];
+static char *tc_clear_line;
+static char *tc_clear_screen;
+static char *tc_clear_to_end;
+static char *tc_cursor_motion;
+static char *tc_start_standout;
+static char *tc_end_standout;
+static char *terminal_init;
+static char *terminal_end;
+
+#ifdef USE_SGTTY
 static struct sgttyb old_settings;
 static struct sgttyb new_settings;
 #endif
-#ifdef TERMIO
+#ifdef USE_TERMIO
 static struct termio old_settings;
 static struct termio new_settings;
 #endif
-#ifdef TERMIOS
+#ifdef USE_TERMIOS
 static struct termios old_settings;
 static struct termios new_settings;
 #endif
@@ -96,11 +158,65 @@
 #define	STDOUT	1
 #define	STDERR	2
 
-init_termcap(interactive)
+/* This has to be defined as a subroutine for tputs (instead of a macro) */
 
-int interactive;
+static int
+putstdout(TPUTS_PUTC_ARGTYPE ch)
 
 {
+    return putchar((int)ch);
+}
+
+void
+screen_getsize()
+
+{
+
+#ifdef TIOCGWINSZ
+
+    struct winsize ws;
+
+    if (ioctl (1, TIOCGWINSZ, &ws) != -1)
+    {
+	if (ws.ws_row != 0)
+	{
+	    screen_length = ws.ws_row;
+	}
+	if (ws.ws_col != 0)
+	{
+	    screen_width = ws.ws_col - 1;
+	}
+    }
+
+#else
+#ifdef TIOCGSIZE
+
+    struct ttysize ts;
+
+    if (ioctl (1, TIOCGSIZE, &ts) != -1)
+    {
+	if (ts.ts_lines != 0)
+	{
+	    screen_length = ts.ts_lines;
+	}
+	if (ts.ts_cols != 0)
+	{
+	    screen_width = ts.ts_cols - 1;
+	}
+    }
+
+#endif /* TIOCGSIZE */
+#endif /* TIOCGWINSZ */
+
+    (void) strncpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1),
+	sizeof(lower_left) - 1);
+    lower_left[sizeof(lower_left) - 1] = '\0';
+}
+
+int
+screen_readtermcap(int interactive)
+
+{
     char *bufptr;
     char *PCptr;
     char *term_name;
@@ -111,11 +227,11 @@
     screen_width = MAX_COLS;
     screen_length = 0;
 
-    if (!interactive)
+    if (interactive == No)
     {
 	/* pretend we have a dumb terminal */
 	smart_terminal = No;
-	return;
+	return No;
     }
 
     /* assume we have a smart terminal until proven otherwise */
@@ -129,7 +245,7 @@
     if (term_name == NULL)
     {
 	smart_terminal = No;
-	return;
+	return No;
     }
 
     /* now get the termcap entry */
@@ -147,21 +263,21 @@
 
 	/* pretend it's dumb and proceed */
 	smart_terminal = No;
-	return;
+	return No;
     }
 
     /* "hardcopy" immediately indicates a very stupid terminal */
     if (tgetflag("hc"))
     {
 	smart_terminal = No;
-	return;
+	return No;
     }
 
     /* set up common terminal capabilities */
     if ((screen_length = tgetnum("li")) <= 0)
     {
 	screen_length = smart_terminal = 0;
-	return;
+	return No;
     }
 
     /* screen_width is a little different */
@@ -175,71 +291,74 @@
     }
 
     /* terminals that overstrike need special attention */
-    overstrike = tgetflag("os");
+    tc_overstrike = tgetflag("os");
 
     /* initialize the pointer into the termcap string buffer */
     bufptr = string_buffer;
 
     /* get "ce", clear to end */
-    if (!overstrike)
+    if (!tc_overstrike)
     {
-	clear_line = tgetstr("ce", &bufptr);
+	tc_clear_line = tgetstr("ce", &bufptr);
     }
 
     /* get necessary capabilities */
-    if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
-	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
+    if ((tc_clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
+	(tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL)
     {
 	smart_terminal = No;
-	return;
+	return No;
     }
 
     /* get some more sophisticated stuff -- these are optional */
-    clear_to_end   = tgetstr("cd", &bufptr);
+    tc_clear_to_end   = tgetstr("cd", &bufptr);
     terminal_init  = tgetstr("ti", &bufptr);
     terminal_end   = tgetstr("te", &bufptr);
-    start_standout = tgetstr("so", &bufptr);
-    end_standout   = tgetstr("se", &bufptr);
+    tc_start_standout = tgetstr("so", &bufptr);
+    tc_end_standout   = tgetstr("se", &bufptr);
 
     /* pad character */
     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
 
     /* set convenience strings */
-    (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
+    (void) strncpy(home, tgoto(tc_cursor_motion, 0, 0), sizeof(home) - 1);
     home[sizeof(home) - 1] = '\0';
-    /* (lower_left is set in get_screensize) */
+    /* (lower_left is set in screen_getsize) */
 
     /* get the actual screen size with an ioctl, if needed */
     /* This may change screen_width and screen_length, and it always
        sets lower_left. */
-    get_screensize();
+    screen_getsize();
 
     /* if stdout is not a terminal, pretend we are a dumb terminal */
-#ifdef SGTTY
+#ifdef USE_SGTTY
     if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
     {
 	smart_terminal = No;
     }
 #endif
-#ifdef TERMIO
+#ifdef USE_TERMIO
     if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
     {
 	smart_terminal = No;
     }
 #endif
-#ifdef TERMIOS
+#ifdef USE_TERMIOS
     if (tcgetattr(STDOUT, &old_settings) == -1)
     {
 	smart_terminal = No;
     }
 #endif
+
+    return smart_terminal;
 }
 
-init_screen()
+void
+screen_init()
 
 {
     /* get the old settings for safe keeping */
-#ifdef SGTTY
+#ifdef USE_SGTTY
     if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
     {
 	/* copy the settings so we can modify them */
@@ -253,6 +372,7 @@
 	/* remember the erase and kill characters */
 	ch_erase = old_settings.sg_erase;
 	ch_kill  = old_settings.sg_kill;
+	ch_werase  = old_settings.sg_werase;
 
 #ifdef TOStop
 	/* get the local mode word */
@@ -269,7 +389,7 @@
 	putcap(terminal_init);
     }
 #endif
-#ifdef TERMIO
+#ifdef USE_TERMIO
     if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
     {
 	/* copy the settings so we can modify them */
@@ -283,8 +403,9 @@
 	(void) ioctl(STDOUT, TCSETA, &new_settings);
 
 	/* remember the erase and kill characters */
-	ch_erase = old_settings.c_cc[VERASE];
-	ch_kill  = old_settings.c_cc[VKILL];
+	ch_erase  = old_settings.c_cc[VERASE];
+	ch_kill   = old_settings.c_cc[VKILL];
+	ch_werase = old_settings.c_cc[VWERASE];
 
 	/* remember that it really is a terminal */
 	is_a_terminal = Yes;
@@ -293,7 +414,7 @@
 	putcap(terminal_init);
     }
 #endif
-#ifdef TERMIOS
+#ifdef USE_TERMIOS
     if (tcgetattr(STDOUT, &old_settings) != -1)
     {
 	/* copy the settings so we can modify them */
@@ -307,8 +428,9 @@
 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
 
 	/* remember the erase and kill characters */
-	ch_erase = old_settings.c_cc[VERASE];
-	ch_kill  = old_settings.c_cc[VKILL];
+	ch_erase  = old_settings.c_cc[VERASE];
+	ch_kill   = old_settings.c_cc[VKILL];
+	ch_werase = old_settings.c_cc[VWERASE];
 
 	/* remember that it really is a terminal */
 	is_a_terminal = Yes;
@@ -325,14 +447,15 @@
     }
 }
 
-end_screen()
+void
+screen_end()
 
 {
     /* move to the lower left, clear the line and send "te" */
     if (smart_terminal)
     {
 	putcap(lower_left);
-	putcap(clear_line);
+	putcap(tc_clear_line);
 	fflush(stdout);
 	putcap(terminal_end);
     }
@@ -340,37 +463,38 @@
     /* if we have settings to reset, then do so */
     if (is_a_terminal)
     {
-#ifdef SGTTY
+#ifdef USE_SGTTY
 	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
 #ifdef TOStop
 	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
 #endif
 #endif
-#ifdef TERMIO
+#ifdef USE_TERMIO
 	(void) ioctl(STDOUT, TCSETA, &old_settings);
 #endif
-#ifdef TERMIOS
+#ifdef USE_TERMIOS
 	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
 #endif
     }
 }
 
-reinit_screen()
+void
+screen_reinit()
 
 {
     /* install our settings if it is a terminal */
     if (is_a_terminal)
     {
-#ifdef SGTTY
+#ifdef USE_SGTTY
 	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
 #ifdef TOStop
 	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
 #endif
 #endif
-#ifdef TERMIO
+#ifdef USE_TERMIO
 	(void) ioctl(STDOUT, TCSETA, &new_settings);
 #endif
-#ifdef TERMIOS
+#ifdef USE_TERMIOS
 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
 #endif
     }
@@ -382,102 +506,87 @@
     }
 }
 
-get_screensize()
+void
+screen_move(int x, int y)
 
 {
+    tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout);
+}
 
-#ifdef TIOCGWINSZ
+void
+screen_standout(char *msg)
 
-    struct winsize ws;
-
-    if (ioctl (1, TIOCGWINSZ, &ws) != -1)
+{
+    if (smart_terminal)
     {
-	if (ws.ws_row != 0)
-	{
-	    screen_length = ws.ws_row;
-	}
-	if (ws.ws_col != 0)
-	{
-	    screen_width = ws.ws_col - 1;
-	}
+	putcap(tc_start_standout);
+	fputs(msg, stdout);
+	putcap(tc_end_standout);
     }
-
-#else
-#ifdef TIOCGSIZE
-
-    struct ttysize ts;
-
-    if (ioctl (1, TIOCGSIZE, &ts) != -1)
+    else
     {
-	if (ts.ts_lines != 0)
-	{
-	    screen_length = ts.ts_lines;
-	}
-	if (ts.ts_cols != 0)
-	{
-	    screen_width = ts.ts_cols - 1;
-	}
+	fputs(msg, stdout);
     }
-
-#endif /* TIOCGSIZE */
-#endif /* TIOCGWINSZ */
-
-    (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
-	sizeof(lower_left) - 1);
-    lower_left[sizeof(lower_left) - 1] = '\0';
 }
 
-standout(msg)
+void
+screen_clear()
 
-char *msg;
-
 {
     if (smart_terminal)
     {
-	putcap(start_standout);
-	fputs(msg, stdout);
-	putcap(end_standout);
+	putcap(tc_clear_screen);
     }
-    else
-    {
-	fputs(msg, stdout);
-    }
 }
 
-clear()
+int
+screen_cte()
 
 {
     if (smart_terminal)
     {
-	putcap(clear_screen);
+	if (tc_clear_to_end)
+	{
+	    putcap(tc_clear_to_end);
+	    return(Yes);
+	}
     }
+    return(No);
 }
 
-clear_eol(len)
+void
+screen_cleareol(int len)
 
-int len;
+{
+    int i;
 
-{
-    if (smart_terminal && !overstrike && len > 0)
+    if (smart_terminal && !tc_overstrike && len > 0)
     {
-	if (clear_line)
+	if (tc_clear_line)
 	{
-	    putcap(clear_line);
-	    return(0);
+	    putcap(tc_clear_line);
+	    return;
 	}
 	else
 	{
-	    while (len-- > 0)
+	    i = 0;
+	    while (i++ < 0)
 	    {
 		putchar(' ');
 	    }
-	    return(1);
+	    i = 0;
+	    while (i++ < 0)
+	    {
+		putchar('\b');
+	    }
+	    return;
 	}
     }
-    return(-1);
+    return;
 }
 
-go_home()
+void
+screen_home()
 
 {
     if (smart_terminal)
@@ -486,13 +595,4 @@
     }
 }
 
-/* This has to be defined as a subroutine for tputs (instead of a macro) */
 
-putstdout(ch)
-
-char ch;
-
-{
-    putchar(ch);
-}
-
Index: username.c
===================================================================
--- username.c	(revision 183246)
+++ username.c	(working copy)
@@ -1,189 +1,156 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
- *
- * $FreeBSD$
  */
 
 /*
  *  Username translation code for top.
  *
- *  These routines handle uid to username mapping.
- *  They use a hashing table scheme to reduce reading overhead.
- *  For the time being, these are very straightforward hashing routines.
- *  Maybe someday I'll put in something better.  But with the advent of
- *  "random access" password files, it might not be worth the effort.
+ *  These routines handle uid to username mapping.  They use a hash table to
+ *  reduce reading overhead.  Entries are refreshed every EXPIRETIME seconds.
  *
- *  Changes to these have been provided by John Gilmore (gnu at toad.com).
- *
- *  The hash has been simplified in this release, to avoid the
- *  table overflow problems of previous releases.  If the value
- *  at the initial hash location is not right, it is replaced
- *  by the right value.  Collisions will cause us to call getpw*
- *  but hey, this is a cache, not the Library of Congress.
- *  This makes the table size independent of the passwd file size.
+ *  The old ad-hoc hash functions have been replaced with something a little
+ *  more formal and (hopefully) more robust (found in hash.c)
  */
 
+#include "os.h"
+
 #include <sys/types.h>
-#include <stdio.h>
 #include <pwd.h>
 #include <utmp.h>
 
-#include "top.local.h"
+#include "top.h"
 #include "utils.h"
+#include "hash.h"
 
-struct hash_el {
-    int  uid;
-    char name[UT_NAMESIZE + 1];
-};
+#define EXPIRETIME (60 * 5)
 
-#define    is_empty_hash(x)	(hash_table[x].name[0] == 0)
+/* we need some sort of idea how long usernames can be */
+#ifndef MAXLOGNAME
+#ifdef _POSIX_LOGIN_NAME_MAX 
+#define MAXLOGNAME _POSIX_LOGIN_NAME_MAX 
+#else
+#define MAXLOGNAME 9
+#endif
+#endif
 
-/* simple minded hashing function */
-/* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for
-   the hash_table.  Applied abs() function to fix. 2/16/96 tpugh
-*/
-#define    hashit(i)	(abs(i) % Table_size)
+struct hash_data {
+    int    uid;
+    char   name[UT_NAMESIZE + 1];  /* big enough? */
+    time_t expire;
+};
 
-/* K&R requires that statically declared tables be initialized to zero. */
-/* We depend on that for hash_table and YOUR compiler had BETTER do it! */
-struct hash_el hash_table[Table_size];
+hash_table *userhash;
 
-init_hash()
 
+void
+init_username()
+
 {
-    /*
-     *  There used to be some steps we had to take to initialize things.
-     *  We don't need to do that anymore, but we will leave this stub in
-     *  just in case future changes require initialization steps.
-     */
+    userhash = hash_create(211);
 }
 
-char *username(uid)
+char *
+username(int uid)
 
-register int uid;
-
 {
-    register int hashindex;
+    struct hash_data *data;
+    struct passwd *pw;
+    time_t now;
 
-    hashindex = hashit(uid);
-    if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid))
-    {
-	/* not here or not right -- get it out of passwd */
-	hashindex = get_user(uid);
-    }
-    return(hash_table[hashindex].name);
-}
+    /* what time is it? */
+    now = time(NULL);
 
-int userid(username)
+    /* get whatever is in the cache */
+    data = hash_lookup_uint(userhash, (unsigned int)uid);
 
-char *username;
+    /* if we had a cache miss, then create space for a new entry */
+    if (data == NULL)
+    {
+	/* make space */
+	data = (struct hash_data *)malloc(sizeof(struct hash_data));
 
-{
-    struct passwd *pwd;
+	/* fill in some data, including an already expired time */
+	data->uid = uid;
+	data->expire = (time_t)0;
 
-    /* Eventually we want this to enter everything in the hash table,
-       but for now we just do it simply and remember just the result.
-     */
-
-    if ((pwd = getpwnam(username)) == NULL)
-    {
-	return(-1);
+	/* add it to the hash: the rest gets filled in later */
+	hash_add_uint(userhash, uid, data);
     }
 
-    /* enter the result in the hash table */
-    enter_user(pwd->pw_uid, username, 1);
-
-    /* return our result */
-    return(pwd->pw_uid);
-}
-
-int enter_user(uid, name, wecare)
-
-register int  uid;
-register char *name;
-int wecare;		/* 1 = enter it always, 0 = nice to have */
-
-{
-    register int hashindex;
-
-#ifdef DEBUG
-    fprintf(stderr, "enter_hash(%d, %s, %d)\n", uid, name, wecare);
-#endif
-
-    hashindex = hashit(uid);
-
-    if (!is_empty_hash(hashindex))
+    /* Now data points to the correct hash entry for "uid".  If this is
+       a new entry, then expire is 0 and the next test will be true. */
+    if (data->expire <= now)
     {
-	if (!wecare)
-	    return 0;		/* Don't clobber a slot for trash */
-	if (hash_table[hashindex].uid == uid)
-	    return(hashindex);	/* Fortuitous find */
+	if ((pw = getpwuid(uid)) != NULL)
+	{
+	    strncpy(data->name, pw->pw_name, UT_NAMESIZE);
+	    data->expire = now + EXPIRETIME;
+	    dprintf("username: updating %d with %s, expires %d\n",
+		    data->uid, data->name, data->expire);
+	}
+	else
+	{
+	    /* username doesnt exist ... so invent one */
+	    snprintf(data->name, sizeof(data->name), "%d", uid);
+	    data->expire = now + EXPIRETIME;
+	    dprintf("username: updating %d with %s, expires %d\n",
+		    data->uid, data->name, data->expire);
+	}	    
     }
 
-    /* empty or wrong slot -- fill it with new value */
-    hash_table[hashindex].uid = uid;
-    (void) strncpy(hash_table[hashindex].name, name, UT_NAMESIZE);
-    return(hashindex);
+    /* return what we have */
+    return data->name;
 }
 
-/*
- * Get a userid->name mapping from the system.
- * If the passwd database is hashed (#define RANDOM_PW), we
- * just handle this uid.  Otherwise we scan the passwd file
- * and cache any entries we pass over while looking.
- */
+int
+userid(char *username)
 
-int get_user(uid)
-
-register int uid;
-
 {
     struct passwd *pwd;
 
-#ifdef RANDOM_PW
-    /* no performance penalty for using getpwuid makes it easy */
-    if ((pwd = getpwuid(uid)) != NULL)
+    if ((pwd = getpwnam(username)) == NULL)
     {
-	return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
+	return(-1);
     }
-#else
 
-    int from_start = 0;
-
-    /*
-     *  If we just called getpwuid each time, things would be very slow
-     *  since that just iterates through the passwd file each time.  So,
-     *  we walk through the file instead (using getpwent) and cache each
-     *  entry as we go.  Once the right record is found, we cache it and
-     *  return immediately.  The next time we come in, getpwent will get
-     *  the next record.  In theory, we never have to read the passwd file
-     *  a second time (because we cache everything we read).  But in
-     *  practice, the cache may not be large enough, so if we don't find
-     *  it the first time we have to scan the file a second time.  This
-     *  is not very efficient, but it will do for now.
-     */
-
-    while (from_start++ < 2)
-    {
-	while ((pwd = getpwent()) != NULL)
-	{
-	    if (pwd->pw_uid == uid)
-	    {
-		return(enter_user(pwd->pw_uid, pwd->pw_name, 1));
-	    }
-	    (void) enter_user(pwd->pw_uid, pwd->pw_name, 0);
-	}
-	/* try again */
-	setpwent();
-    }
-#endif
-    /* if we can't find the name at all, then use the uid as the name */
-    return(enter_user(uid, itoa7(uid), 1));
+    /* return our result */
+    return(pwd->pw_uid);
 }
+
Index: loadavg.h
===================================================================
--- loadavg.h	(revision 183246)
+++ loadavg.h	(working copy)
@@ -1,4 +1,36 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  *  Top - a top users display for Berkeley Unix
  *
  *  Defines required to access load average figures.
Index: screen.h
===================================================================
--- screen.h	(revision 183246)
+++ screen.h	(working copy)
@@ -1,31 +1,65 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  *  top - a top users display for Unix 4.2
  *
  *  This file contains all the definitions necessary to use the hand-written
  *  screen package in "screen.c"
  */
 
-#define TCputs(str)	tputs(str, 1, putstdout)
-#define putcap(str)	(void)((str) != NULL ? TCputs(str) : 0)
-#define Move_to(x, y)	TCputs(tgoto(cursor_motion, x, y))
+#ifndef _SCREEN_H_
+#define _SCREEN_H_
 
-/* declare return values for termcap functions */
-char *tgetstr();
-char *tgoto();
-
 extern char ch_erase;		/* set to the user's erase character */
-extern char ch_kill;		/* set to the user's kill  character */
+extern char ch_kill;		/* set to the user's kill character */
+extern char ch_werase;		/* set to the user's werase character */
 extern char smart_terminal;     /* set if the terminal has sufficient termcap
 				   capabilities for normal operation */
 
-/* These are some termcap strings for use outside of "screen.c" */
-extern char *cursor_motion;
-extern char *clear_line;
-extern char *clear_to_end;
-
 /* rows and columns on the screen according to termcap */
 extern int  screen_length;
 extern int  screen_width;
 
-/* a function that puts a single character on stdout */
-int putstdout();
+void screen_getsize();
+int screen_readtermcap(int interactive);
+void screen_init();
+void screen_end();
+void screen_reinit();
+void screen_move(int x, int y);
+void screen_standout(char *msg);
+void screen_clear();
+int screen_cte();
+void screen_cleareol(int len);
+void screen_home();
+
+#endif /* _SCREEN_H_ */
Index: Y2K
===================================================================
--- Y2K	(revision 183246)
+++ Y2K	(working copy)
@@ -22,5 +22,5 @@
 
 
 THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE.
-Please see the contents of the file "DISCLAIMER" for further
+Please see the contents of the file "LICENSE" for further
 information.
Index: INSTALL
===================================================================
--- INSTALL	(revision 183246)
+++ INSTALL	(working copy)
@@ -1,166 +1,54 @@
                              TOP
-                         Version 3.5
+                       Version 3.8beta1
 
                        William LeFebvre
 		      and a cast of many
 
 INSTALLATION
 
-Configuration and installation of top is very straightforward.  After
-unpacking the sources, run the script "Configure".  It will present you
-with a series of questions, all of which should be explained in the
-presentation.  After you have answered all the questions, "Configure" will
-perform all the necessary configuration.  Once this is finished, type
-"make install".  Make will compile the sources then install the resulting
-executable and manual page in the appropriate places.
+Configuration and installation of top is easy.  Top version 3.6
+comes with a configure script generated by gnu autoconf.  After
+unpacking the tar file, simply run "./configure".  The script will
+automatically perform a series of checks on the system and determine
+what settings are appropriate for the Makefile and certain include
+files.  Once configure completes, simply type "make install" and
+top will be compiled and installed.  By default, the installation
+location is /usr/local/bin.  You can change the destination location
+with the --prefix option to configure.
 
-The most difficult step in the configuration is the choice of an
-appropriate machine-specific module.  The Configure script gives you a
-list of choices complete with brief descriptions of when each choice is
-appropriate.  Each module is contained in a separate c file in the
-directory "machine".  The module contains all of the machine-specific code
-that makes top work correctly on the architecture in question.  All of the
-code in the top-level directory is machine-independent (or at least
-strives to be).  Hints for some module choices that are not obvious are
-given at the end of this file.
+In addition to the standard options, top's configure script supports
+the following:
 
-The first comment in each c file in that directory contains the synopsis
-AND a detailed description of the machines for which that module is
-appropriate.  It also contains a list of authors for that module.  If you
-are really stumped in this choice, use grep to find your machine
-manufacturer's name or operating system name in machine/*.c.  If you still
-can't find one that is appropriate, then chances are very good that one
-hasn't been written yet.  If that is the case, then you are out of luck.
+    --with-module=name
 
-HANDLING MULTIPLE ARCHITECTURES
+	Force the use of a particular module.  Modules are located
+	in the subdirectory "machine".  A module's name is derived
+	from the file's basename without the leading "m_".
 
-If you need to recompile top for a different architecture (that is, using
-a different module) you need to reconfigure top.  A short cut is available
-to make this a little easier.  If all of your previous answers to the
-configuration questions (except for the module name of course) are
-adequate for the new architecture, then you can just use the command
-"Configure <modulename>".  The configuration script will reconfigure top
-using the new module and all the answers you gave last time.  It will
-finish with a "make clean".  Once that completes, type "make install"
-and make will compile the sources and do the installation.
+    --with-ext=name
 
-HANDLING MULTIPLE OS VERSIONS
+	Compile with the extension "name", found in the subdirectory
+	"ext". At the present time, there are no extensions in the
+	standard distribution.
 
-By far the most frequently received bug report for top is something like
-this: "We just upgraded our operating system to version 99.9.9.9 and top
-broke.  What should we do?"  The simple answer is "recompile".
+    --enable-debug
+    --disable-debug
 
-Top is very sensitive to changes in internal kernel data structures
-(especially the proc and user structures).  Some operating systems
-(especially SunOS) are notorious for changing these structure in every
-minor release of the OS.  This means that a top executable made under one
-version of the OS will not always work correctly (if even at all) under
-another version.  This is just one of those tough facts of life.  There is
-really no way around it.
+	Default off. Include debugging output in the compilation,
+	which can be seen with the -D switch.
 
-To make life even worse, some operating systems (SunOS again) will use
-slightly different proc and user structures on different models.  For
-example, "top" built on a SparcStation 2 will not run correctly on a
-SparcStation 10, even if they are both running SunOS 4.1.3.  These
-unfortunate circumstances make maintaining top very difficult, especially
-in an environment that runs several different versions of the same
-operating system.
+    --enable-color
+    --disable-color
 
-But there is hope.  If your operating system has a properly functioning
-"uname" command then you can handle this problem rather gracefully.
-Included in the distribution is a shell file called "metatop".  All this
-shell file does is:
+	Default on.  Include code that allows for the use of color
+	in the output display.  Use --disable-color if you do not
+	want this feature compiled in to the code.  The configure
+	script also recognizes the spelling "colour".
 
-	exec top-`uname -m`-`uname -r` "$@"
+    --enable-kill
+    --disable-kill
 
-So when you run this script, it execs a filename that is unique to your
-specific machine architecture and your OS revision number.
-
-To use "metatop", do the following:
-
-	. on any machine, run Configure and choose the module that is
-	  appropriate for the machine
-	. for all machines which use the same module:
-	    . group machines according to machine architecture AND OS
-	      revision number (i.e.: sun4-4.1.1, sun4c-4.1.1, sun4c-4.1.2,
-	      sun4-4.1.3, sun4c-4.1.3, sun4m-4.1.3, ...)
-	    . for each group, choose one machine from that group and on it
-	      run "make clean; make installmeta".
-
-
-The "installmeta" rule in the makefile will insure that top is compiled,
-install the shell file "metatop" as "top", then install the executable
-"top" with a name appropriate to the machine architecture and OS revision.
-
-
-HINTS FOR CHOOSING THE CORRECT MODULE:
-
-SOLARIS 2.x
-
-All versions of Solaris will now work with the module sunos5.  Version
-specific modules (such as sunos54) no longer exist.
-
-
-SUNOS 4.x AND MULTIPROCESSOR ARCHITECTURES
-
-First, we need to be speaking the same language:
-
-sun4	a regular sparc sun 4 architecture machine (sparc station 1,
-	sparc station 2, IPC, SLC, etc.)
-
-sun4m	a multiprocessor sparc (Sparc 10, 4/670, 4/690)
-
-I intended to write the sunos4 module so that an executable compiled on a
-sun4m machine would work correctly on a sun4 machine.  Unfortunately my
-experiments indicate that this cannot be done.  It turns out that the user
-structure is so different between these two architectures that nothing
-short of a serious hack will make the same executable work correctly on
-both machines.  I recommend that you use the separate module "sunos4mp"
-when making an executable for a sun4m architecture, and use "sunos4" when
-making an executable for sun4 or sun4c architectures.
-
-DIGITAL UNIX V4.0
-
-This is the successor to DECOSF/1.  Use the module decosf1.
-
-SOLBOURNE OPERATING SYSTEM (OS/MP)
-
-If you are running OS/MP version 4.1A, then use the module "osmp4.1a".
-
-If you are running a version of OS/MP OLDER than 4.1A (that is, one
-of its predecessors), use the module "sunos4".
-
-If you are running OS/MP 4.1B or LATER, use the module "sunos4mp".
-
-HP/UX OPERATING SYSTEM
-
-The module hpux8 works on all version 8 systems.  Some say that it works
-with version 9 as well, but one user did send me a separate module for
-version 9.  This module has only been tested on series 800 machines.  I
-would recommend the following for those running version 9: try hpux9 and
-if it doesn't work then try hpux8.  If neither work, then send mail to me
-and/or the modules' authors.  Another note:  we have a model 730 supposedly
-running version 9.01.  The module hpux9 did not compile successfully, but
-the module hpux8 worked fine.  The module hpux10 works on all revisions of
-HP/UX 10 except 10.10, where HP removed the definition of the proc structure
-from the system include files.
-
-NET/2 386BSD SYSTEMS
-
-If your version of the operating system has patchkit 2.4 installed,
-then you will need to modify machine/m_386bsd.c and uncomment the
-definition of PATCHED_KVM.  This patchkit makes what more than a few
-people believe to be a wholly unnecessary patch to the way the kvm
-routines work.
-
-A/UX SYSTEMS
-
-There is a module for A/UX 3.0 and 3.1.  Whether or not it works for
-any other version is not known.  Proceed at your own risk.
-
-Although AUX does not generally have a renice systemcall, it can be
-implemented by tweeking kernel memory.  The flag IMPLEMENT_SETPRIORITY
-controls the inclusion of this code.  It is off be default.  While
-such a simple hack should not be difficult to get right, USE THIS
-FEATURE AT YOUR OWN RISK!
-
+	Default on.  Include code that allows for renicing and sending
+	signals to processes from within top (the 'kill' and 'renice'
+	commands). Use --disable-kill if you do not want this feature
+	compiled in to the code.
Index: os.h
===================================================================
--- os.h	(revision 183246)
+++ os.h	(working copy)
@@ -1,38 +1,143 @@
+/*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
 #include <sys/types.h>
-#include <sys/param.h>	/* This defines BSD */
-#if defined(BSD) && !defined(BSD4_4) && !defined(__osf__)
-# include <stdio.h>
-# include <strings.h>
-# define strchr(a, b)		index((a), (b))
-# define strrchr(a, b)		rindex((a), (b))
-# define memcpy(a, b, c)	bcopy((b), (a), (c))
-# define memzero(a, b)		bzero((a), (b))
-# define memcmp(a, b, c)	bcmp((a), (b), (c))
-#if defined(NeXT)
-  typedef void sigret_t;
+#include <sys/param.h>
+#include <stdio.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
 #else
-  typedef int sigret_t;
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
 #endif
 
-/* system routines that don't return int */
+#if STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#define setbuffer(f, b, s)	setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s))
+#define memzero(a, b)		memset((a), 0, (b))
+#else /* !STDC_HEADERS */
+#ifndef HAVE_STRCHR
+#define strchr(a, b)		index((a), (b))
+#define strrchr(a, b)		rindex((a), (b))
+#endif /* HAVE_STRCHR */
+#ifdef HAVE_MEMCPY
+#define memzero(a, b)		memset((a), 0, (b))
+#else
+#define memcpy(a, b, c)		bcopy((b), (a), (c))
+#define memzero(a, b)		bzero((a), (b))
+#define memcmp(a, b, c)		bcmp((a), (b), (c))
+#endif /* HAVE_MEMCPY */
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#else
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#endif
 char *getenv();
 caddr_t malloc();
+#endif /* STDC_HEADERS */
 
-#else 
-# include <stdio.h>
-# define setbuffer(f, b, s)	setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s))
-# include <string.h>
-# include <memory.h>
-# include <stdlib.h>
-# define memzero(a, b)		memset((a), 0, (b))
-  typedef void sigret_t;
+/* If snprintf or vsnprintf aren't available, we substitute our own.
+   But we have to include stdarg in order to be able to define them.
+*/
+#ifdef HAVE_STDARG_H
+#include <stdarg.h>
+#ifndef HAVE_SNPRINTF
+int ap_snprintf(char *buf, size_t len, const char *format,...);
+#define snprintf ap_snprintf
 #endif
+#ifndef HAVE_VSNPRINTF
+int ap_vsnprintf(char *buf, size_t len, const char *format,va_list ap);
+#define vsnprintf ap_vsnprintf
+#endif
+#endif
 
-/* some systems declare sys_errlist in stdio.h! */
-#if defined(__NetBSD__) || defined(__FreeBSD__)
-#if !defined(__m68k__)
-#  if !defined(__NetBSD132__)
-#define SYS_ERRLIST_DECLARED
-#  endif	/* __NetBSD132__ */
+#if !HAVE_PID_T
+typedef long pid_t;
 #endif
+#if !HAVE_TIME_T
+typedef long time_t;
 #endif
+#if !HAVE_UID_T
+typedef long uid_t;
+#endif
+
+#ifndef INT_MAX
+#define INT_MAX (0x7fffffff)
+#endif
+
+#ifndef UINT_MAX
+#define UINT_MAX (0xffffffffU)
+#endif
+
+/* we must have both sighold and sigrelse to use them */
+#if defined(HAVE_SIGHOLD) && !defined(HAVE_SIGRELSE)
+#undef HAVE_SIGHOLD
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#ifdef HAVE_SYSEXITS_H
+#include <sysexits.h>
+#else
+#define EX_OK		0	/* successful termination */
+#define EX_USAGE	64	/* command line usage error */
+#define EX_DATAERR	65	/* data format error */
+#define EX_NOINPUT	66	/* cannot open input */
+#define EX_NOUSER	67	/* addressee unknown */
+#define EX_NOHOST	68	/* host name unknown */
+#define EX_UNAVAILABLE	69	/* service unavailable */
+#define EX_SOFTWARE	70	/* internal software error */
+#define EX_OSERR	71	/* system error (e.g., can't fork) */
+#define EX_OSFILE	72	/* critical OS file missing */
+#define EX_CANTCREAT	73	/* can't create (user) output file */
+#define EX_IOERR	74	/* input/output error */
+#define EX_TEMPFAIL	75	/* temp failure; user is invited to retry */
+#define EX_PROTOCOL	76	/* remote error in protocol */
+#define EX_NOPERM	77	/* permission denied */
+#define EX_CONFIG	78	/* configuration error */
+#endif
Index: Changes
===================================================================
--- Changes	(revision 183246)
+++ Changes	(working copy)
@@ -1,3 +1,320 @@
+Tue May  6 2008 - wnl (3.8beta1)
+	Main code: fixed bugs in screen_cleareol and in display code. Fixed
+	bug in i_swap when all data is 0.  Added ^W patch (from thaquis).
+	Fixed bug in xdprintf.  Added command line options for the "t" and
+	"m" commands.
+	SunOS 5 changes:  Support for showing individual threads.  Redid
+	allocation of prpsinfo structures.  Added a pidthr hash that uses
+	both pid and thread id for a key. Changed format_process_header
+	and format_next_process to use a table-driven method for generating
+	the columns.  Status files from /proc (psinfo and lpsinfo) are now
+	cached to avoid repeatedly reopening them.  Column showing number of
+	LWPs is now called "NLWP" and column showing lwpid is "LWP".
+	FreeBSD changes: Runtime check to ensure binary is running on
+	the same machine type it was compiled for.  Lots of cleanup and
+	changed nearly everything to use sysctl rather than kvm, and
+	inability to open kvm is no longer fatal. Improved thread reporting:
+	disabled for 7.x and lower.  Added lwpid hash for proper tracking
+	of threads.  Changed format_process_header and format_next_process
+	to use a table-driven method for generating the columns.
+	Dec Alpha: configure uses compile-time options to properly trap
+	and handle exceptions from the Alpha FPU (from Brian Maly).
+
+Tue Feb 26 2008 - wnl (3.7)
+	Prepare for version 3.7 release.
+
+Fri Feb  1 2008 - wnl (3.7beta4)
+	Using the $ notation in printf formats for freebsd apparently was
+	causing problems on 64-bit systems. All such usage has been
+	removed and the process line is formatted piecemeal.
+
+Thu Dec 27 2007 - wnl (3.7beta3)
+	Improved function comments in display.c for message_error functions.
+	Changed some of the error messages in top.c to be more succint.
+
+Fri Dec  7 2007 - wnl (3.7beta3)
+	Changes to freebsd port: moved some functions up front to
+	eliminate forward references. Use sysctl to get all vm stats
+	information, as some of this isn't updated in the struct
+	vmmeter under FreeBSD 7.0.  Added routines to support large-scale
+	sysctl access.
+
+Wed Nov 28 2007 - wnl (3.7beta3)
+	Changes to documentation: FAQ, README, man page.
+
+Tue Nov 27 2007 - wnl (3.7beta3)
+	For freebsd, added page faults, pageins, pageouts, and pages
+	freed to the kernel display line.  These numbers reflect the
+	values presented in vmstat.  For sunos5, added page faults,
+	pageins and pageouts to the kernel display line.
+
+Fri Nov  2 2007 - wnl (3.7beta3)
+	Added copyright notices to the top of every source and include file.
+	Added copyright information to the man page.
+	Removed a few outdated things from the manifest.
+	Minor changes to sigconv.awk. 
+
+Sat Oct 27 2007 - wnl (3.7beta3)
+	Added check for sys_signame at configure time and if it is
+	present then it is used in commands.c to translate signal names
+	in to numbers.
+	Added alternate snprintf and vsnprintf functions from apache (in
+	ap_snprintf.c).  Added configure magic to define and compile them in
+	where needed.  Added check to configure for variadic macros.
+	Preprocessor defintion of dprintf (in utils.h) now depends on
+	support for variadic macros.  Cleaned up m_linux code.
+
+Wed Oct  3 2007 - wnl (3.7beta3)
+	Lots of changes, thanks to Mark Wong.  Most changes were to
+	clean the code up so that it would compile cleanly with -Wall
+	(all warnings).  Changed function names in screen.c so that
+	they all start with "screen_".  Isolated all interaction with
+	termcap to screen.c by adding a real function for cursor
+	addressing (in the past it was just a macro).  Only screen.c
+	now needs to worry about defining templates for the termcap
+	functions.  Added configure and preprocessor magic to ensure
+	that all the termcap functions used in the code are defined
+	with templates.  Changed names of some other functions and
+	global variables to avoid name conflicts with functions in
+	curses and other well established libraries.  Changed dprintf
+	macro to use variadic arguments so that the preprocessor can
+	gobble up the entire call when compiling without debugging
+	(this will have to be made more portable). All include files
+	are surrounded by #ifndef statements to accomodate multiple
+	inclusions.  Platform module is now compiled with
+	-fno-strict-aliasing as some of the modules do type punning
+	that can confuse the optimizer.
+
+Wed Sep 26 2007 - wnl (3.7beta3)
+	For freebsd, priority is no longer normalized by PZERO.  This
+	contradicts the behavior used by ps when it displays priority.
+	But normalizing by PZERO has become a bit of an anachronism
+	and it actually obscures the meaning of the priority without
+	adding any real value.
+
+Wed Sep 19 2007 - wnl (3.7beta3)
+	Many changes to improve the display of threads.  Changed
+	process summary line to use the word "threads" when showing
+	individual threads.  Added the system command to toggle the
+	display of system processes.  Fixed bug in hash.c remove_pos.
+	For freebsd: count threads correctly when they are being
+	displayed, nice column is more closely in line with ps
+	(nothing fancy for real time processes), add two more process
+	states that didn't exist in older releases of freebsd (wait
+	and lock).
+	For linux: Threads done right.  Now track individual threads
+	of multi-threaded processes separately so that we always know
+	their %cpu.  Switch to format_process_header so that we can
+	change the column headings and remove the THR column when
+	displaying individual threads.  Switched process (and thread)
+	tracking over to use generic hash table functions included
+	with the new version of top.  Process states and total now
+	include threads when they are being shown. Added "SHR" column
+	to show the amount of shared memory per process. Improved
+	calculation of elapsed time and percent cpu to avoid
+	overflows. Remove weighted cpu calculations entirely as it is
+	an anachronism.
+	For Solaris: Moved check for libelf to accomodate older systems.
+	
+Sun Sep  9 2007 - wnl (3.7beta2)
+	Documentation changes.  Fixes to sunos5 port.  Added display of
+	thread count and selection by command name to linux port.  Removed
+	the use of inline functions from hash.c as that doesn't appear to
+	be very portable.
+
+Wed Sep  5 2007 - wnl (3.7beta1)
+	Fixed freebsd and linux configuration bugs.  Added configuration
+	options for tweaking program defaults.  Rewrote top level code
+	(top.c) from scratch, including command handling so that adding
+	new commands is much easier.  Changed message-line handling to
+	ensure that the message is displayed for at least 5 seconds
+	regardless of the update frequency.  Added a "miniupdate" that
+	occurs one second after the initial screen on systems that don't
+	already delay the first screen.  The mini-update shows cpu state
+	percentages.  Added ability to select output by command name on
+	some systems.  Fixed color toggling via the "C" command.  Added
+	long options via getopt_long to complement the existing single
+	character options.  Added the freebsd "m" command to chose
+	alternate display modes.  On freebsd this gives a process i/o
+	display.  Added the freebsd "H" command to select the display of
+	individual threads.  Added "-a" option ("all") to set number of
+	displays and number of processes to infinity (equivalent to
+	"-d all all").  Added dual architecture compilation for Solaris
+	to generate both a 32-bit and a 64-bit binary.  This is on by
+	default when compiling on a 64-bit system and can be explicitly
+	set via "configure --enable-dualarch".  Added uniform hashing
+	functions that use bucket hash for uint, pid, and string.  Changed
+	username.c and the sunos and freebsd modules to use these functions.
+	Added the "kernel" information line to the display to show
+	statistics on what the kernel is doing (context switches, forks, 
+	traps, etc.).  This requires explicit support by the platform
+	module, currently only freebsd, linux, and sunos.
+
+Wed Apr 18 2007 - wnl (3.6.1)
+	Fixed a few bugs in sigconv.awk that were causing incorrect
+	results on FreeBSD.  Changed configure.ac to fix a few linux
+	problems: signal include file and /proc/1/stat.
+
+Fri Apr 13 2007 - wnl (3.6.1)
+	Removed the use of VPATH for compiling the system module and used
+	an explicit dependency in the Makefile instead.  VPATH is now set
+	to just srcdir to ensure that top will compile correctly when
+	configured from a different directory.  On systems without VPATH
+	support, top will still configure and compile, but only
+	from within the source directory.  This fixes bug 1699526.
+
+Fri Feb  2 2007 - wnl (3.6.1)
+	Revised the way that configure figures out owner, group, and mode.
+	For systems that don't use the kernel, it tries to match install
+	settings to allow access to stuff in /proc.  More importantly, if
+	mode is 755 then neither owner nor group are set.  This fixes bug
+	1631136.  Added patch from haanjdj at xs4all.nl to fix an occasional
+	core dump in m_decosf1.c.  This checks return code from task_threads.
+	Made sure all get_system_info functions are declared void.  Fixed
+	string termination bug.  Cleaned up documetation for sunos5.
+
+Tue Aug  8 2006 - wnl (3.6.1)
+	For Solaris, changed the tag "swap" to "total swap" to clarify
+	what is beign displayed.  Note that the calculations are still the
+	same: the display is just showing total rather than total - free.
+
+Thu Apr 27 2006 - wnl (3.6)
+	Added patches for linux-style sort shortcuts and for Unixware
+	support in configure (patch 1474427).  Fixed sunos5 to do slow start
+	and to ensure cpucount is set (patch 1477386).  Added pagination
+	routines to display.c and modified show_help to use it, since the
+	help screen is now longer than 24 lines.  Applied patch for unixware
+	support that adds check for mas library (patch #1474423).  Solaris
+	cpu percent now reflects a percentage of the entire server, rather
+	than a single cpu (bug 1478138).
+
+Mon Mar 27 2006 - wnl (3.6)
+	The production release of version 3.6.  Fixed a minor scaling
+	bug in the decosf1 module.  Support for MacOS X is officially
+	withdrawn although the macosx module is still part of the
+	distribution.  Hopefully this is a temporary situation.
+	Documentation updated.
+
+
+Wed Feb 15 2006 - wnl (3.6beta5)
+	Minor changes to eliminate warnings from the Sun Studio compiler.
+	These were mostly sloppy argument declarations.  I also added
+	message.h to provide an interface file for just the message
+	related functions of display.c.
+
+Mon Dec 26 2005 - wnl (3.6beta4)
+	Added new netbsd module, courtesy of Simon Burge.
+	Fixed a few bugs in SVR4 module and added its use to
+	configure.ac, thanks to Sanchet Dighe.  Also ensured that the
+	novpath Makefile was in the distribution.
+	Fixed portability problem in display.c
+
+
+Mon Oct 24 2005 - wnl (3.6beta3)
+	Set up a color tagging mechanism in color.c to allow for the
+	dynamic creation of tag names to contol color highlighting.
+	These names are partially derived from the tags used to label
+	memory and swap information on the screen, thus are driven by
+	the machine module itself.  Added -T option to list color 
+	highlighting information.  Help screen now includes the actual
+	list of sort order names.  Incorporated some minor fixes to
+	the main code from the Freebsd source tree.  Fixed bug #1324582.
+	Freebsd 5: removed WCPU column and added THR column.  Display
+	for freebsd 4 and earlier unchanged since they don't track
+	threads in the kernel.  Added LICENSE file to distribution.
+
+Wed Oct 12 2005 - wnl (3.6beta2)
+	Major overhaul to display.c.  All lines of the display are
+	directly tracked and controlled via display_write and its
+	companion display_fmt.  Added support for complete control
+	of ANSI color on the screen: this will be used in the future
+	to allow for full use of color everywhere on the screen.
+	Signal handling code now uses sigaction on all systems that
+	support it.  Restored the freebsd module and did away with
+	freebsd4, and upgraded freebsd module to support 5.x.
+	Fix bug #1306099 (wio(wait) timer ignored on OSF1).
+
+Fri Sep 23 2005 - wnl (3.6beta1)
+	Fixed bugs #1266341 (compilation errors with gcc 4.x),
+	#1156464 (cpu% field for sunos), #1156243 (compilation
+	errors on AIX).  Applied patches #1217855 (Solaris 10
+	xarch flag).  Overhaul of sunos5 module, making code more
+	efficient and easier to follow.  Got rid of need for MEMTYPE
+	type in utils.h.  Changed all memory statistics data in the
+	module specification from an int to a long to better support
+	64-bit systems with lots of memory. Moved all unused modules
+	out of the distribution (I will add them back in as needed).
+	Moved freebsd module to freebsd4 as it won't work with 5.x
+	(a new module will be necessary). Added support to configure
+	for makes that don't understand VPATH. Updated documentation:
+	man page, FAQ, README, INSTALL.
+
+Mon Jan 24 2005 - wnl (3.6alpha10)
+	Updated aix43 module with ANSI function declarations and fixed
+	declaration of get_system_info.  Configure now uses irixsgi
+	module for irix6* systems.  Updates to the following modules:
+	irixsgi, sunos5.  Fixed null pointer bug in color.c. Removed
+	some useless code and definitions in display.c
+
+
+Sun Nov 28 2004 - wnl (3.6alpha9)
+	Replace AIX 5 module with alternate (bug 1056565).
+	Fixed vulnerability in use of snprintf.
+
+Fri Oct 22 2004 - wnl (3.6alpha8)
+	Support for linux 2.6, added more stuff to memory and swap lines.
+	Updated linuxthr module, which is only useful on 2.4 and earlier.
+	Added some color support back in (feature request 930588), but
+	still need to add it back to the per-process display.  Added
+	OSF 5 support (untested).
+	Fixed bug 1017951 (invalid process count argument not caught)
+
+Tue Apr 20 2004 - wnl (3.6alpha7)
+	Added 64 bit support for AIX.
+
+Thu Apr 15 2004 - wnl (3.6alpha6)
+        Included fixes for decosf1 pid size and updated module.  Also
+        added osf1 to list of recognized operating systems in configure.ac.
+
+Tue Mar 30 2004 - wnl (3.6alpha5)
+	Minor bug fixes and some code rearrangement.  Changes to install
+	rule.  Added several more platforms including: aix 4.2 thru 5,
+	MacOS 10, Dec OSF, HPUX 7 thru 11.  Fixed the core dumping bug
+	in linux.  Code cleanup, including sigdesc.h (by changing
+	sigconv.awk).  Startup error messages are displayed on the
+	first screen rather than beforehand (no more pause).  Cleaned
+	up interrupt handling to avoid a race condition.  Eliminated
+	top.local.h.  REMOVED Configure!!!
+
+Mon Mar 22 2004 - wnl (3.6alpha1)
+	Now using gnu autoconf.  Eliminated the need for CFLAGS and LIBS
+	tags in the module source files.  Autoconf tries to figure all
+	that out now.  Machine module interface now uses flags to determine
+	if module supports sorting, selective display of idle processes,
+	viewing full commands.  Added display of uptime for modules that
+	support it.  Added display of full command lines for modules that
+	support it.  3.5 modules must be changed a bit to work for 3.6:
+	ORDER is no longer defined, and the module must fill in the
+	appropriate fields in struct statics to get the extra features.
+	Added a extenstion interface to allow for putting extra stuff
+	on the screen -- this is still half baked and not documented.
+
+Mon Feb 23 2004 - wnl (3.5)
+	Turned rc1 in to version 3.5.  Only changes were to the FAQ.
+
+Mon Feb  2 2004 - wnl (3.5rc1)
+	Changed format_k (utils.c) to use MEMTYPE for its parameter.
+	On most systems this is a long, but if the module defines 
+	USE_SIZE_T, this is set to be a size_t.  The sunos5 module
+	now defines it, so that it will work correctly on 64-bit
+	machines.  New "linuxthr" module for rolling up processes
+	that are really threads.  Configure autodetects when running
+	on a 64-bit Solaris machine.
+
+Tue Dec 16 2003 - wnl (3.5beta13)
+	Improved linux module.  For Solaris, changed "THR" column
+	heading to "LWP" since that's what they really are.
+
 Thu Mar 30 2000 - wnl (3.5beta12)
 	Updated modules: m_aix41.c, m_aix43.c, m_mtxinu.c, m_sco5.c,
 	and m_ultrix4.c.
Index: README
===================================================================
--- README	(revision 183246)
+++ README	(working copy)
@@ -1,5 +1,5 @@
                              TOP
-                         Version 3.5
+                       Version 3.8beta1
 
                        William LeFebvre
 		     and a cast of dozens
@@ -21,46 +21,47 @@
 reorganized in a major way to make it easy to port to other platforms.
 All system dependent code is now contained in one file.
 
-Top now includes a configuration script called "Configure".  It helps
-the installer choose the correct parameters for this particular
-installation.  This script MUST be run before attempting to compile top.
+Starting with version 3.6, top includes a "configure" script generated
+by Gnu's autoconf.  This script MUST be run before attempting to
+compile top.  It will explore the system and generate approriate
+contents for Makefile, config.h, and top.1.
 
-Top requires read access to the memory files "/dev/kmem" and "/dev/mem"
-as well as the system image "/vmunix".  Some installations have these
-files protected from general access.  These sites would have to install
-this program in the same way that programs such as "ps" are installed.
-In addition, on those Unix variants that support the proc filesystem
-(such as SVR4 and Solaris 2), top requires read access to all the files
-in /proc: typically dictating that top be installed setuid to root.
+On some systems, top requires read access to the memory files
+"/dev/kmem" and "/dev/mem" as well as the system's kernel image.  Most
+installations have these files protected from general access.  These
+sites would have to install this program in the same way that programs
+such as "ps" are installed.  On most systems with a /proc file system,
+top will try to read everything it can from /proc, but may need extra
+permissions to do so.  The configure script will determine the
+permissions needed by the top binary, and a "make install" as root
+will get the binary installed correctly.  Sometimes this requires that
+the binary be installed with set-group-id privileges and, in rare
+cases, set-user-id to root.
 
 CAVEAT: version 3 of top has internal commands that kill and renice
 processes.  Although I have taken steps to insure that top makes
 appropriate checks with these commands, I cannot guarantee that these
-internal commands are totally secure.  IF YOU INSTALL top as a SETUID
-program, you do so AT YOUR OWN RISK!  I realize that some operating
-systems will require top to run setuid, and I will do everything I can
-to make sure that top is a secure setuid program.
+internal commands are totally secure.  IF YOU INSTALL top SET-USER-ID
+TO ROOT, YOU DO SO AT YOUR OWN RISK!  I realize that some operating
+systems will require top to run setuid root, and I will do everything
+I can to make sure that top is a secure setuid program.
 
-Configure will ask you to input values for certain parameters.  Before
-each parameter, Configure will display a description of what the
-parameter does.  Read the description and choose an appropriate value.
-Sometimes a default will appear in brackets.  Typing just return will
-choose the default.
+System support now takes the form of "modules".  Adding support for a
+different architecture requires only adding a module.  These modules
+are contained in the subdirectory "machine".  The "configure" script
+automatically determines which module is approproate.  However, it may
+not be able to determine what the correct module is.  This can happen
+either because it doesn't know about the system or there is no module
+to support the system.  In the former case, if you know which module
+to use, you can force "configure" to choose a particular module with
+the option "--with-module".  For example, if you want to force the use
+of the svr4 module (which appears as "machine/m_svr4.c") then use
+"configure --with-module=svr4" to generate the correct Makefile.  See
+the file "Porting" for a description of how to write your own module.
 
-System support now takes the form of "modules".  Adding support for
-a different architecture requires only adding a module.  Configure
-asks which module to use when it is configuring top.  See the file
-"Porting" for a description of how to write your own module.
-
 To compile and install "top", read the file "INSTALL" and follow the
 directions and advice contained therein.
 
-Once you have created a binary for one particular type of machine, you
-can reconfigure for another type with "./Configure modulename" where
-"modulename" is replaced with the appropriate module name.  All other
-parameter values are kept the same.  Note that in some cases this may
-not be appropriate.
-
 If you make any kind of change to "top" that you feel would be
 beneficial to others who use this program, or if you find and fix a bug,
 please send me the change.
@@ -69,35 +70,52 @@
 answers to the most commonly asked questions about the configuration,
 installation, and operation of top.
 
+COLOR
 
+Version 3.6 incorporated the idea of using ANSI color sequences to
+enhance information on the screen.  By default, no color is used.  But
+you can configure the use of color through the environment variable
+TOPCOLORS (or, for compatibility, TOPCOLOURS).  The interface is
+identical to the one first implemented by chris at spang.uk.eu.org, but
+the implementation is entirely different.  The option -C can be used
+to diable the feature entirely.
+
+Any information at the top of the screen can be enhanced with color.
+However, due to implementation difficulties, the per-process area
+cannot be color-enhanced.  A complete description of color support can
+be found in the man page.  References for ANSI color codes can be
+found all over the Internet, but if you want a handy reference, look
+in color.h.
+
+
 AVAILABILITY
 
-The latest version of "top" is now being made available via anonymous
-FTP from the host "ftp.groupsys.com" in the directory "/pub/top".
-Additional modules will be made available in the directory
-"/pub/top/m".  The site "eecs.nwu.edu" will continue to house copies
-of the distribution as well.
+Note that top is now a sourceforge project!  Its project name is
+"unixtop" and you can access its project page here:
 
-Here are HTML links for the four best "top" archive sites:
+http://sourceforge.net/projects/unixtop
 
-<A HREF="ftp://ftp.groupsys.com/pub/top">Top archive (groupsys.com)</A>
-<A HREF="ftp://eecs.nwu.edu/pub/top">Top archive (eecs.nwu.edu)</A>
-<A HREF="ftp://pharos.dgim.doc.ca/packages/top"> Top mirror (dgim.doc.ca)</A>
-<A HREF="ftp://uiarchive.uiuc.edu/pub/packages/top/">Top mirror (uiuc.edu)</A>
+On the project page you can find more information and access the
+official bug and feature request trackers.  If you find a bug,
+want to request a feature, or need help, please submit a request
+to the appropriate tracker on sourceforge.  Thank you.
 
-New releases will be posted to comp.sources.unix as they become
-available.  Sites which arhive that newsgroup will also contain copies
-of the distribution.
+Subversion access is also provided by Sourceforge.  If Subversion is
+installed on your system you can check out the project with the
+following command:
 
-Announcements about availability will be made to the mailing list
-"top-announce at groupsys.com".  This is an open list maintained by
-majordomo.  To join the list, send a message containing the word
-"subscribe" to "top-announce-request at groupsys.com".  Addresses of
-subscribers to this list are kept confidential and will never be used
-for any purpose other than as recipients of announements concerning
-this software.
+	svn co https://svn.sourceforge.net/svnroot/unixtop unixtop
 
+There is also a web site dedicated to the project, and it is here:
 
+http://www.unixtop.org
+
+The latest version of "top" is available as a download through
+sourceforge.  Start here to access the downloadable files:
+
+http://sourceforge.net/project/showfiles.php?group_id=72892
+
+
 KNOWN PROBLEMS:
 
 Gnu CC
@@ -115,6 +133,16 @@
 sending me a bug report.  Look in the gcc source distribution for the
 shell script "fixincludes".
 
+MacOS X
+
+Since I don't have full time root access to a MacOS X system I cannot
+provide effective support for the platform.  MacOS X uses Mach, and it
+is very difficult to extract accurate system and process information
+from the system.  It takes a lot of trial and error, along with root
+access.  I have included the most up-to-date version of the macosx module
+in the distribution, but I do not claim that it works.  If you want to
+try to use it, you can configure with "./configure --with-module=macosx".
+
 HP/UX 10.10
 
 In their infinite wisdom, the folks at HP have decided that mere mortals
@@ -127,66 +155,37 @@
 obtain a sufficiently complete definition of "struct proc" at some point in
 the near future.  Stay tuned.
 
-DIGITAL UNIX 4.0 (DECOSF/1 V4.0)
 
-A user has reported that idle processes are not displayed regardless
-of the flags used when invoking top.  We have not had time to track
-this problem down.
-
-DECOSF/1 V3.0
-
-There is a bug either in the module, in utils.c, or in DEC's optimizer that
-is tickled by the decosf1 module when compiled under V3.0 (and perhaps
-earlier versions).  Top compiled using DEC's compiler with optimization
-will consistently produce a segmentation fault (in format_next_process
-while calling sprintf).  To work around this problem, either compile top
-with gcc or turn off optimization (compile without -O).  We think that
-one of the bugs fixed in utils.c fixed this problem as well, but we are
-not certain.
-
-
-System V R 4.2
-
-Load average and memory displays do not work.  The problem has been
-traced down to a potential bug in the "mem" driver.  The author
-of the svr42 module is working on a fix.
-
-
-
 GRATITUDE
 
 My perpetual thanks to all the people who have helped me support top
 on so many platforms.  Without these people, top would not be what it
 is.  Here is a partial list of contributors and other individuals.
 
-	Robert Boucher		<boucher at sofkin.ca>
-	Marc Cohen 		<marc at aai.com>
-	David Cutter		<dpc at grail.com>
-	Casper Dik		<Casper.Dik at Sun.COM>
-	Charles Hedrick 	<hedrick at geneva.rutgers.edu>
-	Andrew Herbert		<andrew at werple.apana.org.au>
-	Jeff Janvrin		<jeff.janvrin at columbiasc.ncr.com>
-	Torsten Kasch 		<torsten at techfak.uni-bielefeld.de>
-	Petri Kutvonen		<kutvonen at cs.helsinki.fi>
-	William L. Jones 	<jones at chpc>
-	Tim Pugh 		<tpugh at oce.orst.edu>
-	Steve Scherf		<scherf at swdc.stratus.com>
-	Phillip Wu		<pwu01 at qantek.com.au>
+	Robert Boucher, Marc Cohen, David Cutter, Casper Dik,
+	Charles Hedrick, Andrew Herbert, Jeff Janvrin, Torsten Kasch,
+	Petri Kutvonen, William L. Jones, Tim Pugh, Steve Scherf,
+	Phillip Wu
 
 (My apologies if I missed anyone.)
 
 
+LICENSE
+
+Top is distributed free of charge under the same terms as the BSD
+license.  For an official statement, please refer to the file "LICENSE"
+which should be included with the source distribution.
+
+
 AUTHOR
 
+If you wish to contact me, please send a message to the sourceforge
+username "wnl".
+
 		William LeFebvre
-		Group sys Consulting
-		wnl at groupsys.com
 
-
 		U.S. Mail address:
 		    William LeFebvre
-		    Group sys Consulting
 		    11585 Jones Bridge Road
-		    Suite 420-139
-		    Alpharetta, GA  30022
-		    (770) 813-3224
+		    Suite 420 PMB 139
+		    Alpharetta, GA  30202
Index: layout.h
===================================================================
--- layout.h	(revision 183246)
+++ layout.h	(working copy)
@@ -1,31 +1,74 @@
 /*
- *  Top - a top users display for Berkeley Unix
- *
- *  This file defines the locations on tne screen for various parts of the
- *  display.  These definitions are used by the routines in "display.c" for
- *  cursor addressing.
- *
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  * $FreeBSD$
  */
 
-extern int  x_lastpid;		/* 10 */
-extern int  y_lastpid;		/* 0 */
-extern int  x_loadave;		/* 33 */
-extern int  x_loadave_nompid;	/* 15 */
-extern int  y_loadave;		/* 0 */
-extern int  x_procstate;	/* 0 */
-extern int  y_procstate;	/* 1 */
-extern int  x_brkdn;		/* 15 */
-extern int  y_brkdn;		/* 1 */
-extern int  x_mem;		/* 5 */
-extern int  y_mem;		/* 3 */
-extern int  x_swap;		/* 6 */
-extern int  y_swap;		/* 4 */
-extern int  y_message;		/* 5 */
-extern int  x_header;		/* 0 */
-extern int  y_header;		/* 6 */
-extern int  x_idlecursor;	/* 0 */
-extern int  y_idlecursor;	/* 5 */
-extern int  y_procs;		/* 7 */
+/*
+ *  Top - a top users display for Unix
+ *
+ *  This file defines the default locations on the screen for various parts
+ *  of the display.  These definitions are used by the routines in "display.c"
+ *  for cursor addressing.
+ */
 
-extern int  y_cpustates;	/* 2 */
+#define  X_LASTPID	10
+#define  Y_LASTPID	0
+#define  X_LASTPIDWIDTH 13
+#define  X_LOADAVE	27
+#define  Y_LOADAVE	0
+#define  X_LOADAVE_NOMPID	15
+#define  X_LOADAVEWIDTH 7
+#define  X_MINIBAR      50
+#define  Y_MINIBAR      0
+#define  X_UPTIME       48
+#define  Y_UPTIME       0
+#define  X_PROCSTATE	15
+#define  Y_PROCSTATE	1
+#define  X_BRKDN	15
+#define  Y_BRKDN	1
+#define  X_CPUSTATES	0
+#define  Y_CPUSTATES	2
+#define  X_KERNEL       8
+#define  Y_KERNEL       3
+#define  X_MEM		8
+#define  Y_MEM		3
+#define  X_SWAP		6
+#define  Y_SWAP		4
+#define  Y_MESSAGE	4
+#define  X_HEADER	0
+#define  Y_HEADER	5
+#define  X_IDLECURSOR	0
+#define  Y_IDLECURSOR	4
+#define  Y_PROCS	6
+
Index: version.c
===================================================================
--- version.c	(revision 183246)
+++ version.c	(working copy)
@@ -1,25 +1,46 @@
 /*
+ * Copyright (c) 1984 through 2008, William LeFebvre
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * 
+ *     * Neither the name of William LeFebvre nor the names of other
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
- *
- *  This program may be freely redistributed,
- *  but this entire comment MUST remain intact.
- *
- *  Copyright (c) 1984, 1989, William LeFebvre, Rice University
- *  Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
  */
 
+#include "config.h"
 #include "top.h"
-#include "patchlevel.h"
 
-static char version[16];
+char *
+version_string()
 
-char *version_string()
-
 {
-    sprintf(version, "%d.%d", VERSION, PATCHLEVEL);
-#ifdef BETA
-    strcat(version, BETA);
-#endif
-    return(version);
+    return(PACKAGE_VERSION);
 }
Index: FAQ
===================================================================
--- FAQ	(revision 183246)
+++ FAQ	(working copy)
@@ -1,264 +1,340 @@
-                             TOP
-                          Version 3.5
-		        Beta Release 11
+                                        TOP
+                                  Version 3.8beta1
 
-                       William LeFebvre
-		  with much help from others
+                                  William LeFebvre
+                             with much help from others
 
+                    Frequently Asked Questions and their Answers
 
-FREQUENTLY ASKED QUESTIONS AND THEIR ANSWERS
 
-This FAQ is broken out in to several topics.
 
+     GENERAL
 
-GENERAL
+ 1.  What is top? 
 
- 1.  "Where do I get the latest version of top?"
+     Top provies the user with a regularly updated display showing
+     information about the system and its top cpu-using processes. Think
+     of it as a full-screen "ps" output that gets updated at regular
+     intervals.
 
-The official site for top is "ftp.groupsys.com" in the directory
-"/pub/top".  It is also available from the following mirror sites:
-"pharos.dgim.doc.ca" in /packages/top, "uiarchive.uiuc.edu" in
-/pub/packages/top, "sunsite.auc.dk" in /pub/unix/top.  European
-users should consider using the Denmark (dk) site.
- 
- 2.  "Is there a web page for top?"
+ 2.  Where do I get the latest version of top? 
 
-Yes.  Point your browser at http://www.groupsys.com/top.  It includes
-all documentation, a nice interactive display which describes the
-various components of the output of top, web-based retrieval of the
-package, year 2000 information, and pointers to the mailing list.
+     The official site for top is "ftp.unixtop.org" in the directory
+     "/pub/top". Top is also a SourceForge project, and the most recent
+     releases are available on any of the SourceForge mirrors. The
+     SourceForge project page is at
+     http://sourceforge.net/projects/unixtop.
 
- 3.  "Is there a mailing list for top?"
+ 3.  Is there a web page for top? 
 
-The official list for announcements is "top-announce at groupsys.com".
-This list is managed by "majordomo at groupsys.com".  Announcements of
-importance to all top users will be sent to this list, including new
-releases, availability of beta test versions, emergency revisions and
-patches, etc.  Anyone is welcome to join top-announce.  This is a
-read-only list.  The list of subscribers will not (intentionally) be
-made available, and postings to the list are limited.
+     Yes. Point your browser at http://www.unixtop.org. It includes all
+     documentation, a nice interactive display which describes the various
+     components of the output of top, web-based retrieval of the package,
+     year 2000 information, and other neat stuff.
 
-In addition, there is a top developers mailing list that is used by
-beta testers and other people who help me port the program to various
-machines.  Membership to this list is solely at my discretion.  If you
-feel qualified to act as a beta tester, or if you are doing development
-work on top (such as porting to a new platform), you may submit a
-request by sending a message to "top-spinners-request at groupsys.com"
-containing the word "subscribe".  I will contact you within a few days,
-as my schedule permits.
+ 4.  Is there a mailing list or on-line bulletin board for top? 
 
- 4.  "What about Year 2000 compliance"?
+     There is a mailing list used for general announcements regarding top,
+     including new releases. This mailing list is available to sourceforge
+     members and can be accessed from the unixtop sourceforge project
+     page. Visit SourceForge and search for the project "unixtop", then
+     click on "mailing lists". There are also on-line forums available
+     through SourceForge where members can post questions and comments.
 
-Top should not experience any problems with the transition to the year
-2000.  A full statement concerning top and the year 2000 can be found
-in the file "Y2K" included with the distribution.
+ 5.  What about Year 2000 compliance? 
 
+     Top did not experience any problems with the transition to the year
+     2000. A full statement concerning top and the year 2000 can be found
+     in the file "Y2K" included with the distribution.
 
- 5.  "Why does it take so long for a new version of top to go through the
-      beta test process?"
+ 6.  Will there be another major release of top? Will there be a top
+     version 4? 
 
-This is completely my fault.  I have just not had the time to give top
-the attention it deserves.  I thank everyone for their patience, and I
-hope that with the recent changes in the direction of my career that I
-can spend more time on this.
+     I have some great ideas for the next major release of top, and I very
+     much want to make those ideas a reality. What I don't have much of
+     these days is free time. But I will keep poking at it and I hope to
+     have top version 4.0 ready by the fall of 2006.
 
- 6.  "Top is not written in ANSI C.  Do you ever plan to change that?"
+ 7.  Does top really support multi-processor systems? 
 
-Top predates ANSI C by about 5 years.  Yeah, it'll get "fixed" eventually.  
-Probably in 3.6.
+     On platforms that support multiple processors, top is able to detect
+     and correctly summarize the information about those processors. What
+     top does not do is break down the cpu states summary (the third line
+     of the display) by cpu. Instead it collects the cpu state information
+     from all processors and combines them in to a single line. Some
+     vendors  include a modified version of top that presents this
+     information for each cpu. Top 3.7 may have this functionality but it
+     is not present in the standard top 3.6 release.
 
+ 8.  Is top under CVS control? Can I access the sources via SourceForge
+     CVS or Subversion? 
 
-CONFIGURING
+     I maintain top using subversion, not CVS. Although I utilize my own
+     private subversion repository, it is regularly mirrored in to the
+     SourceForge Subversion repository. You can access the SourceForge
+     repository here: https://svn.unixtop.org/unixtop/top-3.
 
- 7.  "Configure said that it saw /proc and is recommending that I install top
-      setuid root.  Is there any way around this?  Is it safe?"
 
-There is no way around it.  Complain to POSIX.  Every effort has been   made 
-to make top a secure setuid program.  However, we cannot guarantee that 
-there are no security problems associated with this configuration.  The 
-places where top is most vulnerable are the builtin kill and renice 
-commands.  There is no internal top command that causes top to start a shell 
-as a subprocess.  Some SVR4 systems may contain a bug that enables a user to 
-renice his own processes downward (to lower nice values that are more 
-favorable for the process).  This problem has been fixed for the Solaris 2.x 
-modules, but may still exist in others.  We will hopefully fix this up in 
-the next release.
+     COMPILING
 
- 8.  "Why is Configure a c-shell script?  I thought c-shell scripts were 
-      evil?"
+ 9.  We just upgraded our operating system to a new version and top broke.
+     What should we do? 
 
-They are.  :-)  I'll probably be rewriting the Configure script for the
-next release, or switching to something like Gnu configure.
+     Recompile it. Top is very sensitive to changes in internal kernel
+     data structures. It is not uncommon for a new version of the
+     operating system to include changes to kernel data structures.
 
 
-COMPILING
+     RUNNING
 
- 9.  "We just upgraded our operating system to a new version and top broke.  
-      What should we do?"
+10.  I just finished compiling top and it works fine for root, but when I
+     try to run it as a regular user it either complains about files it
+     can't open or it doesn't display all the information it should. Did I
+     do something wrong? 
 
-Recompile it.  Top is very sensitive to changes in internal kernel data 
-structures.  It is not uncommon for a new version of the operating system to 
-include changes to kernel data structures.
+     Well, you're just not done. On many operating systems today, access
+     to many of the kernel memory devices and other system files is
+     restricted to either root or a particular group. The configure script
+     figures this out (usually) and makes sure that the "install" rule in
+     the Makefile will install top so that anyone can run it successfully.
+     However, you have to *install* it first. Do this with the command
+     "make install".
 
+11.  Top is (not) displaying idle processes and I don't (do) want it to. 
 
-RUNNING
+     This default has only changed about a dozen times, and I finally got
+     tired of people whining about it. Go read the manual page for the
+     current version and pay special attention to the description of the
+     "TOP" environment variable.
 
-10.  "I just finished compiling top and it works fine for root, but when
-      I try to run it as a regular user it either complains about files
-      it can't open or it doesn't display all the information it should.
-      Did I do something wrong?"
+12.  We have so much memory in our machine that the memory status display
+     (the fourth line) ends up being longer than 80 characters. This
+     completely messes up top's output. Is there a patch? 
 
-Well, you're just not done.  On many operating systems today, access to
-many of the kernel memory devices and other system files is restricted to
-either root or a particular group.  The Configure script figures this out
-(usually) and makes sure that the "intsall" rule in the Makefile will
-install top so that anyone can run it successfully.  However, you have to
-*install* it first.  Do this with the command "make install".
+     Most modules have been changed to use new memory formatting functions
+     which will display large values in terms of megabytes instead of
+     kilobytes. This should fix all occurences of this problem. Also note
+     that newer versions of top can use columns beyond 79, and understand
+     window resizes. So you can always make your window wider.
 
-11.  "Top is (not) displaying idle processes and I don't (do) want it to."
+13.  I tried to compile top with gcc and it doesn't work. I get
+     compilation errors in the include files, or I get an executable that
+     dumps core, or top displays incorrect numbers in some of the
+     displays. What's wrong? 
 
-This default has only changed about a dozen times, and I finally got tired 
-of people whining about it.  Go read the manual page for the current version 
-and pay special attention to the description of the "TOP" environment 
-variable.
+     Gnu CC likes very much to use its own include files. Not being a gcc
+     expert, I can't explain why it does this. But I can tell you that if
+     you upgrade your operating system (say from Solaris 2.6 to Solaris
+     2.7) after installing gcc, then the include files that gcc uses will
+     be incorrect, especially those found in the "sys" directory. Your
+     choices are: (1) rebuild and reinstall the "standard" include files
+     for gcc (look for scripts in the distribution called "fixincludes"
+     and "fixinc.svr4"), (2) compile machine.c with
+     "CFLAGS=-I/usr/include" then make the rest of the object files
+     normally, or (3) use a different compiler.
 
-12.  "We have so much memory in our machine that the memory status display 
-      (the fourth line) ends up being longer than 80 characters.  This
-      completely messes up top's output.  Is there a patch?"
+14.  The cpu state percentages are all wrong, indicating that my machine
+     is using 95% system time when it is clearly idle. What's wrong? 
 
-Most modules have been changed to use new memory formatting functions which 
-will display large values in terms of megabytes instead of kilobytes.  This 
-should fix all occurences of this problem.  If you encounter a system where 
-this large memory display overflow is still occurring, please let me know 
-(send mail to <wnl at groupsys.com>).  Also note that newer versions of top can 
-use columns beyond 79, and understand window resizes.  So you can always 
-make your window bigger.
+     This can happen if you compiled with gcc using the wrong include
+     files. See the previous question.
 
-13.  "I tried to compile top with gcc and it doesn't work.  I get
-      compilation errors in the include files, or I get an executable that
-      dumps core, or top displays incorrect numbers in some of the displays.
-      What's wrong?"
 
-Gnu CC likes very much to use its own include files.  Not being a gcc
-expert, I can't explain why it does this.  But I can tell you that if
-you upgrade your operating system (say from Solaris 2.4 to Solaris
-2.5) after installing gcc, then the include files that gcc uses will
-be incorrect, especially those found in the "sys" directory.  Your
-choices are: (1) rebuild and reinstall the "standard" include files
-for gcc (look for scripts in the distribution called "fixincludes" and
-"fixinc.svr4"), (2) compile machine.c with "CFLAGS=-I/usr/include"
-then make the rest of the object files normally, or (3) use "cc".
-Solaris 2.6 users should also consult FAQ #20.
+     FREEBSD PROBLEMS
 
-14.  "The cpu state percentages are all wrong, indicating that my machine is 
-      using 95% system time when it is clearly idle.  What's wrong?"
+15.  This version of top does not show individual threads with the "t" or
+     "H" commands. Instead it says "command not available." Why? 
 
-This can happen if you compiled with gcc using the wrong include files.
-See the previous question.
+     Previous versions of top attempted to support the display of
+     individual threads under FreeBSD through the use of the "t" command.
+     However,  the FreeBSD kernel does not supply sufficient or correct
+     information on the individual threads within a process. So the data
+     that was being displayed was incorrect and misleading. Therefore, top
+     version 3.8 disables the use of this command to prevent the display
+     of incorrect information. FreeBSD 8.0 will correctly report
+     per-thread information and top version 3.8 supports the use of the
+     "t" command for version 8.0.
 
+16.  The "f" command (to display full command lines for the processes)
+     does not work and instead says "command not available". Why? 
 
-SUNOS PROBLEMS
+     The current version of top is able to use sysctl to retrieve almost
+     all of the information it needs without having to open /dev/kmem. The
+     one piece of information not available via sysctl is the full command
+     line of each argument. If you run top as a regular user and it cannot
+     open /dev/kmem (in other words, it is not installed set-gid to the
+     kmem group) then it will disable the "f" command. Make sure the top
+     binary is installed with a group ownership of "kmem" and with the
+     set-gid bit on if you want the "f" command to work properly.
 
-15.  "I tried compiling top under SunOS version 4.1.x and it got compile time 
-      errors.  Is there a patch?"
 
-If you try compiling top in a "System V environment" under SunOS (that is, 
-/usr/5bin is before /usr/bin on your path) then the compilation may fail.  
-This is mostly due to the fact that top thinks its being compiled on a 
-System V machine when it really isn't. The only solution is to put /usr/bin 
-and /usr/ucb before /usr/5bin on your path and try again.
+     MACOSX PROBLEMS
 
+17.  I tried to configure top on my Mac OSX system and I got an error
+     claiming "macosx not supported". What up? 
 
-SVR4-derived PROBLEMS
+     Since I don't have full time root access to a Mac OSX system I cannot
+     provide effective support for the platform. MacOSX uses Mach, and it
+     is very difficult to extract accurate system and process information
+     from the system. It takes a lot of trial and error, along with root
+     access. I have included the most up-to-date version of the macosx
+     module in the distribution, but I do not claim that it works. If you
+     want to try to use it, you can configure with "./configure
+     --with-module=macosx".
 
-16.  "When I run top on my SVR4-derived operating system, it displays all
-      the system information at the top but does not display any process
-      information (or only displayes process information for my own
-      processes).  Yet when I run it as root, everything works fine."
 
-Your system probably uses the pseudo file system "/proc", which is by 
-default only accessible by root.  Top needs to be installed setuid root on 
-such systems if it is going to function correctly for normal users.
+     SUNOS PROBLEMS
 
+18.  I tried compiling top under SunOS version 4.1.x and it got compile
+     time errors or run time errors. Is there a patch? 
 
-SOLARIS PROBLEMS
+     If you try compiling top in a "System V environment" under SunOS
+     (that is, /usr/5bin is before /usr/bin on your path) then the
+     compilation may fail. This is mostly due to the fact that top thinks
+     its being compiled on a System V machine when it really isn't. The
+     only solution is to put /usr/bin and /usr/ucb before /usr/5bin on
+     your path and try again.
 
-17.  "Under Solaris 2, when I run top as root it only shows root processes,
-      or it only shows processes with a PID less than 1000.  It refuses to
-      show anything else.  What do I do?"
 
-You probably compiled it with /usr/ucb/cc instead of the real C compiler.  
-/usr/ucb/cc is a cc front end that compiles programs in BSD source-level 
-compatability mode.  You do not want that.  Make sure that /usr/ucb is not 
-on your path and try compiling top again.
+     SOLARIS PROBLEMS
 
-18.  "Under Solaris 2, I compiled top using what I am sure is the correct
-      compiler but when I try to run it it complains about missing dynamic
-      libraries.  What is wrong?"
 
-Check to see if you have LD_LIBRARY_PATH defined in your shell.  If you do, 
-make sure that /usr/ucblib is not on the path anywhere.  Then try compiling 
-top again.
+     NOTE: the most common source of problems with top under Solaris is
+     the result of compiling it with the wrong front end. Make sure that
+     /usr/ucb is not on your path before attempting to compile top under
+     Solaris.
 
-19.  "Under Solaris 2, when I try to run top it complains that it can't open 
-      the library "libucb.so.1".  So I changed the LIBS line in m_sunos5.c
-      to include -R/usr/ucblib to make sure that the dynamic linker will look 
-      there when top runs.  I figured this was just an oversight.  Was I
-      right?"
+19.  Is there somewhere I can get a pre-compiled package? 
 
-No, you were not right.  As distributed, top requires NO alterations
-for successful compilation and operations under any release of Solaris
-2.  You probably compiled top with /usr/ucb/cc instead of the real C
-compiler.  See FAQ #10 for more details.
+     Yes. Although I don't provide pre-compiled binaries, you can get a
+     Sun-style package from www.sunfreeware.com.
 
-20.  "When I try to compile top under Solaris 2.6 using gcc I get compile
-      time errors.  There appear to be problems with the include files,
-      such as 'u_rlimit has incomplete type' and/or 'u_saved_rlimit has
-      incomplete type'.  I've already run fixinc.svr4 as per FAQ #13.
-      Why didn't that fix it?"
+20.  Under Solaris 2, when I type "make", the system says "language
+     optional software package not installed." What's going on? 
 
-Only top versions 3.5 and later are compatible with Solaris 2.6.  Make
-sure you are using the most up-to-date version.  Earlier beta release
-copies of version 3.5 had additional problems when compiled with gcc.
-Retrieve the official version 3.5 (non-beta) release from one of the
-sites listed in FAQ #1 or FAQ #2.
+     You tried to compile with /usr/ucb/cc. Make sure /usr/ucb is not on
+     your path. Furthermore, you do not have a Sun compiler installed on
+     your system. You need a compiler to make top. Either Sun's C compiler
+     or the Gnu C compiler will work fine.
 
+21.  Under Solaris 2, when I run top as root it only shows root processes,
+     or it only shows processes with a PID less than 1000. It refuses to
+     show anything else. What do I do? 
 
-SCO PROBLEMS
+     You probably compiled it with /usr/ucb/cc instead of the real C
+     compiler. /usr/ucb/cc is a cc front end that compiles programs in BSD
+     source-level compatability mode. You do not want that. Make sure that
+     /usr/ucb is not on your path and try compiling top again.
 
-21.  "When I try to run Configure, it complains about a syntax error."
+22.  Under Solaris 2, I compiled top using what I am sure is the correct
+     compiler but when I try to run it it complains about missing dynamic
+     libraries. What is wrong? 
 
-Some versions of SCO's csh do not understand the syntax "$<".  Earlier
-releases of top depended on this syntax to read input from the installer's
-terminal during the installation process.  Version 3.5 fixes this.
+     Check to see if you have LD_LIBRARY_PATH defined in your shell. If
+     you do, make sure that /usr/ucblib is not on the path anywhere. Then
+     try compiling top again.
 
+23.  Under Solaris 2, when I try to run top it complains that it can't
+     open the library "libucb.so.1". So I changed the LIBS line in
+     m_sunos5.c to include -R/usr/ucblib to make sure that the dynamic
+     linker will look there when top runs. I figured this was just an
+     oversight. Was I right? 
 
-SVR42 PROBLEMS
+     No, you were not right. As distributed, top requires no alterations
+     for successful compilation and operations under any release of
+     Solaris 2. You probably compiled top with /usr/ucb/cc instead of the
+     real C compiler. See FAQ 22 for more details.
 
-22.  "The memory display doesn't work right.  Why?"
+24.  On my 64-bit system some processes show up with incorrect information
+     (such as zero memory). 
 
-This is a known bug with the svr42 module. The problem has been traced down 
-to a potential bug in the "mem" driver.  The author of the svr42 module is 
-working on a fix.
+     If you are running a 64-bit system, then you need to make sure that
+     you are running the 64-bit top binary. Top's configure script
+     attempts to detect 64-bit systems, and will automatically generate
+     both 32-bit and 64-bit binaries on such systems. If you use or
+     install the 32-bit binary on a 64-bit system top will still run but
+     will not produce the correct results. This will also happen if you
+     configure your distribution on a 32-bit system then compile with that
+     configuration on a 64-bit system. You must configure and compile on
+     the same system. For Sparc systems the 32-bit binary will be created
+     in the subdirectory "sparcv7" and the 64-bit binary will be created
+     in the subdirectory "sparcv9". For Intel systems the directories will
+     be "i386" (32-bit) and "amd64" (64-bit). In all cases a copy of 
+     /usr/lib/isaexec is made in the main directory and called "top". This
+     program will choose the correct binary to run from one of these
+     subdirectories. See isaexec(3c) for more details.
 
+25.  Can I install both 32-bit and 64-bit binaries on a central file
+     server and have machines which mount it automatically use the correct
+     one? 
 
-STILL STUCK
+     Yes. If you configure and compile on a 64-bit system, top's configure
+     script and makefile will automatically create both 32-bit and 64-bit
+     binaries. The "install" rule in the makefile will install these
+     binaries in subdirectories of /usr/local/bin appropriate to the
+     architecture (sparcv7/sparcv9 or i386/amd64) then create a copy of
+     /usr/lib/isaexec named "top" in /usr/local/bin to ensure that the
+     appropriate is run when a user types "top". If you make sure that you
+     configure and compile on a 64-bit system, then "make install" will do
+     the right thing.
 
-23.  I'm still stuck.  To whom do I report problems with top?"
+26.  This version of top show less available swap space than previous
+     versions. Why does it no longer match the output of the swap summary
+     produced with "swap -s"? 
 
-The most common problems are caused by top's sensitivity to internal kernel 
-data structures.  So make sure that you are using the right include files, 
-and make sure that you test out top on the same machine where you compiled 
-it.  Sun's BSD Source Compatability Mode is also a common culprit.  Make 
-sure you aren't using either /usr/ucb/cc or any of the libraries in 
-/usr/ucblib.  Finally, make sure you are using the correct module.  If there 
-does not appear to be one appropriate for your computer, then top probably 
-will not work on your system.
+     Starting with version 3.6 of top, the amount of swap space reported
+     by top has been changed to reflect only disk-based swap space. The 
+     swap summary produced with "swap -s" also includes memory-based swap
+     space. This changed was made for several reasons. It makes the
+     display under Solaris more like those of other operating systems. The
+     display is more what users expect (except those used to previous
+     versions of top). Most importantly, "swap -s" gets its data via an
+     undocumented system interface. Now that top no longer displays that
+     data it can use publically documented and maintained system
+     interfaces to retrieve its data.
 
-If after reading all of this file and checking everything you can you are 
-still stuck, then send mail to "wnl at groupsys.com".  I will answer your mail 
-when I have time.  Please bear with me in that regard!  If it looks like the 
-problem is machine-specific, I will forward the report along to the module's 
-author.  If you would like to converse directly with the module author, the 
-authors' names are listed at the beginning of the module .c file in the 
-"machine" directory.
+
+     SVR4-DERIVED PROBLEMS
+
+27.  When I run top on my SVR4-derived operating system, it displays all
+     the system information at the top but does not display any process
+     information (or only displays process information for my own
+     processes). Yet when I run it as root, everything works fine. What's
+     wrong? 
+
+     Your system probably uses the pseudo file system "/proc", which is by
+     default only accessible by root. Top needs to be installed setuid
+     root on such systems if it is going to function correctly for normal
+     users.
+
+
+     SVR42 PROBLEMS
+
+28.  The memory display doesn't work right. Why? 
+
+     This is a known bug with the svr42 module. The problem has been
+     traced down to a potential bug in the "mem" driver. The author of the
+     svr42 module is working on a fix.
+
+
+     STILL STUCK
+
+29.  I'm still stuck. To whom do I report problems with top? 
+
+     The most common problems are caused by top's sensitivity to internal
+     kernel data structures. So make sure that you are using the right
+     include files, and make sure that you test out top on the same
+     machine where you compiled it. Sun's BSD Source Compatability Mode is
+     also a common culprit. Make sure you aren't using either /usr/ucb/cc
+     or any of the libraries in /usr/ucblib. Finally, make sure you are
+     using the correct module. If there does not appear to be one
+     appropriate for your computer, then top probably will not work on
+     your system.
+
+     If after reading all of this file and checking everything you can you
+     are still stuck, then please use SourceForge to submit a support
+     request or a bug. Top is supported by the SourceForge project  named
+     "unixtop". On SourceForge you will find defect tracking, a mailing
+     list, and on-line forums. You can also contact the author through
+     SourceForge.
+




And this is the diff against the stock 3.8b1 code:

diff --git a/contrib/top/commands.c b/contrib/top/commands.c
index e93fb54..f9f95d4 100644
--- a/contrib/top/commands.c
+++ b/contrib/top/commands.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ *  $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
  */
@@ -643,6 +647,36 @@ cmd_idle(globalstate *gstate)
 }
 
 int
+cmd_weightedcpu(globalstate *gstate)
+
+{
+    gstate->pselect.wcpu = !gstate->pselect.wcpu;
+    message_error(" Displaying %sCPU.",
+		  gstate->pselect.wcpu ? "W" : "");
+    return CMD_REFRESH;
+}
+
+int
+cmd_jails(globalstate *gstate)
+
+{
+    gstate->pselect.jail = !gstate->pselect.jail;
+    message_error(" %sisplaying jail ID.",
+		  gstate->pselect.jail ? "D" : "Not d");
+    return CMD_REFRESH;
+}
+
+int
+cmd_thisprocess(globalstate *gstate)
+
+{
+    gstate->pselect.self = !gstate->pselect.self;
+    message_error(" %sisplaying self.",
+		  gstate->pselect.self ? "D" : "Not d");
+    return CMD_REFRESH;
+}
+
+int
 cmd_displays(globalstate *gstate)
 
 {
@@ -884,6 +918,8 @@ cmd_system(globalstate *gstate)
 {
     gstate->pselect.system = !gstate->pselect.system;
     display_header(2);
+    message_error(" %showing system processes.",
+		  gstate->pselect.system ? "S" : "Not s");
     return CMD_REFRESH;
 }
 
@@ -895,8 +931,11 @@ cmd_threads(globalstate *gstate)
     {
 	gstate->pselect.threads = !gstate->pselect.threads;
 	display_header(2);
+	message_error(" Displaying threads as %s.",
+		      gstate->pselect.threads ? "seperately" : "a count");
 	return CMD_REFRESH;
     }
+    message_error(" Thread support not enabled.");
     return CMD_NA;
 }
 
@@ -909,9 +948,10 @@ command command_table[] = {
     { ' ', cmd_update, "update screen" },
     { '?', cmd_help, "help; show this text" },
     { 'h', cmd_help, NULL },
-    { 'C', cmd_color, "toggle the use of color" },
+    { 'C', cmd_weightedcpu, "toggle the displaying of weighted CPU percentage" },
+    { 'E', cmd_color, "toggle the use of color" },
     { 'H', cmd_threads, "toggle the display of individual threads" },
-    { 't', cmd_threads, NULL },
+    { 'j', cmd_jails, "toggle the displaying of jail ID" },
     { 'M', cmd_order_mem, "sort by memory usage" },
     { 'N', cmd_order_pid, "sort by process id" },
     { 'P', cmd_order_cpu, "sort by CPU usage" },
@@ -920,13 +960,13 @@ command command_table[] = {
     { 'U', cmd_useruid, "toggle the display of usernames or uids" },
     { 'c', cmd_command, "display processes by command name" },
     { 'd', cmd_displays, "change number of displays to show" },
-    { 'f', cmd_cmdline, "toggle the display of full command paths" },
+    { 'a', cmd_cmdline, "toggle the display of full command paths" },
     { 'i', cmd_idle, "toggle the displaying of idle processes" },
     { 'I', cmd_idle, NULL },
 #ifdef ENABLE_KILL
     { 'k', cmd_kill, "kill processes; send a signal to a list of processes" },
 #endif
-    { 'm', cmd_mode, "toggle between display modes" },
+    { 'm', cmd_mode, "toggle between 'cpu' and 'io' modes" },
     { 'n', cmd_number, "change number of processes to display" },
     { '#', cmd_number, NULL },
     { 'o', cmd_order, "specify sort order (see below)" },
@@ -935,6 +975,7 @@ command command_table[] = {
     { 'r', cmd_renice, "renice a process" },
 #endif
     { 's', cmd_delay, "change number of seconds to delay between updates" },
+    { 't', cmd_thisprocess, "toggle the display of this process" },
     { 'u', cmd_user, "display processes for only one user (+ selects all users)" },
     { '\0', NULL, NULL },
 };
diff --git a/contrib/top/display.c b/contrib/top/display.c
index 2330ca4..019d6de 100644
--- a/contrib/top/display.c
+++ b/contrib/top/display.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
  */
@@ -88,6 +92,7 @@ static int display_width = MAX_COLS;
 static int x_lastpid = X_LASTPID;
 static int y_lastpid = Y_LASTPID;
 static int x_loadave = X_LOADAVE;
+static int x_loadave_nompid = X_LOADAVE_NOMPID;
 static int y_loadave = Y_LOADAVE;
 static int x_minibar = X_MINIBAR;
 static int y_minibar = Y_MINIBAR;
@@ -99,6 +104,8 @@ static int x_cpustates = X_CPUSTATES;
 static int y_cpustates = Y_CPUSTATES;
 static int x_kernel = X_KERNEL;
 static int y_kernel = Y_KERNEL;
+static int x_brkdn = X_BRKDN;
+static int y_brkdn = Y_BRKDN;
 static int x_mem = X_MEM;
 static int y_mem = Y_MEM;
 static int x_swap = X_SWAP;
@@ -110,6 +117,8 @@ static int x_idlecursor = X_IDLECURSOR;
 static int y_idlecursor = Y_IDLECURSOR;
 static int y_procs = Y_PROCS;
 
+static int Header_lines = 7;		/* XXXEG */
+
 /* buffer and colormask that describes the content of the screen */
 /* these are singly dimensioned arrays -- the row boundaries are
    determined on the fly.
@@ -143,9 +152,13 @@ static int num_kernel;
 
 static int *lprocstates;
 static int *lcpustates;
+static int *lmemory;
+static int *lswap;
 
+static int num_cpus;
 static int *cpustate_columns;
 static int cpustate_total_length;
+static int cpustates_column;
 
 static int header_status = Yes;
 
@@ -672,6 +685,8 @@ display_resize()
     /* calculate the current dimensions */
     /* if operating in "dumb" mode, we only need one line */
     top_lines = smart_terminal ? screen_length : 1;
+    if (top_lines < 0)
+	top_lines = 0;
 
     /* we don't want more than MAX_COLS columns, since the machine-dependent
        modules make static allocations based on MAX_COLS and we don't want
@@ -781,9 +796,23 @@ display_init(struct statics *statics)
 	y_idlecursor++;
 	y_procs++;
     }
+    lswap = (int *)malloc(num_swap * sizeof(int));
     
     /* call resize to do the dirty work */
     top_lines = display_resize();
+    num_cpus = statics->ncpus;
+    cpustates_column = 5;	/* CPU: */
+    if (num_cpus != 1)
+	cpustates_column += 2;	/* CPU 0: */
+    for (i = num_cpus; i > 9; i /= 10)
+	cpustates_column++;
+    y_kernel += num_cpus - 1;
+    y_mem += num_cpus - 1;
+    y_swap += num_cpus - 1;
+    y_header += num_cpus - 1;
+    y_idlecursor += num_cpus - 1;
+    y_message += num_cpus - 1;
+    y_procs += num_cpus - 1;
 
     /* only do the rest if we need to */
     if (top_lines > -1)
@@ -795,7 +824,7 @@ display_init(struct statics *statics)
 
 	cpustate_names = statics->cpustate_names;
 	num_cpustates = string_count(cpustate_names);
-	lcpustates = (int *)calloc(num_cpustates, sizeof(int));
+	lcpustates = (int *)calloc(num_cpustates, sizeof(int) * num_cpus);
 	cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
 	memory_names = statics->memory_names;
 	num_memory = string_count(memory_names);
@@ -957,35 +986,32 @@ u_minibar(int (*formatter)(char *, int))
     display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
 }
 
-static int uptime_days;
-static int uptime_hours;
-static int uptime_mins;
-static int uptime_secs;
-
 void
 i_uptime(time_t *bt, time_t *tod)
 
 {
+    int uptime_days;
     time_t uptime;
+    char t[10];
+    struct tm *tm;
 
     if (*bt != -1)
     {
 	uptime = *tod - *bt;
 	uptime += 30;
 	uptime_days = uptime / 86400;
-	uptime %= 86400;
-	uptime_hours = uptime / 3600;
-	uptime %= 3600;
-	uptime_mins = uptime / 60;
-	uptime_secs = uptime % 60;
+
+	tm = gmtime(&uptime);
+	strftime(t, sizeof(t), "%H:%M:%S", tm);
 
 	/*
 	 *  Display the uptime.
 	 */
 
+	x_uptime = (smart_terminal ? screen_width : 79) - 29;
 	display_fmt(x_uptime, y_uptime, 0, 0,
-		    "  up %d+%02d:%02d:%02d",
-		    uptime_days, uptime_hours, uptime_mins, uptime_secs);
+		    "  up %d+%s",
+		    uptime_days, t);
     }
 }
 
@@ -1001,18 +1027,9 @@ void
 i_timeofday(time_t *tod)
 
 {
-    /*
-     *  Display the current time.
-     *  "ctime" always returns a string that looks like this:
-     *  
-     *	Sun Sep 16 01:03:52 1973
-     *  012345678901234567890123
-     *	          1         2
-     *
-     *  We want indices 11 thru 18 (length 8).
-     */
-
     int x;
+    char t[10];
+    struct tm *tm;
 
     /* where on the screen do we start? */
     x = (smart_terminal ? screen_width : 79) - 8;
@@ -1024,7 +1041,9 @@ i_timeofday(time_t *tod)
     }
 
     /* display it */
-    display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
+    tm = localtime(tod);
+    strftime(t, sizeof(t), "%H:%M:%S", tm);
+    display_write(x, 0, 0, 1, t);
 }
 
 static int ltotal = 0;
@@ -1095,6 +1114,7 @@ u_procstates(int total, int *brkdn, int threads)
     }
 }
 
+#ifdef no_more
 /*
  *  *_cpustates(states, names) - print the cpu state percentages
  */
@@ -1125,6 +1145,7 @@ cpustates_tag()
     x_cpustates = strlen(use);
     return(use);
 }
+#endif
 
 void
 i_cpustates(int *states)
@@ -1135,16 +1156,24 @@ i_cpustates(int *states)
     char *thisname;
     int *colp;
     int color = 0;
+    int cpu;
+    char scpu[10];
 #ifdef ENABLE_COLOR
     int *cidx = cpustate_cidx;
 #endif
 
+for (cpu = 0; cpu < num_cpus; cpu++) {
     /* initialize */
     names = cpustate_names;
     colp = cpustate_columns;
 
     /* print tag */
-    display_write(0, y_cpustates, 0, 0, cpustates_tag());
+    if (num_cpus == 1)
+	strcpy(scpu, "CPU: ");
+    else
+	sprintf(scpu, "CPU %d: ", cpu);
+    display_write(0, y_cpustates + cpu, 0, 0, scpu);
+    x_cpustates = strlen(scpu) + 1;
 
     /* now walk thru the names and print the line */
     while ((thisname = *names++) != NULL)
@@ -1160,7 +1189,8 @@ i_cpustates(int *states)
 #endif
 
 	    /* if percentage is >= 1000, print it as 100% */
-	    display_fmt(x_cpustates + *colp, y_cpustates,
+	    display_fmt(
+			x_cpustates + *colp, y_cpustates + cpu,
 			color, 0,
 			(value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
 			((float)value)/10.,
@@ -1172,9 +1202,10 @@ i_cpustates(int *states)
 	colp++;
 	states++;
     }
+}
 
     /* copy over values into "last" array */
-    memcpy(lcpustates, states, num_cpustates * sizeof(int));
+    memcpy(lcpustates, states, num_cpustates * sizeof(int) * num_cpus);
 }
 
 void
@@ -1182,17 +1213,20 @@ u_cpustates(int *states)
 
 {
     int value;
-    char **names = cpustate_names;
+    char **names;
     char *thisname;
     int *lp;
     int *colp;
     int color = 0;
+    int cpu;
 #ifdef ENABLE_COLOR
     int *cidx = cpustate_cidx;
 #endif
 
+for (cpu = 0; cpu < num_cpus; cpu++) {
     lp = lcpustates;
     colp = cpustate_columns;
+    names = cpustate_names;
 
     /* we could be much more optimal about this */
     while ((thisname = *names++) != NULL)
@@ -1212,7 +1246,7 @@ u_cpustates(int *states)
 #endif
 
 		/* if percentage is >= 1000, print it as 100% */
-		display_fmt(x_cpustates + *colp, y_cpustates, color, 0,
+		display_fmt(x_cpustates + *colp, y_cpustates + cpu, color, 0,
 			    (value >= 1000 ? "%4.0f" : "%4.1f"),
 			    ((double)value)/10.);
 
@@ -1230,18 +1264,29 @@ u_cpustates(int *states)
 	colp++;
     }
 }
+}
 
 void
 z_cpustates()
 
 {
     register int i = 0;
-    register char **names = cpustate_names;
+    register char **names;
     register char *thisname;
     register int *lp;
+    int cpu;
+    char scpu[10];
+
+for (cpu = 0; cpu < num_cpus; cpu++) {
+    names = cpustate_names;
 
     /* print tag */
-    display_write(0, y_cpustates, 0, 0, cpustates_tag());
+    if (num_cpus == 1)
+	strcpy(scpu, "CPU: ");
+    else
+	sprintf(scpu, "CPU %d: ", cpu);
+    display_write(0, y_cpustates + cpu, 0, 0, scpu);
+    x_cpustates = strlen(scpu) + 1;
 
     while ((thisname = *names++) != NULL)
     {
@@ -1251,10 +1296,11 @@ z_cpustates()
 			thisname);
 	}
     }
+}
 
     /* fill the "last" array with all -1s, to insure correct updating */
     lp = lcpustates;
-    i = num_cpustates;
+    i = num_cpustates * num_cpus;
     while (--i >= 0)
     {
 	*lp++ = -1;
@@ -1301,7 +1347,7 @@ void
 i_memory(long *stats)
 
 {
-    display_write(0, y_mem, 0, 0, "Memory: ");
+    display_write(0, y_mem, 0, 0, "Mem: ");
 
     /* format and print the memory summary */
     summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
@@ -1447,6 +1493,33 @@ u_message(struct timeval *now)
 static int header_length;
 
 /*
+ * Trim a header string to the current display width and return a newly
+ * allocated area with the trimmed header.
+ */
+
+char *
+trim_header(text)
+
+char *text;
+
+{
+	char *s;
+	int width;
+
+	s = NULL;
+	width = display_width;
+	header_length = strlen(text);
+	if (header_length >= width) {
+		s = malloc((width + 1) * sizeof(char));
+		if (s == NULL)
+			return (NULL);
+		strncpy(s, text, width);
+		s[width] = '\0';
+	}
+	return (s);
+}
+
+/*
  *  *_header(text) - print the header for the process area
  *
  *  Assumptions:  cursor is on the previous line and lastline is consistent
@@ -1456,16 +1529,21 @@ void
 i_header(char *text)
 
 {
+    char *s;
     int header_color = 0;
 
 #ifdef ENABLE_COLOR
     header_color = color_test(header_cidx, 0);
 #endif
+    s = trim_header(text);
+    if (s != NULL)
+	text = s;
     header_length = strlen(text);
     if (header_status)
     {
 	display_write(x_header, y_header, header_color, 1, text);
     }
+    free(s);
 }
 
 /*ARGSUSED*/
diff --git a/contrib/top/layout.h b/contrib/top/layout.h
index 46bf78e..d41bb05 100644
--- a/contrib/top/layout.h
+++ b/contrib/top/layout.h
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top - a top users display for Unix
  *
  *  This file defines the default locations on the screen for various parts
@@ -43,6 +47,7 @@
 #define  X_LASTPIDWIDTH 13
 #define  X_LOADAVE	27
 #define  Y_LOADAVE	0
+#define  X_LOADAVE_NOMPID	15
 #define  X_LOADAVEWIDTH 7
 #define  X_MINIBAR      50
 #define  Y_MINIBAR      0
diff --git a/contrib/top/loadavg.h b/contrib/top/loadavg.h
index c73fc7d..8090520 100644
--- a/contrib/top/loadavg.h
+++ b/contrib/top/loadavg.h
@@ -79,7 +79,7 @@ typedef double pctcpu;
 #endif
 
 #ifdef FIXED_LOADAVG
-  typedef long load_avg;
+  typedef fixpt_t load_avg;
 # define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
 # define intload(i) ((int)((i) * FIXED_LOADAVG))
 #else
diff --git a/contrib/top/machine.h b/contrib/top/machine.h
index f727d3b..2745b86 100644
--- a/contrib/top/machine.h
+++ b/contrib/top/machine.h
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  This file defines the interface between top and the machine-dependent
  *  module.  It is NOT machine dependent and should not need to be changed
  *  for any specific machine.
@@ -56,6 +60,8 @@ struct statics
     char **kernel_names;	/* optional */
     time_t boottime;		/* optional */
     int modemax;		/* optional */
+    int ncpus;
+    int maxcpus;
     struct {
 	unsigned int fullcmds : 1;
 	unsigned int idle : 1;
@@ -85,6 +91,8 @@ struct system_info
     int    *kernel;
     long   *memory;
     long   *swap;
+    struct timeval boottime;
+    int    ncpus;
 };
 
 /* cpu_states is an array of percentages * 10.  For example, 
@@ -99,6 +107,7 @@ struct system_info
 struct process_select
 {
     int idle;		/* show idle processes */
+    int self;		/* show self */
     int system;		/* show system processes */
     int fullcmd;	/* show full command */
     int usernames;      /* show usernames */
@@ -106,6 +115,8 @@ struct process_select
     char *command;	/* only this command (unless == NULL) */
     int mode;		/* select display mode (0 is default) */
     int threads;	/* show threads separately */
+    int wcpu;		/* show weighted cpu */
+    int jail;		/* show jail ID */
 };
 
 /* routines defined by the machine dependent module */
@@ -113,7 +124,7 @@ int machine_init(struct statics *);
 void get_system_info(struct system_info *);
 caddr_t get_process_info(struct system_info *, struct process_select *, int);
 char *format_header(char *);
-char *format_next_process(caddr_t, char *(*)(int));
+char *format_next_process(struct process_select *sel, caddr_t, char *(*)(int));
 int proc_owner(int);
 #ifdef HAVE_FORMAT_PROCESS_HEADER
 
diff --git a/contrib/top/screen.c b/contrib/top/screen.c
index 398a858..a059bf9 100644
--- a/contrib/top/screen.c
+++ b/contrib/top/screen.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
  */
@@ -204,7 +208,9 @@ screen_getsize()
 #endif /* TIOCGSIZE */
 #endif /* TIOCGWINSZ */
 
-    (void) strcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1));
+    (void) strncpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1),
+	sizeof(lower_left) - 1);
+    lower_left[sizeof(lower_left) - 1] = '\0';
 }
 
 int
@@ -315,7 +321,8 @@ screen_readtermcap(int interactive)
     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
 
     /* set convenience strings */
-    (void) strcpy(home, tgoto(tc_cursor_motion, 0, 0));
+    (void) strncpy(home, tgoto(tc_cursor_motion, 0, 0), sizeof(home) - 1);
+    home[sizeof(home) - 1] = '\0';
     /* (lower_left is set in screen_getsize) */
 
     /* get the actual screen size with an ioctl, if needed */
diff --git a/contrib/top/sigconv.awk b/contrib/top/sigconv.awk
index 4f0ef60..18543cc 100644
--- a/contrib/top/sigconv.awk
+++ b/contrib/top/sigconv.awk
@@ -28,6 +28,8 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+# $FreeBSD$
+
 #
 # Awk script converts an include file with definitions for signal names
 # in to a predefined array that associates the signal numbers to the names.
@@ -45,7 +47,7 @@ BEGIN		{
 		    print "struct sigdesc sigdesc[] = {"
 		}
 
-/^#define[ \t][ \t]*SIG[A-Z]/	{
+/^#define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $3);
 				    if (siglist[j] != "") next;
@@ -57,7 +59,7 @@ BEGIN		{
 				    siglist[j] = sprintf("\"%s\",\t%2d", \
 						substr(str, 4), j);
 				}
-/^#[ \t]*define[ \t][ \t]*SIG[A-Z]/	{
+/^#[ \t]*define[ \t][ \t]*SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $4);
 				    if (siglist[j] != "") next;
@@ -66,10 +68,10 @@ BEGIN		{
 				    if (nsig < j)
 					nsig = j;
 
-				    siglist[j] = sprintf("\"%s\",\t%2d", \
+				    siglist[j] = sprintf("\"%s\",\t%2d,", \
 						substr(str, 4), j);
 				}
-/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]/	{
+/^#[ \t]*define[ \t][ \t]*_SIG[A-Z]+[0-9]*[ \t]/	{
 
 				    j = sprintf("%d", $4);
 				    if (siglist[j] != "") next;
diff --git a/contrib/top/top.c b/contrib/top/top.c
index a9dccd8..077b887 100644
--- a/contrib/top/top.c
+++ b/contrib/top/top.c
@@ -30,6 +30,10 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+/*
+ * $FreeBSD$
+ */
+
 char *copyright =
     "Copyright (c) 1984 through 2008, William LeFebvre";
 
@@ -40,9 +44,10 @@ char *copyright =
  */
 
 #include "os.h"
+#include <errno.h>
 #include <signal.h>
-#include <setjmp.h>
 #include <ctype.h>
+#include <setjmp.h>
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
@@ -113,6 +118,7 @@ static jmp_buf jmp_int;
 
 /* globals */
 char *myname = "top";
+int pcpu_stats = No;
 
 void
 quit(int status)
@@ -333,15 +339,15 @@ do_arguments(globalstate *gstate, int ac, char **av)
     optind = 1;
 
 #ifdef HAVE_GETOPT_LONG
-    while ((i = getopt_long(ac, av, "CDSITabcinqtuvs:d:U:o:m:", longopts, NULL)) != -1)
+    while ((i = getopt_long(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P", longopts, NULL)) != -1)
 #else
-    while ((i = getopt(ac, av, "CDSITabcinqtuvs:d:U:o:m:")) != EOF)
+    while ((i = getopt(ac, av, "CDEHSITabcijnqtuvs:d:U:o:m:P")) != EOF)
 #endif
     {
 	switch(i)
 	{
 #ifdef ENABLE_COLOR
-	case 'C':
+	case 'E':
 	    gstate->use_color = !gstate->use_color;
 	    break;
 #endif
@@ -414,8 +420,29 @@ do_arguments(globalstate *gstate, int ac, char **av)
 	    break;
 
 	case 'm':
-	    i = atoi(optarg);
-	    gstate->pselect.mode = i;
+	    if (strcmp(optarg, "io") == 0) {
+		gstate->pselect.mode = DISP_IO;
+	    } else if (strcmp(optarg, "cpu") == 0) {
+		gstate->pselect.mode = DISP_CPU;
+	    } else {
+		fprintf(stderr,
+		    "%s: warning: `-m' option can only take args "
+		    "'io' or 'cpu'\n",
+		    myname);
+		    exit(1);
+	    }
+	    break;
+
+	case 'H':
+	    gstate->pselect.threads = !gstate->pselect.threads;
+	    break;
+
+	case 'C':
+	    gstate->pselect.wcpu = !gstate->pselect.wcpu;
+	    break;
+
+	case 'j':
+	    gstate->pselect.jail = !gstate->pselect.jail;
 	    break;
 
 	case 'S':
@@ -437,7 +464,7 @@ do_arguments(globalstate *gstate, int ac, char **av)
 	    break;
 
 	case 't':
-	    gstate->pselect.threads = !gstate->pselect.threads;
+	    gstate->pselect.self = !gstate->pselect.self;
 	    break;
 
 	case 'q':		/* be quick about it */
@@ -453,10 +480,14 @@ do_arguments(globalstate *gstate, int ac, char **av)
 	    }
 	    break;
 
+	case 'P':
+	    pcpu_stats = Yes;
+	    break;
+
 	default:
-	    fprintf(stderr, "\
-Top version %s\n\
-Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
+	    fprintf(stderr,
+"Top version %s\n"
+"Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n",
 		    version_string(), myname);
 	    exit(EX_USAGE);
 	}
@@ -552,7 +583,8 @@ do_display(globalstate *gstate)
 	i_header(hdr);
 	for (i = 0; i < active_procs; i++)
 	{
-	    i_process(i, format_next_process(processes, gstate->get_userid));
+	    i_process(i,
+		format_next_process(&(gstate->pselect), processes, gstate->get_userid));
 	}
 	i_endscreen();
 	if (gstate->smart_terminal)
@@ -563,8 +595,8 @@ do_display(globalstate *gstate)
     else
     {
 	u_loadave(system_info.last_pid, system_info.load_avg);
-	u_uptime(&(gstate->statics->boottime), &curr_time);
 	i_timeofday(&curr_time);
+	u_uptime(&(gstate->statics->boottime), &curr_time);
 	u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads);
 	u_cpustates(system_info.cpustates);
 	u_kernel(system_info.kernel);
@@ -574,7 +606,8 @@ do_display(globalstate *gstate)
 	u_header(hdr);
 	for (i = 0; i < active_procs; i++)
 	{
-	    u_process(i, format_next_process(processes, gstate->get_userid));
+	    u_process(i,
+		format_next_process(&(gstate->pselect), processes, gstate->get_userid));
 	}
 	u_endscreen();
     }
@@ -609,6 +642,7 @@ do_command(globalstate *gstate)
     struct timeval now;
     fd_set readfds;
     unsigned char ch;
+    int sel_ret = 0;
 
     /* calculate new refresh time */
     gstate->refresh = gstate->now;
@@ -635,7 +669,10 @@ do_command(globalstate *gstate)
 	FD_SET(STDIN_FILENO, &readfds);
 
 	/* wait for something to read or time out */
-	if (select(32, &readfds, NULL, NULL, &wait) > 0)
+	sel_ret = select(2, &readfds, NULL, NULL, &wait);
+	if (sel_ret < 0 && errno != EINTR)
+	    quit(0);
+	if (sel_ret > 0)
 	{
 	    /* read it */
 	    if (read(STDIN_FILENO, &ch, 1) != 1)
@@ -758,11 +795,15 @@ main(int argc, char *argv[])
 
     /* preset defaults for process selection */
     gstate->pselect.idle = Yes;
+    gstate->pselect.self = -1;
+    gstate->pselect.threads = No;
+    gstate->pselect.wcpu = 1;
+    gstate->pselect.jail = No;
     gstate->pselect.system = No;
     gstate->pselect.fullcmd = No;
     gstate->pselect.command = NULL;
     gstate->pselect.uid = -1;
-    gstate->pselect.mode = 0;
+    gstate->pselect.mode = DISP_CPU;
 
     /* use a large buffer for stdout */
 #ifdef HAVE_SETVBUF
@@ -808,6 +849,8 @@ main(int argc, char *argv[])
     {
 	exit(EX_SOFTWARE);
     }
+    if (pcpu_stats == No)
+	statics.ncpus = 1;
 
     /* create a helper list of sort order names */
     gstate->order_namelist = string_list(statics.order_names);
@@ -881,6 +924,7 @@ main(int argc, char *argv[])
     }
 
     /* check for infinity and for overflowed screen */
+    gstate->topn = gstate->max_topn;
     if (gstate->topn == Infinity)
     {
 	gstate->topn = INT_MAX;
@@ -920,6 +964,7 @@ main(int argc, char *argv[])
 	    quit(EX_OSERR);
 	    /*NOTREACHED*/
 	}
+	gstate->topn = gstate->max_topn;
 
 	/* set up for a full redraw, and get the current line count */
 	gstate->fulldraw = Yes;
diff --git a/contrib/top/top.h b/contrib/top/top.h
index 24b1abb..ca7799a 100644
--- a/contrib/top/top.h
+++ b/contrib/top/top.h
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top - a top users display for Berkeley Unix
  *
  *  General (global) definitions
@@ -77,4 +81,8 @@ struct ext_decl {
 void gettime(struct timeval *);
 void quit(int);
 
+extern int pcpu_stats;
+
+enum displaymodes { DISP_CPU = 0, DISP_IO, DISP_MAX };
+
 #endif /* _TOP_H_ */
diff --git a/contrib/top/username.c b/contrib/top/username.c
index 9866bff..6a0c9a1 100644
--- a/contrib/top/username.c
+++ b/contrib/top/username.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
  */
@@ -47,7 +51,9 @@
 
 #include "os.h"
 
+#include <sys/types.h>
 #include <pwd.h>
+#include <utmp.h>
 
 #include "top.h"
 #include "utils.h"
@@ -66,7 +72,7 @@
 
 struct hash_data {
     int    uid;
-    char   name[MAXLOGNAME];  /* big enough? */
+    char   name[UT_NAMESIZE + 1];  /* big enough? */
     time_t expire;
 };
 
@@ -114,7 +120,7 @@ username(int uid)
     {
 	if ((pw = getpwuid(uid)) != NULL)
 	{
-	    strncpy(data->name, pw->pw_name, MAXLOGNAME-1);
+	    strncpy(data->name, pw->pw_name, UT_NAMESIZE);
 	    data->expire = now + EXPIRETIME;
 	    dprintf("username: updating %d with %s, expires %d\n",
 		    data->uid, data->name, data->expire);
diff --git a/contrib/top/utils.c b/contrib/top/utils.c
index 856005a..0875650 100644
--- a/contrib/top/utils.c
+++ b/contrib/top/utils.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  *  Top users/processes display for Unix
  *  Version 3
  */
@@ -454,9 +458,13 @@ percentages(int cnt, int *out, long *new, long *old, long *diffs)
 
     /* calculate percentages based on overall change, rounding up */
     half_total = total_change / 2l;
-    for (i = 0; i < cnt; i++)
-    {
-	*out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
+
+    /* Do not divide by 0. Causes Floating point exception */
+    if (total_change != 0) {
+	for (i = 0; i < cnt; i++)
+	{
+	    *out++ = (int)((*diffs++ * 1000 + half_total) / total_change);
+	}
     }
 
     /* return the total in case the caller wants to use it */
diff --git a/usr.bin/top/Makefile b/usr.bin/top/Makefile
index f908b9e..4a317ad 100644
--- a/usr.bin/top/Makefile
+++ b/usr.bin/top/Makefile
@@ -7,8 +7,8 @@ PROG=	top
 SRCS=	commands.c display.c machine.c screen.c top.c \
 	username.c utils.c version.c color.c hash.c
 SRCS+=	sigdesc.h config.h
-CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
-CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I.
+CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER -DSIGWINCH -DHAS_SHOWTHREADS
+CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. -Wall -g
 
 #
 # The table size should be a prime number approximately twice as
@@ -45,7 +45,7 @@ config.h: config.h.in
 	@${ECHO} Making config.h from config.h.in
 	@sed \
 		-e 's/@DEFAULT_TOPN@/30/' \
-		-e 's/@DEFAULT_DELAY@/5/' \
+		-e 's/@DEFAULT_DELAY@/2/' \
 		-e 's/@HAVE_GETOPT_LONG@/1/' \
 		-e 's/@ENABLE_KILL@/1/' \
 		< config.h.in > config.h
@@ -55,7 +55,7 @@ top.1.local: top.1.in
 	@${ECHO} Making top.1.local from top.1.in
 	@sed \
 		-e 's/@DEFAULT_TOPN@/30/' \
-		-e 's/@DEFAULT_DELAY@/5/' \
+		-e 's/@DEFAULT_DELAY@/2/' \
 		-e 's/@HAVE_GETOPT_LONG@/1/' \
 		-e 's/@ENABLE_KILL@/1/' \
 		< ${TOPDIR}/top.1.in > top.1.local
diff --git a/usr.bin/top/machine.c b/usr.bin/top/machine.c
index e26bca7..46ec54a 100644
--- a/usr.bin/top/machine.c
+++ b/usr.bin/top/machine.c
@@ -31,6 +31,10 @@
  */
 
 /*
+ * $FreeBSD$
+ */
+
+/*
  * top - a top users display for Unix
  *
  * SYNOPSIS:  For FreeBSD 5.x, 6.x, 7.x, 8.x
@@ -41,6 +45,7 @@
  * Order support hacked in from top-3.5beta6/machine/m_aix41.c
  *   by Monte Mitzelfelt
  * Ported to FreeBSD 5.x and higher by William LeFebvre
+ * Updates for the import in FreeBSD 7 by Edwin Groothuis.
  *
  * AUTHOR:  Christos Zoulas <christos at ee.cornell.edu>
  *          Steven Wallace  <swallace at freebsd.org>
@@ -59,6 +64,7 @@
 #include <nlist.h>
 #include <math.h>
 #include <kvm.h>
+#include <paths.h>
 #include <pwd.h>
 #include <sys/errno.h>
 #include <sys/sysctl.h>
@@ -240,21 +246,27 @@ static struct sysctl_mib mibs[] = {
 #define VFS_BUFSPACE 20
     { "kern.cp_time" },
 #define K_CP_TIME 21
+    { "kern.cp_times" },
+#define K_CP_TIMES 22
 #ifdef HAS_SHOWTHREADS
     { "kern.proc.all" },
 #else
     { "kern.proc.proc" },
 #endif
-#define K_PROC 22
+#define K_PROC 23
+#define K_LASTPID 24
+    { "kern.lastpid" },
     { NULL }
 };
     
+/* Local pointer */
+static struct statics *statics;
 
 /* these are for calculating cpu state percentages */
 
-static long cp_time[CPUSTATES];
-static long cp_old[CPUSTATES];
-static long cp_diff[CPUSTATES];
+static long *cp_time;	/* num_cpus * CPUSTATES */
+static long *cp_old;	/* num_cpus * CPUSTATES */
+static long *cp_diff;	/* num_cpus * CPUSTATES */
 
 /* these are for detailing the process states */
 
@@ -267,7 +279,7 @@ char *procstatenames[] = {
 
 /* these are for detailing the cpu states */
 
-int cpu_states[CPUSTATES];
+int *cpu_states;
 char *cpustatenames[] = {
     "user", "nice", "system", "interrupt", "idle", NULL
 };
@@ -380,12 +392,12 @@ static int show_threads = 0;
 
 /* sorting orders. first is default */
 char *ordernames[] = {
-    "cpu", "size", "res", "time", "pri", "io", "pid", NULL
+    "cpu", "size", "res", "time", "pri", "io", "pid", "jid", NULL
 };
 
 /* compare routines */
 int proc_compare(), compare_size(), compare_res(), compare_time(),
-    compare_prio(), compare_io(), compare_pid();
+    compare_prio(), compare_io(), compare_pid(), compare_jid();
 
 int (*proc_compares[])() = {
     proc_compare,
@@ -395,6 +407,7 @@ int (*proc_compares[])() = {
     compare_prio,
     compare_io,
     compare_pid,
+    compare_jid,
     NULL
 };
 
@@ -472,7 +485,7 @@ swap_getdata(long long *retavail, long long *retfree)
  *  	"size" is the size of the buffer (and the object to retrieve).
  *      Return 0 on success, -1 on any kind of failure.
  */
-
+#ifdef notmore
 static int
 getkval(unsigned long offset, int *ptr, int size)
 
@@ -486,6 +499,7 @@ getkval(unsigned long offset, int *ptr, int size)
     }
     return(-1);
 }
+#endif
 
 int
 get_sysctl_mibs()
@@ -548,6 +562,13 @@ fmt_pid(char *buf, int sz, struct kinfo_proc *pp)
 {
     return snprintf(buf, sz, "%6d", PP(pp, pid));
 }
+    
+int
+fmt_jid(char *buf, int sz, struct kinfo_proc *pp)
+
+{
+    return snprintf(buf, sz, "%3d", pp->ki_jid);
+}
 
 int
 fmt_username(char *buf, int sz, struct kinfo_proc *pp)
@@ -699,8 +720,8 @@ fmt_command(char *buf, int sz, struct kinfo_proc *pp)
     int inmem;
     char cmd[MAX_COLS];
     char *bufp;
-    struct pargs pargs;
-    int len;
+    char **args;
+    int argc;
 
 #if OSMAJOR <= 4
     inmem = (PP(pp, flag) & P_INMEM);
@@ -711,37 +732,21 @@ fmt_command(char *buf, int sz, struct kinfo_proc *pp)
     if (show_fullcmd && inmem)
     {
         /* get the pargs structure */
-        if (getkval((unsigned long)PP(pp, args), (int *)&pargs, sizeof(pargs)) != -1)
+        if ((args = kvm_getargv(kd, pp, sz)) != NULL)
         {
-            /* determine workable length */
-            if ((len = pargs.ar_length) >= MAX_COLS)
-            {
-                len = MAX_COLS - 1;
-            }
-
-            /* get the string from that */
-            if (len > 0 && getkval((unsigned long)PP(pp, args) +
-				   sizeof(pargs.ar_ref) +
-				   sizeof(pargs.ar_length),
-				   (int *)cmd, len) != -1)
-            {
-                /* successfull retrieval: now convert nulls in to spaces */
-                bufp = cmd;
-                while (len-- > 0)
-                {
-                    if (*bufp == '\0')
-                    {
-                        *bufp = ' ';
-                    }
-                    bufp++;
-                }
-
-                /* null terminate cmd */
-                *--bufp = '\0';
-
-		/* format cmd as our answer */
-		return snprintf(buf, sz, "%s", cmd);
-            }
+	    /* successfull retrieval: now convert nulls in to spaces */
+	    bufp = cmd;
+	    cmd[0] = '\0';
+	    argc = 0;
+	    while (args[argc] != NULL)
+	    {
+		if (cmd[0] != '\0')
+		    strcat(cmd, " ");
+		strcat(cmd, args[argc++]);
+	    }
+
+	    /* format cmd as our answer */
+	    return snprintf(buf, sz, "%s", cmd);
         }
     }
 
@@ -795,16 +800,29 @@ int
 fmt_iopct(char *buf, int sz, struct kinfo_proc *pp)
 
 {
-    return snprintf(buf, sz, "%6.2f", (SP(pp, totalio) * 100.) / total_io);
+    return snprintf(buf, sz, "%6.2f%%", (SP(pp, totalio) * 100.) / total_io);
 }
 
 
-struct proc_field proc_field[] = {
+/*
+ * The order of proc_fields and proc_field must match!
+ */
+
+enum proc_fields {
+	FIELD_PID = 0, FIELD_JID, FIELD_USERNAME, FIELD_UID,
+	FIELD_THR, FIELD_PRI, FIELD_NICE, FIELD_SIZE, FIELD_RES,
+	FIELD_STATE, FIELD_FLG, FIELD_C, FIELD_TIME, FIELD_CPU,
+	FIELD_WCPU, FIELD_COMMAND, FIELD_VCSW, FIELD_IVCSW, FIELD_READ,
+	FIELD_WRITE, FIELD_FAULT, FIELD_TOTAL, FIELD_PERCENT
+
+};
+
+#define MAX_FIELDS 25
+struct proc_field proc_field[MAX_FIELDS] = {
     { "PID", 6, 1, 0, fmt_pid },
+    { "JID", 3, 1, 0, fmt_jid },
     { "USERNAME", 8, 0, 0, fmt_username },
-#define FIELD_USERNAME 1
     { "UID", 6, 1, 0, fmt_uid },
-#define FIELD_UID 2
     { "THR", 3, 1, 0, fmt_thr },
     { "PRI", 3, 1, 0, fmt_pri },
     { "NICE", 4, 1, 0, fmt_nice },
@@ -815,6 +833,7 @@ struct proc_field proc_field[] = {
     { "C", 1, 0, 0, fmt_c },
     { "TIME", 6, 1, 0, fmt_time },
     { "CPU", 6, 1, 0, fmt_cpu },
+    { "WCPU", 6, 1, 0, fmt_cpu },
     { "COMMAND", 7, 0, 0, fmt_command },
     { "VCSW", 6, 1, 0, fmt_vcsw },
     { "IVCSW", 6, 1, 0, fmt_ivcsw },
@@ -825,10 +844,8 @@ struct proc_field proc_field[] = {
     { "PERCENT", 7, 1, 0, fmt_iopct },
     { NULL, 0, 0, 0, NULL }
 };
-#define MAX_FIELDS 24
 
 static int mode0_display[MAX_FIELDS];
-static int mode0thr_display[MAX_FIELDS];
 static int mode1_display[MAX_FIELDS];
 
 int
@@ -867,26 +884,35 @@ field_subst(int *fp, int old, int new)
 }
 
 int
-machine_init(struct statics *statics)
+machine_init(struct statics *_statics)
 
 {
     int i = 0;
     size_t len;
     int *ip;
 
+    statics = _statics;	/* Keep a local copy */
+
     struct timeval boottime;
 
-    len = sizeof(smpmode);
-    if ((sysctlbyname("machdep.smp_active", &smpmode, &len, NULL, 0) < 0 &&
-         sysctlbyname("smp.smp_active", &smpmode, &len, NULL, 0) < 0) ||
-	len != sizeof(smpmode))
-    {
-	smpmode = 0;
-    }
-    smpmode = smpmode != 0;
+    len = sizeof(statics->ncpus);
+    if (sysctlbyname("kern.smp.cpus", &(statics->ncpus), &len, NULL, 0) < 0 ||
+	len != sizeof(statics->ncpus))
+	statics->ncpus = 1;
+    smpmode = statics->ncpus != 1;
+
+    len = sizeof(statics->maxcpus);
+    if (sysctlbyname("kern.smp.maxcpus", &(statics->maxcpus), &len, NULL, 0) < 0 ||
+	len != sizeof(statics->maxcpus))
+	statics->maxcpus = 1;
+
+    cp_time = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cp_old  = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cp_diff = (long *)calloc(statics->maxcpus * CPUSTATES, sizeof(long));
+    cpu_states = (int *)calloc(statics->maxcpus * CPUSTATES, sizeof(int));
 
     /* kvm_open the active kernel: its okay if this fails */
-    kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL);
+    kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, NULL);
 
     /* get boot time */
     len = sizeof(boottime);
@@ -928,7 +954,7 @@ machine_init(struct statics *statics)
     statics->boottime = boottime.tv_sec;
     statics->swap_names = swapnames;
     statics->order_names = ordernames;
-    statics->flags.warmup = 1;
+    statics->flags.warmup = 0;
     statics->modemax = 2;
 #ifdef HAS_SHOWTHREADS
     statics->flags.threads = 1;
@@ -937,9 +963,10 @@ machine_init(struct statics *statics)
     /* we need kvm descriptor in order to show full commands */
     statics->flags.fullcmds = kd != NULL;
 
-    /* set up the display indices for mode0 */
+    /* set up the display indices for mode0 (DISP_CPU) */
     ip = mode0_display;
     *ip++ = field_index("PID");
+    *ip++ = field_index("JID");
     *ip++ = field_index("USERNAME");
 #ifdef HAS_THREADS
     *ip++ = field_index("THR");
@@ -950,35 +977,16 @@ machine_init(struct statics *statics)
     *ip++ = field_index("RES");
     *ip++ = field_index("STATE");
     *ip++ = field_index("FLG");
-    if (smpmode)
-	*ip++ = field_index("C");
-    *ip++ = field_index("TIME");
-    *ip++ = field_index("CPU");
-    *ip++ = field_index("COMMAND");
-    *ip = -1;
-
-#ifdef HAS_SHOWTHREADS
-    /* set up the display indices for mode0 showing threads */
-    ip = mode0thr_display;
-    *ip++ = field_index("PID");
-    *ip++ = field_index("USERNAME");
-    *ip++ = field_index("PRI");
-    *ip++ = field_index("NICE");
-    *ip++ = field_index("SIZE");
-    *ip++ = field_index("RES");
-    *ip++ = field_index("STATE");
-    *ip++ = field_index("FLG");
-    if (smpmode)
-	*ip++ = field_index("C");
+    *ip++ = field_index("C");
     *ip++ = field_index("TIME");
     *ip++ = field_index("CPU");
     *ip++ = field_index("COMMAND");
     *ip = -1;
-#endif
 
-    /* set up the display indices for mode1 */
+    /* set up the display indices for mode1 (DISP_IO) */
     ip = mode1_display;
     *ip++ = field_index("PID");
+    *ip++ = field_index("JID");
     *ip++ = field_index("USERNAME");
     *ip++ = field_index("VCSW");
     *ip++ = field_index("IVCSW");
@@ -1032,9 +1040,9 @@ void
 get_system_info(struct system_info *si)
 
 {
-    long total;
     struct timeval thistime;
     struct timeval timediff;
+    int i;
 
     /* timestamp and time difference */
     gettimeofday(&thistime, 0);
@@ -1049,11 +1057,19 @@ get_system_info(struct system_info *si)
 	(void) memset(si->load_avg, 0, sizeof(si->load_avg));
     }
 
-    /* get the cp_time array */
-    (void)get_sysctl(K_CP_TIME, &cp_time, sizeof(cp_time));
+    /* get the cp_times array ... */
+    if (statics->ncpus == 1) {
+	(void)get_sysctl(K_CP_TIME, cp_time, sizeof(long) * CPUSTATES);
+    } else {
+	(void)get_sysctl(K_CP_TIMES, cp_time, sizeof(long) * CPUSTATES * statics->maxcpus);
+    }
 
-    /* convert cp_time counts to percentages */
-    total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
+    /* ... and convert cp_time counts to percentages */
+    for (i = 0; i < statics->ncpus; i++) {
+	percentages(CPUSTATES,
+	    &cpu_states[i * CPUSTATES], &cp_time[i * CPUSTATES],
+	    &cp_old[i * CPUSTATES], &cp_diff[i * CPUSTATES]);
+    }
 
     /* sum memory & swap statistics */
     {
@@ -1136,8 +1152,7 @@ get_system_info(struct system_info *si)
     si->memory = memory_stats;
     si->swap = swap_stats;
 
-    si->last_pid = -1;
-
+    get_sysctl(K_LASTPID, &si->last_pid, sizeof(si->last_pid));
     lasttime = thistime;
 }
 
@@ -1399,10 +1414,17 @@ format_process_header(struct process_select *sel, caddr_t handle, int count)
 
     /* mode & threads dictate format */
     fi = display_fields =
-	sel->mode == 0 ?
-	(sel->threads == 0 ? mode0_display : mode0thr_display) :
+	sel->mode == DISP_CPU ?
+	mode0_display :
 	mode1_display;
 
+    /* set CPU vs Weighted CPU */
+    if (sel->wcpu) {
+	field_subst(fi, FIELD_CPU, FIELD_WCPU);
+    } else {
+	field_subst(fi, FIELD_WCPU, FIELD_CPU);
+    }
+
     /* set username field correctly */
     if (!sel->usernames)
     {
@@ -1444,7 +1466,15 @@ format_process_header(struct process_select *sel, caddr_t handle, int count)
     p = p_header;
     while (*fi != -1)
     {
+	if ((*fi == FIELD_JID && sel->jail == 0) ||
+	    (*fi == FIELD_C && smpmode == 0) ||
+	    (*fi == FIELD_THR && sel->threads != 0)) {
+	    fi++;
+	    continue;
+	}
+
 	fp = &(proc_field[*fi++]);
+
 	if (fp->min_screenwidth <= cols)
 	{
 	    p += sprintf(p, fp->rjust ? "%*s" : "%-*s", fp->width, fp->name);
@@ -1459,14 +1489,13 @@ format_process_header(struct process_select *sel, caddr_t handle, int count)
 static char fmt[MAX_COLS];		/* static area where result is built */
 
 char *
-format_next_process(caddr_t handle, char *(*get_userid)(int))
+format_next_process(struct process_select *sel, caddr_t handle, char *(*get_userid)(int))
 
 {
     struct kinfo_proc *pp;
     struct handle *hp;
     struct proc_field *fp;
     int *fi;
-    int i;
     int cols;
     char *p;
     int len;
@@ -1486,9 +1515,16 @@ format_next_process(caddr_t handle, char *(*get_userid)(int))
     /* build output by field */
     p = fmt;
     len = MAX_COLS;
-    while ((i = *fi++) != -1)
+    while (*fi != -1)
     {
-	fp = &(proc_field[i]);
+	if ((*fi == FIELD_JID && sel->jail == 0) ||
+	    (*fi == FIELD_C && smpmode == 0) ||
+	    (*fi == FIELD_THR && sel->threads != 0)) {
+		fi++;
+		continue;
+	}
+
+	fp = &(proc_field[*fi++]);
 	if (len > 0 && fp->min_screenwidth <= cols)
 	{
 	    x = (*(fp->format))(p, len, pp);
@@ -1570,6 +1606,9 @@ static unsigned char sorted_state[] =
 #define ORDERKEY_PID \
   if ( (result = PP(p1, pid) - PP(p2, pid)) == 0)
 
+#define ORDERKEY_JID \
+  if ( (result = PP(p2, jid) - PP(p1, jid)) == 0)
+
 /* compare_cpu - the comparison function for sorting by cpu percentage */
 
 int
@@ -1747,6 +1786,26 @@ compare_pid(struct proc **pp1, struct proc **pp2)
     return(result);
 }
 
+/* compare_jid - the comparison function for sorting by jail id */
+
+int
+compare_jid(struct proc **pp1, struct proc **pp2)
+
+{
+    struct kinfo_proc *p1;
+    struct kinfo_proc *p2;
+    int result;
+
+    /* remove one level of indirection */
+    p1 = *(struct kinfo_proc **) pp1;
+    p2 = *(struct kinfo_proc **) pp2;
+
+    ORDERKEY_JID
+    ;
+
+    return(result);
+}
+
 /*
  * proc_owner(pid) - returns the uid that owns process "pid", or -1 if
  *		the process does not exist.
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list