top - Import 3.8beta1 sources into new vendor/TOP branch. vendor/TOP
authorJan Lentfer <Jan.Lentfer@web.de>
Sat, 28 Nov 2009 19:16:07 +0000 (20:16 +0100)
committerJan Lentfer <Jan.Lentfer@web.de>
Sat, 28 Nov 2009 19:16:07 +0000 (20:16 +0100)
38 files changed:
contrib/top/Changes [new file with mode: 0644]
contrib/top/FAQ [new file with mode: 0644]
contrib/top/INSTALL [new file with mode: 0644]
contrib/top/LICENSE [new file with mode: 0644]
contrib/top/Porting [new file with mode: 0644]
contrib/top/README [new file with mode: 0644]
contrib/top/README.DELETED [new file with mode: 0644]
contrib/top/README.DRAGONFLY [new file with mode: 0644]
contrib/top/Y2K [new file with mode: 0644]
contrib/top/ap_snprintf.c [new file with mode: 0644]
contrib/top/boolean.h [new file with mode: 0644]
contrib/top/color.c [new file with mode: 0644]
contrib/top/color.h [new file with mode: 0644]
contrib/top/commands.c [new file with mode: 0644]
contrib/top/commands.h [new file with mode: 0644]
contrib/top/display.c [new file with mode: 0644]
contrib/top/display.h [new file with mode: 0644]
contrib/top/getopt.c [new file with mode: 0644]
contrib/top/globalstate.h [new file with mode: 0644]
contrib/top/hash.c [new file with mode: 0644]
contrib/top/hash.h [new file with mode: 0644]
contrib/top/hash.m4c [new file with mode: 0755]
contrib/top/hash.m4h [new file with mode: 0755]
contrib/top/layout.h [new file with mode: 0644]
contrib/top/loadavg.h [new file with mode: 0644]
contrib/top/machine.h [new file with mode: 0644]
contrib/top/message.h [new file with mode: 0644]
contrib/top/os.h [new file with mode: 0644]
contrib/top/screen.c [new file with mode: 0644]
contrib/top/screen.h [new file with mode: 0644]
contrib/top/top.c [new file with mode: 0644]
contrib/top/top.h [new file with mode: 0644]
contrib/top/username.c [new file with mode: 0644]
contrib/top/username.h [new file with mode: 0644]
contrib/top/utils.c [new file with mode: 0644]
contrib/top/utils.h [new file with mode: 0644]
contrib/top/version.c [new file with mode: 0644]
contrib/top/version.h [new file with mode: 0644]

