--- /dev/null
+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.
--- /dev/null
+ 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.
+
--- /dev/null
+ 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.
--- /dev/null
+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.
--- /dev/null
+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.
+
+
--- /dev/null
+ 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
--- /dev/null
+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
--- /dev/null
+
+ 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
--- /dev/null
+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.
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* My favorite names for boolean values */
+#define No 0
+#define Yes 1
+#define Maybe 2 /* tri-state boolean, actually */
+
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+ }
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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