diff --git a/contrib/top/Changes b/contrib/top/Changes
new file mode 100644 (file)
index 0000000..98fc82f
--- /dev/null
@@ -0,0 +1,949 @@
+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@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.
+       Included m_irixsgi.c from some source that's been floating around
+       SGI.  Don't yet know how it compares to m_irix62.
+
+Fri Mar 10 2000 - wnl (3.5beta11)
+       top.c: avoid potential loop if stdout gets closed, use macro
+       for p_active to avoid collision with system macros.
+       m_sunos5: widened some fields to accomodate 5.8.
+       m_decosf1: added ordering support
+       m_irix62_64: provides 64-bit module based on m_irix62.
+       m_irix62: skip bogus files in /proc directory
+       m_svr42MP and m_svr5: complete replacement with updated copies
+       m_mtxinu: complete replacement with updated copies
+       m_aix43: new module for 4.3
+       getans: replaced with a Bourne shell script
+
+Mon Mar  6 2000 - wnl (3.5beta10)
+       m_sunos5.c: workaround for curses bug: ensure that TERMINFO has
+       a value.
+
+Fri Jan 15 1999 - wnl (3.5beta10)
+       top.c: now check return code from read to avoid looping on eof.
+       top.c: delay of 0 now only valid for root.
+       decosf1.c: patches from Rainer Orth should fix most of the
+       problems with this module (including the display of certain
+       processes and runtime errors).
+       sunos5.c: Rainer insisted on putting the slash back in the
+       state field ("run/4") and widened the field to accomodate it.
+       aix.c: widened PID field for 6-digit pids (shortened NICE field)
+       module macosx added, thanks to Andrew Townley.
+
+Fri Dec 18 1998 - wnl (3.5beta9)
+       Configure checks status of "make" and complains if it fails.
+
+Thu Dec 17 1998 - wnl (3.5beta9)
+       Added module sco5 from Mike Hopkirk.
+       Added module netbsd132 from moto kawasaki.
+
+Sun Oct 25 1998 - wnl (3.5beta9)
+       Added Casper's patches for sunos5 for the following:
+       produce same results as swap -s (5.5 and higher),
+       don't use system_pages kstat when /dev/kmem can be opened,
+       skip . and .. when reading /proc, replace use of SOLARIS24
+       with OSREV.
+
+Fri Sep 11 1998 - wnl (3.5beta9)
+       Added workaround to getans for the absence of $< in SCO Unix.
+
+Wed Jul  1 1998 - wnl (3.5beta9)
+       Changed structure member "errno" to "errnum" in commands.c.
+       Replaced hpux10 module with one from John Haxby.
+
+Fri Apr 17 1998 - wnl (3.5beta8)
+       Moved definition of _KMEMUSER earlier in m_sunos5.c.  This should
+       fix the compilation problem with gnu 2.7.2.3, obviating the need
+       for the fixinc.svr4 patch, but hopefully will not affect anything
+       else.
+       Added -DORDER to m_sunos4mp.c
+
+Tue Nov 18 1997 - wnl (3.5beta7)
+       Added gcc 2.7.2.3 patch for fixinc.svr4 and changed INSTALL and
+       FAQ to refer to it.
+       Added NetBSD HP9000 fix.  Hopefully it doesn't break other 
+       NetBSD platforms.
+
+Fri Oct 24 1997 - wnl (3.5beta7)
+       Modified m_dcosx.c to change uses of procdir to xprocdir, avoiding
+       a name clash with an include file (Bryn Parrott)
+
+Sat Oct 11 1997 - wnl (3.5beta6)
+       Incorporated Casper's patches for Solaris 2.6 and for the multi-
+       processor bug ("kstat finds too many cpus").
+
+Sun Jan 20 1996 - wnl (3.5beta5)
+       Fixed Casper's m_sunos5 module: there was a poor interaction with 
+       his use of OSREV and SunOS 5.5.1.
+
+Fri Dec 20 1996 - wnl (3.5beta4)
+       Replaced m_sunos5 with a reworked version by Casper Dik.  This one
+       should work under 2.6 and may not require that top be run setuid
+       to root under 2.5 or 2.6.  This also fixed a bug in m_sunos5 that
+       was introduced in beta3.
+       Fixed calculation of OSREV in Configure.
+
+Wed Nov 20 1996 - wnl (3.5beta3)
+       Incorporated contributed fixes to:  bsdos2, irix62, freebsd20,
+       ultrix4, sunos5.  Changed calculation of swap area in sunos5 (now
+       uses swapctl).  sunos5 now understands idled processors.  Changed
+       Configure to determine os revision using uname (when available)
+       and adding it to machine.c compiliation in Makefile as -DOSREV.
+       Changed calls to "exit" in modules to use "quit" instead.
+
+Oct 20 1996 - wnl (3.5beta3)
+       Removed "time" from list of ordering choices:  there's no easy way
+       to get cpu time for all processes (it's in the u area).
+
+Fri Oct 18 1996 - wnl (3.5beta3)
+       hpux10 and hpux9: using a better means for determining when a
+       process is idle.
+       decosf1 now includes utils.h.
+
+Fri Sep 13 1996 - wnl (3.5beta2)
+       Fixed Configure to build Make.desc in such a way that doesn't
+       require a long argument to sed.
+
+Thu Sep 12 1996 - wnl (3.5beta2)
+       Fixed bug in display.c that affected empty cpustate names.
+       Created hpux1010 module - a variant of hpux10 that does not use
+       struct proc or struct user (suitable for HP/UX 10.10).
+
+Wed Sep 11 1996 - wnl (3.5beta2)
+       Changes to sunos5 module:  Removed WCPU column since it is meaningless
+       on a SVR4-based system.  Added THR column to show number of threads
+       for each process.  This was not straightforward: the information is
+       not stored in prpsinfo but rather in prstatus.
+
+Tue Sep 10 1996 - wnl (3.5beta1)
+       Added patches for sunos4mp to provide order support.
+       Added irix62 module.
+       Changed prime.c to include stdio.h for printf prototype.
+       Added conditional code to os.h and utils.c to handle systems
+       where sys_errlist is defined in stdio.h (such as NetBSD).
+
+Mon Sep 09 1996 - wnl (3.5beta1)
+       Removed tar and shar rules from Makefile.X -- don't need them anymore.
+       Added -v option to display version number.  Updated man page.
+
+Thu Aug 29 1996 - wnl (3.4)
+       Replaced modules (from Tim Pugh): next 32, next40.
+       Fixed bug in username.c: hashing negative uids.
+
+Thu Aug 22 1996 - wnl (3.4beta3)
+       Patched modules:  ultrix4, sunos4, sunos5, utek, decosf1, irix5.
+       Added modules: next40, next32.
+       Fixed procstates update bug in display.c.
+       Fixed divide by zero bug in utils.c.
+       Fixed bad number in layout.h
+       Minor fixes to Configure.
+       Complete overhaul of FAQ.
+
+Tue Feb 13 1996 - wnl (3.4beta3)
+       Added convex module from Warren Vosper (originally written by
+       William Jones).
+
+Tue Feb 13 1996 - wnl (3.4beta2)
+       Fixed format_k in utils.c to calculate K and M values correctly.
+       Added check for gigabyte values ('G').  Changed sumamry_format
+       in display.c to use format_k where appropriate.
+       Changed creation of distribution tar file to place everything in
+       a top level directory.
+
+Tue Jan 30 1996 - wnl (3.4beta2)
+       Added m_aix41 module.  Added new tag type to module comments:
+       TERMCAP, which defined the library to use for a termcap library.
+       If no TERMCAP tag is found in the module's initial comment, then
+       Configure will default to "-ltermcap".  AIX needs this since it
+       put all the termcap routines in libcurses(!)
+
+       Added m_bsdos2 (found lingering in my mailbox).
+       Updated m_svr4 to include support for NCR multiprocessors.
+       Fixed small bug in utils.c
+
+Thu Jan 25 1996 - wnl (3.4beta1)
+       Fixed m_sunos5 invocation of gettimeofday to include "NULL" as
+       second argument.  This provides compatability with the Posix-
+       compliant template provided with SunOS 5.5, but doesn't hurt
+       previous versions since they do bother with a template for that
+       function.
+
+       Made changes (recommended by net users) to hpux10, ultrix4,
+       netbsd10, aux3 (replaced aux31).  Added module for linux.
+
+Fri Oct 10 1995 - wnl (3.4beta1)
+       Added user-contributed modules for SCO Unix, IRIX 5, HP/UX 10,
+       Pyramid DC/OSX.  Changed Configure so that it runs in environments
+       whose c-shells have no 'eval'(!).  Added support for multiple sort
+       ordering methods via the -o switch.  This option requires support
+       from the machine dependent module: such support was added to
+       sunos5 (thus sunos54) and sunos4.
+
+       display.c:  Changed CPU states display line to shorten the leading
+       tag if the data won't fit in the current width.  Fixed a divide-by-
+       zero bug that affected ultrasparc servers (and potentially other
+       systems).
+
+       m_sunos5.c: Now asks the system for the correct pagesize rather than
+       assuming it is 4K.
+
+Thu Mar  2 1995 - wnl (3.3 RELEASE)
+       Added module netbsd10 and renamed netbsd to netbsd08.  Changed
+       Configure so that it does not use an initial default module name.
+       Made other compatability fixes to Configure.  Added comments to
+       decosf1 concerning optimizer bug.  Other documentation changes.
+       Added use of "prime.c" to Configure script.
+
+Tue Feb  7 1995 - wnl (3.3beta6)
+       Still one more beta....
+       Fixes for sunos5 2.4 gcc core dump (it was an alignment problem).
+       Fixed and improvements for decosf1 (including use of format_k
+       for proper SIZE column formatting).  Added modules freebsd20 and
+       ncr3000.
+
+Thu Feb  2 1995 - wnl (3.3beta5)
+       One more beta....
+       Fixed a few bugs in the sunos5 port pertaining to casting and
+       very large memory counts.  Added "ifndef HAVE_GETOPT" to getopt.c
+       to provide for conditional compilation of the getopt function.
+       Those systems that have getopt in libc can add -DHAVE_GETOPT to
+       the CFLAGS line in the module to prevent the function from being
+       compiled.  Added sunos54 module to accomodate SunOS 5.4
+       peculiarities.  Added module for aux3.1.
+
+Wed Jan  4 1995 - wnl (3.3beta4)
+       This is really taking too long......sigh.
+       Fixed SIGWINCH handling once and for all.  It now remembers the
+        number of processes you        want displayed even thru window resizes.
+       Fixed buffer conflict in utils.c (itoa and itoa7).
+        Lots of small improvements to the various modules were made over
+       the past month: too numberous to list here.  SunOS 5 module made
+       more secure thru use of seteuid calls (other SVR4 modules should
+       be modified similarly).  One final MP fix to sunos5, too.  Module
+       for decosf1 was modified to accomodate V3.0.
+
+Mon Apr 18 1994 - wnl (3.3beta3)
+       I think I finally got a sunos5 module that will work on MP
+       machines.  Fixed cpu states figure in osmp41a so that 
+       percentages never exceed 100%.  Added shell script "install"
+       since Unix vendors can't seem to make up their minds on what
+       options they want to use for the one that comes with the OS.
+       Added netbsd modules from Christos.  Fixed lots of other little
+       things over the past few months that I have long since forgotten.
+
+Wed Dec 15 1993 - wnl (3.3beta2)
+       Added module patches from various users:  hpux9, sunos5.
+       Fixed bug with batch mode (screen_width wasn't getting set).
+       Changes to accomodate 64 bit machines.
+       Fixed some bugs in command parsing ("renice 19 " did something
+       unexpected).
+
+Mon Aug 30 1993 - wnl (3.3beta)
+       Added lots of little patches from various users.
+       Added routines to utils.c for intelligent formatting of kilobytes
+       and time.  These are intended to be used in the modules when
+       formatting a process line.  Added code to "summary_format" in
+       display.c to do intelligent formatting of memory quantities.
+       Redid display.c to allow for varying line widths and dynamic
+       reallocation of the screen buffer.
+       Added a SIGWINCH handler to top.c!
+       Added a constant, MAX_COLS, to top.h which defines the absolute
+       widest line we will ever allow.  Changed allocations of "char fmt"
+       in all machine modules to use this constant rather than an abitrary
+       number.
+
+Fri Aug 13 1993 - wnl (3.3)
+       Changed return value definition of time-related functions in top.c,
+       display.c, and m_ultrix4.c to time_t (stuart@coral.cs.jcu.edu.au).
+       Fixed bug in display.c: line_update when start != 0.
+
+Wed Aug  4 1993 - wnl (3.2 release)
+       Changes to Configure from Paul Vixie.  Added modules for hpux9 and
+       bsd386.
+
+Tue Jul 13 1993 - wnl (3.1 release)
+       More small changes and minor bug fixes.  Brought bsd44 up to date
+       and added a module for svr4.2.  Changed shar packaging to use Rich
+       Salz's cshar stuff.
+
+Wed Jul  7 1993 - wnl (3.1BETA)
+       More changes and bug fixes to Configure.  Applied some other
+       minor bug fixes and suggestions from the beta testers.  Added
+       the "metatop" shell script and the "installmeta" rule to the
+       Makefile to make handling multiple machine models and OS versions
+       easier.  Added INSTALL and FAQ files.
+
+Tue May 18 1993 - wnl (3.1BETA)
+       Changed Configure to be compatible with most SVR4 environments
+       (differing output from "ls -lg").  Also changed Configure,
+       Makefile.X, etc., to look for module files in the subdirectory
+       "machine" (thanks to Christos Zoulas).
+
+Tue Apr 20 1993 - wnl (3.1BETA)
+       Changed both occurences of "ls -1" in Configure to "ls".  This
+       SHOULD produce the same result, and has the advantage that it
+       doesn't produce an error on a system 5 machine.  Integrated other
+       changes recommended in the first round of beta testing.
+
+Wed Mar 10 1993 - wnl (3.1BETA)
+       MAJOR CHANGE:  I have added a required function to all machine
+       dependent modules, called proc_owner.  It takes a pid as an argument
+       and returns the uid of the process's owner.  Such capability is 
+       necessary for top to run securely as a set-uid program, something
+       that is needed for SVR4 implementations to read /proc.  I have
+       retrofitted all modules except dgux with this function, but was
+       not able to test most of them.  Top should now run securely as
+       a setuid program.  Added 386bsd and sunos5 modules.  Added sunos4mp
+       module for MP Suns.
+
+Sat Feb 20 1993 - wnl (3.1ALPHA)
+       Modified top.c and commands.c to compile correctly on System V
+       derived Unixes (especially SVR4), but in a way that doesn't rely
+       on an oracle-like declaration (that is, I don't use "ifdef SYSV").
+       Fixed some bugs in "Configure" and "getans".  Added inspection of
+       env variable "TOP" for options, and made -I default to showing
+       idle processes.  Added "u" command to change username restriction
+       on the fly.  Created shell script "suntop" for poor multi-version
+       SunOS folks (like myself).
+
+Wed Jun  3 1992 - wnl (3.0)
+       "max_topn" wasn't being used everywhere it was supposed to be
+       in top.c.  Many cosmetic changes, including copyright notices in
+       all the .c files.  Version number is now handled by version.c and
+       reflects the current patchlevel (which is initially set to 0).
+       Changed Configure and Makefile to allow configurable variables for
+       certain commands:  shell, cc, awk, install.  Updated README and
+       Porting.  Ready to release to the world!
+
+Mon May 18 1992 - wnl (2.9BETA)
+       Added modules provided by Christos Zoulas.  Replaced screen.c
+       with one modified by Christos and that will appropriately select
+       and handle the sgtty, termio, or termios system.  Integrated many
+       other changes recommended by Christos.  Fixed (I hope) the "-b"
+       batch mode display bug.  Had to change loadavg to load_avg to avoid
+       a conflict with 4.4BSD.  
+
+Mon Apr 27 1992 - wnl (2.8BETA)
+       Added modules provided by Daniel Trinkle.  Added patchlevel.h,
+       but the patch level is not yet reflected in the version number.
+       Cleaned up m_sunos4.c a little.
+
+Wed Apr 22 1992 - wnl (2.8BETA)
+       Major internal reorganization.  All of the system dependent stuff
+       is now really and truly separated from everything else.  The
+       system dependent functions are contained in a separate .c file
+       called a "module".  The Configure script knows how to find and
+       set up these modules, but the human installer still needs to tell
+       Configure which module to use (no automagic determination of 
+       machine type---sorry).  Added -U option to specify one user's
+       processes, but there is no corresponding command...yet.  Other
+       changes and improvements too numerous to mention here.  Currently
+       there are only two modules:  sunos4 and umax.  But after this beta
+       release is sent around, I expect more to be written.  I just hope
+       that the machine-dependent abstractions don't need to change in
+       the process.
+
+Thu Mar 26 1992 - wnl (2.7BETA)
+       Beta release with minimal architecture support.  Updated README
+       and added a first cut at a Porting guide.  Added ioctl TIOCGWINSZ
+       code from top2.5+ (courtesy of David MacKenzie).  I didn't even
+       try porting the Ultrix support since I don't have access to an
+       Ultrix machine.
+
+Fri Oct 11 1991 - wnl (2.6)
+       This version was not widely released.  It contained many changes.
+       Here are the major ones:
+
+       Put in Vixie's idle process hack.
+
+       Enhanced type field in new_message to handle delayed messages.
+
+       Changed u_process to automatically adjust for varying lines of
+       output.  Management of screenbuf should now be completely contained
+       in display.c.  Removed now extraneous code from CMD_number[12]
+       portion of command switch in top.c.  This was the stuff that dealt
+       with zeroing out lines in screenbuf.
+
+       Finally made it all work correctly on a 386i.  Problems I had to
+       overcome: kvm_nlist doesn't return 0 on success as advertised (it
+       returns 1 instead); the results of a kvm_nlist are different
+       (n_type can be zero even for a symbol that exists).
+
+       Serious rearrangement for processor dependent stuff.  All nlists
+       are now in separate files with the suffix ".nlist".  Most machine
+       specific code is in "machine.c" surrounded by appropriate ifdefs---
+       the goal is to eventually have all machine specific code in this
+       file.  Managed to find a way to detect SunOS 4.x at compile-time:
+       this is contained in the include file "sun.h".  Completely changed
+       the memory display line for SunOS 4.x---it now displays a far
+       more appropriate report.
+
+       Created the shell script "Configure" to aid in the configuration
+       step.
+
+       Fixed a bug in init_termcap:  it will now tolerate an environment
+       which does not have TERM defined (thanks to Sam Horrocks for
+       pointing this out).
+
+Tue Aug  9 1988 - wnl (2.5)
+       Added changes to make top work under version 4.0 of the Sun
+       operating system.  Changes were provided by Scott Alexander of the
+       University of Pennsylvania.  Thanks!  Compile with "-Dsunos4" to
+       get them.  Virtual memory statistics are not readily accessible
+       under 4.0, so they don't show up in the output.
+
+Thu Jul 31 1987 - wnl (2.4)
+       Fixed a problem with the 4.0 Pyramid code.  The label "cp_time"
+       doesn't exist in the 4.0 kernel anymore.  I think the code Carl
+       sent me wants "percpu" instead.  That is what I am using and it
+       appears to work.  375 code is still untested (at least by me).
+       Also picked a great deal of lint out of the source.  Lint now only
+       complains about a very few nitpicky things (there are far too many
+       calls to "printf" to put a "(void)" in front of!), at least under
+       SunOS.
+
+Tue Jul 28 1987 - wnl (2.4a)
+       Added changes for a Symmetrics Computer Systems s/375 machine.
+       Changes were provided by Paul Vixie.  Thanks!  According to Mr.
+       Vixie:  "These changes were not made at, by, or for SCS proper.
+       SCS would probably be interested in them, but so far only the
+       users' group has them.  They were made in February, 1987, to
+       version 2.1 of the program, by Paul Vixie
+       (dual!ptsfa!vixie!paul@ucbvax.Berkeley.EDU)."  His changes were
+       integrated into version 2.3 to make version 2.4.
+
+       The SCS peculiarities are summarized in Changes.scs.
+
+Tue Jun  9 1987 - wnl (2.3 for real)
+       Changed the includes for the extra code Carl sent me to only
+       compile on Version 4.0 Pyramid machines.  This makes top still
+       compilable on pre-4.0 Pyramids.  Specifically, this code is only
+       compiled when both "pyr" and "CPUFOUND" are defined.
+
+Wed Jun  3 1987 - wnl (2.3 with Pyramid additions)
+       It's been a month and I still haven't done anything about
+       distributing this version.  However, Carl Gutekunst from Pyramid
+       has sent me some extra patches for some of the Pyramid code.  I
+       just added those and will make them part of 2.3.  This fixes the
+       following Pyramid problems:  adds the inclusion of <sys/systm.h>,
+       uses the correct size for getting the kernel value _ccpu (this bug
+       affected the Vax version as well), sums the elements of the percpu
+       array to calculate a cp_time value (for OSx 4.0).
+
+Fri May  1 1987 - wnl (2.3)
+       I have finally finished all the changes for better support of
+       oddbal terminals.  Added the low-level routine "clear_eol" which
+       makes handling terminals without "ce" easy:  it uses spaces
+       instead.  All direct uses of "clear_line" outside of screen.c have
+       been changed to use this primitive.  A terminal with "os" is now
+       handled in such that all situations that need overwriting are
+       completely avoided (including several commands).  This required
+       some changes to the way commands are translated into action (in
+       "top.c").  Made several important changes to display.c to prevent
+       overflowing of any of the fields.  Specifically, more than 99
+       total processes and a cpu state that reaches 100%.  Had to make a
+       small change to two casts in top.c, because the Sun 3.2 compiler
+       was giving warnings on them.  Added the "-q" option which lets
+       root run top at a nice of -20 (in case he thinks he really needs it).
+
+Tue Dec 30 1986 - wnl (2.2)
+       I think I fixed a bug reported by Julian Onions at Nottingham.
+       Occasionally, top will core dump when the sprintf in either
+       i_process or u_process overflows due to an exceptionally
+       unrealistic time value.  I think it highly unlikely that top can
+       get a bad proc structure (although I suppose it is possible), but
+       the process time is read from the user structure, and that can
+       sometimes be part garbage.  So, "get_ucpu" checks the value it
+       returns to make sure its formatted form will not overflow the
+       sprintf.  If this doesn't fix the bug, then more drastic measures
+       will be necessary.  I plan to make this version the official
+       "top 2.2".  [[ This version was never distributed very widely. ]]
+
+Tue Dec  2 1986 - wnl (2.2c)
+       Added to top.c the notion of a "failed command".  When a command
+       produces a message (on the message line), an update does not
+       follow it.  Before, the message was written and a new display was
+       shown---purposefully not overwriting the message.  But the
+       improvements to handle overstriking terminals and terminals
+       without "ce" clear the screen before every display, which would
+       erase the message.  Now, the message is displayed and top waits
+       another full time interval before updating the display.  This
+       works much better all around.
+
+Mon Nov 24 1986 - wnl (2.2b)
+       Created a new file, utils.c, and made appropriate changes to
+       Makefile.  This new file holds all utility functions that can and
+       may be used by more than one "module".  Improved i_memory and
+       u_memory (display.c) so that screen updates for the values
+       displayed are only changed when necessary.  Also made the line
+       look better:  the last fixes made for a rather ugly display.
+       Added the locally defined constant "LoadMax" and added code to
+       top.c to send the cursor home after a space command is entered if
+       the load average is higher than "LoadMax".  This provides visual
+       feedback on loaded systems.
+
+Mon Nov  3 1986 - wnl (2.2a)
+       Widened the format for memory usage so that it can display 5
+       digits.  This makes that line look a little ugly---maybe I'll fix
+       that later.  Screen handling now understands "os" and a missing
+       "ce".  It treats them identically:  clear the screen between each
+       display.  Screen handling code now uses "cd" when appropriate
+       (i.e.:  when user has shortened the screen).  Made i_loadave clear
+       then screen and took out most of the explicit calls to "clear" in
+       top.c.  This method is cleaner, especially in conjunction with
+       "os" handling.  Added preprocessor variable "RANDOM_PW" for
+       systems that access the passwd file randomly (Sun's yp and 4.3).
+       With "RANDOM_PW" set, "getpwuid" is used instead of "getpwnam",
+       but uid->username mappings are still hashed internally (because
+       that is still faster than going to disk).
+
+Mon Oct  6 1986 - wnl (2.1)
+       A bug with the kill command was pointed out by "dciem!tim"---
+       specifying a signal by name did not work correctly.  This bug has
+       been fixed with a simple change to commands.c.  Another bug made
+       the cpu state percentages incorrect the first time they were
+       displayed.  This bug has also been fixed (changed top.c).
+
+Thu Sep  4 1986 - wnl (2.0, at last)
+       This is the version that will (hopefully) get released to the
+       world as top 2.0.
+       Added the "r" and "k" commands for renice and kill, respectively.
+       This required adding a way to handle system call errors, and the
+       addition of the "e" command.  Help screen and manual page were
+       changed to reflect this change.  Changed all "#ifdef SUN" directives
+       to "#ifdef sun", and changed all "#ifdef PYRAMID" directives to
+       "#ifdef pyr".  As much as I hate those choices of preprocessor
+       names (they too easily conflict with real variable names), it does
+       make automatic compilation possible---people don't have to change
+       the Makefile anymore for specific machines.  The manual page was
+       changed to automatically incorporate the defaults as set in the
+       Makefile (including an infinite value for TOPN) and the way the
+       manual page is generated by the Makefile was changed to make
+       maintenance of this information automatic.
+
+Mon Jul 28 1986 - wnl (still pre 2.0)
+       Real close now.  I put in a new definition for the macro "pagetok"
+       that does an explicit shift of a constant expression involving
+       PGSHIFT.  Appropriate checks are made if PGSHIFT is to small.
+       "pagetok" is now used exclusively everywhere to convert kernel
+       clicks to kilobytes.  I added a full blown interactive mode with
+       the ability to change some of the runtime parameters (how many to
+       display, time delay, etc.) while top is running.  I also
+       incorporated a few ideas from the net:  control characters in the
+       command name are replaced with '?'; the '-S' option makes the
+       swapper and pager visible; options have been added to control the
+       number of displays produced (this makes it easier to make
+       performance snapshots with top).  I have also added the notion of
+       "infinite" values for number of processes and number of displays.
+       I fixed a long-standing bug in the uid to username mapping code
+       that was only aggravated on the pyramids:  it was an ill-defined
+       expression (akin to i = i++).  I tweaked the proc_compar routine
+       for qsort slightly so that stopped processes were more likely to
+       show up.  Manual page was updated to reflect all changes
+       noticeable to the user.
+
+Tue Jul  1 1986 - wnl (pre 2.0 -- 1.9999?)
+       In the process of major revamping on the way to version 2.0.
+       I have completely done away with curses by adding my own screen
+       management routines in a separate file (screen.c).  The rationale
+       for this is that top knows a whole lot more about what is and is
+       not redundant on the screen and can compare simple integer values
+       where curses would have to compare strings.  This has turned out
+       to be a very big win speed-wise.  The proc_compar routine for
+       sorting has been rewritten to include several more keys.  I
+       decided this was necessary when I noticed that the "top" process
+       itself kept disappearing off the top 10 list on a Sun-3.  All the
+       processes had the same percentage (0%) and the sort wasn't really
+       doing anything worthwhile.  I changed the expression that computes
+       memory usage to use the ctob macro instead of just assuming that
+       pages were 512 bytes.  More work still needs to be done before
+       this version is usable.  I changed options-processing to use
+       getopt and added appropriate incantations to the Makefile.
+
+Wed Feb 20 1985 - wnl (still 1.8)
+       Put in the ifdef FOUR_ONE statements to make top still compilable
+       on a 4.1 system.  Apparently, there are some users out there that
+       need this functionality.  Oh well.  I don't guarantee any of it,
+       since I can't test it.  Made appropriate changes to README and
+       final installation related changes to Makefile.
+
+Sat Feb  2 1985 - wnl (1.8)
+       Removed all the ifdef FOUR_TWO statements and made "top" into a
+       4.2 only program.  If someone really wants to still run it on 4.1,
+       then they can do all the work.  We don't have a 4.1 machine
+       anymore, so I don't even know if the thing still works under 4.1.
+       Cleaned up the Makefile and the README.  Added installation rules
+       to the Makefile, as requested by several sites.  Fixed a very
+       obscure divide-by-zero bug.  Added a second "key" to the qsort
+       comparison function (proc_compar) so that comparisons are based on
+       cpu ticks if the percentages are equal (provided by Jonathon
+       Feiber at Sun).
+
+Tue Dec 11 1984 - wnl (1.7)
+       Added the virtual and real memory status line to the header area
+       (provided by Jonathon Feiber at Sun)
+
+Tue Nov 20 1984 - wnl (1.6)
+       Added an "exit" if sbrk's fail.  Added changes from Jonathon
+       Feiber at Sun:  ifdef SUN to make top work on Suns (they don't use
+       doubles in the proc structure), register declarations, check for
+       getting a user structure that has disappeared since the proc array
+       was read (it used to die, now it just shows the process as swapped).
+
+Tue Nov 13 1984 - wnl (1.5)
+       If the number of displayable processes ("active_procs") was less
+       than the number of requested processes ("topn"), top would
+       segmentation fault.  This bug has been fixed.  Thanks to Prentiss
+       Riddle at ut-sally for pointing out the existence of this bug.
+
+Tue Oct 23 1984 - wnl (1.4)
+       Finally fixed the hash table bug that caused processes owned by
+       root to sometimes appear with either no name or a different name
+       that had UID 0 (such as "operator").  Removed all the ifdef DEBUG
+       blocks to make top ready for distribution to the real world.
+
+Sun Apr  8 1984 - wnl (still 1.3)
+       Made some slight changes to the display format.  It now looks more
+       aesthetically pleasing.  Added some preprocessor constants so that
+       the two defaults (number of processes and seconds of delay) easier
+       to change.
+
+Thu Apr  5 1984 - wnl (1.3)
+       Changed the order in which things are done at initialization time.
+       This way, if an error occurs before starting the main loop, curses
+       will never get started.  Also changed other error handlers so that
+       endwin() is called before any flavor of exit.  Specifying a number
+       of processes that is more than the screen can handle is no longer
+       fatal.  It displays a warning message and pretends the user
+       specified the maximum for the screen.  Finally cured all the TSTP
+       blues (well, almost all).  I removed my TSTP handler and convinced
+       the system to always use the one that curses sets up.  Turns out
+       that "sleep" was stepping all over it during a pause.  So, I don't
+       use sleep anymore.  The only problem that remains with it now is
+       redrawing the old display before updating it after a pause.
+
+Tue Apr  3 1984 - wnl (from 1.0 to 1.2)
+       I changed the format of the TIME column from just "seconds" to
+       "minutes:seconds".  I also made pausing work correctly.  Screen
+       redraws with an up to date display.  For compatibility with 4.2, I
+       changed the name of the "zero" function to "bzero".  The makefile
+       has been altered to handle versions for 4.1 and 4.2, and README
+       has been updated to reflect these recent changes.
diff --git a/contrib/top/FAQ b/contrib/top/FAQ
new file mode 100644 (file)
index 0000000..92613e4
--- /dev/null
@@ -0,0 +1,340 @@
+                                        TOP
+                                  Version 3.8beta1
+
+                                  William LeFebvre
+                             with much help from others
+
+                    Frequently Asked Questions and their Answers
+
+
+
+     GENERAL
+
+ 1.  What is 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.
+
+ 2.  Where do I get the latest version of top? 
+
+     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 web page for top? 
+
+     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.
+
+ 4.  Is there a mailing list or on-line bulletin board for top? 
+
+     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.
+
+ 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.
+
+ 6.  Will there be another major release of top? Will there be a top
+     version 4? 
+
+     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.
+
+ 7.  Does top really support multi-processor systems? 
+
+     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? 
+
+     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.
+
+
+     COMPILING
+
+ 9.  We just upgraded our operating system to a new version and top broke.
+     What should we do? 
+
+     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.
+
+
+     RUNNING
+
+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? 
+
+     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. 
+
+     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.
+
+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? 
+
+     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.
+
+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.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.
+
+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? 
+
+     This can happen if you compiled with gcc using the wrong include
+     files. See the previous question.
+
+
+     FREEBSD PROBLEMS
+
+15.  This version of top does not show individual threads with the "t" or
+     "H" commands. Instead it says "command not available." Why? 
+
+     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? 
+
+     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.
+
+
+     MACOSX PROBLEMS
+
+17.  I tried to configure top on my Mac OSX system and I got an error
+     claiming "macosx not supported". What up? 
+
+     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".
+
+
+     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? 
+
+     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.
+
+
+     SOLARIS PROBLEMS
+
+
+     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.  Is there somewhere I can get a pre-compiled package? 
+
+     Yes. Although I don't provide pre-compiled binaries, you can get a
+     Sun-style package from www.sunfreeware.com.
+
+20.  Under Solaris 2, when I type "make", the system says "language
+     optional software package not installed." What's going on? 
+
+     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? 
+
+     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.
+
+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? 
+
+     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? 
+
+     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.
+
+24.  On my 64-bit system some processes show up with incorrect information
+     (such as zero memory). 
+
+     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? 
+
+     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.
+
+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"? 
+
+     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.
+
+
+     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.
+
diff --git a/contrib/top/INSTALL b/contrib/top/INSTALL
new file mode 100644 (file)
index 0000000..fe99ffe
--- /dev/null
@@ -0,0 +1,54 @@
+                             TOP
+                       Version 3.8beta1
+
+                       William LeFebvre
+                     and a cast of many
+
+INSTALLATION
+
+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.
+
+In addition to the standard options, top's configure script supports
+the following:
+
+    --with-module=name
+
+       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_".
+
+    --with-ext=name
+
+       Compile with the extension "name", found in the subdirectory
+       "ext". At the present time, there are no extensions in the
+       standard distribution.
+
+    --enable-debug
+    --disable-debug
+
+       Default off. Include debugging output in the compilation,
+       which can be seen with the -D switch.
+
+    --enable-color
+    --disable-color
+
+       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".
+
+    --enable-kill
+    --disable-kill
+
+       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.
diff --git a/contrib/top/LICENSE b/contrib/top/LICENSE
new file mode 100644 (file)
index 0000000..3b210dc
--- /dev/null
@@ -0,0 +1,30 @@
+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.
diff --git a/contrib/top/Porting b/contrib/top/Porting
new file mode 100644 (file)
index 0000000..c28fd37
--- /dev/null
@@ -0,0 +1,229 @@
+Instructions for porting top to other architectures.
+
+This is still a preliminary document.  Suggestions for improvement are
+most welcome.
+
+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
+centralized, (3) I want to loosely track the various porting efforts.
+You do not need to wait for an "okay", but I do want to know that you
+are working on it.  And of course, once it is finished, please send me
+the module files so that I can add them to the main distribution!
+
+----------
+
+There is one set of functions which extract all the information that
+top needs for display.  These functions are collected in to one file.
+To make top work on a different architecture simply requires a
+different implementation of these functions.  The functions for a
+given architecture "foo" are stored in a file called "m_foo.c".  The
+Configure script looks for these files and lets the configurer choose
+one of them.  This file is called a "module".  The idea is that making
+top work on a different machine only requires one additional file and
+does not require changes to any existing files.
+
+A module template is included in the distribution, called "m-template".
+To write your own module, it is a good idea to start with this template.
+If you architecture is similar to one for which a module already
+exists, then you can start with that module instead.  If you do so,
+remember to change the "AUTHOR" section at the top!
+
+The first comment in a module contains information which is extracted
+and used by Configure.  This information is marked with words in all
+capitals (such as "SYNOPSIS:" and "LIBS:").  Go look at m-template: it
+is fairly self-explanatory.  The text after "LIBS:" (on the same line)
+is extracted and included in the LIBS definition of the Makefile so
+that extra libraries which may be necessary on some machines (such as
+"-lkvm") can be specified in the module.  The text after "CFLAGS:"
+(on the same line) is extracted and included as flags in the "CFLAGS"
+definition of the Makefile (thus in every compilation step).  This is
+used for rare circumstances only:  please don't abuse this hook.
+
+Some operating systems have idiosyncrasies which will affect the form
+and/or content of the information top displays.  You may wish to
+document such anomalies in the top man page.  This can be done by adding
+a file called m_{modulename}.man (where {modulename} is replaced with
+the name of the module).  Configure will automatically add this file to
+the end of the man page.  See m_sunos4.man for an example.
+
+A module is concerned with two structures:
+
+The statics struct is filled in by machine_init.  Each item is a
+pointer to a list of character pointers.  The list is terminated 
+with a null pointer.
+
+struct statics
+{
+    char **procstate_names;    /* process state names */
+    char **cpustate_names;     /* cpu state names */
+    char **memory_names;       /* memory information names */
+};
+
+The system_info struct is filled in by get_system_info and
+get_process_info.
+
+struct system_info
+{
+    int    last_pid;     /* last pid assigned (0 means non-sequential assignment) */
+    double load_avg[NUM_AVERAGES];     /* see below */
+    int    p_total;      /* total number of processes */
+    int    p_active;     /* number of procs considered "active" */
+    int    *procstates;  /* array of process state counters */
+    int    *cpustates;   /* array of cpustate counters */
+    int    *memory;      /* memory information */
+};
+
+The last three pointers each point to an array of integers.  The
+length of the array is determined by the length of the corresponding
+_names array in the statics structure.  Furthermore, if an entry in a
+_names array is the empty string ("") then the corresponding value in
+the value array will be skipped over.  The display routine displays,
+for example, the string procstate_names[0] then the number
+procstates[0], then procstate_names[1], procstates[1], etc. until
+procstate_names[N] == NULL.  This allows for a tremendous amount of
+flexibility in labeling the displayed values.
+
+"procstates" and "memory" are displayed as straight integer values.
+Values in "cpustates" are displayed as a percentage * 10.  For
+example, the (integer) value 105 is displayed as 10.5%.
+
+These routines must be defined by the machine dependent module.
+
+int machine_init(struct statics *)
+
+       returns 0 on success and -1 on failure,
+       prints error messages
+
+char *format_header(char *)
+
+       Returns a string which should be used as the header for the
+       process display area.  The argument is a string used to label
+       the username column (either "USERNAME" or "UID") and is always
+       8 characters in length.
+
+void get_system_info(struct system_info *)
+
+caddr_t get_process_info(struct system_info *, int, int, int (*func)())
+
+       returns a handle to use with format_next_process
+
+char *format_next_process(caddr_t, char *(*func)())
+
+       returns string which describes next process
+
+int proc_compare(caddr_t, caddr_t)
+
+       qsort comparison function
+
+uid_t proc_owner(pid_t)
+
+       Returns the uid owner of the process specified by the pid argument.
+       This function is VERY IMPORTANT.  If it fails to do its job, then
+       top may pose a security risk.
+
+
+get_process_info is called immediately after get_system_info.  In
+fact, the two functions could be rolled in to one.  The reason they
+are not is mostly historical.
+
+Top relies on the existence of a function called "setpriority" to
+change a process's priority.  This exists as a kernel call on most 4.3
+BSD derived Unixes.  If neither your operating system nor your C
+library supplies such a function, then you will need to add one to the
+module.  It is defined as follows:
+
+       int setpriority (int dummy, int who, int niceval)
+
+       For the purposes of top, the first argument is meaningless.
+       The second is the pid and the third is the new nice value.
+       This function should behave just like a kernel call, setting
+       errno and returning -1 in case of an error.  This function MUST
+       check to make sure that a non-root user does not specify a nice
+       value less than the process's current value.  If it detects such
+       a condition, it should set errno to EACCES and return -1.
+       Other possible ERRNO values:  ESRCH when pid "who" does not exist,
+       EPERM when the invoker is not root and not the same as the
+       process owner.
+
+Note that top checks process ownership and should never call setpriority
+when the invoker's uid is not root and not the same as the process's owner
+uid.
+
+
+The file "machine.h" contains definitions which are useful to modules
+and to top.c (such as the structure definitions).  You SHOULD NOT need
+to change it when porting to a new platform.
+
+Porting to a new platform should NOT require any changes to existing
+files.  You should only need to add m_ files.  If you feel you need a
+change in one of the existing files, please contact me so that we can
+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.
+
+
diff --git a/contrib/top/README b/contrib/top/README
new file mode 100644 (file)
index 0000000..e0e7cf3
--- /dev/null
@@ -0,0 +1,191 @@
+                             TOP
+                       Version 3.8beta1
+
+                       William LeFebvre
+                    and a cast of dozens
+
+
+If you do not want to read this entire file, then at least read
+the section at the end entitled "KNOWN PROBLEMS".
+
+If you are having any problems getting top to work, please read the
+file "FAQ" *before* contacting me.  Thank you.
+
+"top" is a program that will give continual reports about the state of
+the system, including a list of the top cpu using processes.  Version 3
+of "top" has three primary design goals: provide an accurate snapshot of
+the system and process state, not be one of the top processes itself, be
+as portable as possible.
+
+Version 3 has many bug fixes from version 2.5, and it has also been
+reorganized in a major way to make it easy to port to other platforms.
+All system dependent code is now contained in one file.
+
+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.
+
+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 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.
+
+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.
+
+To compile and install "top", read the file "INSTALL" and follow the
+directions and advice contained therein.
+
+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.
+
+Be sure to read the FAQ enclosed with the distrubution.  It contains
+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@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
+
+Note that top is now a sourceforge project!  Its project name is
+"unixtop" and you can access its project page here:
+
+http://sourceforge.net/projects/unixtop
+
+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.
+
+Subversion access is also provided by Sourceforge.  If Subversion is
+installed on your system you can check out the project with the
+following command:
+
+       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
+
+Compiling via Gnu CC continued to be the source of most of the
+questions I receive.  By far the most common mistake made by those
+attempting to compile top with Gnu CC is out of date include files.
+When the operating system is upgraded, the include files that are part
+of the gcc package MUST also be updated.  Gcc maintains its own
+include files.  Even a minor OS upgrade can involve changes to some of
+the kernel's internal data structures, which are defined in include
+files in "sys".  Top is very sensitive to these changes.  If you are
+compiling with gcc and experience any sort of strange problems, please
+make sure the include files you are using are up to date BEFORE
+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
+such as you and I don't need to know what the kernel's proc structure looks
+like.  To that end, they have removed all useful content from the include
+file <sys/proc.h> in version 10.10.  As a result, top will not compile
+under 10.10.  What HP is trying to accomplish with this move is to force
+iconoclasts such as myself to use "pstat" for collecting all process
+information.  I have no immediate solution for this problem, but hope to
+obtain a sufficiently complete definition of "struct proc" at some point in
+the near future.  Stay tuned.
+
+
+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, 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
+
+               U.S. Mail address:
+                   William LeFebvre
+                   11585 Jones Bridge Road
+                   Suite 420 PMB 139
+                   Alpharetta, GA  30202
diff --git a/contrib/top/README.DELETED b/contrib/top/README.DELETED
new file mode 100644 (file)
index 0000000..2a190b2
--- /dev/null
@@ -0,0 +1,15 @@
+Makefile.in
+config.amd64.make
+config.amd64.makeinstall
+config.default.makeinstall
+config.guess
+config.h.in
+config.sparcv9.make
+config.sparcv9.makeinstall
+config.sub
+configure
+configure.ac
+install-sh
+machine/
+sigconv.awk
+top.1.in
diff --git a/contrib/top/README.DRAGONFLY b/contrib/top/README.DRAGONFLY
new file mode 100644 (file)
index 0000000..645d379
--- /dev/null
@@ -0,0 +1,15 @@
+
+                       TOP-3.8beta1 AS USED BY DRAGONFLY
+
+    No files have been moved or modified from their extracted position.
+    
+    ON THE VENDOR BRANCH (vendor/TOP), DO NOT CREATE OR EDIT ANY FILES
+    IN THIS DIRECTORY HIERARCHY!  THIS HIERARCHY REPRESENTS AN EXACT COPY,
+    MINUS UNNEEDED FILES, OF THE TOP DISTRIBUTION.
+
+    All modifications are made in the master or release branches!
+
+    The file README.DELETED contains a list of deleted files.
+
+
+    SHA1 (top-3.8beta1.tar.gz) = d3f911f8cd64d21a8beb7eb3694923b40d2532e2
diff --git a/contrib/top/Y2K b/contrib/top/Y2K
new file mode 100644 (file)
index 0000000..2af998d
--- /dev/null
@@ -0,0 +1,26 @@
+Top and the Year 2000
+
+The software package top will not be affected by years numbering
+between 2000 and 2037.  No portion of the top code stores dates on
+disk.  All date processing in top is performed with functions from the
+Unix C library and Unix kernel.  The specific functions are: time(2)
+and ctime(3S).  These functions deal exclusively with conventional
+Unix time values (number of seconds since Midnight January 1, 1970
+GMT) and produce strings with a 4-digit year.  At no point in the code
+for top are the last two digits used to represent a year.
+
+Top and the Year 2038
+
+In the year 2038 top will fail to represent the time of day correctly
+on 32-bit Unix operating systems.  This is due to a limitation in the
+way Unix represents time.  Top will only work on systems whose kernel
+call "time" and C library call "ctime" have been adjusted to represent
+time with a value greater than 32 bits.  The exact date and time of
+this failure is 3:14:08 January 19, 2038 GMT.  Note that this failure
+will only affect the display of the current time in the output from
+top.
+
+
+THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE.
+Please see the contents of the file "LICENSE" for further
+information.
diff --git a/contrib/top/ap_snprintf.c b/contrib/top/ap_snprintf.c
new file mode 100644 (file)
index 0000000..f9ba350
--- /dev/null
@@ -0,0 +1,1193 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * This code is based on, and used with the permission of, the
+ * SIO stdio-replacement strx_* functions by Panos Tsirigotis
+ * <panos@alumni.cs.colorado.edu> for xinetd.
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+typedef struct {
+    char *curpos;
+    char *endpos;
+} ap_vformatter_buff;
+
+
+#define API_EXPORT(type) type
+#define API_EXPORT_NONSTD(type) type
+
+#define ap_isalnum(c) (isalnum(((unsigned char)(c))))
+#define ap_isalpha(c) (isalpha(((unsigned char)(c))))
+#define ap_iscntrl(c) (iscntrl(((unsigned char)(c))))
+#define ap_isdigit(c) (isdigit(((unsigned char)(c))))
+#define ap_isgraph(c) (isgraph(((unsigned char)(c))))
+#define ap_islower(c) (islower(((unsigned char)(c))))
+#define ap_isprint(c) (isprint(((unsigned char)(c))))
+#define ap_ispunct(c) (ispunct(((unsigned char)(c))))
+#define ap_isspace(c) (isspace(((unsigned char)(c))))
+#define ap_isupper(c) (isupper(((unsigned char)(c))))
+#define ap_isxdigit(c) (isxdigit(((unsigned char)(c))))
+#define ap_tolower(c) (tolower(((unsigned char)(c))))
+#define ap_toupper(c) (toupper(((unsigned char)(c))))
+
+
+typedef enum {
+    NO = 0, YES = 1
+} boolean_e;
+
+#ifndef FALSE
+#define FALSE                  0
+#endif
+#ifndef TRUE
+#define TRUE                   1
+#endif
+#ifndef AP_LONGEST_LONG
+#define AP_LONGEST_LONG                long
+#endif
+#define NUL                    '\0'
+#define WIDE_INT               long
+#define WIDEST_INT             AP_LONGEST_LONG
+
+typedef WIDE_INT wide_int;
+typedef unsigned WIDE_INT u_wide_int;
+typedef WIDEST_INT widest_int;
+#ifdef __TANDEM
+/* Although Tandem supports "long long" there is no unsigned variant. */
+typedef unsigned long       u_widest_int;
+#else
+typedef unsigned WIDEST_INT u_widest_int;
+#endif
+typedef int bool_int;
+
+#define S_NULL                 "(null)"
+#define S_NULL_LEN             6
+
+#define FLOAT_DIGITS           6
+#define EXPONENT_LENGTH                10
+
+/*
+ * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions
+ *
+ * XXX: this is a magic number; do not decrease it
+ */
+#define NUM_BUF_SIZE           512
+
+/*
+ * cvt.c - IEEE floating point formatting routines for FreeBSD
+ * from GNU libc-4.6.27.  Modified to be thread safe.
+ */
+
+/*
+ *    ap_ecvt converts to decimal
+ *      the number of digits is specified by ndigit
+ *      decpt is set to the position of the decimal point
+ *      sign is set to 0 for positive, 1 for negative
+ */
+
+#define        NDIG    80
+
+/* buf must have at least NDIG bytes */
+static char *ap_cvt(double arg, int ndigits, int *decpt, int *sign, int eflag, char *buf)
+{
+    register int r2;
+    double fi, fj;
+    register char *p, *p1;
+    
+    if (ndigits >= NDIG - 1)
+       ndigits = NDIG - 2;
+    r2 = 0;
+    *sign = 0;
+    p = &buf[0];
+    if (arg < 0) {
+       *sign = 1;
+       arg = -arg;
+    }
+    arg = modf(arg, &fi);
+    p1 = &buf[NDIG];
+    /*
+     * Do integer part
+     */
+    if (fi != 0) {
+       p1 = &buf[NDIG];
+       while (p1 > &buf[0] && fi != 0) {
+           fj = modf(fi / 10, &fi);
+           *--p1 = (int) ((fj + .03) * 10) + '0';
+           r2++;
+       }
+       while (p1 < &buf[NDIG])
+           *p++ = *p1++;
+    }
+    else if (arg > 0) {
+       while ((fj = arg * 10) < 1) {
+           arg = fj;
+           r2--;
+       }
+    }
+    p1 = &buf[ndigits];
+    if (eflag == 0)
+       p1 += r2;
+    *decpt = r2;
+    if (p1 < &buf[0]) {
+       buf[0] = '\0';
+       return (buf);
+    }
+    while (p <= p1 && p < &buf[NDIG]) {
+       arg *= 10;
+       arg = modf(arg, &fj);
+       *p++ = (int) fj + '0';
+    }
+    if (p1 >= &buf[NDIG]) {
+       buf[NDIG - 1] = '\0';
+       return (buf);
+    }
+    p = p1;
+    *p1 += 5;
+    while (*p1 > '9') {
+       *p1 = '0';
+       if (p1 > buf)
+           ++ * --p1;
+       else {
+           *p1 = '1';
+           (*decpt)++;
+           if (eflag == 0) {
+               if (p > buf)
+                   *p = '0';
+               p++;
+           }
+       }
+    }
+    *p = '\0';
+    return (buf);
+}
+
+static char *ap_ecvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 1, buf));
+}
+
+static char *ap_fcvt(double arg, int ndigits, int *decpt, int *sign, char *buf)
+{
+    return (ap_cvt(arg, ndigits, decpt, sign, 0, buf));
+}
+
+/*
+ * ap_gcvt  - Floating output conversion to
+ * minimal length string
+ */
+
+static char *ap_gcvt(double number, int ndigit, char *buf, boolean_e altform)
+{
+    int sign, decpt;
+    register char *p1, *p2;
+    register int i;
+    char buf1[NDIG];
+
+    p1 = ap_ecvt(number, ndigit, &decpt, &sign, buf1);
+    p2 = buf;
+    if (sign)
+       *p2++ = '-';
+    for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--)
+       ndigit--;
+    if ((decpt >= 0 && decpt - ndigit > 4)
+       || (decpt < 0 && decpt < -3)) {         /* use E-style */
+       decpt--;
+       *p2++ = *p1++;
+       *p2++ = '.';
+       for (i = 1; i < ndigit; i++)
+           *p2++ = *p1++;
+       *p2++ = 'e';
+       if (decpt < 0) {
+           decpt = -decpt;
+           *p2++ = '-';
+       }
+       else
+           *p2++ = '+';
+       if (decpt / 100 > 0)
+           *p2++ = decpt / 100 + '0';
+       if (decpt / 10 > 0)
+           *p2++ = (decpt % 100) / 10 + '0';
+       *p2++ = decpt % 10 + '0';
+    }
+    else {
+       if (decpt <= 0) {
+           if (*p1 != '0')
+               *p2++ = '.';
+           while (decpt < 0) {
+               decpt++;
+               *p2++ = '0';
+           }
+       }
+       for (i = 1; i <= ndigit; i++) {
+           *p2++ = *p1++;
+           if (i == decpt)
+               *p2++ = '.';
+       }
+       if (ndigit < decpt) {
+           while (ndigit++ < decpt)
+               *p2++ = '0';
+           *p2++ = '.';
+       }
+    }
+    if (p2[-1] == '.' && !altform)
+       p2--;
+    *p2 = '\0';
+    return (buf);
+}
+
+/*
+ * The INS_CHAR macro inserts a character in the buffer and writes
+ * the buffer back to disk if necessary
+ * It uses the char pointers sp and bep:
+ *      sp points to the next available character in the buffer
+ *      bep points to the end-of-buffer+1
+ * While using this macro, note that the nextb pointer is NOT updated.
+ *
+ * NOTE: Evaluation of the c argument should not have any side-effects
+ */
+#define INS_CHAR(c, sp, bep, cc)                               \
+           {                                                   \
+               if (sp >= bep) {                                \
+                   vbuff->curpos = sp;                         \
+                   if (flush_func(vbuff))                      \
+                       return -1;                              \
+                   sp = vbuff->curpos;                         \
+                   bep = vbuff->endpos;                        \
+               }                                               \
+               *sp++ = (c);                                    \
+               cc++;                                           \
+           }
+
+#define NUM( c )                       ( c - '0' )
+
+#define STR_TO_DEC( str, num )         \
+    num = NUM( *str++ ) ;              \
+    while ( ap_isdigit( *str ) )               \
+    {                                  \
+       num *= 10 ;                     \
+       num += NUM( *str++ ) ;          \
+    }
+
+/*
+ * This macro does zero padding so that the precision
+ * requirement is satisfied. The padding is done by
+ * adding '0's to the left of the string that is going
+ * to be printed. We don't allow precision to be large
+ * enough that we continue past the start of s.
+ *
+ * NOTE: this makes use of the magic info that s is
+ * always based on num_buf with a size of NUM_BUF_SIZE.
+ */
+#define FIX_PRECISION( adjust, precision, s, s_len )   \
+    if ( adjust ) {                                    \
+        int p = precision < NUM_BUF_SIZE - 1 ? precision : NUM_BUF_SIZE - 1; \
+       while ( s_len < p )                             \
+       {                                               \
+           *--s = '0' ;                                \
+           s_len++ ;                                   \
+       }                                               \
+    }
+
+/*
+ * Macro that does padding. The padding is done by printing
+ * the character ch.
+ */
+#define PAD( width, len, ch )  do              \
+       {                                       \
+           INS_CHAR( ch, sp, bep, cc ) ;       \
+           width-- ;                           \
+       }                                       \
+       while ( width > len )
+
+/*
+ * Prefix the character ch to the string str
+ * Increase length
+ * Set the has_prefix flag
+ */
+#define PREFIX( str, length, ch )       *--str = ch ; length++ ; has_prefix = YES
+
+
+/*
+ * Convert num to its decimal format.
+ * Return value:
+ *   - a pointer to a string containing the number (no sign)
+ *   - len contains the length of the string
+ *   - is_negative is set to TRUE or FALSE depending on the sign
+ *     of the number (always set to FALSE if is_unsigned is TRUE)
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ *
+ * Note: we have 2 versions. One is used when we need to use quads
+ * (conv_10_quad), the other when we don't (conv_10). We're assuming the
+ * latter is faster.
+ */
+static char *conv_10(register wide_int num, register bool_int is_unsigned,
+                    register bool_int *is_negative, char *buf_end,
+                    register int *len)
+{
+    register char *p = buf_end;
+    register u_wide_int magnitude;
+
+    if (is_unsigned) {
+       magnitude = (u_wide_int) num;
+       *is_negative = FALSE;
+    }
+    else {
+       *is_negative = (num < 0);
+
+       /*
+        * On a 2's complement machine, negating the most negative integer 
+        * results in a number that cannot be represented as a signed integer.
+        * Here is what we do to obtain the number's magnitude:
+        *      a. add 1 to the number
+        *      b. negate it (becomes positive)
+        *      c. convert it to unsigned
+        *      d. add 1
+        */
+       if (*is_negative) {
+           wide_int t = num + 1;
+
+           magnitude = ((u_wide_int) -t) + 1;
+       }
+       else
+           magnitude = (u_wide_int) num;
+    }
+
+    /*
+     * We use a do-while loop so that we write at least 1 digit 
+     */
+    do {
+       register u_wide_int new_magnitude = magnitude / 10;
+
+       *--p = (char) (magnitude - new_magnitude * 10 + '0');
+       magnitude = new_magnitude;
+    }
+    while (magnitude);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+static char *conv_10_quad(widest_int num, register bool_int is_unsigned,
+                    register bool_int *is_negative, char *buf_end,
+                    register int *len)
+{
+    register char *p = buf_end;
+    u_widest_int magnitude;
+
+    /*
+     * We see if we can use the faster non-quad version by checking the
+     * number against the largest long value it can be. If <=, we
+     * punt to the quicker version.
+     */
+    if ((num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned))
+       return(conv_10( (wide_int)num, is_unsigned, is_negative,
+              buf_end, len));
+
+    if (is_unsigned) {
+       magnitude = (u_widest_int) num;
+       *is_negative = FALSE;
+    }
+    else {
+       *is_negative = (num < 0);
+
+       /*
+        * On a 2's complement machine, negating the most negative integer 
+        * results in a number that cannot be represented as a signed integer.
+        * Here is what we do to obtain the number's magnitude:
+        *      a. add 1 to the number
+        *      b. negate it (becomes positive)
+        *      c. convert it to unsigned
+        *      d. add 1
+        */
+       if (*is_negative) {
+           widest_int t = num + 1;
+
+           magnitude = ((u_widest_int) -t) + 1;
+       }
+       else
+           magnitude = (u_widest_int) num;
+    }
+
+    /*
+     * We use a do-while loop so that we write at least 1 digit 
+     */
+    do {
+       u_widest_int new_magnitude = magnitude / 10;
+
+       *--p = (char) (magnitude - new_magnitude * 10 + '0');
+       magnitude = new_magnitude;
+    }
+    while (magnitude);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+static char *conv_in_addr(struct in_addr *ia, char *buf_end, int *len)
+{
+    unsigned addr = ntohl(ia->s_addr);
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10((addr & 0x000000FF)      , TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x0000FF00) >>  8, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0x00FF0000) >> 16, TRUE, &is_negative, p, &sub_len);
+    *--p = '.';
+    p = conv_10((addr & 0xFF000000) >> 24, TRUE, &is_negative, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+static char *conv_sockaddr_in(struct sockaddr_in *si, char *buf_end, int *len)
+{
+    char *p = buf_end;
+    bool_int is_negative;
+    int sub_len;
+
+    p = conv_10(ntohs(si->sin_port), TRUE, &is_negative, p, &sub_len);
+    *--p = ':';
+    p = conv_in_addr(&si->sin_addr, p, &sub_len);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+
+/*
+ * Convert a floating point number to a string formats 'f', 'e' or 'E'.
+ * The result is placed in buf, and len denotes the length of the string
+ * The sign is returned in the is_negative argument (and is not placed
+ * in buf).
+ */
+static char *conv_fp(register char format, register double num,
+    boolean_e add_dp, int precision, bool_int *is_negative,
+    char *buf, int *len)
+{
+    register char *s = buf;
+    register char *p;
+    int decimal_point;
+    char buf1[NDIG];
+
+    if (format == 'f')
+       p = ap_fcvt(num, precision, &decimal_point, is_negative, buf1);
+    else                       /* either e or E format */
+       p = ap_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
+
+    /*
+     * Check for Infinity and NaN
+     */
+    if (ap_isalpha(*p)) {
+       *len = strlen(strcpy(buf, p));
+       *is_negative = FALSE;
+       return (buf);
+    }
+
+    if (format == 'f') {
+       if (decimal_point <= 0) {
+           *s++ = '0';
+           if (precision > 0) {
+               *s++ = '.';
+               while (decimal_point++ < 0)
+                   *s++ = '0';
+           }
+           else if (add_dp)
+               *s++ = '.';
+       }
+       else {
+           while (decimal_point-- > 0)
+               *s++ = *p++;
+           if (precision > 0 || add_dp)
+               *s++ = '.';
+       }
+    }
+    else {
+       *s++ = *p++;
+       if (precision > 0 || add_dp)
+           *s++ = '.';
+    }
+
+    /*
+     * copy the rest of p, the NUL is NOT copied
+     */
+    while (*p)
+       *s++ = *p++;
+
+    if (format != 'f') {
+       char temp[EXPONENT_LENGTH];     /* for exponent conversion */
+       int t_len;
+       bool_int exponent_is_negative;
+
+       *s++ = format;          /* either e or E */
+       decimal_point--;
+       if (decimal_point != 0) {
+           p = conv_10((wide_int) decimal_point, FALSE, &exponent_is_negative,
+                       &temp[EXPONENT_LENGTH], &t_len);
+           *s++ = exponent_is_negative ? '-' : '+';
+
+           /*
+            * Make sure the exponent has at least 2 digits
+            */
+           if (t_len == 1)
+               *s++ = '0';
+           while (t_len--)
+               *s++ = *p++;
+       }
+       else {
+           *s++ = '+';
+           *s++ = '0';
+           *s++ = '0';
+       }
+    }
+
+    *len = s - buf;
+    return (buf);
+}
+
+
+/*
+ * Convert num to a base X number where X is a power of 2. nbits determines X.
+ * For example, if nbits is 3, we do base 8 conversion
+ * Return value:
+ *      a pointer to a string containing the number
+ *
+ * The caller provides a buffer for the string: that is the buf_end argument
+ * which is a pointer to the END of the buffer + 1 (i.e. if the buffer
+ * is declared as buf[ 100 ], buf_end should be &buf[ 100 ])
+ *
+ * As with conv_10, we have a faster version which is used when
+ * the number isn't quad size.
+ */
+static char *conv_p2(register u_wide_int num, register int nbits,
+                    char format, char *buf_end, register int *len)
+{
+    register int mask = (1 << nbits) - 1;
+    register char *p = buf_end;
+    static const char low_digits[] = "0123456789abcdef";
+    static const char upper_digits[] = "0123456789ABCDEF";
+    register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+    do {
+       *--p = digits[num & mask];
+       num >>= nbits;
+    }
+    while (num);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+static char *conv_p2_quad(u_widest_int num, register int nbits,
+                    char format, char *buf_end, register int *len)
+{
+    register int mask = (1 << nbits) - 1;
+    register char *p = buf_end;
+    static const char low_digits[] = "0123456789abcdef";
+    static const char upper_digits[] = "0123456789ABCDEF";
+    register const char *digits = (format == 'X') ? upper_digits : low_digits;
+
+    if (num <= ULONG_MAX)
+       return(conv_p2( (u_wide_int)num, nbits, format, buf_end, len));
+
+    do {
+       *--p = digits[num & mask];
+       num >>= nbits;
+    }
+    while (num);
+
+    *len = buf_end - p;
+    return (p);
+}
+
+
+/*
+ * Do format conversion placing the output in buffer
+ */
+API_EXPORT(int) ap_vformatter(int (*flush_func)(ap_vformatter_buff *),
+    ap_vformatter_buff *vbuff, const char *fmt, va_list ap)
+{
+    register char *sp;
+    register char *bep;
+    register int cc = 0;
+    register int i;
+
+    register char *s = NULL;
+    char *q;
+    int s_len;
+
+    register int min_width = 0;
+    int precision = 0;
+    enum {
+       LEFT, RIGHT
+    } adjust;
+    char pad_char;
+    char prefix_char;
+
+    double fp_num;
+    widest_int i_quad = (widest_int) 0;
+    u_widest_int ui_quad;
+    wide_int i_num = (wide_int) 0;
+    u_wide_int ui_num;
+
+    char num_buf[NUM_BUF_SIZE];
+    char char_buf[2];          /* for printing %% and %<unknown> */
+
+    enum var_type_enum {
+       IS_QUAD, IS_LONG, IS_SHORT, IS_INT
+    };
+    enum var_type_enum var_type = IS_INT;
+
+    /*
+     * Flag variables
+     */
+    boolean_e alternate_form;
+    boolean_e print_sign;
+    boolean_e print_blank;
+    boolean_e adjust_precision;
+    boolean_e adjust_width;
+    bool_int is_negative;
+
+    sp = vbuff->curpos;
+    bep = vbuff->endpos;
+
+    while (*fmt) {
+       if (*fmt != '%') {
+           INS_CHAR(*fmt, sp, bep, cc);
+       }
+       else {
+           /*
+            * Default variable settings
+            */
+           adjust = RIGHT;
+           alternate_form = print_sign = print_blank = NO;
+           pad_char = ' ';
+           prefix_char = NUL;
+
+           fmt++;
+
+           /*
+            * Try to avoid checking for flags, width or precision
+            */
+           if (!ap_islower(*fmt)) {
+               /*
+                * Recognize flags: -, #, BLANK, +
+                */
+               for (;; fmt++) {
+                   if (*fmt == '-')
+                       adjust = LEFT;
+                   else if (*fmt == '+')
+                       print_sign = YES;
+                   else if (*fmt == '#')
+                       alternate_form = YES;
+                   else if (*fmt == ' ')
+                       print_blank = YES;
+                   else if (*fmt == '0')
+                       pad_char = '0';
+                   else
+                       break;
+               }
+
+               /*
+                * Check if a width was specified
+                */
+               if (ap_isdigit(*fmt)) {
+                   STR_TO_DEC(fmt, min_width);
+                   adjust_width = YES;
+               }
+               else if (*fmt == '*') {
+                   min_width = va_arg(ap, int);
+                   fmt++;
+                   adjust_width = YES;
+                   if (min_width < 0) {
+                       adjust = LEFT;
+                       min_width = -min_width;
+                   }
+               }
+               else
+                   adjust_width = NO;
+
+               /*
+                * Check if a precision was specified
+                */
+               if (*fmt == '.') {
+                   adjust_precision = YES;
+                   fmt++;
+                   if (ap_isdigit(*fmt)) {
+                       STR_TO_DEC(fmt, precision);
+                   }
+                   else if (*fmt == '*') {
+                       precision = va_arg(ap, int);
+                       fmt++;
+                       if (precision < 0)
+                           precision = 0;
+                   }
+                   else
+                       precision = 0;
+               }
+               else
+                   adjust_precision = NO;
+           }
+           else
+               adjust_precision = adjust_width = NO;
+
+           /*
+            * Modifier check
+            */
+           if (*fmt == 'q') {
+               var_type = IS_QUAD;
+               fmt++;
+           }
+           else if (*fmt == 'l') {
+               var_type = IS_LONG;
+               fmt++;
+           }
+           else if (*fmt == 'h') {
+               var_type = IS_SHORT;
+               fmt++;
+           }
+           else {
+               var_type = IS_INT;
+           }
+
+           /*
+            * Argument extraction and printing.
+            * First we determine the argument type.
+            * Then, we convert the argument to a string.
+            * On exit from the switch, s points to the string that
+            * must be printed, s_len has the length of the string
+            * The precision requirements, if any, are reflected in s_len.
+            *
+            * NOTE: pad_char may be set to '0' because of the 0 flag.
+            *   It is reset to ' ' by non-numeric formats
+            */
+           switch (*fmt) {
+           case 'u':
+               if (var_type == IS_QUAD) {
+                   i_quad = va_arg(ap, u_widest_int);
+                   s = conv_10_quad(i_quad, 1, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               else {
+                   if (var_type == IS_LONG)
+                       i_num = (wide_int) va_arg(ap, u_wide_int);
+                   else if (var_type == IS_SHORT)
+                       i_num = (wide_int) (unsigned short) va_arg(ap, unsigned int);
+                   else
+                       i_num = (wide_int) va_arg(ap, unsigned int);
+                   s = conv_10(i_num, 1, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               break;
+
+           case 'd':
+           case 'i':
+               if (var_type == IS_QUAD) {
+                   i_quad = va_arg(ap, widest_int);
+                   s = conv_10_quad(i_quad, 0, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               else {
+                   if (var_type == IS_LONG)
+                       i_num = (wide_int) va_arg(ap, wide_int);
+                   else if (var_type == IS_SHORT)
+                       i_num = (wide_int) (short) va_arg(ap, int);
+                   else
+                       i_num = (wide_int) va_arg(ap, int);
+                   s = conv_10(i_num, 0, &is_negative,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+
+               if (is_negative)
+                   prefix_char = '-';
+               else if (print_sign)
+                   prefix_char = '+';
+               else if (print_blank)
+                   prefix_char = ' ';
+               break;
+
+
+           case 'o':
+               if (var_type == IS_QUAD) {
+                   ui_quad = va_arg(ap, u_widest_int);
+                   s = conv_p2_quad(ui_quad, 3, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               else {
+                   if (var_type == IS_LONG)
+                       ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+                   else if (var_type == IS_SHORT)
+                       ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
+                   else
+                       ui_num = (u_wide_int) va_arg(ap, unsigned int);
+                   s = conv_p2(ui_num, 3, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               if (alternate_form && *s != '0') {
+                   *--s = '0';
+                   s_len++;
+               }
+               break;
+
+
+           case 'x':
+           case 'X':
+               if (var_type == IS_QUAD) {
+                   ui_quad = va_arg(ap, u_widest_int);
+                   s = conv_p2_quad(ui_quad, 4, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               else {
+                   if (var_type == IS_LONG)
+                       ui_num = (u_wide_int) va_arg(ap, u_wide_int);
+                   else if (var_type == IS_SHORT)
+                       ui_num = (u_wide_int) (unsigned short) va_arg(ap, unsigned int);
+                   else
+                       ui_num = (u_wide_int) va_arg(ap, unsigned int);
+                   s = conv_p2(ui_num, 4, *fmt,
+                           &num_buf[NUM_BUF_SIZE], &s_len);
+               }
+               FIX_PRECISION(adjust_precision, precision, s, s_len);
+               if (alternate_form && i_num != 0) {
+                   *--s = *fmt;        /* 'x' or 'X' */
+                   *--s = '0';
+                   s_len += 2;
+               }
+               break;
+
+
+           case 's':
+               s = va_arg(ap, char *);
+               if (s != NULL) {
+                   s_len = strlen(s);
+                   if (adjust_precision && precision < s_len)
+                       s_len = precision;
+               }
+               else {
+                   s = S_NULL;
+                   s_len = S_NULL_LEN;
+               }
+               pad_char = ' ';
+               break;
+
+
+           case 'f':
+           case 'e':
+           case 'E':
+               fp_num = va_arg(ap, double);
+               /*
+                * * We use &num_buf[ 1 ], so that we have room for the sign
+                */
+#ifdef HAVE_ISNAN
+               if (isnan(fp_num)) {
+                   s = "nan";
+                   s_len = 3;
+               }
+               else
+#endif
+#ifdef HAVE_ISINF
+               if (isinf(fp_num)) {
+                   s = "inf";
+                   s_len = 3;
+               }
+               else
+#endif
+               {
+                   s = conv_fp(*fmt, fp_num, alternate_form,
+                           (adjust_precision == NO) ? FLOAT_DIGITS : precision,
+                               &is_negative, &num_buf[1], &s_len);
+                   if (is_negative)
+                       prefix_char = '-';
+                   else if (print_sign)
+                       prefix_char = '+';
+                   else if (print_blank)
+                       prefix_char = ' ';
+               }
+               break;
+
+
+           case 'g':
+           case 'G':
+               if (adjust_precision == NO)
+                   precision = FLOAT_DIGITS;
+               else if (precision == 0)
+                   precision = 1;
+               /*
+                * * We use &num_buf[ 1 ], so that we have room for the sign
+                */
+               s = ap_gcvt(va_arg(ap, double), precision, &num_buf[1],
+                           alternate_form);
+               if (*s == '-')
+                   prefix_char = *s++;
+               else if (print_sign)
+                   prefix_char = '+';
+               else if (print_blank)
+                   prefix_char = ' ';
+
+               s_len = strlen(s);
+
+               if (alternate_form && (q = strchr(s, '.')) == NULL) {
+                   s[s_len++] = '.';
+                   s[s_len] = '\0'; /* delimit for following strchr() */
+               }
+               if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL)
+                   *q = 'E';
+               break;
+
+
+           case 'c':
+               char_buf[0] = (char) (va_arg(ap, int));
+               s = &char_buf[0];
+               s_len = 1;
+               pad_char = ' ';
+               break;
+
+
+           case '%':
+               char_buf[0] = '%';
+               s = &char_buf[0];
+               s_len = 1;
+               pad_char = ' ';
+               break;
+
+
+           case 'n':
+               if (var_type == IS_QUAD)
+                   *(va_arg(ap, widest_int *)) = cc;
+               else if (var_type == IS_LONG)
+                   *(va_arg(ap, long *)) = cc;
+               else if (var_type == IS_SHORT)
+                   *(va_arg(ap, short *)) = cc;
+               else
+                   *(va_arg(ap, int *)) = cc;
+               break;
+
+               /*
+                * This is where we extend the printf format, with a second
+                * type specifier
+                */
+           case 'p':
+               switch(*++fmt) {
+                   /*
+                    * If the pointer size is equal to or smaller than the size
+                    * of the largest unsigned int, we convert the pointer to a
+                    * hex number, otherwise we print "%p" to indicate that we
+                    * don't handle "%p".
+                    */
+               case 'p':
+#ifdef AP_VOID_P_IS_QUAD
+                   if (sizeof(void *) <= sizeof(u_widest_int)) {
+                       ui_quad = (u_widest_int) va_arg(ap, void *);
+                       s = conv_p2_quad(ui_quad, 4, 'x',
+                               &num_buf[NUM_BUF_SIZE], &s_len);
+                   }
+#else
+                   if (sizeof(void *) <= sizeof(u_wide_int)) {
+                       ui_num = (u_wide_int) va_arg(ap, void *);
+                       s = conv_p2(ui_num, 4, 'x',
+                               &num_buf[NUM_BUF_SIZE], &s_len);
+                   }
+#endif
+                   else {
+                       s = "%p";
+                       s_len = 2;
+                       prefix_char = NUL;
+                   }
+                   pad_char = ' ';
+                   break;
+
+                   /* print a struct sockaddr_in as a.b.c.d:port */
+               case 'I':
+                   {
+                       struct sockaddr_in *si;
+
+                       si = va_arg(ap, struct sockaddr_in *);
+                       if (si != NULL) {
+                           s = conv_sockaddr_in(si, &num_buf[NUM_BUF_SIZE], &s_len);
+                           if (adjust_precision && precision < s_len)
+                               s_len = precision;
+                       }
+                       else {
+                           s = S_NULL;
+                           s_len = S_NULL_LEN;
+                       }
+                       pad_char = ' ';
+                   }
+                   break;
+
+                   /* print a struct in_addr as a.b.c.d */
+               case 'A':
+                   {
+                       struct in_addr *ia;
+
+                       ia = va_arg(ap, struct in_addr *);
+                       if (ia != NULL) {
+                           s = conv_in_addr(ia, &num_buf[NUM_BUF_SIZE], &s_len);
+                           if (adjust_precision && precision < s_len)
+                               s_len = precision;
+                       }
+                       else {
+                           s = S_NULL;
+                           s_len = S_NULL_LEN;
+                       }
+                       pad_char = ' ';
+                   }
+                   break;
+
+               case NUL:
+                   /* if %p ends the string, oh well ignore it */
+                   continue;
+
+               default:
+                   s = "bogus %p";
+                   s_len = 8;
+                   prefix_char = NUL;
+                   break;
+               }
+               break;
+
+           case NUL:
+               /*
+                * The last character of the format string was %.
+                * We ignore it.
+                */
+               continue;
+
+
+               /*
+                * The default case is for unrecognized %'s.
+                * We print %<char> to help the user identify what
+                * option is not understood.
+                * This is also useful in case the user wants to pass
+                * the output of format_converter to another function
+                * that understands some other %<char> (like syslog).
+                * Note that we can't point s inside fmt because the
+                * unknown <char> could be preceded by width etc.
+                */
+           default:
+               char_buf[0] = '%';
+               char_buf[1] = *fmt;
+               s = char_buf;
+               s_len = 2;
+               pad_char = ' ';
+               break;
+           }
+
+           if (prefix_char != NUL && s != S_NULL && s != char_buf) {
+               *--s = prefix_char;
+               s_len++;
+           }
+
+           if (adjust_width && adjust == RIGHT && min_width > s_len) {
+               if (pad_char == '0' && prefix_char != NUL) {
+                   INS_CHAR(*s, sp, bep, cc);
+                   s++;
+                   s_len--;
+                   min_width--;
+               }
+               PAD(min_width, s_len, pad_char);
+           }
+
+           /*
+            * Print the string s. 
+            */
+           for (i = s_len; i != 0; i--) {
+               INS_CHAR(*s, sp, bep, cc);
+               s++;
+           }
+
+           if (adjust_width && adjust == LEFT && min_width > s_len)
+               PAD(min_width, s_len, pad_char);
+       }
+       fmt++;
+    }
+    vbuff->curpos = sp;
+
+    return cc;
+}
+
+
+static int snprintf_flush(ap_vformatter_buff *vbuff)
+{
+    /* if the buffer fills we have to abort immediately, there is no way
+     * to "flush" an ap_snprintf... there's nowhere to flush it to.
+     */
+    return -1;
+}
+
+
+API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...)
+{
+    int cc;
+    va_list ap;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+       return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    va_start(ap, format);
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    va_end(ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
+
+
+API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format,
+                            va_list ap)
+{
+    int cc;
+    ap_vformatter_buff vbuff;
+
+    if (len == 0)
+       return 0;
+
+    /* save one byte for nul terminator */
+    vbuff.curpos = buf;
+    vbuff.endpos = buf + len - 1;
+    cc = ap_vformatter(snprintf_flush, &vbuff, format, ap);
+    *vbuff.curpos = '\0';
+    return (cc == -1) ? len : cc;
+}
diff --git a/contrib/top/boolean.h b/contrib/top/boolean.h
new file mode 100644 (file)
index 0000000..c6bcf4d
--- /dev/null
@@ -0,0 +1,5 @@
+/* My favorite names for boolean values */
+#define  No    0
+#define  Yes   1
+#define  Maybe 2               /* tri-state boolean, actually */
+
diff --git a/contrib/top/color.c b/contrib/top/color.c
new file mode 100644 (file)
index 0000000..c681082
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * 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 file handles color definitions and access for augmenting
+ * the output with ansi color sequences.
+ *
+ * The definition of a color setting is as follows, separated by
+ * colons:
+ *
+ * tag=minimum,maximum#code
+ *
+ * "tag" is the name of the value to display with color.
+ *
+ * "minimum" and "maximum" are positive integer values defining a range:
+ * when the value is within this range it will be shown with the
+ * specified color.  A missing value indicates that no check should be
+ * made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50,"
+ * is 50 <= n).
+ * 
+ * "code" is the ansi sequence that defines the color to use with the
+ * escape sequence "[m".  Semi-colons are allowed in this string to
+ * combine attributes.
+ */
+
+#include "os.h"
+#include "display.h"
+#include "message.h"
+#include "color.h"
+#include "utils.h"
+
+typedef struct color_entry {
+    char *tag;
+    int min;
+    int max;
+    char color;
+    struct color_entry *next;
+    struct color_entry *tagnext;
+} color_entry;
+
+static color_entry *entries = NULL;
+
+static color_entry **bytag = NULL;
+static char **bytag_names = NULL;
+static int totaltags = 0;
+static int tagcnt = 0;
+static int color_off = 0;
+
+static char **color_ansi = NULL;
+static int num_color_ansi = 0;
+static int max_color_ansi = 0;
+
+static int
+color_slot(char *str)
+
+{
+    int i;
+
+    for (i = 0; i < num_color_ansi; i++)
+    {
+       if (strcmp(color_ansi[i], str) == 0)
+       {
+           return i;
+       }
+    }
+
+    /* need a new slot */
+    if (num_color_ansi >= max_color_ansi)
+    {
+       max_color_ansi += COLOR_ANSI_SLOTS;
+       color_ansi = (char **)realloc(color_ansi, max_color_ansi * sizeof(char *));
+    }
+    color_ansi[num_color_ansi] = strdup(str);
+    return num_color_ansi++;
+}
+
+/*
+ * int color_env_parse(char *env)
+ *
+ * Parse a color specification "env" (such as one found in the environment) and
+ * add them to the list of entries.  Always returns 0.  Should only be called
+ * once.
+ */
+
+int
+color_env_parse(char *env)
+
+{
+    char *p;
+    char *min;
+    char *max;
+    char *str;
+    int len;
+    color_entry *ce;
+
+    /* initialization */
+    color_ansi = (char **)malloc(COLOR_ANSI_SLOTS * sizeof(char *));
+    max_color_ansi = COLOR_ANSI_SLOTS;
+
+    /* color slot 0 is always "0" */
+    color_slot("0");
+
+    if (env != NULL)
+    {
+       p = strtok(env, ":");
+       while (p != NULL)
+       {
+           if ((min = strchr(p, '=')) != NULL &&
+               (max = strchr(min, ',')) != NULL &&
+               (str = strchr(max, '#')) != NULL)
+           {
+               ce = (color_entry *)malloc(sizeof(color_entry));
+               len = min - p;
+               ce->tag = (char *)malloc(len + 1);
+               strncpy(ce->tag, p, len);
+               ce->tag[len] = '\0';
+               ce->min = atoi(++min);
+               ce->max = atoi(++max);
+               ce->color = color_slot(++str);
+               ce->next = entries;
+               entries = ce;
+           }
+           else
+           {
+               if (min != NULL)
+               {
+                   len = min - p;
+               }
+               else
+               {
+                   len = strlen(p);
+               }
+               message_error(" %.*s: bad color entry", len, p);
+           }
+           p = strtok(NULL, ":");
+       }
+    }
+    return 0;
+}
+
+/*
+ * int color_tag(char *tag)
+ *
+ * Declare "tag" as a color tag.  Return a tag index to use when testing
+ * a value against the tests for this tag.  Should not be called before
+ * color_env_parse.
+ */
+
+int
+color_tag(char *tag)
+
+{
+    color_entry *entryp;
+    color_entry *tp;
+
+    /* check for absurd arguments */
+    if (tag == NULL || *tag == '\0')
+    {
+       return -1;
+    }
+
+    dprintf("color_tag(%s)\n", tag);
+
+    /* initial allocation */
+    if (bytag == NULL)
+    {
+       totaltags = 10;
+       bytag = (color_entry **)malloc(totaltags * sizeof(color_entry *));
+       bytag_names = (char **)malloc(totaltags * sizeof(char *));
+    }
+
+    /* if we dont have enough room then reallocate */
+    if (tagcnt >= totaltags)
+    {
+       totaltags *= 2;
+       bytag = (color_entry **)realloc(bytag, totaltags * sizeof(color_entry *));
+       bytag_names = (char **)realloc(bytag_names, totaltags * sizeof(char *));
+    }
+
+    /* initialize scan */
+    entryp = entries;
+    tp = NULL;
+
+    /* look for tag in the list of entries */
+    while (entryp != NULL)
+    {
+       if (strcmp(entryp->tag, tag) == 0)
+       {
+           entryp->tagnext = tp;
+           tp = entryp;
+       }
+       entryp = entryp->next;
+    }
+
+    /* we track names in the array bytag */
+    bytag[tagcnt] = tp;
+    bytag_names[tagcnt] = strdup(tag);
+
+    /* return this index number as a reference */
+    dprintf("color_tag: returns %d\n", tagcnt);
+    return (tagcnt++);
+}
+
+/*
+ * int color_test(int tagidx, int value)
+ *
+ * Test "value" against tests for tag "tagidx", a number previously returned
+ * by color_tag.  Return the correct color number to use when highlighting.
+ * If there is no match, return 0 (color 0).
+ */
+
+int
+color_test(int tagidx, int value)
+
+{
+    color_entry *ce;
+
+    /* sanity check */
+    if (tagidx < 0 || tagidx >= tagcnt || color_off)
+    {
+       return 0;
+    }
+
+    ce = bytag[tagidx];
+
+    while (ce != NULL)
+    {
+       if ((!ce->min || ce->min <= value) &&
+           (!ce->max || ce->max >= value))
+       {
+           return ce->color;
+       }
+       ce = ce->tagnext;
+    }
+
+    return 0;
+}
+
+/*
+ * char *color_setstr(int color)
+ *
+ * Return ANSI string to set the terminal for color number "color".
+ */
+
+char *
+color_setstr(int color)
+
+{
+    static char v[32];
+
+    v[0] = '\0';
+    if (color >= 0 && color < num_color_ansi)
+    {
+       snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]);
+    }
+    return v;
+}
+
+void
+color_dump(FILE *f)
+
+{
+    color_entry *ep;
+    int i;
+    int col;
+    int len;
+
+    fputs("These color tags are available:", f);
+    col = 81;
+    for (i = 0; i < tagcnt; i++)
+    {
+       len = strlen(bytag_names[i]) + 1;
+       if (len + col > 79)
+       {
+           fputs("\n  ", f);
+           col = 2;
+       }
+       fprintf(f, " %s", bytag_names[i]);
+       col += len;
+    }
+
+    fputs("\n\nTop color settings:\n", f);
+
+    for (i = 0; i < tagcnt; i++)
+    {
+       ep = bytag[i];
+       while (ep != NULL)
+       {
+           fprintf(f, "   %s (%d-", ep->tag, ep->min);
+           if (ep->max != 0)
+           {
+               fprintf(f, "%d", ep->max);
+           }
+           fprintf(f, "): ansi color %s, %sSample Text",
+                   color_ansi[(int)ep->color],
+                   color_setstr(ep->color));
+           fprintf(f, "%s\n", color_setstr(0));
+           ep = ep -> tagnext;
+       }
+    }
+}
+
+void
+color_debug(FILE *f)
+
+{
+    color_entry *ep;
+    int i;
+
+    fprintf(f, "color debug dump\n");
+    ep = entries;
+    while (ep != NULL)
+    {
+       fprintf(f, "%s(%d,%d): slot %d, ansi %s, %sSample Text",
+               ep->tag, ep->min, ep->max, ep->color, color_ansi[(int)ep->color],
+              color_setstr(ep->color));
+       fprintf(f, "%s\n", color_setstr(0));
+       ep = ep -> next;
+    }
+
+    fprintf(f, "\ntags:");
+    for (i = 0; i < tagcnt; i++)
+    {
+       fprintf(f, " %s", bytag_names[i]);
+    }
+    fprintf(f, "\n");
+}
+
+int
+color_activate(int i)
+
+{
+    if (i == -1)
+    {
+       color_off = !color_off;
+    }
+    else
+    {
+       color_off = !i;
+    }
+    return color_off;
+}
diff --git a/contrib/top/color.h b/contrib/top/color.h
new file mode 100644 (file)
index 0000000..9a210a2
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * 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
+ *
+ * Definition of the color interface.
+ */
+
+#ifndef _COLOR_H_
+#define _COLOR_H_
+
+#define COLOR_ANSI_SLOTS 20
+
+int color_env_parse(char *env);
+int color_tag(char *tag);
+int color_test(int tagidx, int value);
+char *color_setstr(int color);
+void color_dump(FILE *f);
+int color_activate(int i);
+
+
+/*
+ * These color tag names are currently in use
+ * (or reserved for future use):
+ *
+ * cpu, size, res, time, 1min, 5min, 15min, host
+ */
+
+/*
+ * Valid ANSI values for colors are:
+ *
+ * 0   Reset all attributes
+ * 1   Bright
+ * 2   Dim
+ * 4   Underscore      
+ * 5   Blink
+ * 7   Reverse
+ * 8   Hidden
+ * 
+ *     Foreground Colours
+ * 30  Black
+ * 31  Red
+ * 32  Green
+ * 33  Yellow
+ * 34  Blue
+ * 35  Magenta
+ * 36  Cyan
+ * 37  White
+ * 
+ *     Background Colours
+ * 40  Black
+ * 41  Red
+ * 42  Green
+ * 43  Yellow
+ * 44  Blue
+ * 45  Magenta
+ * 46  Cyan
+ * 47  White
+ */
+
+#endif /*_COLOR_H_ */
diff --git a/contrib/top/commands.c b/contrib/top/commands.c
new file mode 100644 (file)
index 0000000..e93fb54
--- /dev/null
@@ -0,0 +1,1034 @@
+/*
+ * 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 file contains the routines that implement some of the interactive
+ *  mode commands.  Note that some of the commands are implemented in-line
+ *  in "main".  This is necessary because they change the global state of
+ *  "top" (i.e.:  changing the number of processes to display).
+ */
+
+#include "os.h"
+#include <ctype.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <color.h>
+#include <errno.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;
+
+typedef struct command {
+    int ch;
+    int (*cmd_func)(globalstate *);
+    char *help;
+} command;
+
+/*
+ *  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.  We need an upper limit on
+ *  the number of errors, so we arbitrarily choose 20.
+ */
+
+#define ERRMAX 20
+
+struct errs            /* structure for a system-call error */
+{
+    int  errnum;       /* value of errno (that is, the actual error) */
+    char *arg;         /* argument that caused the error */
+};
+
+static struct errs errs[ERRMAX];
+static int errcnt;
+
+/* These macros get used to reset and log the errors */
+#define ERR_RESET   errcnt = 0
+#define ERROR(p, e) if (errcnt < ERRMAX) \
+                   { \
+                       errs[errcnt].arg = (p); \
+                       errs[errcnt++].errnum = (e); \
+                   }
+
+/*
+ *  err_compar(p1, p2) - comparison routine used by "qsort"
+ *     for sorting errors.
+ */
+
+int
+err_compar(const void *p1, const void *p2)
+
+{
+    register int result;
+
+    if ((result = ((struct errs *)p1)->errnum -
+        ((struct errs *)p2)->errnum) == 0)
+    {
+       return(strcmp(((struct errs *)p1)->arg,
+                     ((struct errs *)p2)->arg));
+    }
+    return(result);
+}
+
+/*
+ *  str_adderr(str, len, err) - add an explanation of error "err" to
+ *     the string "str" without overflowing length "len".  return
+ *      number of characters remaining in str, or 0 if overflowed.
+ */
+
+int
+str_adderr(char *str, int len, int err)
+
+{
+    register char *msg;
+    register int  msglen;
+
+    msg = err == 0 ? "Not a number" : errmsg(err);
+    msglen = strlen(msg) + 2;
+    if (len <= msglen)
+    {
+       return(0);
+    }
+    (void) strcat(str, ": ");
+    (void) strcat(str, msg);
+    return(len - msglen);
+}
+
+/*
+ *  str_addarg(str, len, arg, first) - add the string argument "arg" to
+ *     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.
+ */
+
+int
+str_addarg(char *str, int len, char *arg, int first)
+
+{
+    register int arglen;
+
+    arglen = strlen(arg);
+    if (!first)
+    {
+       arglen += 2;
+    }
+    if (len <= arglen)
+    {
+       return(0);
+    }
+    if (!first)
+    {
+       (void) strcat(str, ", ");
+    }
+    (void) strcat(str, arg);
+    return(len - arglen);
+}
+
+/*
+ * 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.
+ */
+
+#define STRMAX 80
+
+void
+err_string()
+
+{
+    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 there are no errors, our job is easy */
+    if (errcnt == 0)
+    {
+       return;
+    }
+
+    /* 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()
+
+{
+    return(errcnt);
+}
+
+/*
+ *  show_errors() - display on stdout the current log of errors.
+ */
+
+void
+show_errors()
+
+{
+    register int cnt = 0;
+    register struct errs *errp = errs;
+
+    printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s");
+    while (cnt++ < errcnt)
+    {
+       printf("%5s: %s\n", errp->arg,
+           errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum));
+       errp++;
+    }
+}
+
+/*
+ *  kill_procs(str) - send signals to processes, much like the "kill"
+ *             command does; invoked in response to 'k'.
+ */
+
+void
+kill_procs(char *str)
+
+{
+    register char *nptr;
+    int signum = SIGTERM;      /* default */
+    int procnum;
+    int uid;
+    int owner;
+#ifndef USE_SYS_SIGLIST
+    struct sigdesc *sigp;
+#endif
+
+    /* reset error array */
+    ERR_RESET;
+
+    /* remember our uid */
+    uid = getuid();
+
+    /* skip over leading white space */
+    while (isspace((int)*str)) str++;
+
+    if (str[0] == '-')
+    {
+       /* explicit signal specified */
+       if ((nptr = next_field(str)) == NULL)
+       {
+           message_error(" kill: no processes specified");
+           return;
+       }
+
+       str++;
+       if (isdigit((int)str[0]))
+       {
+           (void) scanint(str, &signum);
+           if (signum <= 0 || signum >= NSIG)
+           {
+               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++)
+           {
+#ifdef HAVE_STRCASECMP
+               if (strcasecmp(sigp->name, str) == 0)
+#else
+               if (strcmp(sigp->name, str) == 0)
+#endif
+               {
+                   signum = sigp->number;
+                   break;
+               }
+           }
+
+           /* was it ever found */
+           if (sigp->name == NULL)
+           {
+               message_error(" kill: bad signal name");
+               return;
+           }
+#endif
+       }
+       /* put the new pointer in place */
+       str = nptr;
+    }
+
+    /* loop thru the string, killing processes */
+    do
+    {
+       if (scanint(str, &procnum) == -1)
+       {
+           ERROR(str, 0);
+       }
+       else
+       {
+           /* check process owner if we're not root */
+           owner = proc_owner(procnum);
+           if (uid && (uid != owner))
+           {
+               ERROR(str, owner == -1 ? ESRCH : EACCES);
+           }
+           /* go in for the kill */
+           else if (kill(procnum, signum) == -1)
+           {
+               /* chalk up an error */
+               ERROR(str, errno);
+           }
+       }
+    } while ((str = next_field(str)) != NULL);
+
+    /* process errors */
+    err_string();
+}
+
+/*
+ *  renice_procs(str) - change the "nice" of processes, much like the
+ *             "renice" command does; invoked in response to 'r'.
+ */
+
+void
+renice_procs(char *str)
+
+{
+    register char negate;
+    int prio;
+    int procnum;
+    int uid;
+
+    ERR_RESET;
+    uid = getuid();
+
+    /* allow for negative priority values */
+    if ((negate = (*str == '-')) != 0)
+    {
+       /* move past the minus sign */
+       str++;
+    }
+
+    /* use procnum as a temporary holding place and get the number */
+    procnum = scanint(str, &prio);
+
+    /* negate if necessary */
+    if (negate)
+    {
+       prio = -prio;
+    }
+
+#if defined(PRIO_MIN) && defined(PRIO_MAX)
+    /* check for validity */
+    if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX)
+    {
+       message_error(" renice: bad priority value");
+    }
+#endif
+
+    /* move to the first process number */
+    if ((str = next_field(str)) == NULL)
+    {
+       message_error(" remice: no processes specified");
+    }
+
+#ifdef HAVE_SETPRIORITY
+    /* loop thru the process numbers, renicing each one */
+    do
+    {
+       if (scanint(str, &procnum) == -1)
+       {
+           ERROR(str, 0);
+       }
+
+       /* check process owner if we're not root */
+       else if (uid && (uid != proc_owner(procnum)))
+       {
+           ERROR(str, EACCES);
+       }
+       else if (setpriority(PRIO_PROCESS, procnum, prio) == -1)
+       {
+           ERROR(str, errno);
+       }
+    } while ((str = next_field(str)) != NULL);
+    err_string();
+#else
+    message_error(" renice operation not supported");
+#endif
+}
+
+/* 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_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);
+    return CMD_REFRESH;
+}
+
+int
+cmd_threads(globalstate *gstate)
+
+{
+    if (gstate->statics->flags.threads)
+    {
+       gstate->pselect.threads = !gstate->pselect.threads;
+       display_header(2);
+       return CMD_REFRESH;
+    }
+    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_color, "toggle the use of color" },
+    { 'H', cmd_threads, "toggle the display of individual threads" },
+    { 't', cmd_threads, NULL },
+    { '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" },
+    { 'f', 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" },
+    { '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" },
+    { '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;
+}
diff --git a/contrib/top/commands.h b/contrib/top/commands.h
new file mode 100644 (file)
index 0000000..cf98c8d
--- /dev/null
@@ -0,0 +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.
+ */
+
+/* call specifications for commands.c */
+
+int command_process(globalstate *gstate, int cmd);
+
+/* returns from command routines */
+#define CMD_ERROR  -1
+#define CMD_OK      0
+#define CMD_REFRESH 1
+#define CMD_UNKNOWN 2
+#define CMD_NA      3
diff --git a/contrib/top/display.c b/contrib/top/display.c
new file mode 100644 (file)
index 0000000..2330ca4
--- /dev/null
@@ -0,0 +1,1937 @@
+/*
+ * 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 file contains the routines that display information on the screen.
+ *  Each section of the screen has two routines:  one for initially writing
+ *  all constant and dynamic text, and one for only updating the text that
+ *  changes.  The prefix "i_" is used on all the "initial" routines and the
+ *  prefix "u_" is used for all the "updating" routines.
+ *
+ *  ASSUMPTIONS:
+ *        None of the "i_" routines use any of the termcap capabilities.
+ *        In this way, those routines can be safely used on terminals that
+ *        have minimal (or nonexistant) terminal capabilities.
+ *
+ *        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 <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 "boolean.h"
+#include "utils.h"
+
+#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 = -1;
+static int display_width = MAX_COLS;
+
+/* 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 */
+
+static int x_lastpid = X_LASTPID;
+static int y_lastpid = Y_LASTPID;
+static int x_loadave = X_LOADAVE;
+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_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;
+
+/* 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;
+
+static int *cpustate_columns;
+static int cpustate_total_length;
+
+static int header_status = Yes;
+
+/* 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;
+
+#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
+
+
+/* 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 cnt = 0;
+
+    if (pp != NULL)
+    {
+       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 */
+    top_lines = smart_terminal ? screen_length : 1;
+
+    /* 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 */
+    display_width = screen_width;
+    if (display_width >= MAX_COLS)
+    {
+       display_width = MAX_COLS - 1;
+    }
+
+    /* 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)
+    {
+       /* 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 ? top_lines : Largest);
+}
+
+int
+display_lines()
+
+{
+    return(smart_terminal ? screen_length : Largest);
+}
+
+int
+display_columns()
+
+{
+    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++;
+    }
+    
+    /* call resize to do the dirty work */
+    top_lines = display_resize();
+
+    /* only do the rest if we need to */
+    if (top_lines > -1)
+    {
+       /* save pointers and allocate space for names */
+       procstate_names = statics->procstate_names;
+       num_procstates = string_count(procstate_names);
+       lprocstates = (int *)calloc(num_procstates, sizeof(int));
+
+       cpustate_names = statics->cpustate_names;
+       num_cpustates = string_count(cpustate_names);
+       lcpustates = (int *)calloc(num_cpustates, sizeof(int));
+       cpustate_columns = (int *)calloc(num_cpustates, sizeof(int));
+       memory_names = statics->memory_names;
+       num_memory = string_count(memory_names);
+
+       /* calculate starting columns where needed */
+       cpustate_total_length = 0;
+       pp = cpustate_names;
+       ip = cpustate_columns;
+       while (*pp != NULL)
+       {
+           *ip++ = cpustate_total_length;
+           if ((i = strlen(*pp++)) > 0)
+           {
+               cpustate_total_length += i + 8;
+           }
+       }
+    }
+
+#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);
+}
+
+static void
+pr_loadavg(double avg, int i)
+
+{
+    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;
+
+    /* mpid == -1 implies this system doesn't have an _mpid */
+    if (mpid != -1)
+    {
+       display_fmt(0, 0, 0, 0,
+                   "last pid: %5d;  load avg:", mpid);
+       x_loadave = X_LOADAVE;
+    }
+    else
+    {
+       display_write(0, 0, 0, 0, "load averages:");
+       x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
+    }
+    for (i = 0; i < 3; i++)
+    {
+       pr_loadavg(avenrun[i], i);
+    }
+
+    lmpid = mpid;
+}
+
+void
+u_loadave(int mpid, double *avenrun)
+
+{
+    register int i;
+
+    if (mpid != -1)
+    {
+       /* change screen only when value has really changed */
+       if (mpid != lmpid)
+       {
+           display_fmt(x_lastpid, y_lastpid, 0, 0,
+                       "%5d", mpid);
+           lmpid = mpid;
+       }
+    }
+
+    /* display new load averages */
+    for (i = 0; i < 3; i++)
+    {
+       pr_loadavg(avenrun[i], i);
+    }
+}
+
+static char minibar_buffer[64];
+#define MINIBAR_WIDTH 20
+
+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))
+{
+    (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH));
+
+    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)
+
+{
+    time_t uptime;
+
+    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;
+
+       /*
+        *  Display the uptime.
+        */
+
+       display_fmt(x_uptime, y_uptime, 0, 0,
+                   "  up %d+%02d:%02d:%02d",
+                   uptime_days, uptime_hours, uptime_mins, uptime_secs);
+    }
+}
+
+void
+u_uptime(time_t *bt, time_t *tod)
+
+{
+    i_uptime(bt, tod);
+}
+
+
+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;
+
+    /* 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)
+    {
+       x = x_uptime + 19;
+    }
+
+    /* display it */
+    display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11]));
+}
+
+static int ltotal = 0;
+static int lthreads = 0;
+
+/*
+ *  *_procstates(total, brkdn, names) - print the process summary line
+ */
+
+
+void
+i_procstates(int total, int *brkdn, int threads)
+
+{
+    /* write current number of processes and remember the value */
+    display_fmt(0, y_procstate, 0, 0,
+               "%d %s: ", total, threads ? "threads" : "processes");
+    ltotal = total;
+
+    /* remember where the summary starts */
+    x_procstate = virt_x;
+
+    if (total > 0)
+    {
+       /* 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;
+    }
+}
+
+void
+u_procstates(int total, int *brkdn, int threads)
+
+{
+    /* 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)
+    {
+       display_fmt(0, y_procstate, 0, 0,
+                   "%d", total);
+
+       /* if number of digits differs, rewrite the label */
+       if (digits(total) != digits(ltotal))
+       {
+           display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes");
+           x_procstate = virt_x;
+       }
+
+       /* save new total */
+       ltotal = total;
+    }
+
+    /* see if any of the state numbers has changed */
+    if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
+    {
+       /* format and update the line */
+       summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL);
+       memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
+    }
+}
+
+/*
+ *  *_cpustates(states, names) - print the cpu state percentages
+ */
+
+/* cpustates_tag() calculates the correct tag to use to label the line */
+
+char *
+cpustates_tag()
+
+{
+    register char *use;
+
+    static char *short_tag = "CPU: ";
+    static char *long_tag = "CPU states: ";
+
+    /* if length + strlen(long_tag) >= screen_width, then we have to
+       use the shorter tag (we subtract 2 to account for ": ") */
+    if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width)
+    {
+       use = short_tag;
+    }
+    else
+    {
+       use = long_tag;
+    }
+
+    /* set x_cpustates accordingly then return result */
+    x_cpustates = strlen(use);
+    return(use);
+}
+
+void
+i_cpustates(int *states)
+
+{
+    int value;
+    char **names;
+    char *thisname;
+    int *colp;
+    int color = 0;
+#ifdef ENABLE_COLOR
+    int *cidx = cpustate_cidx;
+#endif
+
+    /* initialize */
+    names = cpustate_names;
+    colp = cpustate_columns;
+
+    /* print tag */
+    display_write(0, y_cpustates, 0, 0, cpustates_tag());
+
+    /* now walk thru the names and print the line */
+    while ((thisname = *names++) != NULL)
+    {
+       if (*thisname != '\0')
+       {
+           /* retrieve the 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% */
+           display_fmt(x_cpustates + *colp, y_cpustates,
+                       color, 0,
+                       (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"),
+                       ((float)value)/10.,
+                       thisname,
+                       *names != NULL ? ", " : "");
+
+       }
+       /* increment */
+       colp++;
+       states++;
+    }
+
+    /* copy over values into "last" array */
+    memcpy(lcpustates, states, num_cpustates * sizeof(int));
+}
+
+void
+u_cpustates(int *states)
+
+{
+    int value;
+    char **names = cpustate_names;
+    char *thisname;
+    int *lp;
+    int *colp;
+    int color = 0;
+#ifdef ENABLE_COLOR
+    int *cidx = cpustate_cidx;
+#endif
+
+    lp = lcpustates;
+    colp = cpustate_columns;
+
+    /* we could be much more optimal about this */
+    while ((thisname = *names++) != NULL)
+    {
+       if (*thisname != '\0')
+       {
+           /* did the value change since last time? */
+           if (*lp != *states)
+           {
+               /* 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% */
+               display_fmt(x_cpustates + *colp, y_cpustates, 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 */
+       lp++;
+       states++;
+       colp++;
+    }
+}
+
+void
+z_cpustates()
+
+{
+    register int i = 0;
+    register char **names = cpustate_names;
+    register char *thisname;
+    register int *lp;
+
+    /* print tag */
+    display_write(0, y_cpustates, 0, 0, cpustates_tag());
+
+    while ((thisname = *names++) != NULL)
+    {
+       if (*thisname != '\0')
+       {
+           display_fmt(-1, -1, 0, 0, "%s    %% %s", i++ == 0 ? "" : ", ",
+                       thisname);
+       }
+    }
+
+    /* fill the "last" array with all -1s, to insure correct updating */
+    lp = lcpustates;
+    i = num_cpustates;
+    while (--i >= 0)
+    {
+       *lp++ = -1;
+    }
+}
+
+/*
+ *  *_kernel(stats) - print "Kernel: " followed by the kernel summary string
+ *
+ *  Assumptions:  cursor is on "lastline", the previous line
+ */
+
+void
+i_kernel(int *stats)
+
+{
+    if (num_kernel > 0)
+    {
+       display_write(0, y_kernel, 0, 0, "Kernel: ");
+
+       /* format and print the kernel summary */
+       summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx);
+    }
+}
+
+void
+u_kernel(int *stats)
+
+{
+    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, "Memory: ");
+
+    /* format and print the memory summary */
+    summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
+}
+
+void
+u_memory(long *stats)
+
+{
+    /* format the new line */
+    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", the previous line
+ *
+ *  These functions only print something when num_swap > 0
+ */
+
+void
+i_swap(long *stats)
+
+{
+    if (num_swap > 0)
+    {
+       /* print the tag */
+       display_write(0, y_swap, 0, 0, "Swap: ");
+
+       /* format and print the swap summary */
+       summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+    }
+}
+
+void
+u_swap(long *stats)
+
+{
+    if (num_swap > 0)
+    {
+       /* format the new line */
+       summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx);
+    }
+}
+
+/*
+ *  *_message() - print the next pending message line, or erase the one
+ *                that is there.
+ *
+ *  Note that u_message is (currently) the same as i_message.
+ *
+ *  Assumptions:  lastline is consistent
+ */
+
+/*
+ *  i_message is funny because it gets its message asynchronously (with
+ *     respect to screen updates).  Messages are taken out of the
+ *      circular message_buf and displayed one at a time.
+ */
+
+void
+i_message(struct timeval *now)
+
+{
+    struct timeval my_now;
+    int i = 0;
+
+    dprintf("i_message(%08x)\n", now);
+
+    /* if now is NULL we have to get it ourselves */
+    if (now == NULL)
+    {
+       time_get(&my_now);
+       now = &my_now;
+    }
+
+    /* 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)
+    {
+       /* 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;
+               }
+           }
+       }
+    }
+}
+
+void
+u_message(struct timeval *now)
+
+{
+    i_message(now);
+}
+
+static int header_length;
+
+/*
+ *  *_header(text) - print the header for the process area
+ *
+ *  Assumptions:  cursor is on the previous line and lastline is consistent
+ */
+
+void
+i_header(char *text)
+
+{
+    int header_color = 0;
+
+#ifdef ENABLE_COLOR
+    header_color = color_test(header_cidx, 0);
+#endif
+    header_length = strlen(text);
+    if (header_status)
+    {
+       display_write(x_header, y_header, header_color, 1, text);
+    }
+}
+
+/*ARGSUSED*/
+void
+u_header(char *text)
+
+{
+    int header_color = 0;
+
+#ifdef ENABLE_COLOR
+    header_color = color_test(header_cidx, 0);
+#endif
+    display_write(x_header, y_header, header_color, 1,
+                 header_status ? text : "");
+}
+
+/*
+ *  *_process(line, thisline) - print one process line
+ *
+ *  Assumptions:  lastline is consistent
+ */
+
+void
+i_process(int line, char *thisline)
+
+{
+    /* truncate the line to conform to our current screen width */
+    thisline[display_width] = '\0';
+
+    /* write the line out */
+    display_write(0, y_procs + line, 0, 1, thisline);
+}
+
+void
+u_process(int line, char *new_line)
+
+{
+    i_process(line, new_line);
+}
+
+void
+i_endscreen()
+
+{
+    if (smart_terminal)
+    {
+       /* move the cursor to a pleasant place */
+       display_move(x_idlecursor, y_idlecursor);
+    }
+    else
+    {
+       /* separate this display from the next with some vertical room */
+       fputs("\n\n", stdout);
+    }
+    fflush(stdout);
+}
+
+void
+u_endscreen()
+
+{
+    if (smart_terminal)
+    {
+       /* clear-to-end the display */
+       display_cte();
+
+       /* move the cursor to a pleasant place */
+       display_move(x_idlecursor, y_idlecursor);
+       fflush(stdout);
+    }
+    else
+    {
+       /* separate this display from the next with some vertical room */
+       fputs("\n\n", stdout);
+    }
+}
+
+void
+display_header(int t)
+
+{
+    header_status = t != 0;
+}
+
+void
+message_mark()
+
+{
+    message_barrier = Yes;
+}
+
+void
+message_expire()
+
+{
+    message_time.tv_sec = 0;
+    message_time.tv_usec = 0;
+}
+
+void
+message_flush()
+
+{
+    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) vsnprintf(msg, sizeof(msg), msgfmt, ap);
+
+    /* 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)
+    {
+       /* 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)
+       {
+           /* we can display the message now */
+           i_message(NULL);
+       }
+    }
+}
+
+/*
+ * 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)
+    {
+       display_move(0, y_message);
+       screen_cleareol(message_length);
+       free(message_current);
+       message_current = 0;
+    }
+
+    /* flush all pending messages */
+    message_flush();
+}
+
+/*
+ * 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)
+
+{
+    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)
+    {
+       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;
+}
+
+/*
+ * void message_prompt(char *msgfmt, ...)
+ *
+ * Place a prompt in the message area (see message_prompt_v).
+ */
+
+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;
+
+    /* allow room for null terminator */
+    size -= 1;
+
+    /* read loop */
+    while ((fflush(stdout), read(0, ptr, 1) > 0))
+    {
+       /* newline or return means we are done */
+       if ((ch = *ptr) == '\n' || ch == '\r')
+       {
+           break;
+       }
+
+       /* handle special editing characters */
+       if (ch == ch_kill)
+       {
+           /* 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 */
+           if (cnt <= 0)
+           {
+               /* none to erase! */
+               putchar('\7');
+           }
+           else
+           {
+               fputs("\b \b", stdout);
+               ptr--;
+               cnt--;
+           }
+       }
+       /* check for character validity and buffer overflow */
+       else if (cnt == size || (numeric && !isdigit((int)ch)) ||
+               !isprint((int)ch))
+       {
+           /* not legal */
+           putchar('\7');
+       }
+       else
+       {
+           /* echo it and store it in the buffer */
+           putchar(ch);
+           ptr++;
+           cnt++;
+       }
+    }
+
+    /* all done -- null terminate the string */
+    *ptr = '\0';
+
+    /* 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);
+}
+
+void
+display_pagerstart()
+
+{
+    display_clear();
+}
+
+void
+display_pagerend()
+
+{
+    char ch;
+
+    screen_standout("Hit any key to continue: ");
+    fflush(stdout);
+    (void) read(0, &ch, 1);
+}
+
+void
+display_pager(char *fmt, ...)
+
+{
+    va_list ap;
+
+    int ch;
+    char readch;
+    char buffer[MAX_COLS];
+    char *data;
+
+    /* format into buffer */
+    va_start(ap, fmt);
+    (void) vsnprintf(buffer, MAX_COLS, fmt, ap);
+    va_end(ap);
+    data = buffer;
+
+    while ((ch = *data++) != '\0')
+    {
+       putchar(ch);
+       if (ch == '\n')
+       {
+           if (++curr_y >= screen_length - 1)
+           {
+               screen_standout("...More...");
+               fflush(stdout);
+               (void) read(0, &readch, 1);
+               putchar('\r');
+               switch(readch)
+               {
+               case '\r':
+               case '\n':
+                   curr_y--;
+                   break;
+
+               case 'q':
+                   return;
+
+               default:
+                   curr_y = 0;
+               }
+           }
+       }
+    }
+}
diff --git a/contrib/top/display.h b/contrib/top/display.h
new file mode 100644 (file)
index 0000000..a44a7bb
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+/* interface declaration for display.c */
+
+#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
diff --git a/contrib/top/getopt.c b/contrib/top/getopt.c
new file mode 100644 (file)
index 0000000..9a4af2f
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+/*
+ * Many modern-day Unix implementations already have this function
+ * in libc.  The standard "getopt" is perfectly sufficient for top's
+ * needs.  If such a function exists in libc then you certainly don't
+ * need to compile this one in.  To prevent this function from being 
+ * compiled, define "HAVE_GETOPT".  This is usually done in the "CFLAGS"
+ * line of the corresponding machine module.
+ */
+
+/*
+ * This empty declaration exists solely to placate overexhuberant C
+ * compilers that like to warn you about content-free files.
+ */
+static void __empty();
+
+#ifndef HAVE_GETOPT
+
+/*LINTLIBRARY*/
+
+#include "os.h"
+#ifndef NULL
+#define NULL   0
+#endif
+#ifndef EOF
+#define EOF    (-1)
+#endif
+#define ERR(s, c)      if(opterr){\
+       extern int write();\
+       char errbuf[2];\
+       errbuf[0] = c; errbuf[1] = '\n';\
+       (void) write(2, argv[0], strlen(argv[0]));\
+       (void) write(2, s, strlen(s));\
+       (void) write(2, errbuf, 2);}
+
+
+int    opterr = 1;
+int    optind = 1;
+int    optopt;
+char   *optarg;
+
+int
+getopt(int argc, char **argv, char *opts)
+
+{
+       static int sp = 1;
+       register int c;
+       register char *cp;
+
+       if(sp == 1)
+               if(optind >= argc ||
+                  argv[optind][0] != '-' || argv[optind][1] == '\0')
+                       return(EOF);
+               else if(strcmp(argv[optind], "--") == 0) {
+                       optind++;
+                       return(EOF);
+               }
+       optopt = c = argv[optind][sp];
+       if(c == ':' || (cp=strchr(opts, c)) == NULL) {
+               ERR(": unknown option, -", c);
+               if(argv[optind][++sp] == '\0') {
+                       optind++;
+                       sp = 1;
+               }
+               return('?');
+       }
+       if(*++cp == ':') {
+               if(argv[optind][sp+1] != '\0')
+                       optarg = &argv[optind++][sp+1];
+               else if(++optind >= argc) {
+                       ERR(": argument missing for -", c);
+                       sp = 1;
+                       return('?');
+               } else
+                       optarg = argv[optind++];
+               sp = 1;
+       } else {
+               if(argv[optind][++sp] == '\0') {
+                       sp = 1;
+                       optind++;
+               }
+               optarg = NULL;
+       }
+       return(c);
+}
+#endif /* HAVE_GETOPT */
diff --git a/contrib/top/globalstate.h b/contrib/top/globalstate.h
new file mode 100644 (file)
index 0000000..fd1d21d
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+/*
+ * The global state of top is described in this structure.  It is passed
+ * to routines that may need to examine or alter it.
+ */
+
+#ifndef _GLOBALSTATE_H_
+#define _GLOBALSTATE_H_
+
+#include "machine.h"
+
+typedef struct globalstate {
+    struct timeval now;
+    struct timeval refresh;
+    int fulldraw;
+    int topn;
+    int max_topn;
+    int delay;
+    int displays;
+    int order_index;
+    int show_cpustates;
+    int show_tags;
+    int show_usernames;
+    int use_color;
+    int smart_terminal;
+    int interactive;
+    char *header_text;
+    char *order_name;    /* only used before call to machine_init */
+    char *order_namelist;
+    char *(*get_userid)(int);
+    struct process_select pselect;
+    struct statics *statics;
+} globalstate;
+
+#endif /* _GLOBALSTATE_H_ */
diff --git a/contrib/top/hash.c b/contrib/top/hash.c
new file mode 100644 (file)
index 0000000..f65fe92
--- /dev/null
@@ -0,0 +1,2001 @@
+/*
+ * 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.
+ */
+
+/* hash.m4c */
+
+/* The file hash.c is generated from hash.m4c via the preprocessor M4 */
+
+/*
+ * Hash table functions:  init, add, lookup, first, next, sizeinfo.
+ * This is a conventional "bucket hash".  The key is hashed in to a number
+ * less than or equal to the number of buckets and the result is used
+ * to index in to the array of buckets.  Each bucket is a linked list
+ * that contains all the key/value pairs which hashed to that index.
+ */
+
+#include "config.h"
+
+#if STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#define memzero(a, b)          memset((a), 0, (b))
+#else /* !STDC_HEADERS */
+#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
+void *malloc();
+void free();
+char *strdup();
+#endif /* !STDC_HEADERS */
+
+/* After all that there are still some systems that don't have NULL defined */
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+#if !HAVE_PID_T
+typedef long pid_t;
+#endif
+#if !HAVE_ID_T
+typedef long id_t;
+#endif
+
+#include "hash.h"
+
+
+
+
+
+
+static int
+next_prime(int x)
+
+{
+    double i, j;
+    int f;
+
+    i = x;
+    while (i++)
+    {
+       f=1;
+       for (j=2; j<i; j++)
+       {
+           if ((i/j)==floor(i/j))
+           {
+               f=0;
+               break;
+           }
+       }
+       if (f)
+       {
+           return (int)i;
+       }
+    }
+    return 1;
+}
+
+/* string hashes */
+
+static int
+string_hash(hash_table *ht, char *key)
+
+{
+    unsigned long s = 0;
+    unsigned char ch;
+    int shifting = 24;
+
+    while ((ch = (unsigned char)*key++) != '\0')
+    {
+       if (shifting == 0)
+       {
+           s = s + ch;
+           shifting = 24;
+       }
+       else
+       {
+           s = s + (ch << shifting);
+           shifting -= 8;
+       }
+    }
+
+    return (s % ht->num_buckets);
+}
+
+void ll_init(llist *q)
+
+{
+    q->head = NULL;
+    q->count = 0;
+}
+
+llistitem *ll_newitem(int size)
+
+{
+    llistitem *qi;
+
+    qi = (llistitem *)malloc(sizeof(llistitem) + size);
+    qi->datum = ((void *)qi + sizeof(llistitem));
+    return qi;
+}
+
+void ll_freeitem(llistitem *li)
+
+{
+    free(li);
+}
+
+void ll_add(llist *q, llistitem *new)
+
+{
+    new->next = q->head;
+    q->head = new;
+    q->count++;
+}
+
+void ll_extract(llist *q, llistitem *qi, llistitem *last)
+
+{
+    if (last == NULL)
+    {
+       q->head = qi->next;
+    }
+    else
+    {
+       last->next = qi->next;
+    }
+    qi->next = NULL;
+    q->count--;
+}
+
+#define LL_FIRST(q) ((q)->head)
+llistitem *
+ll_first(llist *q)
+
+{
+    return q->head;
+}
+
+#define LL_NEXT(q, qi)  ((qi) != NULL ? (qi)->next : NULL)
+llistitem *
+ll_next(llist *q, llistitem *qi)
+
+{
+    return (qi != NULL ? qi->next : NULL);
+}
+
+#define LL_ISEMPTY(ll)  ((ll)->count == 0)
+int
+ll_isempty(llist *ll)
+
+{
+    return (ll->count == 0);
+}
+
+/*
+ * hash_table *hash_create(int num)
+ *
+ * Creates a hash table structure with at least "num" buckets.
+ */
+
+hash_table *
+hash_create(int num)
+
+{
+    hash_table *result;
+    bucket_t *b;
+    int bytes;
+    int i;
+
+    /* create the resultant structure */
+    result = (hash_table *)malloc(sizeof(hash_table));
+
+    /* adjust bucket count to be prime */
+    num = next_prime(num);
+
+    /* create the buckets */
+    bytes = sizeof(bucket_t) * num;
+    result->buckets = b = (bucket_t *)malloc(bytes);
+    result->num_buckets = num;
+
+    /* create each bucket as a linked list */
+    i = num;
+    while (--i >= 0)
+    {
+       ll_init(&(b->list));
+       b++;
+    }
+
+    return result;
+}
+
+/*
+ * unsigned int hash_count(hash_table *ht)
+ *
+ * Return total number of elements contained in hash table.
+ */
+
+unsigned int
+hash_count(hash_table *ht)
+
+{
+    register int i = 0;
+    register int cnt = 0;
+    register bucket_t *bucket;
+
+    bucket = ht->buckets;
+    while (i++ < ht->num_buckets)
+    {
+       cnt += bucket->list.count;
+       bucket++;
+    }
+
+    return cnt;
+}
+
+/*
+ * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
+ *
+ * Fill in "sizes" with information about bucket lengths in hash
+ * table "max".
+ */
+
+void 
+hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
+
+{
+    register int i;
+    register int idx;
+    register bucket_t *bucket;
+
+    memzero(sizes, max * sizeof(unsigned int));
+
+    bucket = ht->buckets;
+    i = 0;
+    while (i++ < ht->num_buckets)
+    {
+       idx = bucket->list.count;
+       sizes[idx >= max ? max-1 : idx]++;
+       bucket++;
+    }
+}
+
+
+
+
+
+
+
+/*
+ * void hash_add_uint(hash_table *ht, unsigned int key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is unsigned int
+ */
+
+void *
+hash_add_uint(hash_table *ht, unsigned int key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_uint *hi;
+    hash_item_uint *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    unsigned int k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_uint));
+    hi = (hash_item_uint *)newli->datum;
+
+    /* fill in the values */
+    hi->key = key;
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_uint *)li->datum;
+       k1 = h->key;
+       if (key == k1)
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_uint *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_uint(hash_table *ht, unsigned int key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is unsigned int
+ */
+
+void *
+hash_replace_uint(hash_table *ht, unsigned int key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_uint *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    unsigned int k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_uint *)li->datum;
+       k1 = hi->key;
+       if (key == k1)
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_uint));
+       hi = (hash_item_uint *)li->datum;
+       hi->key = key;
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_uint(hash_table *ht, unsigned int key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is unsigned int
+ */
+
+void *
+hash_lookup_uint(hash_table *ht, unsigned int key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_uint *h;
+    void *result;
+    unsigned int k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_uint *)li->datum;
+           k1 = h->key;
+           if (key == k1)
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_uint(hash_table *ht, unsigned int key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_uint(hash_table *ht, unsigned int key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_uint *hi;
+    void *result;
+    unsigned int k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_uint *)li->datum;
+           k1 = hi->key;
+           if (key == k1)
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               ;
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_uint *
+hash_first_uint(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_uint *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_uint *hash_next_uint(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_uint *
+hash_next_uint(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_uint *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_uint(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_uint(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_uint *hi;
+    unsigned int key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_uint *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    ;
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+
+
+
+/*
+ * void hash_add_pid(hash_table *ht, pid_t key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is pid_t
+ */
+
+void *
+hash_add_pid(hash_table *ht, pid_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_pid *hi;
+    hash_item_pid *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    pid_t k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_pid));
+    hi = (hash_item_pid *)newli->datum;
+
+    /* fill in the values */
+    hi->key = key;
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_pid *)li->datum;
+       k1 = h->key;
+       if (key == k1)
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_pid *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_pid(hash_table *ht, pid_t key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is pid_t
+ */
+
+void *
+hash_replace_pid(hash_table *ht, pid_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_pid *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    pid_t k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_pid *)li->datum;
+       k1 = hi->key;
+       if (key == k1)
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_pid));
+       hi = (hash_item_pid *)li->datum;
+       hi->key = key;
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_pid(hash_table *ht, pid_t key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is pid_t
+ */
+
+void *
+hash_lookup_pid(hash_table *ht, pid_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_pid *h;
+    void *result;
+    pid_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_pid *)li->datum;
+           k1 = h->key;
+           if (key == k1)
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_pid(hash_table *ht, pid_t key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_pid(hash_table *ht, pid_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_pid *hi;
+    void *result;
+    pid_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_pid *)li->datum;
+           k1 = hi->key;
+           if (key == k1)
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               ;
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_pid *
+hash_first_pid(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_pid *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_pid *hash_next_pid(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_pid *
+hash_next_pid(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_pid *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_pid(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_pid(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_pid *hi;
+    pid_t key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_pid *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    ;
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+
+
+
+/*
+ * void hash_add_string(hash_table *ht, char * key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is char *
+ */
+
+void *
+hash_add_string(hash_table *ht, char * key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_string *hi;
+    hash_item_string *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    char * k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_string));
+    hi = (hash_item_string *)newli->datum;
+
+    /* fill in the values */
+    hi->key = strdup(key);
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[string_hash(ht, key)]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_string *)li->datum;
+       k1 = h->key;
+       if (strcmp(key, k1) == 0)
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_string *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_string(hash_table *ht, char * key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is char *
+ */
+
+void *
+hash_replace_string(hash_table *ht, char * key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_string *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    char * k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[string_hash(ht, key)]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_string *)li->datum;
+       k1 = hi->key;
+       if (strcmp(key, k1) == 0)
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_string));
+       hi = (hash_item_string *)li->datum;
+       hi->key = strdup(key);
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_string(hash_table *ht, char * key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is char *
+ */
+
+void *
+hash_lookup_string(hash_table *ht, char * key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_string *h;
+    void *result;
+    char * k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_string *)li->datum;
+           k1 = h->key;
+           if (strcmp(key, k1) == 0)
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_string(hash_table *ht, char * key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_string(hash_table *ht, char * key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_string *hi;
+    void *result;
+    char * k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_string *)li->datum;
+           k1 = hi->key;
+           if (strcmp(key, k1) == 0)
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               free(key);
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_string *
+hash_first_string(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_string *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_string *hash_next_string(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_string *
+hash_next_string(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_string *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_string(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_string(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_string *hi;
+    char * key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_string *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    free(key);
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+
+
+
+/*
+ * void hash_add_pidthr(hash_table *ht, pidthr_t key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is pidthr_t
+ */
+
+void *
+hash_add_pidthr(hash_table *ht, pidthr_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_pidthr *hi;
+    hash_item_pidthr *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    pidthr_t k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_pidthr));
+    hi = (hash_item_pidthr *)newli->datum;
+
+    /* fill in the values */
+    hi->key = key;
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_pidthr *)li->datum;
+       k1 = h->key;
+       if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr))
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_pidthr *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is pidthr_t
+ */
+
+void *
+hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_pidthr *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    pidthr_t k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_pidthr *)li->datum;
+       k1 = hi->key;
+       if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr))
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_pidthr));
+       hi = (hash_item_pidthr *)li->datum;
+       hi->key = key;
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_pidthr(hash_table *ht, pidthr_t key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is pidthr_t
+ */
+
+void *
+hash_lookup_pidthr(hash_table *ht, pidthr_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_pidthr *h;
+    void *result;
+    pidthr_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_pidthr *)li->datum;
+           k1 = h->key;
+           if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr))
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_pidthr(hash_table *ht, pidthr_t key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_pidthr(hash_table *ht, pidthr_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_pidthr *hi;
+    void *result;
+    pidthr_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_pidthr *)li->datum;
+           k1 = hi->key;
+           if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr))
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               ;
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_pidthr *
+hash_first_pidthr(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_pidthr *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_pidthr *hash_next_pidthr(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_pidthr *
+hash_next_pidthr(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_pidthr *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_pidthr(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_pidthr(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_pidthr *hi;
+    pidthr_t key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_pidthr *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    ;
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+
+#if HAVE_LWPID_T
+
+
+/*
+ * void hash_add_lwpid(hash_table *ht, lwpid_t key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is lwpid_t
+ */
+
+void *
+hash_add_lwpid(hash_table *ht, lwpid_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_lwpid *hi;
+    hash_item_lwpid *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    lwpid_t k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_lwpid));
+    hi = (hash_item_lwpid *)newli->datum;
+
+    /* fill in the values */
+    hi->key = key;
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_lwpid *)li->datum;
+       k1 = h->key;
+       if (key == k1)
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_lwpid *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is lwpid_t
+ */
+
+void *
+hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_lwpid *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    lwpid_t k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[(key % ht->num_buckets)]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_lwpid *)li->datum;
+       k1 = hi->key;
+       if (key == k1)
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_lwpid));
+       hi = (hash_item_lwpid *)li->datum;
+       hi->key = key;
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_lwpid(hash_table *ht, lwpid_t key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is lwpid_t
+ */
+
+void *
+hash_lookup_lwpid(hash_table *ht, lwpid_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_lwpid *h;
+    void *result;
+    lwpid_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_lwpid *)li->datum;
+           k1 = h->key;
+           if (key == k1)
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_lwpid(hash_table *ht, lwpid_t key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_lwpid(hash_table *ht, lwpid_t key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_lwpid *hi;
+    void *result;
+    lwpid_t k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_lwpid *)li->datum;
+           k1 = hi->key;
+           if (key == k1)
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               ;
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_lwpid *
+hash_first_lwpid(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_lwpid *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_lwpid *hash_next_lwpid(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_lwpid *
+hash_next_lwpid(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_lwpid *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_lwpid(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_lwpid(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_lwpid *hi;
+    lwpid_t key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_lwpid *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    ;
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+
+#endif
diff --git a/contrib/top/hash.h b/contrib/top/hash.h
new file mode 100644 (file)
index 0000000..44a121b
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+
+/* hash.m4h */
+
+/* Interface definition for hash.c */
+
+/* The file hash.h is generated from hash.m4h via the preprocessor M4 */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#include <sys/types.h>
+
+typedef struct pidthr_t {
+    pid_t k_pid;
+    id_t k_thr;
+} pidthr_t;
+
+typedef struct llistitem {
+    void *datum;
+    struct llistitem *next;
+} llistitem;
+
+typedef struct llist {
+    llistitem *head;
+    unsigned int count;
+} llist;
+
+typedef struct bucket {
+    llist list;
+} bucket_t;
+
+typedef struct hash_table {
+    int num_buckets;
+    bucket_t *buckets;
+} hash_table;
+
+typedef struct hash_pos {
+    int num_buckets;
+    int curr;
+    bucket_t *hash_bucket;
+    llistitem *ll_item;
+    llistitem *ll_last;
+} hash_pos;
+
+hash_table *hash_create(int num);
+void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht);
+
+
+
+
+typedef struct hash_item_uint {
+    unsigned int key;
+    void *value;
+} hash_item_uint;
+
+void *hash_add_uint(hash_table *ht, unsigned int key, void *value);
+void *hash_replace_uint(hash_table *ht, unsigned int key, void *value);
+void *hash_lookup_uint(hash_table *ht, unsigned int key);
+void *hash_remove_uint(hash_table *ht, unsigned int key);
+hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos);
+hash_item_uint *hash_next_uint(hash_pos *pos);
+void *hash_remove_pos_uint(hash_pos *pos);
+
+
+typedef struct hash_item_pid {
+    pid_t key;
+    void *value;
+} hash_item_pid;
+
+void *hash_add_pid(hash_table *ht, pid_t key, void *value);
+void *hash_replace_pid(hash_table *ht, pid_t key, void *value);
+void *hash_lookup_pid(hash_table *ht, pid_t key);
+void *hash_remove_pid(hash_table *ht, pid_t key);
+hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos);
+hash_item_pid *hash_next_pid(hash_pos *pos);
+void *hash_remove_pos_pid(hash_pos *pos);
+
+
+typedef struct hash_item_string {
+    char * key;
+    void *value;
+} hash_item_string;
+
+void *hash_add_string(hash_table *ht, char * key, void *value);
+void *hash_replace_string(hash_table *ht, char * key, void *value);
+void *hash_lookup_string(hash_table *ht, char * key);
+void *hash_remove_string(hash_table *ht, char * key);
+hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos);
+hash_item_string *hash_next_string(hash_pos *pos);
+void *hash_remove_pos_string(hash_pos *pos);
+
+
+typedef struct hash_item_pidthr {
+    pidthr_t key;
+    void *value;
+} hash_item_pidthr;
+
+void *hash_add_pidthr(hash_table *ht, pidthr_t key, void *value);
+void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value);
+void *hash_lookup_pidthr(hash_table *ht, pidthr_t key);
+void *hash_remove_pidthr(hash_table *ht, pidthr_t key);
+hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos);
+hash_item_pidthr *hash_next_pidthr(hash_pos *pos);
+void *hash_remove_pos_pidthr(hash_pos *pos);
+
+#if HAVE_LWPID_T
+
+typedef struct hash_item_lwpid {
+    lwpid_t key;
+    void *value;
+} hash_item_lwpid;
+
+void *hash_add_lwpid(hash_table *ht, lwpid_t key, void *value);
+void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value);
+void *hash_lookup_lwpid(hash_table *ht, lwpid_t key);
+void *hash_remove_lwpid(hash_table *ht, lwpid_t key);
+hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos);
+hash_item_lwpid *hash_next_lwpid(hash_pos *pos);
+void *hash_remove_pos_lwpid(hash_pos *pos);
+
+#endif
+
+
+#endif
diff --git a/contrib/top/hash.m4c b/contrib/top/hash.m4c
new file mode 100755 (executable)
index 0000000..6e8a736
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * 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.
+ */
+
+/* hash.m4c */
+
+/* The file hash.c is generated from hash.m4c via the preprocessor M4 */
+
+/*
+ * Hash table functions:  init, add, lookup, first, next, sizeinfo.
+ * This is a conventional "bucket hash".  The key is hashed in to a number
+ * less than or equal to the number of buckets and the result is used
+ * to index in to the array of buckets.  Each bucket is a linked list
+ * that contains all the key/value pairs which hashed to that index.
+ */
+
+#include "config.h"
+
+#if STDC_HEADERS
+#include <string.h>
+#include <stdlib.h>
+#define memzero(a, b)          memset((a), 0, (b))
+#else /* !STDC_HEADERS */
+#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
+void *malloc();
+void free();
+char *strdup();
+#endif /* !STDC_HEADERS */
+
+/* After all that there are still some systems that don't have NULL defined */
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifdef HAVE_MATH_H
+#include <math.h>
+#endif
+
+#if !HAVE_PID_T
+typedef long pid_t;
+#endif
+#if !HAVE_ID_T
+typedef long id_t;
+#endif
+
+#include "hash.h"
+
+dnl # The m4 code uses MALLOC, FREE, and STRDUP for dynamic allocation.
+dnl # You can change what these get mapped to here:
+
+define(`MALLOC', `malloc($1)')
+define(`FREE', `free($1)')
+define(`STRDUP', `strdup($1)')
+
+static int
+next_prime(int x)
+
+{
+    double i, j;
+    int f;
+
+    i = x;
+    while (i++)
+    {
+       f=1;
+       for (j=2; j<i; j++)
+       {
+           if ((i/j)==floor(i/j))
+           {
+               f=0;
+               break;
+           }
+       }
+       if (f)
+       {
+           return (int)i;
+       }
+    }
+    return 1;
+}
+
+/* string hashes */
+
+static int
+string_hash(hash_table *ht, char *key)
+
+{
+    unsigned long s = 0;
+    unsigned char ch;
+    int shifting = 24;
+
+    while ((ch = (unsigned char)*key++) != '\0')
+    {
+       if (shifting == 0)
+       {
+           s = s + ch;
+           shifting = 24;
+       }
+       else
+       {
+           s = s + (ch << shifting);
+           shifting -= 8;
+       }
+    }
+
+    return (s % ht->num_buckets);
+}
+
+void ll_init(llist *q)
+
+{
+    q->head = NULL;
+    q->count = 0;
+}
+
+llistitem *ll_newitem(int size)
+
+{
+    llistitem *qi;
+
+    qi = (llistitem *)MALLOC(sizeof(llistitem) + size);
+    qi->datum = ((void *)qi + sizeof(llistitem));
+    return qi;
+}
+
+void ll_freeitem(llistitem *li)
+
+{
+    FREE(li);
+}
+
+void ll_add(llist *q, llistitem *new)
+
+{
+    new->next = q->head;
+    q->head = new;
+    q->count++;
+}
+
+void ll_extract(llist *q, llistitem *qi, llistitem *last)
+
+{
+    if (last == NULL)
+    {
+       q->head = qi->next;
+    }
+    else
+    {
+       last->next = qi->next;
+    }
+    qi->next = NULL;
+    q->count--;
+}
+
+#define LL_FIRST(q) ((q)->head)
+llistitem *
+ll_first(llist *q)
+
+{
+    return q->head;
+}
+
+#define LL_NEXT(q, qi)  ((qi) != NULL ? (qi)->next : NULL)
+llistitem *
+ll_next(llist *q, llistitem *qi)
+
+{
+    return (qi != NULL ? qi->next : NULL);
+}
+
+#define LL_ISEMPTY(ll)  ((ll)->count == 0)
+int
+ll_isempty(llist *ll)
+
+{
+    return (ll->count == 0);
+}
+
+/*
+ * hash_table *hash_create(int num)
+ *
+ * Creates a hash table structure with at least "num" buckets.
+ */
+
+hash_table *
+hash_create(int num)
+
+{
+    hash_table *result;
+    bucket_t *b;
+    int bytes;
+    int i;
+
+    /* create the resultant structure */
+    result = (hash_table *)MALLOC(sizeof(hash_table));
+
+    /* adjust bucket count to be prime */
+    num = next_prime(num);
+
+    /* create the buckets */
+    bytes = sizeof(bucket_t) * num;
+    result->buckets = b = (bucket_t *)MALLOC(bytes);
+    result->num_buckets = num;
+
+    /* create each bucket as a linked list */
+    i = num;
+    while (--i >= 0)
+    {
+       ll_init(&(b->list));
+       b++;
+    }
+
+    return result;
+}
+
+/*
+ * unsigned int hash_count(hash_table *ht)
+ *
+ * Return total number of elements contained in hash table.
+ */
+
+unsigned int
+hash_count(hash_table *ht)
+
+{
+    register int i = 0;
+    register int cnt = 0;
+    register bucket_t *bucket;
+
+    bucket = ht->buckets;
+    while (i++ < ht->num_buckets)
+    {
+       cnt += bucket->list.count;
+       bucket++;
+    }
+
+    return cnt;
+}
+
+/*
+ * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
+ *
+ * Fill in "sizes" with information about bucket lengths in hash
+ * table "max".
+ */
+
+void 
+hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht)
+
+{
+    register int i;
+    register int idx;
+    register bucket_t *bucket;
+
+    memzero(sizes, max * sizeof(unsigned int));
+
+    bucket = ht->buckets;
+    i = 0;
+    while (i++ < ht->num_buckets)
+    {
+       idx = bucket->list.count;
+       sizes[idx >= max ? max-1 : idx]++;
+       bucket++;
+    }
+}
+
+dnl # HASH_TABLE_TMPL(suffix, keytype, to_hash, to_cmp, to_alloc, to_free)
+dnl #
+dnl # This generates hash table functions suitable for keys
+dnl # of type "keytype".  The function names all end with "suffix".
+dnl # "to_hash" is an expression that generates a hash index (this
+dnl # expression can include key and ht).  "to_cmp" is an expression
+dnl # that compares "key" to "k1".  "to_alloc" is an expression that
+dnl # allocates space for "key", or empty if no allocation is needed.
+dnl # "to_free" is an expression that frees "key", or empty if no
+dnl # allocation is needed.
+
+define(`HASH_TABLE_TMPL', `
+
+/*
+ * void hash_add_$1(hash_table *ht, $2 key, void *value)
+ *
+ * Add an element to table "ht".  The element is described by
+ * "key" and "value".  Return NULL on success.  If the key
+ * already exists in the table, then no action is taken and
+ * the data for the existing element is returned.
+ * Key type is $2
+ */
+
+void *
+hash_add_$1(hash_table *ht, $2 key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_$1 *hi;
+    hash_item_$1 *h;
+    llist *ll;
+    llistitem *li;
+    llistitem *newli;
+    $2 k1;
+
+    /* allocate the space we will need */
+    newli = ll_newitem(sizeof(hash_item_$1));
+    hi = (hash_item_$1 *)newli->datum;
+
+    /* fill in the values */
+    hi->key = ifelse($5, , key, $5);
+    hi->value = value;
+
+    /* hash to the bucket */
+    bucket = &(ht->buckets[$3]);
+
+    /* walk the list to make sure we do not have a duplicate */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       h = (hash_item_$1 *)li->datum;
+       k1 = h->key;
+       if ($4)
+       {
+           /* found one */
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+    li = NULL;
+
+    /* is there already one there? */
+    if (li == NULL)
+    {
+       /* add the unique element to the buckets list */
+       ll_add(&(bucket->list), newli);
+       return NULL;
+    }
+    else
+    {
+       /* free the stuff we just allocated */
+       ll_freeitem(newli);
+       return ((hash_item_$1 *)(li->datum))->value;
+    }
+}
+
+/*
+ * void *hash_replace_$1(hash_table *ht, $2 key, void *value)
+ *
+ * Replace an existing value in the hash table with a new one and
+ * return the old value.  If the key does not already exist in
+ * the hash table, add a new element and return NULL.
+ * Key type is $2
+ */
+
+void *
+hash_replace_$1(hash_table *ht, $2 key, void *value)
+
+{
+    bucket_t *bucket;
+    hash_item_$1 *hi;
+    llist *ll;
+    llistitem *li;
+    void *result = NULL;
+    $2 k1;
+
+    /* find the bucket */
+    bucket = &(ht->buckets[$3]);
+
+    /* walk the list until we find the existing item */
+    ll = &(bucket->list);
+    li = LL_FIRST(ll);
+    while (li != NULL)
+    {
+       hi = (hash_item_$1 *)li->datum;
+       k1 = hi->key;
+       if ($4)
+       {
+           /* found it: now replace the value with the new one */
+           result = hi->value;
+           hi->value = value;
+           break;
+       }
+       li = LL_NEXT(ll, li);
+    }
+
+    /* if the element wasnt found, add it */
+    if (result == NULL)
+    {
+       li = ll_newitem(sizeof(hash_item_$1));
+       hi = (hash_item_$1 *)li->datum;
+       hi->key = ifelse($5, , key, $5);
+       hi->value = value;
+       ll_add(&(bucket->list), li);
+    }
+
+    /* return the old value (so it can be freed) */
+    return result;
+}
+
+/*
+ * void *hash_lookup_$1(hash_table *ht, $2 key)
+ *
+ * Look up "key" in "ht" and return the associated value.  If "key"
+ * is not found, return NULL.  Key type is $2
+ */
+
+void *
+hash_lookup_$1(hash_table *ht, $2 key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    hash_item_$1 *h;
+    void *result;
+    $2 k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[$3])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       while (li != NULL)
+       {
+           h = (hash_item_$1 *)li->datum;
+           k1 = h->key;
+           if ($4)
+           {
+               result = h->value;
+               break;
+           }
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * void *hash_remove_$1(hash_table *ht, $2 key)
+ *
+ * Remove the element associated with "key" from the hash table
+ * "ht".  Return the value or NULL if the key was not found.
+ */
+
+void *
+hash_remove_$1(hash_table *ht, $2 key)
+
+{
+    bucket_t *bucket;
+    llist *ll;
+    llistitem *li;
+    llistitem *lilast;
+    hash_item_$1 *hi;
+    void *result;
+    $2 k1;
+
+    result = NULL;
+    if ((bucket = &(ht->buckets[$3])) != NULL)
+    {
+       ll = &(bucket->list);
+       li = LL_FIRST(ll);
+       lilast = NULL;
+       while (li != NULL)
+       {
+           hi = (hash_item_$1 *)li->datum;
+           k1 = hi->key;
+           if ($4)
+           {
+               ll_extract(ll, li, lilast);
+               result = hi->value;
+               key = hi->key;
+               $6;
+               ll_freeitem(li);
+               break;
+           }
+           lilast = li;
+           li = LL_NEXT(ll, li);
+       }
+    }
+    return result;
+}
+
+/*
+ * hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos)
+ *
+ * First function to call when iterating through all items in the hash
+ * table.  Returns the first item in "ht" and initializes "*pos" to track
+ * the current position.
+ */
+
+hash_item_$1 *
+hash_first_$1(hash_table *ht, hash_pos *pos)
+
+{
+    /* initialize pos for first item in first bucket */
+    pos->num_buckets = ht->num_buckets;
+    pos->hash_bucket = ht->buckets;
+    pos->curr = 0;
+    pos->ll_last = NULL;
+
+    /* find the first non-empty bucket */
+    while(pos->hash_bucket->list.count == 0)
+    {
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           return NULL;
+       }
+    }
+
+    /* set and return the first item */
+    pos->ll_item = LL_FIRST(&(pos->hash_bucket->list));
+    return (hash_item_$1 *)pos->ll_item->datum;
+}
+
+
+/*
+ * hash_item_$1 *hash_next_$1(hash_pos *pos)
+ *
+ * Return the next item in the hash table, using "pos" as a description
+ * of the present position in the hash table.  "pos" also identifies the
+ * specific hash table.  Return NULL if there are no more elements.
+ */
+
+hash_item_$1 *
+hash_next_$1(hash_pos *pos)
+
+{
+    llistitem *li;
+
+    /* move item to last and check for NULL */
+    if ((pos->ll_last = pos->ll_item) == NULL)
+    {
+       /* are we really at the end of the hash table? */
+       if (pos->curr >= pos->num_buckets)
+       {
+           /* yes: return NULL */
+           return NULL;
+       }
+       /* no: regrab first item in current bucket list (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+    else
+    {
+       /* get the next item in the llist */
+       li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item);
+    }
+
+    /* if its NULL we have to find another bucket */
+    while (li == NULL)
+    {
+       /* locate another bucket */
+       pos->ll_last = NULL;
+
+       /* move to the next one */
+       pos->hash_bucket++;
+       if (++pos->curr >= pos->num_buckets)
+       {
+           /* at the end of the hash table */
+           pos->ll_item = NULL;
+           return NULL;
+       }
+
+       /* get the first element (might be NULL) */
+       li = LL_FIRST(&(pos->hash_bucket->list));
+    }
+
+    /* li is the next element to dish out */
+    pos->ll_item = li;
+    return (hash_item_$1 *)li->datum;
+}
+
+/*
+ * void *hash_remove_pos_$1(hash_pos *pos)
+ *
+ * Remove the hash table entry pointed to by position marker "pos".
+ * The data from the entry is returned upon success, otherwise NULL.
+ */
+
+
+void *
+hash_remove_pos_$1(hash_pos *pos)
+
+{
+    llistitem *li;
+    void *ans;
+    hash_item_$1 *hi;
+    $2 key;
+
+    /* sanity checks */
+    if (pos == NULL || pos->ll_last == pos->ll_item)
+    {
+       return NULL;
+    }
+
+    /* at this point pos contains the item to remove (ll_item)
+       and its predecesor (ll_last) */
+    /* extract the item from the llist */
+    li = pos->ll_item;
+    ll_extract(&(pos->hash_bucket->list), li, pos->ll_last);
+
+    /* retain the data */
+    hi = (hash_item_$1 *)li->datum;
+    ans = hi->value;
+
+    /* free up the space */
+    key = hi->key;
+    $6;
+    ll_freeitem(li);
+
+    /* back up to previous item */
+    /* its okay for ll_item to be null: hash_next will detect it */
+    pos->ll_item = pos->ll_last;
+
+    return ans;
+}
+')
+
+dnl # create hash talbe functions for unsigned int and for strings */
+
+HASH_TABLE_TMPL(`uint', `unsigned int', `(key % ht->num_buckets)', `key == k1', ,)
+HASH_TABLE_TMPL(`pid', `pid_t', `(key % ht->num_buckets)', `key == k1', ,)
+HASH_TABLE_TMPL(`string', `char *', `string_hash(ht, key)', `strcmp(key, k1) == 0', `STRDUP(key)', `FREE(key)')
+HASH_TABLE_TMPL(`pidthr', `pidthr_t', `((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)', `(key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)', ,)
+#if HAVE_LWPID_T
+HASH_TABLE_TMPL(`lwpid', `lwpid_t', `(key % ht->num_buckets)', `key == k1', ,)
+#endif
diff --git a/contrib/top/hash.m4h b/contrib/top/hash.m4h
new file mode 100755 (executable)
index 0000000..63a386c
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+
+/* hash.m4h */
+
+/* Interface definition for hash.c */
+
+/* The file hash.h is generated from hash.m4h via the preprocessor M4 */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#include <sys/types.h>
+
+typedef struct pidthr_t {
+    pid_t k_pid;
+    id_t k_thr;
+} pidthr_t;
+
+typedef struct llistitem {
+    void *datum;
+    struct llistitem *next;
+} llistitem;
+
+typedef struct llist {
+    llistitem *head;
+    unsigned int count;
+} llist;
+
+typedef struct bucket {
+    llist list;
+} bucket_t;
+
+typedef struct hash_table {
+    int num_buckets;
+    bucket_t *buckets;
+} hash_table;
+
+typedef struct hash_pos {
+    int num_buckets;
+    int curr;
+    bucket_t *hash_bucket;
+    llistitem *ll_item;
+    llistitem *ll_last;
+} hash_pos;
+
+hash_table *hash_create(int num);
+void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht);
+
+define(`HASH_TYPE_TMPL', `
+typedef struct hash_item_$1 {
+    $2 key;
+    void *value;
+} hash_item_$1;
+
+void *hash_add_$1(hash_table *ht, $2 key, void *value);
+void *hash_replace_$1(hash_table *ht, $2 key, void *value);
+void *hash_lookup_$1(hash_table *ht, $2 key);
+void *hash_remove_$1(hash_table *ht, $2 key);
+hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos);
+hash_item_$1 *hash_next_$1(hash_pos *pos);
+void *hash_remove_pos_$1(hash_pos *pos);
+')
+
+HASH_TYPE_TMPL(`uint', `unsigned int')
+HASH_TYPE_TMPL(`pid', `pid_t')
+HASH_TYPE_TMPL(`string', `char *')
+HASH_TYPE_TMPL(`pidthr', `pidthr_t')
+#if HAVE_LWPID_T
+HASH_TYPE_TMPL(`lwpid', `lwpid_t')
+#endif
+
+
+#endif
diff --git a/contrib/top/layout.h b/contrib/top/layout.h
new file mode 100644 (file)
index 0000000..46bf78e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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
+ *
+ *  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.
+ */
+
+#define  X_LASTPID     10
+#define  Y_LASTPID     0
+#define  X_LASTPIDWIDTH 13
+#define  X_LOADAVE     27
+#define  Y_LOADAVE     0
+#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
+
diff --git a/contrib/top/loadavg.h b/contrib/top/loadavg.h
new file mode 100644 (file)
index 0000000..c73fc7d
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ *
+ *  This include file sets up everything we need to access the load average
+ *  values in the kernel in a machine independent way.  First, it sets the
+ *  typedef "load_avg" to be either double or long (depending on what is
+ *  needed), then it defines these macros appropriately:
+ *
+ *     loaddouble(la) - convert load_avg to double.
+ *     intload(i)     - convert integer to load_avg.
+ */
+
+/*
+ * We assume that if FSCALE is defined, then avenrun and ccpu are type long.
+ * If your machine is an exception (mips, perhaps?) then make adjustments
+ * here.
+ *
+ * Defined types:  load_avg for load averages, pctcpu for cpu percentages.
+ */
+#if defined(mips) && !defined(NetBSD)
+# include <sys/fixpoint.h>
+# if defined(FBITS) && !defined(FSCALE)
+#  define FSCALE (1 << FBITS)  /* mips */
+# endif
+#endif
+
+#ifdef FSCALE
+# define FIXED_LOADAVG FSCALE
+# define FIXED_PCTCPU FSCALE
+#endif
+
+#ifdef ibm032
+# undef FIXED_LOADAVG
+# undef FIXED_PCTCPU
+# define FIXED_PCTCPU PCT_SCALE
+#endif
+
+
+#ifdef FIXED_PCTCPU
+  typedef long pctcpu;
+# define pctdouble(p) ((double)(p) / FIXED_PCTCPU)
+#else
+typedef double pctcpu;
+# define pctdouble(p) (p)
+#endif
+
+#ifdef FIXED_LOADAVG
+  typedef long load_avg;
+# define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
+# define intload(i) ((int)((i) * FIXED_LOADAVG))
+#else
+  typedef double load_avg;
+# define loaddouble(la) (la)
+# define intload(i) ((double)(i))
+#endif
diff --git a/contrib/top/machine.h b/contrib/top/machine.h
new file mode 100644 (file)
index 0000000..f727d3b
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+/*
+ *  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.
+ */
+
+#ifndef _MACHINE_H_
+#define _MACHINE_H_
+
+#include "top.h"
+
+/*
+ * 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;         /* optional */
+    char **order_names;                /* optional */
+    char **top_color_names;    /* optional */
+    char **kernel_names;       /* optional */
+    time_t boottime;           /* optional */
+    int modemax;               /* optional */
+    struct {
+       unsigned int fullcmds : 1;
+       unsigned int idle : 1;
+       unsigned int warmup : 1;
+       unsigned int threads : 1;
+    } flags;
+};
+
+/*
+ * the system_info struct is filled in by a machine dependent routine.
+ */
+
+#ifdef p_active     /* uw7 define macro p_active */
+#define P_ACTIVE p_pactive
+#else
+#define P_ACTIVE p_active
+#endif
+
+struct system_info
+{
+    int    last_pid;
+    double load_avg[NUM_AVERAGES];
+    int    p_total;
+    int    P_ACTIVE;     /* number of procs considered "active" */
+    int    *procstates;
+    int    *cpustates;
+    int    *kernel;
+    long   *memory;
+    long   *swap;
+};
+
+/* cpu_states is an array of percentages * 10.  For example, 
+   the (integer) value 105 is 10.5% (or .105).
+ */
+
+/*
+ * the process_select struct tells get_process_info what processes we
+ * are interested in seeing
+ */
+
+struct process_select
+{
+    int idle;          /* show idle processes */
+    int system;                /* show system processes */
+    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 */
+};
+
+/* 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(caddr_t, char *(*)(int));
+int proc_owner(int);
+#ifdef HAVE_FORMAT_PROCESS_HEADER
+
+#endif /* _MACHINE_H_ */
+char *format_process_header(struct process_select *sel, caddr_t handle, int count);
+#endif
diff --git a/contrib/top/message.h b/contrib/top/message.h
new file mode 100644 (file)
index 0000000..f8a50b2
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+/* interface declaration for display messages */
+/* This is a small subset of the interface from display.c that
+   just contains the calls for displaying messages.  Do not include
+   this and display.h at the same time. */
+
+#ifndef _MESSAGE_H
+#define _MESSAGE_H
+
+void error_message(char *msgfmt, ...);
+void clear_message();
+
+#endif /* _MESSAGE_H_ */
diff --git a/contrib/top/os.h b/contrib/top/os.h
new file mode 100644 (file)
index 0000000..5502df6
--- /dev/null
@@ -0,0 +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>
+#include <stdio.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#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 */
+
+/* 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
+
+#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
diff --git a/contrib/top/screen.c b/contrib/top/screen.c
new file mode 100644 (file)
index 0000000..398a858
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+ * 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.
+ *