From: Jan Lentfer Date: Sat, 28 Nov 2009 19:16:07 +0000 (+0100) Subject: top - Import 3.8beta1 sources into new vendor/TOP branch. X-Git-Tag: v2.7.1~362^2~1 X-Git-Url: https://gitweb.dragonflybsd.org/~nant/dragonfly.git/commitdiff_plain/63d9232357cccd947402f22bb9107ed749fda55e top - Import 3.8beta1 sources into new vendor/TOP branch. --- diff --git a/contrib/top/Changes b/contrib/top/Changes new file mode 100644 index 0000000000..98fc82f528 --- /dev/null +++ b/contrib/top/Changes @@ -0,0 +1,949 @@ +Tue May 6 2008 - wnl (3.8beta1) + Main code: fixed bugs in screen_cleareol and in display code. Fixed + bug in i_swap when all data is 0. Added ^W patch (from thaquis). + Fixed bug in xdprintf. Added command line options for the "t" and + "m" commands. + SunOS 5 changes: Support for showing individual threads. Redid + allocation of prpsinfo structures. Added a pidthr hash that uses + both pid and thread id for a key. Changed format_process_header + and format_next_process to use a table-driven method for generating + the columns. Status files from /proc (psinfo and lpsinfo) are now + cached to avoid repeatedly reopening them. Column showing number of + LWPs is now called "NLWP" and column showing lwpid is "LWP". + FreeBSD changes: Runtime check to ensure binary is running on + the same machine type it was compiled for. Lots of cleanup and + changed nearly everything to use sysctl rather than kvm, and + inability to open kvm is no longer fatal. Improved thread reporting: + disabled for 7.x and lower. Added lwpid hash for proper tracking + of threads. Changed format_process_header and format_next_process + to use a table-driven method for generating the columns. + Dec Alpha: configure uses compile-time options to properly trap + and handle exceptions from the Alpha FPU (from Brian Maly). + +Tue Feb 26 2008 - wnl (3.7) + Prepare for version 3.7 release. + +Fri Feb 1 2008 - wnl (3.7beta4) + Using the $ notation in printf formats for freebsd apparently was + causing problems on 64-bit systems. All such usage has been + removed and the process line is formatted piecemeal. + +Thu Dec 27 2007 - wnl (3.7beta3) + Improved function comments in display.c for message_error functions. + Changed some of the error messages in top.c to be more succint. + +Fri Dec 7 2007 - wnl (3.7beta3) + Changes to freebsd port: moved some functions up front to + eliminate forward references. Use sysctl to get all vm stats + information, as some of this isn't updated in the struct + vmmeter under FreeBSD 7.0. Added routines to support large-scale + sysctl access. + +Wed Nov 28 2007 - wnl (3.7beta3) + Changes to documentation: FAQ, README, man page. + +Tue Nov 27 2007 - wnl (3.7beta3) + For freebsd, added page faults, pageins, pageouts, and pages + freed to the kernel display line. These numbers reflect the + values presented in vmstat. For sunos5, added page faults, + pageins and pageouts to the kernel display line. + +Fri Nov 2 2007 - wnl (3.7beta3) + Added copyright notices to the top of every source and include file. + Added copyright information to the man page. + Removed a few outdated things from the manifest. + Minor changes to sigconv.awk. + +Sat Oct 27 2007 - wnl (3.7beta3) + Added check for sys_signame at configure time and if it is + present then it is used in commands.c to translate signal names + in to numbers. + Added alternate snprintf and vsnprintf functions from apache (in + ap_snprintf.c). Added configure magic to define and compile them in + where needed. Added check to configure for variadic macros. + Preprocessor defintion of dprintf (in utils.h) now depends on + support for variadic macros. Cleaned up m_linux code. + +Wed Oct 3 2007 - wnl (3.7beta3) + Lots of changes, thanks to Mark Wong. Most changes were to + clean the code up so that it would compile cleanly with -Wall + (all warnings). Changed function names in screen.c so that + they all start with "screen_". Isolated all interaction with + termcap to screen.c by adding a real function for cursor + addressing (in the past it was just a macro). Only screen.c + now needs to worry about defining templates for the termcap + functions. Added configure and preprocessor magic to ensure + that all the termcap functions used in the code are defined + with templates. Changed names of some other functions and + global variables to avoid name conflicts with functions in + curses and other well established libraries. Changed dprintf + macro to use variadic arguments so that the preprocessor can + gobble up the entire call when compiling without debugging + (this will have to be made more portable). All include files + are surrounded by #ifndef statements to accomodate multiple + inclusions. Platform module is now compiled with + -fno-strict-aliasing as some of the modules do type punning + that can confuse the optimizer. + +Wed Sep 26 2007 - wnl (3.7beta3) + For freebsd, priority is no longer normalized by PZERO. This + contradicts the behavior used by ps when it displays priority. + But normalizing by PZERO has become a bit of an anachronism + and it actually obscures the meaning of the priority without + adding any real value. + +Wed Sep 19 2007 - wnl (3.7beta3) + Many changes to improve the display of threads. Changed + process summary line to use the word "threads" when showing + individual threads. Added the system command to toggle the + display of system processes. Fixed bug in hash.c remove_pos. + For freebsd: count threads correctly when they are being + displayed, nice column is more closely in line with ps + (nothing fancy for real time processes), add two more process + states that didn't exist in older releases of freebsd (wait + and lock). + For linux: Threads done right. Now track individual threads + of multi-threaded processes separately so that we always know + their %cpu. Switch to format_process_header so that we can + change the column headings and remove the THR column when + displaying individual threads. Switched process (and thread) + tracking over to use generic hash table functions included + with the new version of top. Process states and total now + include threads when they are being shown. Added "SHR" column + to show the amount of shared memory per process. Improved + calculation of elapsed time and percent cpu to avoid + overflows. Remove weighted cpu calculations entirely as it is + an anachronism. + For Solaris: Moved check for libelf to accomodate older systems. + +Sun Sep 9 2007 - wnl (3.7beta2) + Documentation changes. Fixes to sunos5 port. Added display of + thread count and selection by command name to linux port. Removed + the use of inline functions from hash.c as that doesn't appear to + be very portable. + +Wed Sep 5 2007 - wnl (3.7beta1) + Fixed freebsd and linux configuration bugs. Added configuration + options for tweaking program defaults. Rewrote top level code + (top.c) from scratch, including command handling so that adding + new commands is much easier. Changed message-line handling to + ensure that the message is displayed for at least 5 seconds + regardless of the update frequency. Added a "miniupdate" that + occurs one second after the initial screen on systems that don't + already delay the first screen. The mini-update shows cpu state + percentages. Added ability to select output by command name on + some systems. Fixed color toggling via the "C" command. Added + long options via getopt_long to complement the existing single + character options. Added the freebsd "m" command to chose + alternate display modes. On freebsd this gives a process i/o + display. Added the freebsd "H" command to select the display of + individual threads. Added "-a" option ("all") to set number of + displays and number of processes to infinity (equivalent to + "-d all all"). Added dual architecture compilation for Solaris + to generate both a 32-bit and a 64-bit binary. This is on by + default when compiling on a 64-bit system and can be explicitly + set via "configure --enable-dualarch". Added uniform hashing + functions that use bucket hash for uint, pid, and string. Changed + username.c and the sunos and freebsd modules to use these functions. + Added the "kernel" information line to the display to show + statistics on what the kernel is doing (context switches, forks, + traps, etc.). This requires explicit support by the platform + module, currently only freebsd, linux, and sunos. + +Wed Apr 18 2007 - wnl (3.6.1) + Fixed a few bugs in sigconv.awk that were causing incorrect + results on FreeBSD. Changed configure.ac to fix a few linux + problems: signal include file and /proc/1/stat. + +Fri Apr 13 2007 - wnl (3.6.1) + Removed the use of VPATH for compiling the system module and used + an explicit dependency in the Makefile instead. VPATH is now set + to just srcdir to ensure that top will compile correctly when + configured from a different directory. On systems without VPATH + support, top will still configure and compile, but only + from within the source directory. This fixes bug 1699526. + +Fri Feb 2 2007 - wnl (3.6.1) + Revised the way that configure figures out owner, group, and mode. + For systems that don't use the kernel, it tries to match install + settings to allow access to stuff in /proc. More importantly, if + mode is 755 then neither owner nor group are set. This fixes bug + 1631136. Added patch from haanjdj@xs4all.nl to fix an occasional + core dump in m_decosf1.c. This checks return code from task_threads. + Made sure all get_system_info functions are declared void. Fixed + string termination bug. Cleaned up documetation for sunos5. + +Tue Aug 8 2006 - wnl (3.6.1) + For Solaris, changed the tag "swap" to "total swap" to clarify + what is beign displayed. Note that the calculations are still the + same: the display is just showing total rather than total - free. + +Thu Apr 27 2006 - wnl (3.6) + Added patches for linux-style sort shortcuts and for Unixware + support in configure (patch 1474427). Fixed sunos5 to do slow start + and to ensure cpucount is set (patch 1477386). Added pagination + routines to display.c and modified show_help to use it, since the + help screen is now longer than 24 lines. Applied patch for unixware + support that adds check for mas library (patch #1474423). Solaris + cpu percent now reflects a percentage of the entire server, rather + than a single cpu (bug 1478138). + +Mon Mar 27 2006 - wnl (3.6) + The production release of version 3.6. Fixed a minor scaling + bug in the decosf1 module. Support for MacOS X is officially + withdrawn although the macosx module is still part of the + distribution. Hopefully this is a temporary situation. + Documentation updated. + + +Wed Feb 15 2006 - wnl (3.6beta5) + Minor changes to eliminate warnings from the Sun Studio compiler. + These were mostly sloppy argument declarations. I also added + message.h to provide an interface file for just the message + related functions of display.c. + +Mon Dec 26 2005 - wnl (3.6beta4) + Added new netbsd module, courtesy of Simon Burge. + Fixed a few bugs in SVR4 module and added its use to + configure.ac, thanks to Sanchet Dighe. Also ensured that the + novpath Makefile was in the distribution. + Fixed portability problem in display.c + + +Mon Oct 24 2005 - wnl (3.6beta3) + Set up a color tagging mechanism in color.c to allow for the + dynamic creation of tag names to contol color highlighting. + These names are partially derived from the tags used to label + memory and swap information on the screen, thus are driven by + the machine module itself. Added -T option to list color + highlighting information. Help screen now includes the actual + list of sort order names. Incorporated some minor fixes to + the main code from the Freebsd source tree. Fixed bug #1324582. + Freebsd 5: removed WCPU column and added THR column. Display + for freebsd 4 and earlier unchanged since they don't track + threads in the kernel. Added LICENSE file to distribution. + +Wed Oct 12 2005 - wnl (3.6beta2) + Major overhaul to display.c. All lines of the display are + directly tracked and controlled via display_write and its + companion display_fmt. Added support for complete control + of ANSI color on the screen: this will be used in the future + to allow for full use of color everywhere on the screen. + Signal handling code now uses sigaction on all systems that + support it. Restored the freebsd module and did away with + freebsd4, and upgraded freebsd module to support 5.x. + Fix bug #1306099 (wio(wait) timer ignored on OSF1). + +Fri Sep 23 2005 - wnl (3.6beta1) + Fixed bugs #1266341 (compilation errors with gcc 4.x), + #1156464 (cpu% field for sunos), #1156243 (compilation + errors on AIX). Applied patches #1217855 (Solaris 10 + xarch flag). Overhaul of sunos5 module, making code more + efficient and easier to follow. Got rid of need for MEMTYPE + type in utils.h. Changed all memory statistics data in the + module specification from an int to a long to better support + 64-bit systems with lots of memory. Moved all unused modules + out of the distribution (I will add them back in as needed). + Moved freebsd module to freebsd4 as it won't work with 5.x + (a new module will be necessary). Added support to configure + for makes that don't understand VPATH. Updated documentation: + man page, FAQ, README, INSTALL. + +Mon Jan 24 2005 - wnl (3.6alpha10) + Updated aix43 module with ANSI function declarations and fixed + declaration of get_system_info. Configure now uses irixsgi + module for irix6* systems. Updates to the following modules: + irixsgi, sunos5. Fixed null pointer bug in color.c. Removed + some useless code and definitions in display.c + + +Sun Nov 28 2004 - wnl (3.6alpha9) + Replace AIX 5 module with alternate (bug 1056565). + Fixed vulnerability in use of snprintf. + +Fri Oct 22 2004 - wnl (3.6alpha8) + Support for linux 2.6, added more stuff to memory and swap lines. + Updated linuxthr module, which is only useful on 2.4 and earlier. + Added some color support back in (feature request 930588), but + still need to add it back to the per-process display. Added + OSF 5 support (untested). + Fixed bug 1017951 (invalid process count argument not caught) + +Tue Apr 20 2004 - wnl (3.6alpha7) + Added 64 bit support for AIX. + +Thu Apr 15 2004 - wnl (3.6alpha6) + Included fixes for decosf1 pid size and updated module. Also + added osf1 to list of recognized operating systems in configure.ac. + +Tue Mar 30 2004 - wnl (3.6alpha5) + Minor bug fixes and some code rearrangement. Changes to install + rule. Added several more platforms including: aix 4.2 thru 5, + MacOS 10, Dec OSF, HPUX 7 thru 11. Fixed the core dumping bug + in linux. Code cleanup, including sigdesc.h (by changing + sigconv.awk). Startup error messages are displayed on the + first screen rather than beforehand (no more pause). Cleaned + up interrupt handling to avoid a race condition. Eliminated + top.local.h. REMOVED Configure!!! + +Mon Mar 22 2004 - wnl (3.6alpha1) + Now using gnu autoconf. Eliminated the need for CFLAGS and LIBS + tags in the module source files. Autoconf tries to figure all + that out now. Machine module interface now uses flags to determine + if module supports sorting, selective display of idle processes, + viewing full commands. Added display of uptime for modules that + support it. Added display of full command lines for modules that + support it. 3.5 modules must be changed a bit to work for 3.6: + ORDER is no longer defined, and the module must fill in the + appropriate fields in struct statics to get the extra features. + Added a extenstion interface to allow for putting extra stuff + on the screen -- this is still half baked and not documented. + +Mon Feb 23 2004 - wnl (3.5) + Turned rc1 in to version 3.5. Only changes were to the FAQ. + +Mon Feb 2 2004 - wnl (3.5rc1) + Changed format_k (utils.c) to use MEMTYPE for its parameter. + On most systems this is a long, but if the module defines + USE_SIZE_T, this is set to be a size_t. The sunos5 module + now defines it, so that it will work correctly on 64-bit + machines. New "linuxthr" module for rolling up processes + that are really threads. Configure autodetects when running + on a 64-bit Solaris machine. + +Tue Dec 16 2003 - wnl (3.5beta13) + Improved linux module. For Solaris, changed "THR" column + heading to "LWP" since that's what they really are. + +Thu Mar 30 2000 - wnl (3.5beta12) + Updated modules: m_aix41.c, m_aix43.c, m_mtxinu.c, m_sco5.c, + and m_ultrix4.c. + Included m_irixsgi.c from some source that's been floating around + SGI. Don't yet know how it compares to m_irix62. + +Fri Mar 10 2000 - wnl (3.5beta11) + top.c: avoid potential loop if stdout gets closed, use macro + for p_active to avoid collision with system macros. + m_sunos5: widened some fields to accomodate 5.8. + m_decosf1: added ordering support + m_irix62_64: provides 64-bit module based on m_irix62. + m_irix62: skip bogus files in /proc directory + m_svr42MP and m_svr5: complete replacement with updated copies + m_mtxinu: complete replacement with updated copies + m_aix43: new module for 4.3 + getans: replaced with a Bourne shell script + +Mon Mar 6 2000 - wnl (3.5beta10) + m_sunos5.c: workaround for curses bug: ensure that TERMINFO has + a value. + +Fri Jan 15 1999 - wnl (3.5beta10) + top.c: now check return code from read to avoid looping on eof. + top.c: delay of 0 now only valid for root. + decosf1.c: patches from Rainer Orth should fix most of the + problems with this module (including the display of certain + processes and runtime errors). + sunos5.c: Rainer insisted on putting the slash back in the + state field ("run/4") and widened the field to accomodate it. + aix.c: widened PID field for 6-digit pids (shortened NICE field) + module macosx added, thanks to Andrew Townley. + +Fri Dec 18 1998 - wnl (3.5beta9) + Configure checks status of "make" and complains if it fails. + +Thu Dec 17 1998 - wnl (3.5beta9) + Added module sco5 from Mike Hopkirk. + Added module netbsd132 from moto kawasaki. + +Sun Oct 25 1998 - wnl (3.5beta9) + Added Casper's patches for sunos5 for the following: + produce same results as swap -s (5.5 and higher), + don't use system_pages kstat when /dev/kmem can be opened, + skip . and .. when reading /proc, replace use of SOLARIS24 + with OSREV. + +Fri Sep 11 1998 - wnl (3.5beta9) + Added workaround to getans for the absence of $< in SCO Unix. + +Wed Jul 1 1998 - wnl (3.5beta9) + Changed structure member "errno" to "errnum" in commands.c. + Replaced hpux10 module with one from John Haxby. + +Fri Apr 17 1998 - wnl (3.5beta8) + Moved definition of _KMEMUSER earlier in m_sunos5.c. This should + fix the compilation problem with gnu 2.7.2.3, obviating the need + for the fixinc.svr4 patch, but hopefully will not affect anything + else. + Added -DORDER to m_sunos4mp.c + +Tue Nov 18 1997 - wnl (3.5beta7) + Added gcc 2.7.2.3 patch for fixinc.svr4 and changed INSTALL and + FAQ to refer to it. + Added NetBSD HP9000 fix. Hopefully it doesn't break other + NetBSD platforms. + +Fri Oct 24 1997 - wnl (3.5beta7) + Modified m_dcosx.c to change uses of procdir to xprocdir, avoiding + a name clash with an include file (Bryn Parrott) + +Sat Oct 11 1997 - wnl (3.5beta6) + Incorporated Casper's patches for Solaris 2.6 and for the multi- + processor bug ("kstat finds too many cpus"). + +Sun Jan 20 1996 - wnl (3.5beta5) + Fixed Casper's m_sunos5 module: there was a poor interaction with + his use of OSREV and SunOS 5.5.1. + +Fri Dec 20 1996 - wnl (3.5beta4) + Replaced m_sunos5 with a reworked version by Casper Dik. This one + should work under 2.6 and may not require that top be run setuid + to root under 2.5 or 2.6. This also fixed a bug in m_sunos5 that + was introduced in beta3. + Fixed calculation of OSREV in Configure. + +Wed Nov 20 1996 - wnl (3.5beta3) + Incorporated contributed fixes to: bsdos2, irix62, freebsd20, + ultrix4, sunos5. Changed calculation of swap area in sunos5 (now + uses swapctl). sunos5 now understands idled processors. Changed + Configure to determine os revision using uname (when available) + and adding it to machine.c compiliation in Makefile as -DOSREV. + Changed calls to "exit" in modules to use "quit" instead. + +Oct 20 1996 - wnl (3.5beta3) + Removed "time" from list of ordering choices: there's no easy way + to get cpu time for all processes (it's in the u area). + +Fri Oct 18 1996 - wnl (3.5beta3) + hpux10 and hpux9: using a better means for determining when a + process is idle. + decosf1 now includes utils.h. + +Fri Sep 13 1996 - wnl (3.5beta2) + Fixed Configure to build Make.desc in such a way that doesn't + require a long argument to sed. + +Thu Sep 12 1996 - wnl (3.5beta2) + Fixed bug in display.c that affected empty cpustate names. + Created hpux1010 module - a variant of hpux10 that does not use + struct proc or struct user (suitable for HP/UX 10.10). + +Wed Sep 11 1996 - wnl (3.5beta2) + Changes to sunos5 module: Removed WCPU column since it is meaningless + on a SVR4-based system. Added THR column to show number of threads + for each process. This was not straightforward: the information is + not stored in prpsinfo but rather in prstatus. + +Tue Sep 10 1996 - wnl (3.5beta1) + Added patches for sunos4mp to provide order support. + Added irix62 module. + Changed prime.c to include stdio.h for printf prototype. + Added conditional code to os.h and utils.c to handle systems + where sys_errlist is defined in stdio.h (such as NetBSD). + +Mon Sep 09 1996 - wnl (3.5beta1) + Removed tar and shar rules from Makefile.X -- don't need them anymore. + Added -v option to display version number. Updated man page. + +Thu Aug 29 1996 - wnl (3.4) + Replaced modules (from Tim Pugh): next 32, next40. + Fixed bug in username.c: hashing negative uids. + +Thu Aug 22 1996 - wnl (3.4beta3) + Patched modules: ultrix4, sunos4, sunos5, utek, decosf1, irix5. + Added modules: next40, next32. + Fixed procstates update bug in display.c. + Fixed divide by zero bug in utils.c. + Fixed bad number in layout.h + Minor fixes to Configure. + Complete overhaul of FAQ. + +Tue Feb 13 1996 - wnl (3.4beta3) + Added convex module from Warren Vosper (originally written by + William Jones). + +Tue Feb 13 1996 - wnl (3.4beta2) + Fixed format_k in utils.c to calculate K and M values correctly. + Added check for gigabyte values ('G'). Changed sumamry_format + in display.c to use format_k where appropriate. + Changed creation of distribution tar file to place everything in + a top level directory. + +Tue Jan 30 1996 - wnl (3.4beta2) + Added m_aix41 module. Added new tag type to module comments: + TERMCAP, which defined the library to use for a termcap library. + If no TERMCAP tag is found in the module's initial comment, then + Configure will default to "-ltermcap". AIX needs this since it + put all the termcap routines in libcurses(!) + + Added m_bsdos2 (found lingering in my mailbox). + Updated m_svr4 to include support for NCR multiprocessors. + Fixed small bug in utils.c + +Thu Jan 25 1996 - wnl (3.4beta1) + Fixed m_sunos5 invocation of gettimeofday to include "NULL" as + second argument. This provides compatability with the Posix- + compliant template provided with SunOS 5.5, but doesn't hurt + previous versions since they do bother with a template for that + function. + + Made changes (recommended by net users) to hpux10, ultrix4, + netbsd10, aux3 (replaced aux31). Added module for linux. + +Fri Oct 10 1995 - wnl (3.4beta1) + Added user-contributed modules for SCO Unix, IRIX 5, HP/UX 10, + Pyramid DC/OSX. Changed Configure so that it runs in environments + whose c-shells have no 'eval'(!). Added support for multiple sort + ordering methods via the -o switch. This option requires support + from the machine dependent module: such support was added to + sunos5 (thus sunos54) and sunos4. + + display.c: Changed CPU states display line to shorten the leading + tag if the data won't fit in the current width. Fixed a divide-by- + zero bug that affected ultrasparc servers (and potentially other + systems). + + m_sunos5.c: Now asks the system for the correct pagesize rather than + assuming it is 4K. + +Thu Mar 2 1995 - wnl (3.3 RELEASE) + Added module netbsd10 and renamed netbsd to netbsd08. Changed + Configure so that it does not use an initial default module name. + Made other compatability fixes to Configure. Added comments to + decosf1 concerning optimizer bug. Other documentation changes. + Added use of "prime.c" to Configure script. + +Tue Feb 7 1995 - wnl (3.3beta6) + Still one more beta.... + Fixes for sunos5 2.4 gcc core dump (it was an alignment problem). + Fixed and improvements for decosf1 (including use of format_k + for proper SIZE column formatting). Added modules freebsd20 and + ncr3000. + +Thu Feb 2 1995 - wnl (3.3beta5) + One more beta.... + Fixed a few bugs in the sunos5 port pertaining to casting and + very large memory counts. Added "ifndef HAVE_GETOPT" to getopt.c + to provide for conditional compilation of the getopt function. + Those systems that have getopt in libc can add -DHAVE_GETOPT to + the CFLAGS line in the module to prevent the function from being + compiled. Added sunos54 module to accomodate SunOS 5.4 + peculiarities. Added module for aux3.1. + +Wed Jan 4 1995 - wnl (3.3beta4) + This is really taking too long......sigh. + Fixed SIGWINCH handling once and for all. It now remembers the + number of processes you want displayed even thru window resizes. + Fixed buffer conflict in utils.c (itoa and itoa7). + Lots of small improvements to the various modules were made over + the past month: too numberous to list here. SunOS 5 module made + more secure thru use of seteuid calls (other SVR4 modules should + be modified similarly). One final MP fix to sunos5, too. Module + for decosf1 was modified to accomodate V3.0. + +Mon Apr 18 1994 - wnl (3.3beta3) + I think I finally got a sunos5 module that will work on MP + machines. Fixed cpu states figure in osmp41a so that + percentages never exceed 100%. Added shell script "install" + since Unix vendors can't seem to make up their minds on what + options they want to use for the one that comes with the OS. + Added netbsd modules from Christos. Fixed lots of other little + things over the past few months that I have long since forgotten. + +Wed Dec 15 1993 - wnl (3.3beta2) + Added module patches from various users: hpux9, sunos5. + Fixed bug with batch mode (screen_width wasn't getting set). + Changes to accomodate 64 bit machines. + Fixed some bugs in command parsing ("renice 19 " did something + unexpected). + +Mon Aug 30 1993 - wnl (3.3beta) + Added lots of little patches from various users. + Added routines to utils.c for intelligent formatting of kilobytes + and time. These are intended to be used in the modules when + formatting a process line. Added code to "summary_format" in + display.c to do intelligent formatting of memory quantities. + Redid display.c to allow for varying line widths and dynamic + reallocation of the screen buffer. + Added a SIGWINCH handler to top.c! + Added a constant, MAX_COLS, to top.h which defines the absolute + widest line we will ever allow. Changed allocations of "char fmt" + in all machine modules to use this constant rather than an abitrary + number. + +Fri Aug 13 1993 - wnl (3.3) + Changed return value definition of time-related functions in top.c, + display.c, and m_ultrix4.c to time_t (stuart@coral.cs.jcu.edu.au). + Fixed bug in display.c: line_update when start != 0. + +Wed Aug 4 1993 - wnl (3.2 release) + Changes to Configure from Paul Vixie. Added modules for hpux9 and + bsd386. + +Tue Jul 13 1993 - wnl (3.1 release) + More small changes and minor bug fixes. Brought bsd44 up to date + and added a module for svr4.2. Changed shar packaging to use Rich + Salz's cshar stuff. + +Wed Jul 7 1993 - wnl (3.1BETA) + More changes and bug fixes to Configure. Applied some other + minor bug fixes and suggestions from the beta testers. Added + the "metatop" shell script and the "installmeta" rule to the + Makefile to make handling multiple machine models and OS versions + easier. Added INSTALL and FAQ files. + +Tue May 18 1993 - wnl (3.1BETA) + Changed Configure to be compatible with most SVR4 environments + (differing output from "ls -lg"). Also changed Configure, + Makefile.X, etc., to look for module files in the subdirectory + "machine" (thanks to Christos Zoulas). + +Tue Apr 20 1993 - wnl (3.1BETA) + Changed both occurences of "ls -1" in Configure to "ls". This + SHOULD produce the same result, and has the advantage that it + doesn't produce an error on a system 5 machine. Integrated other + changes recommended in the first round of beta testing. + +Wed Mar 10 1993 - wnl (3.1BETA) + MAJOR CHANGE: I have added a required function to all machine + dependent modules, called proc_owner. It takes a pid as an argument + and returns the uid of the process's owner. Such capability is + necessary for top to run securely as a set-uid program, something + that is needed for SVR4 implementations to read /proc. I have + retrofitted all modules except dgux with this function, but was + not able to test most of them. Top should now run securely as + a setuid program. Added 386bsd and sunos5 modules. Added sunos4mp + module for MP Suns. + +Sat Feb 20 1993 - wnl (3.1ALPHA) + Modified top.c and commands.c to compile correctly on System V + derived Unixes (especially SVR4), but in a way that doesn't rely + on an oracle-like declaration (that is, I don't use "ifdef SYSV"). + Fixed some bugs in "Configure" and "getans". Added inspection of + env variable "TOP" for options, and made -I default to showing + idle processes. Added "u" command to change username restriction + on the fly. Created shell script "suntop" for poor multi-version + SunOS folks (like myself). + +Wed Jun 3 1992 - wnl (3.0) + "max_topn" wasn't being used everywhere it was supposed to be + in top.c. Many cosmetic changes, including copyright notices in + all the .c files. Version number is now handled by version.c and + reflects the current patchlevel (which is initially set to 0). + Changed Configure and Makefile to allow configurable variables for + certain commands: shell, cc, awk, install. Updated README and + Porting. Ready to release to the world! + +Mon May 18 1992 - wnl (2.9BETA) + Added modules provided by Christos Zoulas. Replaced screen.c + with one modified by Christos and that will appropriately select + and handle the sgtty, termio, or termios system. Integrated many + other changes recommended by Christos. Fixed (I hope) the "-b" + batch mode display bug. Had to change loadavg to load_avg to avoid + a conflict with 4.4BSD. + +Mon Apr 27 1992 - wnl (2.8BETA) + Added modules provided by Daniel Trinkle. Added patchlevel.h, + but the patch level is not yet reflected in the version number. + Cleaned up m_sunos4.c a little. + +Wed Apr 22 1992 - wnl (2.8BETA) + Major internal reorganization. All of the system dependent stuff + is now really and truly separated from everything else. The + system dependent functions are contained in a separate .c file + called a "module". The Configure script knows how to find and + set up these modules, but the human installer still needs to tell + Configure which module to use (no automagic determination of + machine type---sorry). Added -U option to specify one user's + processes, but there is no corresponding command...yet. Other + changes and improvements too numerous to mention here. Currently + there are only two modules: sunos4 and umax. But after this beta + release is sent around, I expect more to be written. I just hope + that the machine-dependent abstractions don't need to change in + the process. + +Thu Mar 26 1992 - wnl (2.7BETA) + Beta release with minimal architecture support. Updated README + and added a first cut at a Porting guide. Added ioctl TIOCGWINSZ + code from top2.5+ (courtesy of David MacKenzie). I didn't even + try porting the Ultrix support since I don't have access to an + Ultrix machine. + +Fri Oct 11 1991 - wnl (2.6) + This version was not widely released. It contained many changes. + Here are the major ones: + + Put in Vixie's idle process hack. + + Enhanced type field in new_message to handle delayed messages. + + Changed u_process to automatically adjust for varying lines of + output. Management of screenbuf should now be completely contained + in display.c. Removed now extraneous code from CMD_number[12] + portion of command switch in top.c. This was the stuff that dealt + with zeroing out lines in screenbuf. + + Finally made it all work correctly on a 386i. Problems I had to + overcome: kvm_nlist doesn't return 0 on success as advertised (it + returns 1 instead); the results of a kvm_nlist are different + (n_type can be zero even for a symbol that exists). + + Serious rearrangement for processor dependent stuff. All nlists + are now in separate files with the suffix ".nlist". Most machine + specific code is in "machine.c" surrounded by appropriate ifdefs--- + the goal is to eventually have all machine specific code in this + file. Managed to find a way to detect SunOS 4.x at compile-time: + this is contained in the include file "sun.h". Completely changed + the memory display line for SunOS 4.x---it now displays a far + more appropriate report. + + Created the shell script "Configure" to aid in the configuration + step. + + Fixed a bug in init_termcap: it will now tolerate an environment + which does not have TERM defined (thanks to Sam Horrocks for + pointing this out). + +Tue Aug 9 1988 - wnl (2.5) + Added changes to make top work under version 4.0 of the Sun + operating system. Changes were provided by Scott Alexander of the + University of Pennsylvania. Thanks! Compile with "-Dsunos4" to + get them. Virtual memory statistics are not readily accessible + under 4.0, so they don't show up in the output. + +Thu Jul 31 1987 - wnl (2.4) + Fixed a problem with the 4.0 Pyramid code. The label "cp_time" + doesn't exist in the 4.0 kernel anymore. I think the code Carl + sent me wants "percpu" instead. That is what I am using and it + appears to work. 375 code is still untested (at least by me). + Also picked a great deal of lint out of the source. Lint now only + complains about a very few nitpicky things (there are far too many + calls to "printf" to put a "(void)" in front of!), at least under + SunOS. + +Tue Jul 28 1987 - wnl (2.4a) + Added changes for a Symmetrics Computer Systems s/375 machine. + Changes were provided by Paul Vixie. Thanks! According to Mr. + Vixie: "These changes were not made at, by, or for SCS proper. + SCS would probably be interested in them, but so far only the + users' group has them. They were made in February, 1987, to + version 2.1 of the program, by Paul Vixie + (dual!ptsfa!vixie!paul@ucbvax.Berkeley.EDU)." His changes were + integrated into version 2.3 to make version 2.4. + + The SCS peculiarities are summarized in Changes.scs. + +Tue Jun 9 1987 - wnl (2.3 for real) + Changed the includes for the extra code Carl sent me to only + compile on Version 4.0 Pyramid machines. This makes top still + compilable on pre-4.0 Pyramids. Specifically, this code is only + compiled when both "pyr" and "CPUFOUND" are defined. + +Wed Jun 3 1987 - wnl (2.3 with Pyramid additions) + It's been a month and I still haven't done anything about + distributing this version. However, Carl Gutekunst from Pyramid + has sent me some extra patches for some of the Pyramid code. I + just added those and will make them part of 2.3. This fixes the + following Pyramid problems: adds the inclusion of , + uses the correct size for getting the kernel value _ccpu (this bug + affected the Vax version as well), sums the elements of the percpu + array to calculate a cp_time value (for OSx 4.0). + +Fri May 1 1987 - wnl (2.3) + I have finally finished all the changes for better support of + oddbal terminals. Added the low-level routine "clear_eol" which + makes handling terminals without "ce" easy: it uses spaces + instead. All direct uses of "clear_line" outside of screen.c have + been changed to use this primitive. A terminal with "os" is now + handled in such that all situations that need overwriting are + completely avoided (including several commands). This required + some changes to the way commands are translated into action (in + "top.c"). Made several important changes to display.c to prevent + overflowing of any of the fields. Specifically, more than 99 + total processes and a cpu state that reaches 100%. Had to make a + small change to two casts in top.c, because the Sun 3.2 compiler + was giving warnings on them. Added the "-q" option which lets + root run top at a nice of -20 (in case he thinks he really needs it). + +Tue Dec 30 1986 - wnl (2.2) + I think I fixed a bug reported by Julian Onions at Nottingham. + Occasionally, top will core dump when the sprintf in either + i_process or u_process overflows due to an exceptionally + unrealistic time value. I think it highly unlikely that top can + get a bad proc structure (although I suppose it is possible), but + the process time is read from the user structure, and that can + sometimes be part garbage. So, "get_ucpu" checks the value it + returns to make sure its formatted form will not overflow the + sprintf. If this doesn't fix the bug, then more drastic measures + will be necessary. I plan to make this version the official + "top 2.2". [[ This version was never distributed very widely. ]] + +Tue Dec 2 1986 - wnl (2.2c) + Added to top.c the notion of a "failed command". When a command + produces a message (on the message line), an update does not + follow it. Before, the message was written and a new display was + shown---purposefully not overwriting the message. But the + improvements to handle overstriking terminals and terminals + without "ce" clear the screen before every display, which would + erase the message. Now, the message is displayed and top waits + another full time interval before updating the display. This + works much better all around. + +Mon Nov 24 1986 - wnl (2.2b) + Created a new file, utils.c, and made appropriate changes to + Makefile. This new file holds all utility functions that can and + may be used by more than one "module". Improved i_memory and + u_memory (display.c) so that screen updates for the values + displayed are only changed when necessary. Also made the line + look better: the last fixes made for a rather ugly display. + Added the locally defined constant "LoadMax" and added code to + top.c to send the cursor home after a space command is entered if + the load average is higher than "LoadMax". This provides visual + feedback on loaded systems. + +Mon Nov 3 1986 - wnl (2.2a) + Widened the format for memory usage so that it can display 5 + digits. This makes that line look a little ugly---maybe I'll fix + that later. Screen handling now understands "os" and a missing + "ce". It treats them identically: clear the screen between each + display. Screen handling code now uses "cd" when appropriate + (i.e.: when user has shortened the screen). Made i_loadave clear + then screen and took out most of the explicit calls to "clear" in + top.c. This method is cleaner, especially in conjunction with + "os" handling. Added preprocessor variable "RANDOM_PW" for + systems that access the passwd file randomly (Sun's yp and 4.3). + With "RANDOM_PW" set, "getpwuid" is used instead of "getpwnam", + but uid->username mappings are still hashed internally (because + that is still faster than going to disk). + +Mon Oct 6 1986 - wnl (2.1) + A bug with the kill command was pointed out by "dciem!tim"--- + specifying a signal by name did not work correctly. This bug has + been fixed with a simple change to commands.c. Another bug made + the cpu state percentages incorrect the first time they were + displayed. This bug has also been fixed (changed top.c). + +Thu Sep 4 1986 - wnl (2.0, at last) + This is the version that will (hopefully) get released to the + world as top 2.0. + Added the "r" and "k" commands for renice and kill, respectively. + This required adding a way to handle system call errors, and the + addition of the "e" command. Help screen and manual page were + changed to reflect this change. Changed all "#ifdef SUN" directives + to "#ifdef sun", and changed all "#ifdef PYRAMID" directives to + "#ifdef pyr". As much as I hate those choices of preprocessor + names (they too easily conflict with real variable names), it does + make automatic compilation possible---people don't have to change + the Makefile anymore for specific machines. The manual page was + changed to automatically incorporate the defaults as set in the + Makefile (including an infinite value for TOPN) and the way the + manual page is generated by the Makefile was changed to make + maintenance of this information automatic. + +Mon Jul 28 1986 - wnl (still pre 2.0) + Real close now. I put in a new definition for the macro "pagetok" + that does an explicit shift of a constant expression involving + PGSHIFT. Appropriate checks are made if PGSHIFT is to small. + "pagetok" is now used exclusively everywhere to convert kernel + clicks to kilobytes. I added a full blown interactive mode with + the ability to change some of the runtime parameters (how many to + display, time delay, etc.) while top is running. I also + incorporated a few ideas from the net: control characters in the + command name are replaced with '?'; the '-S' option makes the + swapper and pager visible; options have been added to control the + number of displays produced (this makes it easier to make + performance snapshots with top). I have also added the notion of + "infinite" values for number of processes and number of displays. + I fixed a long-standing bug in the uid to username mapping code + that was only aggravated on the pyramids: it was an ill-defined + expression (akin to i = i++). I tweaked the proc_compar routine + for qsort slightly so that stopped processes were more likely to + show up. Manual page was updated to reflect all changes + noticeable to the user. + +Tue Jul 1 1986 - wnl (pre 2.0 -- 1.9999?) + In the process of major revamping on the way to version 2.0. + I have completely done away with curses by adding my own screen + management routines in a separate file (screen.c). The rationale + for this is that top knows a whole lot more about what is and is + not redundant on the screen and can compare simple integer values + where curses would have to compare strings. This has turned out + to be a very big win speed-wise. The proc_compar routine for + sorting has been rewritten to include several more keys. I + decided this was necessary when I noticed that the "top" process + itself kept disappearing off the top 10 list on a Sun-3. All the + processes had the same percentage (0%) and the sort wasn't really + doing anything worthwhile. I changed the expression that computes + memory usage to use the ctob macro instead of just assuming that + pages were 512 bytes. More work still needs to be done before + this version is usable. I changed options-processing to use + getopt and added appropriate incantations to the Makefile. + +Wed Feb 20 1985 - wnl (still 1.8) + Put in the ifdef FOUR_ONE statements to make top still compilable + on a 4.1 system. Apparently, there are some users out there that + need this functionality. Oh well. I don't guarantee any of it, + since I can't test it. Made appropriate changes to README and + final installation related changes to Makefile. + +Sat Feb 2 1985 - wnl (1.8) + Removed all the ifdef FOUR_TWO statements and made "top" into a + 4.2 only program. If someone really wants to still run it on 4.1, + then they can do all the work. We don't have a 4.1 machine + anymore, so I don't even know if the thing still works under 4.1. + Cleaned up the Makefile and the README. Added installation rules + to the Makefile, as requested by several sites. Fixed a very + obscure divide-by-zero bug. Added a second "key" to the qsort + comparison function (proc_compar) so that comparisons are based on + cpu ticks if the percentages are equal (provided by Jonathon + Feiber at Sun). + +Tue Dec 11 1984 - wnl (1.7) + Added the virtual and real memory status line to the header area + (provided by Jonathon Feiber at Sun) + +Tue Nov 20 1984 - wnl (1.6) + Added an "exit" if sbrk's fail. Added changes from Jonathon + Feiber at Sun: ifdef SUN to make top work on Suns (they don't use + doubles in the proc structure), register declarations, check for + getting a user structure that has disappeared since the proc array + was read (it used to die, now it just shows the process as swapped). + +Tue Nov 13 1984 - wnl (1.5) + If the number of displayable processes ("active_procs") was less + than the number of requested processes ("topn"), top would + segmentation fault. This bug has been fixed. Thanks to Prentiss + Riddle at ut-sally for pointing out the existence of this bug. + +Tue Oct 23 1984 - wnl (1.4) + Finally fixed the hash table bug that caused processes owned by + root to sometimes appear with either no name or a different name + that had UID 0 (such as "operator"). Removed all the ifdef DEBUG + blocks to make top ready for distribution to the real world. + +Sun Apr 8 1984 - wnl (still 1.3) + Made some slight changes to the display format. It now looks more + aesthetically pleasing. Added some preprocessor constants so that + the two defaults (number of processes and seconds of delay) easier + to change. + +Thu Apr 5 1984 - wnl (1.3) + Changed the order in which things are done at initialization time. + This way, if an error occurs before starting the main loop, curses + will never get started. Also changed other error handlers so that + endwin() is called before any flavor of exit. Specifying a number + of processes that is more than the screen can handle is no longer + fatal. It displays a warning message and pretends the user + specified the maximum for the screen. Finally cured all the TSTP + blues (well, almost all). I removed my TSTP handler and convinced + the system to always use the one that curses sets up. Turns out + that "sleep" was stepping all over it during a pause. So, I don't + use sleep anymore. The only problem that remains with it now is + redrawing the old display before updating it after a pause. + +Tue Apr 3 1984 - wnl (from 1.0 to 1.2) + I changed the format of the TIME column from just "seconds" to + "minutes:seconds". I also made pausing work correctly. Screen + redraws with an up to date display. For compatibility with 4.2, I + changed the name of the "zero" function to "bzero". The makefile + has been altered to handle versions for 4.1 and 4.2, and README + has been updated to reflect these recent changes. diff --git a/contrib/top/FAQ b/contrib/top/FAQ new file mode 100644 index 0000000000..92613e4278 --- /dev/null +++ b/contrib/top/FAQ @@ -0,0 +1,340 @@ + TOP + Version 3.8beta1 + + William LeFebvre + with much help from others + + Frequently Asked Questions and their Answers + + + + GENERAL + + 1. What is top? + + Top provies the user with a regularly updated display showing + information about the system and its top cpu-using processes. Think + of it as a full-screen "ps" output that gets updated at regular + intervals. + + 2. Where do I get the latest version of top? + + The official site for top is "ftp.unixtop.org" in the directory + "/pub/top". Top is also a SourceForge project, and the most recent + releases are available on any of the SourceForge mirrors. The + SourceForge project page is at + http://sourceforge.net/projects/unixtop. + + 3. Is there a web page for top? + + Yes. Point your browser at http://www.unixtop.org. It includes all + documentation, a nice interactive display which describes the various + components of the output of top, web-based retrieval of the package, + year 2000 information, and other neat stuff. + + 4. Is there a mailing list or on-line bulletin board for top? + + There is a mailing list used for general announcements regarding top, + including new releases. This mailing list is available to sourceforge + members and can be accessed from the unixtop sourceforge project + page. Visit SourceForge and search for the project "unixtop", then + click on "mailing lists". There are also on-line forums available + through SourceForge where members can post questions and comments. + + 5. What about Year 2000 compliance? + + Top did not experience any problems with the transition to the year + 2000. A full statement concerning top and the year 2000 can be found + in the file "Y2K" included with the distribution. + + 6. Will there be another major release of top? Will there be a top + version 4? + + I have some great ideas for the next major release of top, and I very + much want to make those ideas a reality. What I don't have much of + these days is free time. But I will keep poking at it and I hope to + have top version 4.0 ready by the fall of 2006. + + 7. Does top really support multi-processor systems? + + On platforms that support multiple processors, top is able to detect + and correctly summarize the information about those processors. What + top does not do is break down the cpu states summary (the third line + of the display) by cpu. Instead it collects the cpu state information + from all processors and combines them in to a single line. Some + vendors include a modified version of top that presents this + information for each cpu. Top 3.7 may have this functionality but it + is not present in the standard top 3.6 release. + + 8. Is top under CVS control? Can I access the sources via SourceForge + CVS or Subversion? + + I maintain top using subversion, not CVS. Although I utilize my own + private subversion repository, it is regularly mirrored in to the + SourceForge Subversion repository. You can access the SourceForge + repository here: https://svn.unixtop.org/unixtop/top-3. + + + COMPILING + + 9. We just upgraded our operating system to a new version and top broke. + What should we do? + + Recompile it. Top is very sensitive to changes in internal kernel + data structures. It is not uncommon for a new version of the + operating system to include changes to kernel data structures. + + + RUNNING + +10. I just finished compiling top and it works fine for root, but when I + try to run it as a regular user it either complains about files it + can't open or it doesn't display all the information it should. Did I + do something wrong? + + Well, you're just not done. On many operating systems today, access + to many of the kernel memory devices and other system files is + restricted to either root or a particular group. The configure script + figures this out (usually) and makes sure that the "install" rule in + the Makefile will install top so that anyone can run it successfully. + However, you have to *install* it first. Do this with the command + "make install". + +11. Top is (not) displaying idle processes and I don't (do) want it to. + + This default has only changed about a dozen times, and I finally got + tired of people whining about it. Go read the manual page for the + current version and pay special attention to the description of the + "TOP" environment variable. + +12. We have so much memory in our machine that the memory status display + (the fourth line) ends up being longer than 80 characters. This + completely messes up top's output. Is there a patch? + + Most modules have been changed to use new memory formatting functions + which will display large values in terms of megabytes instead of + kilobytes. This should fix all occurences of this problem. Also note + that newer versions of top can use columns beyond 79, and understand + window resizes. So you can always make your window wider. + +13. I tried to compile top with gcc and it doesn't work. I get + compilation errors in the include files, or I get an executable that + dumps core, or top displays incorrect numbers in some of the + displays. What's wrong? + + Gnu CC likes very much to use its own include files. Not being a gcc + expert, I can't explain why it does this. But I can tell you that if + you upgrade your operating system (say from Solaris 2.6 to Solaris + 2.7) after installing gcc, then the include files that gcc uses will + be incorrect, especially those found in the "sys" directory. Your + choices are: (1) rebuild and reinstall the "standard" include files + for gcc (look for scripts in the distribution called "fixincludes" + and "fixinc.svr4"), (2) compile machine.c with + "CFLAGS=-I/usr/include" then make the rest of the object files + normally, or (3) use a different compiler. + +14. The cpu state percentages are all wrong, indicating that my machine + is using 95% system time when it is clearly idle. What's wrong? + + This can happen if you compiled with gcc using the wrong include + files. See the previous question. + + + FREEBSD PROBLEMS + +15. This version of top does not show individual threads with the "t" or + "H" commands. Instead it says "command not available." Why? + + Previous versions of top attempted to support the display of + individual threads under FreeBSD through the use of the "t" command. + However, the FreeBSD kernel does not supply sufficient or correct + information on the individual threads within a process. So the data + that was being displayed was incorrect and misleading. Therefore, top + version 3.8 disables the use of this command to prevent the display + of incorrect information. FreeBSD 8.0 will correctly report + per-thread information and top version 3.8 supports the use of the + "t" command for version 8.0. + +16. The "f" command (to display full command lines for the processes) + does not work and instead says "command not available". Why? + + The current version of top is able to use sysctl to retrieve almost + all of the information it needs without having to open /dev/kmem. The + one piece of information not available via sysctl is the full command + line of each argument. If you run top as a regular user and it cannot + open /dev/kmem (in other words, it is not installed set-gid to the + kmem group) then it will disable the "f" command. Make sure the top + binary is installed with a group ownership of "kmem" and with the + set-gid bit on if you want the "f" command to work properly. + + + MACOSX PROBLEMS + +17. I tried to configure top on my Mac OSX system and I got an error + claiming "macosx not supported". What up? + + Since I don't have full time root access to a Mac OSX system I cannot + provide effective support for the platform. MacOSX uses Mach, and it + is very difficult to extract accurate system and process information + from the system. It takes a lot of trial and error, along with root + access. I have included the most up-to-date version of the macosx + module in the distribution, but I do not claim that it works. If you + want to try to use it, you can configure with "./configure + --with-module=macosx". + + + SUNOS PROBLEMS + +18. I tried compiling top under SunOS version 4.1.x and it got compile + time errors or run time errors. Is there a patch? + + If you try compiling top in a "System V environment" under SunOS + (that is, /usr/5bin is before /usr/bin on your path) then the + compilation may fail. This is mostly due to the fact that top thinks + its being compiled on a System V machine when it really isn't. The + only solution is to put /usr/bin and /usr/ucb before /usr/5bin on + your path and try again. + + + SOLARIS PROBLEMS + + + NOTE: the most common source of problems with top under Solaris is + the result of compiling it with the wrong front end. Make sure that + /usr/ucb is not on your path before attempting to compile top under + Solaris. + +19. Is there somewhere I can get a pre-compiled package? + + Yes. Although I don't provide pre-compiled binaries, you can get a + Sun-style package from www.sunfreeware.com. + +20. Under Solaris 2, when I type "make", the system says "language + optional software package not installed." What's going on? + + You tried to compile with /usr/ucb/cc. Make sure /usr/ucb is not on + your path. Furthermore, you do not have a Sun compiler installed on + your system. You need a compiler to make top. Either Sun's C compiler + or the Gnu C compiler will work fine. + +21. Under Solaris 2, when I run top as root it only shows root processes, + or it only shows processes with a PID less than 1000. It refuses to + show anything else. What do I do? + + You probably compiled it with /usr/ucb/cc instead of the real C + compiler. /usr/ucb/cc is a cc front end that compiles programs in BSD + source-level compatability mode. You do not want that. Make sure that + /usr/ucb is not on your path and try compiling top again. + +22. Under Solaris 2, I compiled top using what I am sure is the correct + compiler but when I try to run it it complains about missing dynamic + libraries. What is wrong? + + Check to see if you have LD_LIBRARY_PATH defined in your shell. If + you do, make sure that /usr/ucblib is not on the path anywhere. Then + try compiling top again. + +23. Under Solaris 2, when I try to run top it complains that it can't + open the library "libucb.so.1". So I changed the LIBS line in + m_sunos5.c to include -R/usr/ucblib to make sure that the dynamic + linker will look there when top runs. I figured this was just an + oversight. Was I right? + + No, you were not right. As distributed, top requires no alterations + for successful compilation and operations under any release of + Solaris 2. You probably compiled top with /usr/ucb/cc instead of the + real C compiler. See FAQ 22 for more details. + +24. On my 64-bit system some processes show up with incorrect information + (such as zero memory). + + If you are running a 64-bit system, then you need to make sure that + you are running the 64-bit top binary. Top's configure script + attempts to detect 64-bit systems, and will automatically generate + both 32-bit and 64-bit binaries on such systems. If you use or + install the 32-bit binary on a 64-bit system top will still run but + will not produce the correct results. This will also happen if you + configure your distribution on a 32-bit system then compile with that + configuration on a 64-bit system. You must configure and compile on + the same system. For Sparc systems the 32-bit binary will be created + in the subdirectory "sparcv7" and the 64-bit binary will be created + in the subdirectory "sparcv9". For Intel systems the directories will + be "i386" (32-bit) and "amd64" (64-bit). In all cases a copy of + /usr/lib/isaexec is made in the main directory and called "top". This + program will choose the correct binary to run from one of these + subdirectories. See isaexec(3c) for more details. + +25. Can I install both 32-bit and 64-bit binaries on a central file + server and have machines which mount it automatically use the correct + one? + + Yes. If you configure and compile on a 64-bit system, top's configure + script and makefile will automatically create both 32-bit and 64-bit + binaries. The "install" rule in the makefile will install these + binaries in subdirectories of /usr/local/bin appropriate to the + architecture (sparcv7/sparcv9 or i386/amd64) then create a copy of + /usr/lib/isaexec named "top" in /usr/local/bin to ensure that the + appropriate is run when a user types "top". If you make sure that you + configure and compile on a 64-bit system, then "make install" will do + the right thing. + +26. This version of top show less available swap space than previous + versions. Why does it no longer match the output of the swap summary + produced with "swap -s"? + + Starting with version 3.6 of top, the amount of swap space reported + by top has been changed to reflect only disk-based swap space. The + swap summary produced with "swap -s" also includes memory-based swap + space. This changed was made for several reasons. It makes the + display under Solaris more like those of other operating systems. The + display is more what users expect (except those used to previous + versions of top). Most importantly, "swap -s" gets its data via an + undocumented system interface. Now that top no longer displays that + data it can use publically documented and maintained system + interfaces to retrieve its data. + + + SVR4-DERIVED PROBLEMS + +27. When I run top on my SVR4-derived operating system, it displays all + the system information at the top but does not display any process + information (or only displays process information for my own + processes). Yet when I run it as root, everything works fine. What's + wrong? + + Your system probably uses the pseudo file system "/proc", which is by + default only accessible by root. Top needs to be installed setuid + root on such systems if it is going to function correctly for normal + users. + + + SVR42 PROBLEMS + +28. The memory display doesn't work right. Why? + + This is a known bug with the svr42 module. The problem has been + traced down to a potential bug in the "mem" driver. The author of the + svr42 module is working on a fix. + + + STILL STUCK + +29. I'm still stuck. To whom do I report problems with top? + + The most common problems are caused by top's sensitivity to internal + kernel data structures. So make sure that you are using the right + include files, and make sure that you test out top on the same + machine where you compiled it. Sun's BSD Source Compatability Mode is + also a common culprit. Make sure you aren't using either /usr/ucb/cc + or any of the libraries in /usr/ucblib. Finally, make sure you are + using the correct module. If there does not appear to be one + appropriate for your computer, then top probably will not work on + your system. + + If after reading all of this file and checking everything you can you + are still stuck, then please use SourceForge to submit a support + request or a bug. Top is supported by the SourceForge project named + "unixtop". On SourceForge you will find defect tracking, a mailing + list, and on-line forums. You can also contact the author through + SourceForge. + diff --git a/contrib/top/INSTALL b/contrib/top/INSTALL new file mode 100644 index 0000000000..fe99ffeecd --- /dev/null +++ b/contrib/top/INSTALL @@ -0,0 +1,54 @@ + TOP + Version 3.8beta1 + + William LeFebvre + and a cast of many + +INSTALLATION + +Configuration and installation of top is easy. Top version 3.6 +comes with a configure script generated by gnu autoconf. After +unpacking the tar file, simply run "./configure". The script will +automatically perform a series of checks on the system and determine +what settings are appropriate for the Makefile and certain include +files. Once configure completes, simply type "make install" and +top will be compiled and installed. By default, the installation +location is /usr/local/bin. You can change the destination location +with the --prefix option to configure. + +In addition to the standard options, top's configure script supports +the following: + + --with-module=name + + Force the use of a particular module. Modules are located + in the subdirectory "machine". A module's name is derived + from the file's basename without the leading "m_". + + --with-ext=name + + Compile with the extension "name", found in the subdirectory + "ext". At the present time, there are no extensions in the + standard distribution. + + --enable-debug + --disable-debug + + Default off. Include debugging output in the compilation, + which can be seen with the -D switch. + + --enable-color + --disable-color + + Default on. Include code that allows for the use of color + in the output display. Use --disable-color if you do not + want this feature compiled in to the code. The configure + script also recognizes the spelling "colour". + + --enable-kill + --disable-kill + + Default on. Include code that allows for renicing and sending + signals to processes from within top (the 'kill' and 'renice' + commands). Use --disable-kill if you do not want this feature + compiled in to the code. diff --git a/contrib/top/LICENSE b/contrib/top/LICENSE new file mode 100644 index 0000000000..3b210dc104 --- /dev/null +++ b/contrib/top/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 1984 through 2008, William LeFebvre +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + + * Neither the name of William LeFebvre nor the names of other +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/contrib/top/Porting b/contrib/top/Porting new file mode 100644 index 0000000000..c28fd378bb --- /dev/null +++ b/contrib/top/Porting @@ -0,0 +1,229 @@ +Instructions for porting top to other architectures. + +This is still a preliminary document. Suggestions for improvement are +most welcome. + +Before you embark on a port, please send me a mail message telling me +what platform you are porting top to. There are three reasons for +this: (1) I may already have a port, (2) module naming needs to be +centralized, (3) I want to loosely track the various porting efforts. +You do not need to wait for an "okay", but I do want to know that you +are working on it. And of course, once it is finished, please send me +the module files so that I can add them to the main distribution! + +---------- + +There is one set of functions which extract all the information that +top needs for display. These functions are collected in to one file. +To make top work on a different architecture simply requires a +different implementation of these functions. The functions for a +given architecture "foo" are stored in a file called "m_foo.c". The +Configure script looks for these files and lets the configurer choose +one of them. This file is called a "module". The idea is that making +top work on a different machine only requires one additional file and +does not require changes to any existing files. + +A module template is included in the distribution, called "m-template". +To write your own module, it is a good idea to start with this template. +If you architecture is similar to one for which a module already +exists, then you can start with that module instead. If you do so, +remember to change the "AUTHOR" section at the top! + +The first comment in a module contains information which is extracted +and used by Configure. This information is marked with words in all +capitals (such as "SYNOPSIS:" and "LIBS:"). Go look at m-template: it +is fairly self-explanatory. The text after "LIBS:" (on the same line) +is extracted and included in the LIBS definition of the Makefile so +that extra libraries which may be necessary on some machines (such as +"-lkvm") can be specified in the module. The text after "CFLAGS:" +(on the same line) is extracted and included as flags in the "CFLAGS" +definition of the Makefile (thus in every compilation step). This is +used for rare circumstances only: please don't abuse this hook. + +Some operating systems have idiosyncrasies which will affect the form +and/or content of the information top displays. You may wish to +document such anomalies in the top man page. This can be done by adding +a file called m_{modulename}.man (where {modulename} is replaced with +the name of the module). Configure will automatically add this file to +the end of the man page. See m_sunos4.man for an example. + +A module is concerned with two structures: + +The statics struct is filled in by machine_init. Each item is a +pointer to a list of character pointers. The list is terminated +with a null pointer. + +struct statics +{ + char **procstate_names; /* process state names */ + char **cpustate_names; /* cpu state names */ + char **memory_names; /* memory information names */ +}; + +The system_info struct is filled in by get_system_info and +get_process_info. + +struct system_info +{ + int last_pid; /* last pid assigned (0 means non-sequential assignment) */ + double load_avg[NUM_AVERAGES]; /* see below */ + int p_total; /* total number of processes */ + int p_active; /* number of procs considered "active" */ + int *procstates; /* array of process state counters */ + int *cpustates; /* array of cpustate counters */ + int *memory; /* memory information */ +}; + +The last three pointers each point to an array of integers. The +length of the array is determined by the length of the corresponding +_names array in the statics structure. Furthermore, if an entry in a +_names array is the empty string ("") then the corresponding value in +the value array will be skipped over. The display routine displays, +for example, the string procstate_names[0] then the number +procstates[0], then procstate_names[1], procstates[1], etc. until +procstate_names[N] == NULL. This allows for a tremendous amount of +flexibility in labeling the displayed values. + +"procstates" and "memory" are displayed as straight integer values. +Values in "cpustates" are displayed as a percentage * 10. For +example, the (integer) value 105 is displayed as 10.5%. + +These routines must be defined by the machine dependent module. + +int machine_init(struct statics *) + + returns 0 on success and -1 on failure, + prints error messages + +char *format_header(char *) + + Returns a string which should be used as the header for the + process display area. The argument is a string used to label + the username column (either "USERNAME" or "UID") and is always + 8 characters in length. + +void get_system_info(struct system_info *) + +caddr_t get_process_info(struct system_info *, int, int, int (*func)()) + + returns a handle to use with format_next_process + +char *format_next_process(caddr_t, char *(*func)()) + + returns string which describes next process + +int proc_compare(caddr_t, caddr_t) + + qsort comparison function + +uid_t proc_owner(pid_t) + + Returns the uid owner of the process specified by the pid argument. + This function is VERY IMPORTANT. If it fails to do its job, then + top may pose a security risk. + + +get_process_info is called immediately after get_system_info. In +fact, the two functions could be rolled in to one. The reason they +are not is mostly historical. + +Top relies on the existence of a function called "setpriority" to +change a process's priority. This exists as a kernel call on most 4.3 +BSD derived Unixes. If neither your operating system nor your C +library supplies such a function, then you will need to add one to the +module. It is defined as follows: + + int setpriority (int dummy, int who, int niceval) + + For the purposes of top, the first argument is meaningless. + The second is the pid and the third is the new nice value. + This function should behave just like a kernel call, setting + errno and returning -1 in case of an error. This function MUST + check to make sure that a non-root user does not specify a nice + value less than the process's current value. If it detects such + a condition, it should set errno to EACCES and return -1. + Other possible ERRNO values: ESRCH when pid "who" does not exist, + EPERM when the invoker is not root and not the same as the + process owner. + +Note that top checks process ownership and should never call setpriority +when the invoker's uid is not root and not the same as the process's owner +uid. + + +The file "machine.h" contains definitions which are useful to modules +and to top.c (such as the structure definitions). You SHOULD NOT need +to change it when porting to a new platform. + +Porting to a new platform should NOT require any changes to existing +files. You should only need to add m_ files. If you feel you need a +change in one of the existing files, please contact me so that we can +discuss the details. I want to keep such changes as general as +possible. + +-------- + +Changes were made to the module interface between 3.5 and 3.6. Here are +the changes that need to be made to port a 3.5 module to 3.6: + +The array that stores memory statistics and is passed back in the system +information structure as "memory" must now be an array of (signed) longs. +This was done to more easily accomodate systems that have gigabytes of +memory. Since the numbers are supposed to be kilobytes, a long can still +represent up to 2 terabytes. Look for "int memory_stats[X]" (where "X" +is some arbitrary number) and change it to "long memory_stats[X]". If +the module support reporting swap information on a separate line, then +its "swap_stats" array also needs to be an array of longs. + +The argument to proc_owner should be an int, as in "int pid". When it is +used in proc_owner it should be cast as necessary. Many operating systems +will require it to be cast to a pid_t before being compared to the appropriate +element in the proc structure. + +In the function format_next_process, the last argument in the main call +to sprintf is the string that contains the command for the process. +Make sure that this last argument is enclosed in a call to "printable". +For example: "printable(MPP(pp, p_comm))". + +The third argument to "get_process_info" needs to be changed to an integer, +typically "int compare_index". The call to qsort in get_process_info may +be guarded by "if (compare != NULL)". If it is, remove the if statement. + +The other changes to get_process_info depends on whether or not the module +supports multiple sort orders. + +To support multiple keys: + +Create an array int (*proc_compares[])() and assign to it the list of +comparison functions, NULL terminated. For example: + +int (*proc_compares[])() = { + compare_cpu, + compare_size, + compare_res, + compare_time, + NULL }; + +In get_process_info there is a call to qsort which uses one of the +functions in proc_compares. It should be changed so that its fourth +argument is "proc_compares[compare_index]". + +If the module contains the function "proc_compare", it should be removed. + +There should also be a NULL-terminated array of strings which list the names +for the sort keys, for example: + +char *ordernames[] = +{"cpu", "size", "res", "time", NULL}; + +To indicate that this module supports multiple sort keys, add the following +line in machine_init: + + statics->order_names = ordernames; + +If there is no support for multiple keys: + +Leave statics->order_names alone and call the comparison function of +your choice in get_process_info, ignoring the third argument. + + diff --git a/contrib/top/README b/contrib/top/README new file mode 100644 index 0000000000..e0e7cf3e01 --- /dev/null +++ b/contrib/top/README @@ -0,0 +1,191 @@ + TOP + Version 3.8beta1 + + William LeFebvre + and a cast of dozens + + +If you do not want to read this entire file, then at least read +the section at the end entitled "KNOWN PROBLEMS". + +If you are having any problems getting top to work, please read the +file "FAQ" *before* contacting me. Thank you. + +"top" is a program that will give continual reports about the state of +the system, including a list of the top cpu using processes. Version 3 +of "top" has three primary design goals: provide an accurate snapshot of +the system and process state, not be one of the top processes itself, be +as portable as possible. + +Version 3 has many bug fixes from version 2.5, and it has also been +reorganized in a major way to make it easy to port to other platforms. +All system dependent code is now contained in one file. + +Starting with version 3.6, top includes a "configure" script generated +by Gnu's autoconf. This script MUST be run before attempting to +compile top. It will explore the system and generate approriate +contents for Makefile, config.h, and top.1. + +On some systems, top requires read access to the memory files +"/dev/kmem" and "/dev/mem" as well as the system's kernel image. Most +installations have these files protected from general access. These +sites would have to install this program in the same way that programs +such as "ps" are installed. On most systems with a /proc file system, +top will try to read everything it can from /proc, but may need extra +permissions to do so. The configure script will determine the +permissions needed by the top binary, and a "make install" as root +will get the binary installed correctly. Sometimes this requires that +the binary be installed with set-group-id privileges and, in rare +cases, set-user-id to root. + +CAVEAT: version 3 of top has internal commands that kill and renice +processes. Although I have taken steps to insure that top makes +appropriate checks with these commands, I cannot guarantee that these +internal commands are totally secure. IF YOU INSTALL top SET-USER-ID +TO ROOT, YOU DO SO AT YOUR OWN RISK! I realize that some operating +systems will require top to run setuid root, and I will do everything +I can to make sure that top is a secure setuid program. + +System support now takes the form of "modules". Adding support for a +different architecture requires only adding a module. These modules +are contained in the subdirectory "machine". The "configure" script +automatically determines which module is approproate. However, it may +not be able to determine what the correct module is. This can happen +either because it doesn't know about the system or there is no module +to support the system. In the former case, if you know which module +to use, you can force "configure" to choose a particular module with +the option "--with-module". For example, if you want to force the use +of the svr4 module (which appears as "machine/m_svr4.c") then use +"configure --with-module=svr4" to generate the correct Makefile. See +the file "Porting" for a description of how to write your own module. + +To compile and install "top", read the file "INSTALL" and follow the +directions and advice contained therein. + +If you make any kind of change to "top" that you feel would be +beneficial to others who use this program, or if you find and fix a bug, +please send me the change. + +Be sure to read the FAQ enclosed with the distrubution. It contains +answers to the most commonly asked questions about the configuration, +installation, and operation of top. + +COLOR + +Version 3.6 incorporated the idea of using ANSI color sequences to +enhance information on the screen. By default, no color is used. But +you can configure the use of color through the environment variable +TOPCOLORS (or, for compatibility, TOPCOLOURS). The interface is +identical to the one first implemented by chris@spang.uk.eu.org, but +the implementation is entirely different. The option -C can be used +to diable the feature entirely. + +Any information at the top of the screen can be enhanced with color. +However, due to implementation difficulties, the per-process area +cannot be color-enhanced. A complete description of color support can +be found in the man page. References for ANSI color codes can be +found all over the Internet, but if you want a handy reference, look +in color.h. + + +AVAILABILITY + +Note that top is now a sourceforge project! Its project name is +"unixtop" and you can access its project page here: + +http://sourceforge.net/projects/unixtop + +On the project page you can find more information and access the +official bug and feature request trackers. If you find a bug, +want to request a feature, or need help, please submit a request +to the appropriate tracker on sourceforge. Thank you. + +Subversion access is also provided by Sourceforge. If Subversion is +installed on your system you can check out the project with the +following command: + + svn co https://svn.sourceforge.net/svnroot/unixtop unixtop + +There is also a web site dedicated to the project, and it is here: + +http://www.unixtop.org + +The latest version of "top" is available as a download through +sourceforge. Start here to access the downloadable files: + +http://sourceforge.net/project/showfiles.php?group_id=72892 + + +KNOWN PROBLEMS: + +Gnu CC + +Compiling via Gnu CC continued to be the source of most of the +questions I receive. By far the most common mistake made by those +attempting to compile top with Gnu CC is out of date include files. +When the operating system is upgraded, the include files that are part +of the gcc package MUST also be updated. Gcc maintains its own +include files. Even a minor OS upgrade can involve changes to some of +the kernel's internal data structures, which are defined in include +files in "sys". Top is very sensitive to these changes. If you are +compiling with gcc and experience any sort of strange problems, please +make sure the include files you are using are up to date BEFORE +sending me a bug report. Look in the gcc source distribution for the +shell script "fixincludes". + +MacOS X + +Since I don't have full time root access to a MacOS X system I cannot +provide effective support for the platform. MacOS X uses Mach, and it +is very difficult to extract accurate system and process information +from the system. It takes a lot of trial and error, along with root +access. I have included the most up-to-date version of the macosx module +in the distribution, but I do not claim that it works. If you want to +try to use it, you can configure with "./configure --with-module=macosx". + +HP/UX 10.10 + +In their infinite wisdom, the folks at HP have decided that mere mortals +such as you and I don't need to know what the kernel's proc structure looks +like. To that end, they have removed all useful content from the include +file in version 10.10. As a result, top will not compile +under 10.10. What HP is trying to accomplish with this move is to force +iconoclasts such as myself to use "pstat" for collecting all process +information. I have no immediate solution for this problem, but hope to +obtain a sufficiently complete definition of "struct proc" at some point in +the near future. Stay tuned. + + +GRATITUDE + +My perpetual thanks to all the people who have helped me support top +on so many platforms. Without these people, top would not be what it +is. Here is a partial list of contributors and other individuals. + + Robert Boucher, Marc Cohen, David Cutter, Casper Dik, + Charles Hedrick, Andrew Herbert, Jeff Janvrin, Torsten Kasch, + Petri Kutvonen, William L. Jones, Tim Pugh, Steve Scherf, + Phillip Wu + +(My apologies if I missed anyone.) + + +LICENSE + +Top is distributed free of charge under the same terms as the BSD +license. For an official statement, please refer to the file "LICENSE" +which should be included with the source distribution. + + +AUTHOR + +If you wish to contact me, please send a message to the sourceforge +username "wnl". + + William LeFebvre + + U.S. Mail address: + William LeFebvre + 11585 Jones Bridge Road + Suite 420 PMB 139 + Alpharetta, GA 30202 diff --git a/contrib/top/README.DELETED b/contrib/top/README.DELETED new file mode 100644 index 0000000000..2a190b23de --- /dev/null +++ b/contrib/top/README.DELETED @@ -0,0 +1,15 @@ +Makefile.in +config.amd64.make +config.amd64.makeinstall +config.default.makeinstall +config.guess +config.h.in +config.sparcv9.make +config.sparcv9.makeinstall +config.sub +configure +configure.ac +install-sh +machine/ +sigconv.awk +top.1.in diff --git a/contrib/top/README.DRAGONFLY b/contrib/top/README.DRAGONFLY new file mode 100644 index 0000000000..645d37935d --- /dev/null +++ b/contrib/top/README.DRAGONFLY @@ -0,0 +1,15 @@ + + TOP-3.8beta1 AS USED BY DRAGONFLY + + No files have been moved or modified from their extracted position. + + ON THE VENDOR BRANCH (vendor/TOP), DO NOT CREATE OR EDIT ANY FILES + IN THIS DIRECTORY HIERARCHY! THIS HIERARCHY REPRESENTS AN EXACT COPY, + MINUS UNNEEDED FILES, OF THE TOP DISTRIBUTION. + + All modifications are made in the master or release branches! + + The file README.DELETED contains a list of deleted files. + + + SHA1 (top-3.8beta1.tar.gz) = d3f911f8cd64d21a8beb7eb3694923b40d2532e2 diff --git a/contrib/top/Y2K b/contrib/top/Y2K new file mode 100644 index 0000000000..2af998d4e5 --- /dev/null +++ b/contrib/top/Y2K @@ -0,0 +1,26 @@ +Top and the Year 2000 + +The software package top will not be affected by years numbering +between 2000 and 2037. No portion of the top code stores dates on +disk. All date processing in top is performed with functions from the +Unix C library and Unix kernel. The specific functions are: time(2) +and ctime(3S). These functions deal exclusively with conventional +Unix time values (number of seconds since Midnight January 1, 1970 +GMT) and produce strings with a 4-digit year. At no point in the code +for top are the last two digits used to represent a year. + +Top and the Year 2038 + +In the year 2038 top will fail to represent the time of day correctly +on 32-bit Unix operating systems. This is due to a limitation in the +way Unix represents time. Top will only work on systems whose kernel +call "time" and C library call "ctime" have been adjusted to represent +time with a value greater than 32 bits. The exact date and time of +this failure is 3:14:08 January 19, 2038 GMT. Note that this failure +will only affect the display of the current time in the output from +top. + + +THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE. +Please see the contents of the file "LICENSE" for further +information. diff --git a/contrib/top/ap_snprintf.c b/contrib/top/ap_snprintf.c new file mode 100644 index 0000000000..f9ba3500f2 --- /dev/null +++ b/contrib/top/ap_snprintf.c @@ -0,0 +1,1193 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * This code is based on, and used with the permission of, the + * SIO stdio-replacement strx_* functions by Panos Tsirigotis + * for xinetd. + */ + +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#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 % */ + + 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 % 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 % (like syslog). + * Note that we can't point s inside fmt because the + * unknown could be preceded by width etc. + */ + default: + char_buf[0] = '%'; + char_buf[1] = *fmt; + s = char_buf; + s_len = 2; + pad_char = ' '; + break; + } + + if (prefix_char != NUL && s != S_NULL && s != char_buf) { + *--s = prefix_char; + s_len++; + } + + if (adjust_width && adjust == RIGHT && min_width > s_len) { + if (pad_char == '0' && prefix_char != NUL) { + INS_CHAR(*s, sp, bep, cc); + s++; + s_len--; + min_width--; + } + PAD(min_width, s_len, pad_char); + } + + /* + * Print the string s. + */ + for (i = s_len; i != 0; i--) { + INS_CHAR(*s, sp, bep, cc); + s++; + } + + if (adjust_width && adjust == LEFT && min_width > s_len) + PAD(min_width, s_len, pad_char); + } + fmt++; + } + vbuff->curpos = sp; + + return cc; +} + + +static int snprintf_flush(ap_vformatter_buff *vbuff) +{ + /* if the buffer fills we have to abort immediately, there is no way + * to "flush" an ap_snprintf... there's nowhere to flush it to. + */ + return -1; +} + + +API_EXPORT_NONSTD(int) ap_snprintf(char *buf, size_t len, const char *format,...) +{ + int cc; + va_list ap; + ap_vformatter_buff vbuff; + + if (len == 0) + return 0; + + /* save one byte for nul terminator */ + vbuff.curpos = buf; + vbuff.endpos = buf + len - 1; + va_start(ap, format); + cc = ap_vformatter(snprintf_flush, &vbuff, format, ap); + va_end(ap); + *vbuff.curpos = '\0'; + return (cc == -1) ? len : cc; +} + + +API_EXPORT(int) ap_vsnprintf(char *buf, size_t len, const char *format, + va_list ap) +{ + int cc; + ap_vformatter_buff vbuff; + + if (len == 0) + return 0; + + /* save one byte for nul terminator */ + vbuff.curpos = buf; + vbuff.endpos = buf + len - 1; + cc = ap_vformatter(snprintf_flush, &vbuff, format, ap); + *vbuff.curpos = '\0'; + return (cc == -1) ? len : cc; +} diff --git a/contrib/top/boolean.h b/contrib/top/boolean.h new file mode 100644 index 0000000000..c6bcf4db73 --- /dev/null +++ b/contrib/top/boolean.h @@ -0,0 +1,5 @@ +/* My favorite names for boolean values */ +#define No 0 +#define Yes 1 +#define Maybe 2 /* tri-state boolean, actually */ + diff --git a/contrib/top/color.c b/contrib/top/color.c new file mode 100644 index 0000000000..c681082505 --- /dev/null +++ b/contrib/top/color.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + * Version 3 + */ + +/* + * This file handles color definitions and access for augmenting + * the output with ansi color sequences. + * + * The definition of a color setting is as follows, separated by + * colons: + * + * tag=minimum,maximum#code + * + * "tag" is the name of the value to display with color. + * + * "minimum" and "maximum" are positive integer values defining a range: + * when the value is within this range it will be shown with the + * specified color. A missing value indicates that no check should be + * made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50," + * is 50 <= n). + * + * "code" is the ansi sequence that defines the color to use with the + * escape sequence "[m". Semi-colons are allowed in this string to + * combine attributes. + */ + +#include "os.h" +#include "display.h" +#include "message.h" +#include "color.h" +#include "utils.h" + +typedef struct color_entry { + char *tag; + int min; + int max; + char color; + struct color_entry *next; + struct color_entry *tagnext; +} color_entry; + +static color_entry *entries = NULL; + +static color_entry **bytag = NULL; +static char **bytag_names = NULL; +static int totaltags = 0; +static int tagcnt = 0; +static int color_off = 0; + +static char **color_ansi = NULL; +static int num_color_ansi = 0; +static int max_color_ansi = 0; + +static int +color_slot(char *str) + +{ + int i; + + for (i = 0; i < num_color_ansi; i++) + { + if (strcmp(color_ansi[i], str) == 0) + { + return i; + } + } + + /* need a new slot */ + if (num_color_ansi >= max_color_ansi) + { + max_color_ansi += COLOR_ANSI_SLOTS; + color_ansi = (char **)realloc(color_ansi, max_color_ansi * sizeof(char *)); + } + color_ansi[num_color_ansi] = strdup(str); + return num_color_ansi++; +} + +/* + * int color_env_parse(char *env) + * + * Parse a color specification "env" (such as one found in the environment) and + * add them to the list of entries. Always returns 0. Should only be called + * once. + */ + +int +color_env_parse(char *env) + +{ + char *p; + char *min; + char *max; + char *str; + int len; + color_entry *ce; + + /* initialization */ + color_ansi = (char **)malloc(COLOR_ANSI_SLOTS * sizeof(char *)); + max_color_ansi = COLOR_ANSI_SLOTS; + + /* color slot 0 is always "0" */ + color_slot("0"); + + if (env != NULL) + { + p = strtok(env, ":"); + while (p != NULL) + { + if ((min = strchr(p, '=')) != NULL && + (max = strchr(min, ',')) != NULL && + (str = strchr(max, '#')) != NULL) + { + ce = (color_entry *)malloc(sizeof(color_entry)); + len = min - p; + ce->tag = (char *)malloc(len + 1); + strncpy(ce->tag, p, len); + ce->tag[len] = '\0'; + ce->min = atoi(++min); + ce->max = atoi(++max); + ce->color = color_slot(++str); + ce->next = entries; + entries = ce; + } + else + { + if (min != NULL) + { + len = min - p; + } + else + { + len = strlen(p); + } + message_error(" %.*s: bad color entry", len, p); + } + p = strtok(NULL, ":"); + } + } + return 0; +} + +/* + * int color_tag(char *tag) + * + * Declare "tag" as a color tag. Return a tag index to use when testing + * a value against the tests for this tag. Should not be called before + * color_env_parse. + */ + +int +color_tag(char *tag) + +{ + color_entry *entryp; + color_entry *tp; + + /* check for absurd arguments */ + if (tag == NULL || *tag == '\0') + { + return -1; + } + + dprintf("color_tag(%s)\n", tag); + + /* initial allocation */ + if (bytag == NULL) + { + totaltags = 10; + bytag = (color_entry **)malloc(totaltags * sizeof(color_entry *)); + bytag_names = (char **)malloc(totaltags * sizeof(char *)); + } + + /* if we dont have enough room then reallocate */ + if (tagcnt >= totaltags) + { + totaltags *= 2; + bytag = (color_entry **)realloc(bytag, totaltags * sizeof(color_entry *)); + bytag_names = (char **)realloc(bytag_names, totaltags * sizeof(char *)); + } + + /* initialize scan */ + entryp = entries; + tp = NULL; + + /* look for tag in the list of entries */ + while (entryp != NULL) + { + if (strcmp(entryp->tag, tag) == 0) + { + entryp->tagnext = tp; + tp = entryp; + } + entryp = entryp->next; + } + + /* we track names in the array bytag */ + bytag[tagcnt] = tp; + bytag_names[tagcnt] = strdup(tag); + + /* return this index number as a reference */ + dprintf("color_tag: returns %d\n", tagcnt); + return (tagcnt++); +} + +/* + * int color_test(int tagidx, int value) + * + * Test "value" against tests for tag "tagidx", a number previously returned + * by color_tag. Return the correct color number to use when highlighting. + * If there is no match, return 0 (color 0). + */ + +int +color_test(int tagidx, int value) + +{ + color_entry *ce; + + /* sanity check */ + if (tagidx < 0 || tagidx >= tagcnt || color_off) + { + return 0; + } + + ce = bytag[tagidx]; + + while (ce != NULL) + { + if ((!ce->min || ce->min <= value) && + (!ce->max || ce->max >= value)) + { + return ce->color; + } + ce = ce->tagnext; + } + + return 0; +} + +/* + * char *color_setstr(int color) + * + * Return ANSI string to set the terminal for color number "color". + */ + +char * +color_setstr(int color) + +{ + static char v[32]; + + v[0] = '\0'; + if (color >= 0 && color < num_color_ansi) + { + snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]); + } + return v; +} + +void +color_dump(FILE *f) + +{ + color_entry *ep; + int i; + int col; + int len; + + fputs("These color tags are available:", f); + col = 81; + for (i = 0; i < tagcnt; i++) + { + len = strlen(bytag_names[i]) + 1; + if (len + col > 79) + { + fputs("\n ", f); + col = 2; + } + fprintf(f, " %s", bytag_names[i]); + col += len; + } + + fputs("\n\nTop color settings:\n", f); + + for (i = 0; i < tagcnt; i++) + { + ep = bytag[i]; + while (ep != NULL) + { + fprintf(f, " %s (%d-", ep->tag, ep->min); + if (ep->max != 0) + { + fprintf(f, "%d", ep->max); + } + fprintf(f, "): ansi color %s, %sSample Text", + color_ansi[(int)ep->color], + color_setstr(ep->color)); + fprintf(f, "%s\n", color_setstr(0)); + ep = ep -> tagnext; + } + } +} + +void +color_debug(FILE *f) + +{ + color_entry *ep; + int i; + + fprintf(f, "color debug dump\n"); + ep = entries; + while (ep != NULL) + { + fprintf(f, "%s(%d,%d): slot %d, ansi %s, %sSample Text", + ep->tag, ep->min, ep->max, ep->color, color_ansi[(int)ep->color], + color_setstr(ep->color)); + fprintf(f, "%s\n", color_setstr(0)); + ep = ep -> next; + } + + fprintf(f, "\ntags:"); + for (i = 0; i < tagcnt; i++) + { + fprintf(f, " %s", bytag_names[i]); + } + fprintf(f, "\n"); +} + +int +color_activate(int i) + +{ + if (i == -1) + { + color_off = !color_off; + } + else + { + color_off = !i; + } + return color_off; +} diff --git a/contrib/top/color.h b/contrib/top/color.h new file mode 100644 index 0000000000..9a210a2959 --- /dev/null +++ b/contrib/top/color.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top - a top users display for Unix + * + * Definition of the color interface. + */ + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#define COLOR_ANSI_SLOTS 20 + +int color_env_parse(char *env); +int color_tag(char *tag); +int color_test(int tagidx, int value); +char *color_setstr(int color); +void color_dump(FILE *f); +int color_activate(int i); + + +/* + * These color tag names are currently in use + * (or reserved for future use): + * + * cpu, size, res, time, 1min, 5min, 15min, host + */ + +/* + * Valid ANSI values for colors are: + * + * 0 Reset all attributes + * 1 Bright + * 2 Dim + * 4 Underscore + * 5 Blink + * 7 Reverse + * 8 Hidden + * + * Foreground Colours + * 30 Black + * 31 Red + * 32 Green + * 33 Yellow + * 34 Blue + * 35 Magenta + * 36 Cyan + * 37 White + * + * Background Colours + * 40 Black + * 41 Red + * 42 Green + * 43 Yellow + * 44 Blue + * 45 Magenta + * 46 Cyan + * 47 White + */ + +#endif /*_COLOR_H_ */ diff --git a/contrib/top/commands.c b/contrib/top/commands.c new file mode 100644 index 0000000000..e93fb54646 --- /dev/null +++ b/contrib/top/commands.c @@ -0,0 +1,1034 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + * Version 3 + */ + +/* + * This file contains the routines that implement some of the interactive + * mode commands. Note that some of the commands are implemented in-line + * in "main". This is necessary because they change the global state of + * "top" (i.e.: changing the number of processes to display). + */ + +#include "os.h" +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_RESOURCE_H +#include +#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, ""); + } + else + { + buf[0] = c->ch; + buf[1] = '\0'; + } + + /* if the next command is the same, fold them onto one line */ + if ((c+1)->cmd_func == c->cmd_func) + { + strcat(buf, " or "); + p = buf + strlen(buf); + *p++ = (c+1)->ch; + *p = '\0'; + c++; + } + + display_pager("%-7s - %s\n", buf, help); + c++; + } + + display_pager("\nNot all commands are available on all systems.\n\n"); + display_pager("Available sort orders: %s\n", gstate->order_namelist); + display_pagerend(); + gstate->fulldraw = Yes; + return CMD_REFRESH; +} + +/* + * int command_process(globalstate *gstate, int cmd) + * + * Process the single-character command "cmd". The global state may + * be modified by the command to alter the output. Returns CMD_ERROR + * if there was a serious error that requires an immediate exit, CMD_OK + * to indicate success, CMD_REFRESH to indicate that the screen needs + * to be refreshed immediately, CMD_UNKNOWN when the command is not known, + * and CMD_NA when the command is not available. Error messages for + * CMD_NA and CMD_UNKNOWN must be handled by the caller. + */ + +int +command_process(globalstate *gstate, int cmd) + +{ + command *c; + + c = command_table; + while (c->cmd_func != NULL) + { + if (c->ch == cmd) + { + return (c->cmd_func)(gstate); + } + c++; + } + + return CMD_UNKNOWN; +} diff --git a/contrib/top/commands.h b/contrib/top/commands.h new file mode 100644 index 0000000000..cf98c8ddbe --- /dev/null +++ b/contrib/top/commands.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* call specifications for commands.c */ + +int command_process(globalstate *gstate, int cmd); + +/* returns from command routines */ +#define CMD_ERROR -1 +#define CMD_OK 0 +#define CMD_REFRESH 1 +#define CMD_UNKNOWN 2 +#define CMD_NA 3 diff --git a/contrib/top/display.c b/contrib/top/display.c new file mode 100644 index 0000000000..2330ca422e --- /dev/null +++ b/contrib/top/display.c @@ -0,0 +1,1937 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + * Version 3 + */ + +/* + * This file contains the routines that display information on the screen. + * Each section of the screen has two routines: one for initially writing + * all constant and dynamic text, and one for only updating the text that + * changes. The prefix "i_" is used on all the "initial" routines and the + * prefix "u_" is used for all the "updating" routines. + * + * ASSUMPTIONS: + * None of the "i_" routines use any of the termcap capabilities. + * In this way, those routines can be safely used on terminals that + * have minimal (or nonexistant) terminal capabilities. + * + * The routines should be called in this order: *_loadave, *_uptime, + * i_timeofday, *_procstates, *_cpustates, *_memory, *_swap, + * *_message, *_header, *_process, *_endscreen. + */ + +#include "os.h" +#include +#include +#include +#include +#include + +#include "top.h" +#include "machine.h" +#include "screen.h" /* interface to screen package */ +#include "layout.h" /* defines for screen position layout */ +#include "display.h" +#include "boolean.h" +#include "utils.h" + +#ifdef ENABLE_COLOR +#include "color.h" +#endif + +#define CURSOR_COST 8 + +#define MESSAGE_DISPLAY_TIME 5 + +/* imported from screen.c */ +extern int overstrike; + +static int lmpid = -1; +static int display_width = MAX_COLS; + +/* cursor positions of key points on the screen are maintained here */ +/* layout.h has static definitions, but we may change our minds on some + of the positions as we make decisions about what needs to be displayed */ + +static int x_lastpid = X_LASTPID; +static int y_lastpid = Y_LASTPID; +static int x_loadave = X_LOADAVE; +static int y_loadave = Y_LOADAVE; +static int x_minibar = X_MINIBAR; +static int y_minibar = Y_MINIBAR; +static int x_uptime = X_UPTIME; +static int y_uptime = Y_UPTIME; +static int x_procstate = X_PROCSTATE; +static int y_procstate = Y_PROCSTATE; +static int x_cpustates = X_CPUSTATES; +static int y_cpustates = Y_CPUSTATES; +static int x_kernel = X_KERNEL; +static int y_kernel = Y_KERNEL; +static int x_mem = X_MEM; +static int y_mem = Y_MEM; +static int x_swap = X_SWAP; +static int y_swap = Y_SWAP; +static int y_message = Y_MESSAGE; +static int x_header = X_HEADER; +static int y_header = Y_HEADER; +static int x_idlecursor = X_IDLECURSOR; +static int y_idlecursor = Y_IDLECURSOR; +static int y_procs = Y_PROCS; + +/* buffer and colormask that describes the content of the screen */ +/* these are singly dimensioned arrays -- the row boundaries are + determined on the fly. +*/ +static char *screenbuf = NULL; +static char *colorbuf = NULL; +static char scratchbuf[MAX_COLS]; +static int bufsize = 0; + +/* lineindex tells us where the beginning of a line is in the buffer */ +#define lineindex(l) ((l)*MAX_COLS) + +/* screen's cursor */ +static int curr_x, curr_y; +static int curr_color; + +/* virtual cursor */ +static int virt_x, virt_y; + +static char **procstate_names; +static char **cpustate_names; +static char **memory_names; +static char **swap_names; +static char **kernel_names; + +static int num_procstates; +static int num_cpustates; +static int num_memory; +static int num_swap; +static int num_kernel; + +static int *lprocstates; +static int *lcpustates; + +static int *cpustate_columns; +static int cpustate_total_length; + +static int header_status = Yes; + +/* pending messages are stored in a circular buffer, where message_first + is the next one to display, and message_last is the last one + in the buffer. Counters wrap around at MAX_MESSAGES. The buffer is + empty when message_first == message_last and full when + message_last + 1 == message_first. The pointer message_current holds + the message currently being displayed, or "" if there is none. +*/ +#define MAX_MESSAGES 16 +static char *message_buf[MAX_MESSAGES]; +static int message_first = 0; +static int message_last = 0; +static struct timeval message_time = {0, 0}; +static char *message_current = NULL; +static int message_length = 0; +static int message_hold = 1; +static int message_barrier = No; + +#ifdef ENABLE_COLOR +static int load_cidx[3]; +static int header_cidx; +static int *cpustate_cidx; +static int *memory_cidx; +static int *swap_cidx; +static int *kernel_cidx; +#else +#define memory_cidx NULL +#define swap_cidx NULL +#define kernel_cidx NULL +#endif + + +/* internal support routines */ + +/* + * static int string_count(char **pp) + * + * Pointer "pp" points to an array of string pointers, which is + * terminated by a NULL. Return the number of string pointers in + * this array. + */ + +static int +string_count(char **pp) + +{ + register int cnt = 0; + + if (pp != NULL) + { + while (*pp++ != NULL) + { + cnt++; + } + } + return(cnt); +} + +void +display_clear() + +{ + dprintf("display_clear\n"); + screen_clear(); + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); + curr_x = curr_y = 0; +} + +/* + * void display_move(int x, int y) + * + * Efficiently move the cursor to x, y. This assumes the cursor is + * currently located at curr_x, curr_y, and will only use cursor + * addressing when it is less expensive than overstriking what's + * already on the screen. + */ + +void +display_move(int x, int y) + +{ + char buff[128]; + char *p; + char *bufp; + char *colorp; + int cnt = 0; + int color = curr_color; + + dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y); + + /* are we in a position to do this without cursor addressing? */ + if (curr_y < y || (curr_y == y && curr_x <= x)) + { + /* start buffering up what it would take to move there by rewriting + what's on the screen */ + cnt = CURSOR_COST; + p = buff; + + /* one newline for every line */ + while (cnt > 0 && curr_y < y) + { +#ifdef ENABLE_COLOR + if (color != 0) + { + p = strcpyend(p, color_setstr(0)); + color = 0; + cnt -= 5; + } +#endif + *p++ = '\n'; + curr_y++; + curr_x = 0; + cnt--; + } + + /* write whats in the screenbuf */ + bufp = &screenbuf[lineindex(curr_y) + curr_x]; + colorp = &colorbuf[lineindex(curr_y) + curr_x]; + while (cnt > 0 && curr_x < x) + { +#ifdef ENABLE_COLOR + if (color != *colorp) + { + color = *colorp; + p = strcpyend(p, color_setstr(color)); + cnt -= 5; + } +#endif + if ((*p = *bufp) == '\0') + { + /* somwhere on screen we haven't been before */ + *p = *bufp = ' '; + } + p++; + bufp++; + colorp++; + curr_x++; + cnt--; + } + } + + /* move the cursor */ + if (cnt > 0) + { + /* screen rewrite is cheaper */ + *p = '\0'; + fputs(buff, stdout); + curr_color = color; + } + else + { + screen_move(x, y); + } + + /* update our position */ + curr_x = x; + curr_y = y; +} + +/* + * display_write(int x, int y, int newcolor, int eol, char *new) + * + * Optimized write to the display. This writes characters to the + * screen in a way that optimizes the number of characters actually + * sent, by comparing what is being written to what is already on + * the screen (according to screenbuf and colorbuf). The string to + * write is "new", the first character of "new" should appear at + * screen position x, y. If x is -1 then "new" begins wherever the + * cursor is currently positioned. The string is written with color + * "newcolor". If "eol" is true then the remainder of the line is + * cleared. It is expected that "new" will have no newlines and no + * escape sequences. + */ + +void +display_write(int x, int y, int newcolor, int eol, char *new) + +{ + char *bufp; + char *colorp; + int ch; + int diff; + + dprintf("display_write(%d, %d, %d, %d, \"%s\")\n", + x, y, newcolor, eol, new); + + /* dumb terminal handling here */ + if (!smart_terminal) + { + if (x != -1) + { + /* make sure we are on the right line */ + while (curr_y < y) + { + putchar('\n'); + curr_y++; + curr_x = 0; + } + + /* make sure we are on the right column */ + while (curr_x < x) + { + putchar(' '); + curr_x++; + } + } + + /* write */ + fputs(new, stdout); + curr_x += strlen(new); + + return; + } + + /* adjust for "here" */ + if (x == -1) + { + x = virt_x; + y = virt_y; + } + else + { + virt_x = x; + virt_y = y; + } + + /* a pointer to where we start */ + bufp = &screenbuf[lineindex(y) + x]; + colorp = &colorbuf[lineindex(y) + x]; + + /* main loop */ + while ((ch = *new++) != '\0') + { + /* if either character or color are different, an update is needed */ + /* but only when the screen is wide enough */ + if (x < display_width && (ch != *bufp || newcolor != *colorp)) + { + /* check cursor */ + if (y != curr_y || x != curr_x) + { + /* have to move the cursor */ + display_move(x, y); + } + + /* write character */ +#ifdef ENABLE_COLOR + if (curr_color != newcolor) + { + fputs(color_setstr(newcolor), stdout); + curr_color = newcolor; + } +#endif + putchar(ch); + *bufp = ch; + *colorp = curr_color; + curr_x++; + } + + /* move */ + x++; + virt_x++; + bufp++; + colorp++; + } + + /* eol handling */ + if (eol && *bufp != '\0') + { + dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp); + /* make sure we are color 0 */ +#ifdef ENABLE_COLOR + if (curr_color != 0) + { + fputs(color_setstr(0), stdout); + curr_color = 0; + } +#endif + + /* make sure we are at the end */ + if (x != curr_x || y != curr_y) + { + screen_move(x, y); + curr_x = x; + curr_y = y; + } + + /* clear to end */ + screen_cleareol(strlen(bufp)); + + /* clear out whats left of this line's buffer */ + diff = display_width - x; + if (diff > 0) + { + memzero(bufp, diff); + memzero(colorp, diff); + } + } +} + +void +display_fmt(int x, int y, int newcolor, int eol, char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + vsnprintf(scratchbuf, MAX_COLS, fmt, argp); + display_write(x, y, newcolor, eol, scratchbuf); +} + +void +display_cte() + +{ + int len; + int y; + char *p; + int need_clear = 0; + + /* is there anything out there that needs to be cleared? */ + p = &screenbuf[lineindex(virt_y) + virt_x]; + if (*p != '\0') + { + need_clear = 1; + } + else + { + /* this line is clear, what about the rest? */ + y = virt_y; + while (++y < screen_length) + { + if (screenbuf[lineindex(y)] != '\0') + { + need_clear = 1; + break; + } + } + } + + if (need_clear) + { + dprintf("display_cte: clearing\n"); + + /* we will need this later */ + len = lineindex(virt_y) + virt_x; + + /* move to x and y, then clear to end */ + display_move(virt_x, virt_y); + if (!screen_cte()) + { + /* screen has no clear to end, so do it by hand */ + p = &screenbuf[len]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + while (++virt_y < screen_length) + { + display_move(0, virt_y); + p = &screenbuf[lineindex(virt_y)]; + len = strlen(p); + if (len > 0) + { + screen_cleareol(len); + } + } + } + + /* clear the screenbuf */ + memzero(&screenbuf[len], bufsize - len); + memzero(&colorbuf[len], bufsize - len); + } +} + +static void +summary_format(int x, int y, int *numbers, char **names, int *cidx) + +{ + register int num; + register char *thisname; + register char *lastname = NULL; + register int color; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + +#ifdef ENABLE_COLOR + if (cidx != NULL) + { + /* choose a color */ + color = color_test(*cidx++, num); + } +#endif + + /* write this number if positive */ + if (num > 0) + { + display_write(x, y, color, 0, itoa(num)); + } + + /* defer writing this name */ + lastname = thisname; + + /* next iteration will not start at x, y */ + x = y = -1; + } + } + + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) + { + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } + } +} + +static void +summary_format_memory(int x, int y, long *numbers, char **names, int *cidx) + +{ + register long num; + register int color; + register char *thisname; + register char *lastname = NULL; + + /* format each number followed by its string */ + while ((thisname = *names++) != NULL) + { + /* get the number to format */ + num = *numbers++; + color = 0; + + /* display only non-zero numbers */ + if (num != 0) + { + /* write the previous name */ + if (lastname != NULL) + { + display_write(-1, -1, 0, 0, lastname); + } + + /* defer writing this name */ + lastname = thisname; + +#ifdef ENABLE_COLOR + /* choose a color */ + color = color_test(*cidx++, num); +#endif + + /* is this number in kilobytes? */ + if (thisname[0] == 'K') + { + display_write(x, y, color, 0, format_k(num)); + lastname++; + } + else + { + display_write(x, y, color, 0, itoa((int)num)); + } + + /* next iteration will not start at x, y */ + x = y = -1; + } + } + + /* if the last string has a separator on the end, it has to be + written with care */ + if (lastname != NULL) + { + if ((num = strlen(lastname)) > 1 && + lastname[num-2] == ',' && lastname[num-1] == ' ') + { + display_fmt(-1, -1, 0, 1, "%.*s", num-2, lastname); + } + else + { + display_write(-1, -1, 0, 1, lastname); + } + } +} + +/* + * int display_resize() + * + * Reallocate buffer space needed by the display package to accomodate + * a new screen size. Must be called whenever the screen's size has + * changed. Returns the number of lines available for displaying + * processes or -1 if there was a problem allocating space. + */ + +int +display_resize() + +{ + register int top_lines; + register int newsize; + + /* calculate the current dimensions */ + /* if operating in "dumb" mode, we only need one line */ + top_lines = smart_terminal ? screen_length : 1; + + /* we don't want more than MAX_COLS columns, since the machine-dependent + modules make static allocations based on MAX_COLS and we don't want + to run off the end of their buffers */ + display_width = screen_width; + if (display_width >= MAX_COLS) + { + display_width = MAX_COLS - 1; + } + + /* see how much space we need */ + newsize = top_lines * (MAX_COLS + 1); + + /* reallocate only if we need more than we already have */ + if (newsize > bufsize) + { + /* deallocate any previous buffer that may have been there */ + if (screenbuf != NULL) + { + free(screenbuf); + } + if (colorbuf != NULL) + { + free(colorbuf); + } + + /* allocate space for the screen and color buffers */ + bufsize = newsize; + screenbuf = (char *)calloc(bufsize, sizeof(char)); + colorbuf = (char *)calloc(bufsize, sizeof(char)); + if (screenbuf == NULL || colorbuf == NULL) + { + /* oops! */ + return(-1); + } + } + else + { + /* just clear them out */ + memzero(screenbuf, bufsize); + memzero(colorbuf, bufsize); + } + + /* adjust total lines on screen to lines available for procs */ + top_lines -= y_procs; + + /* return number of lines available */ + /* for dumb terminals, pretend like we can show any amount */ + return(smart_terminal ? top_lines : Largest); +} + +int +display_lines() + +{ + return(smart_terminal ? screen_length : Largest); +} + +int +display_columns() + +{ + return(display_width); +} + +/* + * int display_init(struct statics *statics) + * + * Initialize the display system based on information in the statics + * structure. Returns the number of lines available for displaying + * processes or -1 if there was an error. + */ + +int +display_init(struct statics *statics) + +{ + register int top_lines; + register char **pp; + register char *p; + register int *ip; + register int i; + + /* certain things may influence the screen layout, + so look at those first */ + + /* a kernel line shifts parts of the display down */ + kernel_names = statics->kernel_names; + if ((num_kernel = string_count(kernel_names)) > 0) + { + /* adjust screen placements */ + y_mem++; + y_swap++; + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* a swap line shifts parts of the display down one */ + swap_names = statics->swap_names; + if ((num_swap = string_count(swap_names)) > 0) + { + /* adjust screen placements */ + y_message++; + y_header++; + y_idlecursor++; + y_procs++; + } + + /* call resize to do the dirty work */ + top_lines = display_resize(); + + /* only do the rest if we need to */ + if (top_lines > -1) + { + /* save pointers and allocate space for names */ + procstate_names = statics->procstate_names; + num_procstates = string_count(procstate_names); + lprocstates = (int *)calloc(num_procstates, sizeof(int)); + + cpustate_names = statics->cpustate_names; + num_cpustates = string_count(cpustate_names); + lcpustates = (int *)calloc(num_cpustates, sizeof(int)); + cpustate_columns = (int *)calloc(num_cpustates, sizeof(int)); + memory_names = statics->memory_names; + num_memory = string_count(memory_names); + + /* calculate starting columns where needed */ + cpustate_total_length = 0; + pp = cpustate_names; + ip = cpustate_columns; + while (*pp != NULL) + { + *ip++ = cpustate_total_length; + if ((i = strlen(*pp++)) > 0) + { + cpustate_total_length += i + 8; + } + } + } + +#ifdef ENABLE_COLOR + /* set up color tags for loadavg */ + load_cidx[0] = color_tag("1min"); + load_cidx[1] = color_tag("5min"); + load_cidx[2] = color_tag("15min"); + + /* find header color */ + header_cidx = color_tag("header"); + + /* color tags for cpu states */ + cpustate_cidx = (int *)malloc(num_cpustates * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "cpu."); + while (i < num_cpustates) + { + strcpy(p, cpustate_names[i]); + cpustate_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for kernel */ + if (num_kernel > 0) + { + kernel_cidx = (int *)malloc(num_kernel * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "kernel."); + while (i < num_kernel) + { + strcpy(p, homogenize(kernel_names[i]+1)); + kernel_cidx[i++] = color_tag(scratchbuf); + } + } + + /* color tags for memory */ + memory_cidx = (int *)malloc(num_memory * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "memory."); + while (i < num_memory) + { + strcpy(p, homogenize(memory_names[i]+1)); + memory_cidx[i++] = color_tag(scratchbuf); + } + + /* color tags for swap */ + if (num_swap > 0) + { + swap_cidx = (int *)malloc(num_swap * sizeof(int)); + i = 0; + p = strcpyend(scratchbuf, "swap."); + while (i < num_swap) + { + strcpy(p, homogenize(swap_names[i]+1)); + swap_cidx[i++] = color_tag(scratchbuf); + } + } +#endif + + /* return number of lines available (or error) */ + return(top_lines); +} + +static void +pr_loadavg(double avg, int i) + +{ + int color = 0; + +#ifdef ENABLE_COLOR + color = color_test(load_cidx[i], (int)(avg * 100)); +#endif + display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0, + avg < 10.0 ? " %5.2f" : " %5.1f", avg); + display_write(-1, -1, 0, 0, (i < 2 ? "," : ";")); +} + +void +i_loadave(int mpid, double *avenrun) + +{ + register int i; + + /* mpid == -1 implies this system doesn't have an _mpid */ + if (mpid != -1) + { + display_fmt(0, 0, 0, 0, + "last pid: %5d; load avg:", mpid); + x_loadave = X_LOADAVE; + } + else + { + display_write(0, 0, 0, 0, "load averages:"); + x_loadave = X_LOADAVE - X_LASTPIDWIDTH; + } + for (i = 0; i < 3; i++) + { + pr_loadavg(avenrun[i], i); + } + + lmpid = mpid; +} + +void +u_loadave(int mpid, double *avenrun) + +{ + register int i; + + if (mpid != -1) + { + /* change screen only when value has really changed */ + if (mpid != lmpid) + { + display_fmt(x_lastpid, y_lastpid, 0, 0, + "%5d", mpid); + lmpid = mpid; + } + } + + /* display new load averages */ + for (i = 0; i < 3; i++) + { + pr_loadavg(avenrun[i], i); + } +} + +static char minibar_buffer[64]; +#define MINIBAR_WIDTH 20 + +void +i_minibar(int (*formatter)(char *, int)) +{ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); + + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +void +u_minibar(int (*formatter)(char *, int)) +{ + (void)((*formatter)(minibar_buffer, MINIBAR_WIDTH)); + + display_write(x_minibar, y_minibar, 0, 0, minibar_buffer); +} + +static int uptime_days; +static int uptime_hours; +static int uptime_mins; +static int uptime_secs; + +void +i_uptime(time_t *bt, time_t *tod) + +{ + time_t uptime; + + if (*bt != -1) + { + uptime = *tod - *bt; + uptime += 30; + uptime_days = uptime / 86400; + uptime %= 86400; + uptime_hours = uptime / 3600; + uptime %= 3600; + uptime_mins = uptime / 60; + uptime_secs = uptime % 60; + + /* + * Display the uptime. + */ + + display_fmt(x_uptime, y_uptime, 0, 0, + " up %d+%02d:%02d:%02d", + uptime_days, uptime_hours, uptime_mins, uptime_secs); + } +} + +void +u_uptime(time_t *bt, time_t *tod) + +{ + i_uptime(bt, tod); +} + + +void +i_timeofday(time_t *tod) + +{ + /* + * Display the current time. + * "ctime" always returns a string that looks like this: + * + * Sun Sep 16 01:03:52 1973 + * 012345678901234567890123 + * 1 2 + * + * We want indices 11 thru 18 (length 8). + */ + + int x; + + /* where on the screen do we start? */ + x = (smart_terminal ? screen_width : 79) - 8; + + /* but don't bump in to uptime */ + if (x < x_uptime + 19) + { + x = x_uptime + 19; + } + + /* display it */ + display_fmt(x, 0, 0, 1, "%-8.8s", &(ctime(tod)[11])); +} + +static int ltotal = 0; +static int lthreads = 0; + +/* + * *_procstates(total, brkdn, names) - print the process summary line + */ + + +void +i_procstates(int total, int *brkdn, int threads) + +{ + /* write current number of processes and remember the value */ + display_fmt(0, y_procstate, 0, 0, + "%d %s: ", total, threads ? "threads" : "processes"); + ltotal = total; + + /* remember where the summary starts */ + x_procstate = virt_x; + + if (total > 0) + { + /* format and print the process state summary */ + summary_format(-1, -1, brkdn, procstate_names, NULL); + + /* save the numbers for next time */ + memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); + lthreads = threads; + } +} + +void +u_procstates(int total, int *brkdn, int threads) + +{ + /* if threads state has changed, do a full update */ + if (lthreads != threads) + { + i_procstates(total, brkdn, threads); + return; + } + + /* update number of processes only if it has changed */ + if (ltotal != total) + { + display_fmt(0, y_procstate, 0, 0, + "%d", total); + + /* if number of digits differs, rewrite the label */ + if (digits(total) != digits(ltotal)) + { + display_fmt(-1, -1, 0, 0, " %s: ", threads ? "threads" : "processes"); + x_procstate = virt_x; + } + + /* save new total */ + ltotal = total; + } + + /* see if any of the state numbers has changed */ + if (total > 0 && memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0) + { + /* format and update the line */ + summary_format(x_procstate, y_procstate, brkdn, procstate_names, NULL); + memcpy(lprocstates, brkdn, num_procstates * sizeof(int)); + } +} + +/* + * *_cpustates(states, names) - print the cpu state percentages + */ + +/* cpustates_tag() calculates the correct tag to use to label the line */ + +char * +cpustates_tag() + +{ + register char *use; + + static char *short_tag = "CPU: "; + static char *long_tag = "CPU states: "; + + /* if length + strlen(long_tag) >= screen_width, then we have to + use the shorter tag (we subtract 2 to account for ": ") */ + if (cpustate_total_length + (int)strlen(long_tag) - 2 >= screen_width) + { + use = short_tag; + } + else + { + use = long_tag; + } + + /* set x_cpustates accordingly then return result */ + x_cpustates = strlen(use); + return(use); +} + +void +i_cpustates(int *states) + +{ + int value; + char **names; + char *thisname; + int *colp; + int color = 0; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif + + /* initialize */ + names = cpustate_names; + colp = cpustate_columns; + + /* print tag */ + display_write(0, y_cpustates, 0, 0, cpustates_tag()); + + /* now walk thru the names and print the line */ + while ((thisname = *names++) != NULL) + { + if (*thisname != '\0') + { + /* retrieve the value and remember it */ + value = *states; + +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx++, value/10); +#endif + + /* if percentage is >= 1000, print it as 100% */ + display_fmt(x_cpustates + *colp, y_cpustates, + color, 0, + (value >= 1000 ? "%4.0f%% %s%s" : "%4.1f%% %s%s"), + ((float)value)/10., + thisname, + *names != NULL ? ", " : ""); + + } + /* increment */ + colp++; + states++; + } + + /* copy over values into "last" array */ + memcpy(lcpustates, states, num_cpustates * sizeof(int)); +} + +void +u_cpustates(int *states) + +{ + int value; + char **names = cpustate_names; + char *thisname; + int *lp; + int *colp; + int color = 0; +#ifdef ENABLE_COLOR + int *cidx = cpustate_cidx; +#endif + + lp = lcpustates; + colp = cpustate_columns; + + /* we could be much more optimal about this */ + while ((thisname = *names++) != NULL) + { + if (*thisname != '\0') + { + /* did the value change since last time? */ + if (*lp != *states) + { + /* yes, change it */ + /* retrieve value and remember it */ + value = *states; + +#ifdef ENABLE_COLOR + /* determine color number to use */ + color = color_test(*cidx, value/10); +#endif + + /* if percentage is >= 1000, print it as 100% */ + display_fmt(x_cpustates + *colp, y_cpustates, color, 0, + (value >= 1000 ? "%4.0f" : "%4.1f"), + ((double)value)/10.); + + /* remember it for next time */ + *lp = value; + } +#ifdef ENABLE_COLOR + cidx++; +#endif + } + + /* increment and move on */ + lp++; + states++; + colp++; + } +} + +void +z_cpustates() + +{ + register int i = 0; + register char **names = cpustate_names; + register char *thisname; + register int *lp; + + /* print tag */ + display_write(0, y_cpustates, 0, 0, cpustates_tag()); + + while ((thisname = *names++) != NULL) + { + if (*thisname != '\0') + { + display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ", + thisname); + } + } + + /* fill the "last" array with all -1s, to insure correct updating */ + lp = lcpustates; + i = num_cpustates; + while (--i >= 0) + { + *lp++ = -1; + } +} + +/* + * *_kernel(stats) - print "Kernel: " followed by the kernel summary string + * + * Assumptions: cursor is on "lastline", the previous line + */ + +void +i_kernel(int *stats) + +{ + if (num_kernel > 0) + { + display_write(0, y_kernel, 0, 0, "Kernel: "); + + /* format and print the kernel summary */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } +} + +void +u_kernel(int *stats) + +{ + if (num_kernel > 0) + { + /* format the new line */ + summary_format(x_kernel, y_kernel, stats, kernel_names, kernel_cidx); + } +} + +/* + * *_memory(stats) - print "Memory: " followed by the memory summary string + * + * Assumptions: cursor is on "lastline", the previous line + */ + +void +i_memory(long *stats) + +{ + display_write(0, y_mem, 0, 0, "Memory: "); + + /* format and print the memory summary */ + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); +} + +void +u_memory(long *stats) + +{ + /* format the new line */ + summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx); +} + +/* + * *_swap(stats) - print "Swap: " followed by the swap summary string + * + * Assumptions: cursor is on "lastline", the previous line + * + * These functions only print something when num_swap > 0 + */ + +void +i_swap(long *stats) + +{ + if (num_swap > 0) + { + /* print the tag */ + display_write(0, y_swap, 0, 0, "Swap: "); + + /* format and print the swap summary */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } +} + +void +u_swap(long *stats) + +{ + if (num_swap > 0) + { + /* format the new line */ + summary_format_memory(x_swap, y_swap, stats, swap_names, swap_cidx); + } +} + +/* + * *_message() - print the next pending message line, or erase the one + * that is there. + * + * Note that u_message is (currently) the same as i_message. + * + * Assumptions: lastline is consistent + */ + +/* + * i_message is funny because it gets its message asynchronously (with + * respect to screen updates). Messages are taken out of the + * circular message_buf and displayed one at a time. + */ + +void +i_message(struct timeval *now) + +{ + struct timeval my_now; + int i = 0; + + dprintf("i_message(%08x)\n", now); + + /* if now is NULL we have to get it ourselves */ + if (now == NULL) + { + time_get(&my_now); + now = &my_now; + } + + /* now that we have been called, messages no longer need to be held */ + message_hold = 0; + + dprintf("i_message: now %d, message_time %d\n", + now->tv_sec, message_time.tv_sec); + + if (smart_terminal) + { + /* is it time to change the message? */ + if (timercmp(now, &message_time, > )) + { + /* yes, free the current message */ + dprintf("i_message: timer expired\n"); + if (message_current != NULL) + { + free(message_current); + message_current = NULL; + } + + /* is there a new message to be displayed? */ + if (message_first != message_last) + { + /* move index to next message */ + if (++message_first == MAX_MESSAGES) message_first = 0; + + /* make the next message the current one */ + message_current = message_buf[message_first]; + + /* show it */ + dprintf("i_message: showing \"%s\"\n", message_current); + display_move(0, y_message); + screen_standout(message_current); + i = strlen(message_current); + + /* set the expiration timer */ + message_time = *now; + message_time.tv_sec += MESSAGE_DISPLAY_TIME; + + /* clear the rest of the line */ + screen_cleareol(message_length - i); + putchar('\r'); + message_length = i; + } + else + { + /* just clear what was there before, if anything */ + if (message_length > 0) + { + display_move(0, y_message); + screen_cleareol(message_length); + putchar('\r'); + message_length = 0; + } + } + } + } +} + +void +u_message(struct timeval *now) + +{ + i_message(now); +} + +static int header_length; + +/* + * *_header(text) - print the header for the process area + * + * Assumptions: cursor is on the previous line and lastline is consistent + */ + +void +i_header(char *text) + +{ + int header_color = 0; + +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif + header_length = strlen(text); + if (header_status) + { + display_write(x_header, y_header, header_color, 1, text); + } +} + +/*ARGSUSED*/ +void +u_header(char *text) + +{ + int header_color = 0; + +#ifdef ENABLE_COLOR + header_color = color_test(header_cidx, 0); +#endif + display_write(x_header, y_header, header_color, 1, + header_status ? text : ""); +} + +/* + * *_process(line, thisline) - print one process line + * + * Assumptions: lastline is consistent + */ + +void +i_process(int line, char *thisline) + +{ + /* truncate the line to conform to our current screen width */ + thisline[display_width] = '\0'; + + /* write the line out */ + display_write(0, y_procs + line, 0, 1, thisline); +} + +void +u_process(int line, char *new_line) + +{ + i_process(line, new_line); +} + +void +i_endscreen() + +{ + if (smart_terminal) + { + /* move the cursor to a pleasant place */ + display_move(x_idlecursor, y_idlecursor); + } + else + { + /* separate this display from the next with some vertical room */ + fputs("\n\n", stdout); + } + fflush(stdout); +} + +void +u_endscreen() + +{ + if (smart_terminal) + { + /* clear-to-end the display */ + display_cte(); + + /* move the cursor to a pleasant place */ + display_move(x_idlecursor, y_idlecursor); + fflush(stdout); + } + else + { + /* separate this display from the next with some vertical room */ + fputs("\n\n", stdout); + } +} + +void +display_header(int t) + +{ + header_status = t != 0; +} + +void +message_mark() + +{ + message_barrier = Yes; +} + +void +message_expire() + +{ + message_time.tv_sec = 0; + message_time.tv_usec = 0; +} + +void +message_flush() + +{ + message_first = message_last; + message_time.tv_sec = 0; + message_time.tv_usec = 0; +} + +/* + * void new_message_v(char *msgfmt, va_list ap) + * + * Display a message in the message area. This function takes a va_list for + * the arguments. Safe to call before display_init. This function only + * queues a message for display, and allowed for multiple messages to be + * queued. The i_message function drains the queue and actually writes the + * messages on the display. + */ + + +void +new_message_v(char *msgfmt, va_list ap) + +{ + int i; + int empty; + char msg[MAX_COLS]; + + /* if message_barrier is active, remove all pending messages */ + if (message_barrier) + { + message_flush(); + message_barrier = No; + } + + /* first, format the message */ + (void) vsnprintf(msg, sizeof(msg), msgfmt, ap); + + /* where in the buffer will it go? */ + i = message_last + 1; + if (i >= MAX_MESSAGES) i = 0; + + /* make sure the buffer is not full */ + if (i != message_first) + { + /* insert it in to message_buf */ + message_buf[i] = strdup(msg); + dprintf("new_message_v: new message inserted in slot %d\n", i); + + /* remember if the buffer is empty and set the index */ + empty = message_last == message_first; + message_last = i; + + /* is message_buf otherwise empty and have we started displaying? */ + if (empty && !message_hold) + { + /* we can display the message now */ + i_message(NULL); + } + } +} + +/* + * void new_message(int type, char *msgfmt, ...) + * + * Display a message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. + */ + +void +new_message(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + va_end(ap); +} + +/* + * void message_error(char *msgfmt, ...) + * + * Put an error message in the message area. It is safe to call this function + * before display_init. Messages logged before the display is drawn will be + * held and displayed later. + */ + +void +message_error(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + new_message_v(msgfmt, ap); + fflush(stdout); + va_end(ap); +} + +/* + * void message_clear() + * + * Clear message area and flush all pending messages. + */ + +void +message_clear() + +{ + /* remove any existing message */ + if (message_current != NULL) + { + display_move(0, y_message); + screen_cleareol(message_length); + free(message_current); + message_current = 0; + } + + /* flush all pending messages */ + message_flush(); +} + +/* + * void message_prompt_v(int so, char *msgfmt, va_list ap) + * + * Place a prompt in the message area. A prompt is different from a + * message as follows: it is displayed immediately, overwriting any + * message that may already be there, it may be highlighted in standout + * mode (if "so" is true), the cursor is left to rest at the end of the + * prompt. This call causes all pending messages to be flushed. + */ + +void +message_prompt_v(int so, char *msgfmt, va_list ap) + +{ + char msg[MAX_COLS]; + int i; + + /* clear out the message buffer */ + message_flush(); + + /* format the message */ + i = vsnprintf(msg, sizeof(msg), msgfmt, ap); + + /* this goes over any existing message */ + display_move(0, y_message); + + /* clear the entire line */ + screen_cleareol(message_length); + + /* show the prompt */ + if (so) + { + screen_standout(msg); + } + else + { + fputs(msg, stdout); + } + + /* make it all visible */ + fflush(stdout); + + /* even though we dont keep a copy of the prompt, track its length */ + message_length = i < MAX_COLS ? i : MAX_COLS; +} + +/* + * void message_prompt(char *msgfmt, ...) + * + * Place a prompt in the message area (see message_prompt_v). + */ + +void +message_prompt(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + message_prompt_v(Yes, msgfmt, ap); + va_end(ap); +} + +void +message_prompt_plain(char *msgfmt, ...) + +{ + va_list ap; + + va_start(ap, msgfmt); + message_prompt_v(No, msgfmt, ap); + va_end(ap); +} + +/* + * int readline(char *buffer, int size, int numeric) + * + * Read a line of input from the terminal. The line is placed in + * "buffer" not to exceed "size". If "numeric" is true then the input + * can only consist of digits. This routine handles all character + * editing while keeping the terminal in cbreak mode. If "numeric" + * is true then the number entered is returned. Otherwise the number + * of character read in to "buffer" is returned. + */ + +int +readline(char *buffer, int size, int numeric) + +{ + register char *ptr = buffer; + register char ch; + register char cnt = 0; + + /* allow room for null terminator */ + size -= 1; + + /* read loop */ + while ((fflush(stdout), read(0, ptr, 1) > 0)) + { + /* newline or return means we are done */ + if ((ch = *ptr) == '\n' || ch == '\r') + { + break; + } + + /* handle special editing characters */ + if (ch == ch_kill) + { + /* return null string */ + *buffer = '\0'; + putchar('\r'); + return(-1); + } + else if (ch == ch_werase) + { + /* erase previous word */ + if (cnt <= 0) + { + /* none to erase! */ + putchar('\7'); + } + else + { + /* + * First: remove all spaces till the first-non-space + * Second: remove all non-spaces till the first-space + */ + while(cnt > 0 && ptr[-1] == ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + while(cnt > 0 && ptr[-1] != ' ') + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + } + } + else if (ch == ch_erase) + { + /* erase previous character */ + if (cnt <= 0) + { + /* none to erase! */ + putchar('\7'); + } + else + { + fputs("\b \b", stdout); + ptr--; + cnt--; + } + } + /* check for character validity and buffer overflow */ + else if (cnt == size || (numeric && !isdigit((int)ch)) || + !isprint((int)ch)) + { + /* not legal */ + putchar('\7'); + } + else + { + /* echo it and store it in the buffer */ + putchar(ch); + ptr++; + cnt++; + } + } + + /* all done -- null terminate the string */ + *ptr = '\0'; + + /* add response length to message_length */ + message_length += cnt; + + /* return either inputted number or string length */ + putchar('\r'); + return(cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt); +} + +void +display_pagerstart() + +{ + display_clear(); +} + +void +display_pagerend() + +{ + char ch; + + screen_standout("Hit any key to continue: "); + fflush(stdout); + (void) read(0, &ch, 1); +} + +void +display_pager(char *fmt, ...) + +{ + va_list ap; + + int ch; + char readch; + char buffer[MAX_COLS]; + char *data; + + /* format into buffer */ + va_start(ap, fmt); + (void) vsnprintf(buffer, MAX_COLS, fmt, ap); + va_end(ap); + data = buffer; + + while ((ch = *data++) != '\0') + { + putchar(ch); + if (ch == '\n') + { + if (++curr_y >= screen_length - 1) + { + screen_standout("...More..."); + fflush(stdout); + (void) read(0, &readch, 1); + putchar('\r'); + switch(readch) + { + case '\r': + case '\n': + curr_y--; + break; + + case 'q': + return; + + default: + curr_y = 0; + } + } + } + } +} diff --git a/contrib/top/display.h b/contrib/top/display.h new file mode 100644 index 0000000000..a44a7bb41e --- /dev/null +++ b/contrib/top/display.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* interface declaration for display.c */ + +#ifndef _DISPLAY_H +#define _DISPLAY_H + +#include "globalstate.h" + +void display_clear(); +int display_resize(); +int display_lines(); +int display_columns(); +int display_init(struct statics *statics); +void i_loadave(int mpid, double *avenrun); +void u_loadave(int mpid, double *avenrun); +void i_minibar(int (*formatter)(char *, int)); +void u_minibar(int (*formatter)(char *, int)); +void i_uptime(time_t *bt, time_t *tod); +void u_uptime(time_t *bt, time_t *tod); +void i_timeofday(time_t *tod); +void i_procstates(int total, int *brkdn, int threads); +void u_procstates(int total, int *brkdn, int threads); +void i_cpustates(int *states); +void u_cpustates(int *states); +void z_cpustates(); +void i_kernel(int *stats); +void u_kernel(int *stats); +void i_memory(long *stats); +void u_memory(long *stats); +void i_swap(long *stats); +void u_swap(long *stats); +void i_message(struct timeval *now); +void u_message(struct timeval *now); +void i_header(char *text); +void u_header(char *text); +void i_process(int line, char *thisline); +void u_process(int, char *); +void i_endscreen(); +void u_endscreen(); +void display_header(int t); +void new_message(char *msgfmt, ...); +void message_error(char *msgfmt, ...); +void message_mark(); +void message_clear(); +void message_expire(); +void message_prompt(char *msgfmt, ...); +void message_prompt_plain(char *msgfmt, ...); +int readline(char *buffer, int size, int numeric); +void display_pagerstart(); +void display_pagerend(); +void display_pager(char *fmt, ...); + +#endif diff --git a/contrib/top/getopt.c b/contrib/top/getopt.c new file mode 100644 index 0000000000..9a4af2f4c1 --- /dev/null +++ b/contrib/top/getopt.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * "getopt" routine customized for top. + */ + +/* + * Many modern-day Unix implementations already have this function + * in libc. The standard "getopt" is perfectly sufficient for top's + * needs. If such a function exists in libc then you certainly don't + * need to compile this one in. To prevent this function from being + * compiled, define "HAVE_GETOPT". This is usually done in the "CFLAGS" + * line of the corresponding machine module. + */ + +/* + * This empty declaration exists solely to placate overexhuberant C + * compilers that like to warn you about content-free files. + */ +static void __empty(); + +#ifndef HAVE_GETOPT + +/*LINTLIBRARY*/ + +#include "os.h" +#ifndef NULL +#define NULL 0 +#endif +#ifndef EOF +#define EOF (-1) +#endif +#define ERR(s, c) if(opterr){\ + extern int write();\ + char errbuf[2];\ + errbuf[0] = c; errbuf[1] = '\n';\ + (void) write(2, argv[0], strlen(argv[0]));\ + (void) write(2, s, strlen(s));\ + (void) write(2, errbuf, 2);} + + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(int argc, char **argv, char *opts) + +{ + static int sp = 1; + register int c; + register char *cp; + + if(sp == 1) + if(optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + else if(strcmp(argv[optind], "--") == 0) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR(": unknown option, -", c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + if(argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + ERR(": argument missing for -", c); + sp = 1; + return('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} +#endif /* HAVE_GETOPT */ diff --git a/contrib/top/globalstate.h b/contrib/top/globalstate.h new file mode 100644 index 0000000000..fd1d21dea1 --- /dev/null +++ b/contrib/top/globalstate.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * The global state of top is described in this structure. It is passed + * to routines that may need to examine or alter it. + */ + +#ifndef _GLOBALSTATE_H_ +#define _GLOBALSTATE_H_ + +#include "machine.h" + +typedef struct globalstate { + struct timeval now; + struct timeval refresh; + int fulldraw; + int topn; + int max_topn; + int delay; + int displays; + int order_index; + int show_cpustates; + int show_tags; + int show_usernames; + int use_color; + int smart_terminal; + int interactive; + char *header_text; + char *order_name; /* only used before call to machine_init */ + char *order_namelist; + char *(*get_userid)(int); + struct process_select pselect; + struct statics *statics; +} globalstate; + +#endif /* _GLOBALSTATE_H_ */ diff --git a/contrib/top/hash.c b/contrib/top/hash.c new file mode 100644 index 0000000000..f65fe92105 --- /dev/null +++ b/contrib/top/hash.c @@ -0,0 +1,2001 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4c */ + +/* The file hash.c is generated from hash.m4c via the preprocessor M4 */ + +/* + * Hash table functions: init, add, lookup, first, next, sizeinfo. + * This is a conventional "bucket hash". The key is hashed in to a number + * less than or equal to the number of buckets and the result is used + * to index in to the array of buckets. Each bucket is a linked list + * that contains all the key/value pairs which hashed to that index. + */ + +#include "config.h" + +#if STDC_HEADERS +#include +#include +#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 +#else +#ifdef HAVE_STRING_H +#include +#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 +#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; jnum_buckets); +} + +void ll_init(llist *q) + +{ + q->head = NULL; + q->count = 0; +} + +llistitem *ll_newitem(int size) + +{ + llistitem *qi; + + qi = (llistitem *)malloc(sizeof(llistitem) + size); + qi->datum = ((void *)qi + sizeof(llistitem)); + return qi; +} + +void ll_freeitem(llistitem *li) + +{ + free(li); +} + +void ll_add(llist *q, llistitem *new) + +{ + new->next = q->head; + q->head = new; + q->count++; +} + +void ll_extract(llist *q, llistitem *qi, llistitem *last) + +{ + if (last == NULL) + { + q->head = qi->next; + } + else + { + last->next = qi->next; + } + qi->next = NULL; + q->count--; +} + +#define LL_FIRST(q) ((q)->head) +llistitem * +ll_first(llist *q) + +{ + return q->head; +} + +#define LL_NEXT(q, qi) ((qi) != NULL ? (qi)->next : NULL) +llistitem * +ll_next(llist *q, llistitem *qi) + +{ + return (qi != NULL ? qi->next : NULL); +} + +#define LL_ISEMPTY(ll) ((ll)->count == 0) +int +ll_isempty(llist *ll) + +{ + return (ll->count == 0); +} + +/* + * hash_table *hash_create(int num) + * + * Creates a hash table structure with at least "num" buckets. + */ + +hash_table * +hash_create(int num) + +{ + hash_table *result; + bucket_t *b; + int bytes; + int i; + + /* create the resultant structure */ + result = (hash_table *)malloc(sizeof(hash_table)); + + /* adjust bucket count to be prime */ + num = next_prime(num); + + /* create the buckets */ + bytes = sizeof(bucket_t) * num; + result->buckets = b = (bucket_t *)malloc(bytes); + result->num_buckets = num; + + /* create each bucket as a linked list */ + i = num; + while (--i >= 0) + { + ll_init(&(b->list)); + b++; + } + + return result; +} + +/* + * unsigned int hash_count(hash_table *ht) + * + * Return total number of elements contained in hash table. + */ + +unsigned int +hash_count(hash_table *ht) + +{ + register int i = 0; + register int cnt = 0; + register bucket_t *bucket; + + bucket = ht->buckets; + while (i++ < ht->num_buckets) + { + cnt += bucket->list.count; + bucket++; + } + + return cnt; +} + +/* + * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + * + * Fill in "sizes" with information about bucket lengths in hash + * table "max". + */ + +void +hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + +{ + register int i; + register int idx; + register bucket_t *bucket; + + memzero(sizes, max * sizeof(unsigned int)); + + bucket = ht->buckets; + i = 0; + while (i++ < ht->num_buckets) + { + idx = bucket->list.count; + sizes[idx >= max ? max-1 : idx]++; + bucket++; + } +} + + + + + + + +/* + * void hash_add_uint(hash_table *ht, unsigned int key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is unsigned int + */ + +void * +hash_add_uint(hash_table *ht, unsigned int key, void *value) + +{ + bucket_t *bucket; + hash_item_uint *hi; + hash_item_uint *h; + llist *ll; + llistitem *li; + llistitem *newli; + unsigned int k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_uint)); + hi = (hash_item_uint *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_uint *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_uint *)(li->datum))->value; + } +} + +/* + * void *hash_replace_uint(hash_table *ht, unsigned int key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is unsigned int + */ + +void * +hash_replace_uint(hash_table *ht, unsigned int key, void *value) + +{ + bucket_t *bucket; + hash_item_uint *hi; + llist *ll; + llistitem *li; + void *result = NULL; + unsigned int k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_uint *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_uint)); + hi = (hash_item_uint *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_uint(hash_table *ht, unsigned int key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is unsigned int + */ + +void * +hash_lookup_uint(hash_table *ht, unsigned int key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_uint *h; + void *result; + unsigned int k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_uint *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_uint(hash_table *ht, unsigned int key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_uint(hash_table *ht, unsigned int key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_uint *hi; + void *result; + unsigned int k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_uint *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_uint * +hash_first_uint(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_uint *)pos->ll_item->datum; +} + + +/* + * hash_item_uint *hash_next_uint(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_uint * +hash_next_uint(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_uint *)li->datum; +} + +/* + * void *hash_remove_pos_uint(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_uint(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_uint *hi; + unsigned int key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_uint *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_pid(hash_table *ht, pid_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is pid_t + */ + +void * +hash_add_pid(hash_table *ht, pid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pid *hi; + hash_item_pid *h; + llist *ll; + llistitem *li; + llistitem *newli; + pid_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_pid)); + hi = (hash_item_pid *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pid *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_pid *)(li->datum))->value; + } +} + +/* + * void *hash_replace_pid(hash_table *ht, pid_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is pid_t + */ + +void * +hash_replace_pid(hash_table *ht, pid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pid *hi; + llist *ll; + llistitem *li; + void *result = NULL; + pid_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_pid *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_pid)); + hi = (hash_item_pid *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_pid(hash_table *ht, pid_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is pid_t + */ + +void * +hash_lookup_pid(hash_table *ht, pid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_pid *h; + void *result; + pid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pid *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_pid(hash_table *ht, pid_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_pid(hash_table *ht, pid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_pid *hi; + void *result; + pid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_pid *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_pid * +hash_first_pid(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_pid *)pos->ll_item->datum; +} + + +/* + * hash_item_pid *hash_next_pid(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_pid * +hash_next_pid(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_pid *)li->datum; +} + +/* + * void *hash_remove_pos_pid(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_pid(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_pid *hi; + pid_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_pid *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_string(hash_table *ht, char * key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is char * + */ + +void * +hash_add_string(hash_table *ht, char * key, void *value) + +{ + bucket_t *bucket; + hash_item_string *hi; + hash_item_string *h; + llist *ll; + llistitem *li; + llistitem *newli; + char * k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_string)); + hi = (hash_item_string *)newli->datum; + + /* fill in the values */ + hi->key = strdup(key); + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[string_hash(ht, key)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_string *)li->datum; + k1 = h->key; + if (strcmp(key, k1) == 0) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_string *)(li->datum))->value; + } +} + +/* + * void *hash_replace_string(hash_table *ht, char * key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is char * + */ + +void * +hash_replace_string(hash_table *ht, char * key, void *value) + +{ + bucket_t *bucket; + hash_item_string *hi; + llist *ll; + llistitem *li; + void *result = NULL; + char * k1; + + /* find the bucket */ + bucket = &(ht->buckets[string_hash(ht, key)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_string *)li->datum; + k1 = hi->key; + if (strcmp(key, k1) == 0) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_string)); + hi = (hash_item_string *)li->datum; + hi->key = strdup(key); + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_string(hash_table *ht, char * key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is char * + */ + +void * +hash_lookup_string(hash_table *ht, char * key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_string *h; + void *result; + char * k1; + + result = NULL; + if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_string *)li->datum; + k1 = h->key; + if (strcmp(key, k1) == 0) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_string(hash_table *ht, char * key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_string(hash_table *ht, char * key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_string *hi; + void *result; + char * k1; + + result = NULL; + if ((bucket = &(ht->buckets[string_hash(ht, key)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_string *)li->datum; + k1 = hi->key; + if (strcmp(key, k1) == 0) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + free(key); + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_string * +hash_first_string(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_string *)pos->ll_item->datum; +} + + +/* + * hash_item_string *hash_next_string(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_string * +hash_next_string(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_string *)li->datum; +} + +/* + * void *hash_remove_pos_string(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_string(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_string *hi; + char * key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_string *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + free(key); + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + + + +/* + * void hash_add_pidthr(hash_table *ht, pidthr_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is pidthr_t + */ + +void * +hash_add_pidthr(hash_table *ht, pidthr_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pidthr *hi; + hash_item_pidthr *h; + llist *ll; + llistitem *li; + llistitem *newli; + pidthr_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_pidthr)); + hi = (hash_item_pidthr *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pidthr *)li->datum; + k1 = h->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_pidthr *)(li->datum))->value; + } +} + +/* + * void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is pidthr_t + */ + +void * +hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value) + +{ + bucket_t *bucket; + hash_item_pidthr *hi; + llist *ll; + llistitem *li; + void *result = NULL; + pidthr_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_pidthr *)li->datum; + k1 = hi->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_pidthr)); + hi = (hash_item_pidthr *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_pidthr(hash_table *ht, pidthr_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is pidthr_t + */ + +void * +hash_lookup_pidthr(hash_table *ht, pidthr_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_pidthr *h; + void *result; + pidthr_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_pidthr *)li->datum; + k1 = h->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_pidthr(hash_table *ht, pidthr_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_pidthr(hash_table *ht, pidthr_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_pidthr *hi; + void *result; + pidthr_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_pidthr *)li->datum; + k1 = hi->key; + if ((key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_pidthr * +hash_first_pidthr(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_pidthr *)pos->ll_item->datum; +} + + +/* + * hash_item_pidthr *hash_next_pidthr(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_pidthr * +hash_next_pidthr(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_pidthr *)li->datum; +} + +/* + * void *hash_remove_pos_pidthr(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_pidthr(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_pidthr *hi; + pidthr_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_pidthr *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + +#if HAVE_LWPID_T + + +/* + * void hash_add_lwpid(hash_table *ht, lwpid_t key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is lwpid_t + */ + +void * +hash_add_lwpid(hash_table *ht, lwpid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_lwpid *hi; + hash_item_lwpid *h; + llist *ll; + llistitem *li; + llistitem *newli; + lwpid_t k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_lwpid)); + hi = (hash_item_lwpid *)newli->datum; + + /* fill in the values */ + hi->key = key; + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_lwpid *)li->datum; + k1 = h->key; + if (key == k1) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_lwpid *)(li->datum))->value; + } +} + +/* + * void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is lwpid_t + */ + +void * +hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value) + +{ + bucket_t *bucket; + hash_item_lwpid *hi; + llist *ll; + llistitem *li; + void *result = NULL; + lwpid_t k1; + + /* find the bucket */ + bucket = &(ht->buckets[(key % ht->num_buckets)]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_lwpid *)li->datum; + k1 = hi->key; + if (key == k1) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_lwpid)); + hi = (hash_item_lwpid *)li->datum; + hi->key = key; + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_lwpid(hash_table *ht, lwpid_t key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is lwpid_t + */ + +void * +hash_lookup_lwpid(hash_table *ht, lwpid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_lwpid *h; + void *result; + lwpid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_lwpid *)li->datum; + k1 = h->key; + if (key == k1) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_lwpid(hash_table *ht, lwpid_t key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_lwpid(hash_table *ht, lwpid_t key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_lwpid *hi; + void *result; + lwpid_t k1; + + result = NULL; + if ((bucket = &(ht->buckets[(key % ht->num_buckets)])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_lwpid *)li->datum; + k1 = hi->key; + if (key == k1) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + ; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_lwpid * +hash_first_lwpid(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_lwpid *)pos->ll_item->datum; +} + + +/* + * hash_item_lwpid *hash_next_lwpid(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_lwpid * +hash_next_lwpid(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_lwpid *)li->datum; +} + +/* + * void *hash_remove_pos_lwpid(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_lwpid(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_lwpid *hi; + lwpid_t key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_lwpid *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + ; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} + +#endif diff --git a/contrib/top/hash.h b/contrib/top/hash.h new file mode 100644 index 0000000000..44a121bc0f --- /dev/null +++ b/contrib/top/hash.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4h */ + +/* Interface definition for hash.c */ + +/* The file hash.h is generated from hash.m4h via the preprocessor M4 */ + +#ifndef _HASH_H +#define _HASH_H + +#include + +typedef struct pidthr_t { + pid_t k_pid; + id_t k_thr; +} pidthr_t; + +typedef struct llistitem { + void *datum; + struct llistitem *next; +} llistitem; + +typedef struct llist { + llistitem *head; + unsigned int count; +} llist; + +typedef struct bucket { + llist list; +} bucket_t; + +typedef struct hash_table { + int num_buckets; + bucket_t *buckets; +} hash_table; + +typedef struct hash_pos { + int num_buckets; + int curr; + bucket_t *hash_bucket; + llistitem *ll_item; + llistitem *ll_last; +} hash_pos; + +hash_table *hash_create(int num); +void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht); + + + + +typedef struct hash_item_uint { + unsigned int key; + void *value; +} hash_item_uint; + +void *hash_add_uint(hash_table *ht, unsigned int key, void *value); +void *hash_replace_uint(hash_table *ht, unsigned int key, void *value); +void *hash_lookup_uint(hash_table *ht, unsigned int key); +void *hash_remove_uint(hash_table *ht, unsigned int key); +hash_item_uint *hash_first_uint(hash_table *ht, hash_pos *pos); +hash_item_uint *hash_next_uint(hash_pos *pos); +void *hash_remove_pos_uint(hash_pos *pos); + + +typedef struct hash_item_pid { + pid_t key; + void *value; +} hash_item_pid; + +void *hash_add_pid(hash_table *ht, pid_t key, void *value); +void *hash_replace_pid(hash_table *ht, pid_t key, void *value); +void *hash_lookup_pid(hash_table *ht, pid_t key); +void *hash_remove_pid(hash_table *ht, pid_t key); +hash_item_pid *hash_first_pid(hash_table *ht, hash_pos *pos); +hash_item_pid *hash_next_pid(hash_pos *pos); +void *hash_remove_pos_pid(hash_pos *pos); + + +typedef struct hash_item_string { + char * key; + void *value; +} hash_item_string; + +void *hash_add_string(hash_table *ht, char * key, void *value); +void *hash_replace_string(hash_table *ht, char * key, void *value); +void *hash_lookup_string(hash_table *ht, char * key); +void *hash_remove_string(hash_table *ht, char * key); +hash_item_string *hash_first_string(hash_table *ht, hash_pos *pos); +hash_item_string *hash_next_string(hash_pos *pos); +void *hash_remove_pos_string(hash_pos *pos); + + +typedef struct hash_item_pidthr { + pidthr_t key; + void *value; +} hash_item_pidthr; + +void *hash_add_pidthr(hash_table *ht, pidthr_t key, void *value); +void *hash_replace_pidthr(hash_table *ht, pidthr_t key, void *value); +void *hash_lookup_pidthr(hash_table *ht, pidthr_t key); +void *hash_remove_pidthr(hash_table *ht, pidthr_t key); +hash_item_pidthr *hash_first_pidthr(hash_table *ht, hash_pos *pos); +hash_item_pidthr *hash_next_pidthr(hash_pos *pos); +void *hash_remove_pos_pidthr(hash_pos *pos); + +#if HAVE_LWPID_T + +typedef struct hash_item_lwpid { + lwpid_t key; + void *value; +} hash_item_lwpid; + +void *hash_add_lwpid(hash_table *ht, lwpid_t key, void *value); +void *hash_replace_lwpid(hash_table *ht, lwpid_t key, void *value); +void *hash_lookup_lwpid(hash_table *ht, lwpid_t key); +void *hash_remove_lwpid(hash_table *ht, lwpid_t key); +hash_item_lwpid *hash_first_lwpid(hash_table *ht, hash_pos *pos); +hash_item_lwpid *hash_next_lwpid(hash_pos *pos); +void *hash_remove_pos_lwpid(hash_pos *pos); + +#endif + + +#endif diff --git a/contrib/top/hash.m4c b/contrib/top/hash.m4c new file mode 100755 index 0000000000..6e8a736593 --- /dev/null +++ b/contrib/top/hash.m4c @@ -0,0 +1,666 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4c */ + +/* The file hash.c is generated from hash.m4c via the preprocessor M4 */ + +/* + * Hash table functions: init, add, lookup, first, next, sizeinfo. + * This is a conventional "bucket hash". The key is hashed in to a number + * less than or equal to the number of buckets and the result is used + * to index in to the array of buckets. Each bucket is a linked list + * that contains all the key/value pairs which hashed to that index. + */ + +#include "config.h" + +#if STDC_HEADERS +#include +#include +#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 +#else +#ifdef HAVE_STRING_H +#include +#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 +#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; jnum_buckets); +} + +void ll_init(llist *q) + +{ + q->head = NULL; + q->count = 0; +} + +llistitem *ll_newitem(int size) + +{ + llistitem *qi; + + qi = (llistitem *)MALLOC(sizeof(llistitem) + size); + qi->datum = ((void *)qi + sizeof(llistitem)); + return qi; +} + +void ll_freeitem(llistitem *li) + +{ + FREE(li); +} + +void ll_add(llist *q, llistitem *new) + +{ + new->next = q->head; + q->head = new; + q->count++; +} + +void ll_extract(llist *q, llistitem *qi, llistitem *last) + +{ + if (last == NULL) + { + q->head = qi->next; + } + else + { + last->next = qi->next; + } + qi->next = NULL; + q->count--; +} + +#define LL_FIRST(q) ((q)->head) +llistitem * +ll_first(llist *q) + +{ + return q->head; +} + +#define LL_NEXT(q, qi) ((qi) != NULL ? (qi)->next : NULL) +llistitem * +ll_next(llist *q, llistitem *qi) + +{ + return (qi != NULL ? qi->next : NULL); +} + +#define LL_ISEMPTY(ll) ((ll)->count == 0) +int +ll_isempty(llist *ll) + +{ + return (ll->count == 0); +} + +/* + * hash_table *hash_create(int num) + * + * Creates a hash table structure with at least "num" buckets. + */ + +hash_table * +hash_create(int num) + +{ + hash_table *result; + bucket_t *b; + int bytes; + int i; + + /* create the resultant structure */ + result = (hash_table *)MALLOC(sizeof(hash_table)); + + /* adjust bucket count to be prime */ + num = next_prime(num); + + /* create the buckets */ + bytes = sizeof(bucket_t) * num; + result->buckets = b = (bucket_t *)MALLOC(bytes); + result->num_buckets = num; + + /* create each bucket as a linked list */ + i = num; + while (--i >= 0) + { + ll_init(&(b->list)); + b++; + } + + return result; +} + +/* + * unsigned int hash_count(hash_table *ht) + * + * Return total number of elements contained in hash table. + */ + +unsigned int +hash_count(hash_table *ht) + +{ + register int i = 0; + register int cnt = 0; + register bucket_t *bucket; + + bucket = ht->buckets; + while (i++ < ht->num_buckets) + { + cnt += bucket->list.count; + bucket++; + } + + return cnt; +} + +/* + * void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + * + * Fill in "sizes" with information about bucket lengths in hash + * table "max". + */ + +void +hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht) + +{ + register int i; + register int idx; + register bucket_t *bucket; + + memzero(sizes, max * sizeof(unsigned int)); + + bucket = ht->buckets; + i = 0; + while (i++ < ht->num_buckets) + { + idx = bucket->list.count; + sizes[idx >= max ? max-1 : idx]++; + bucket++; + } +} + +dnl # HASH_TABLE_TMPL(suffix, keytype, to_hash, to_cmp, to_alloc, to_free) +dnl # +dnl # This generates hash table functions suitable for keys +dnl # of type "keytype". The function names all end with "suffix". +dnl # "to_hash" is an expression that generates a hash index (this +dnl # expression can include key and ht). "to_cmp" is an expression +dnl # that compares "key" to "k1". "to_alloc" is an expression that +dnl # allocates space for "key", or empty if no allocation is needed. +dnl # "to_free" is an expression that frees "key", or empty if no +dnl # allocation is needed. + +define(`HASH_TABLE_TMPL', ` + +/* + * void hash_add_$1(hash_table *ht, $2 key, void *value) + * + * Add an element to table "ht". The element is described by + * "key" and "value". Return NULL on success. If the key + * already exists in the table, then no action is taken and + * the data for the existing element is returned. + * Key type is $2 + */ + +void * +hash_add_$1(hash_table *ht, $2 key, void *value) + +{ + bucket_t *bucket; + hash_item_$1 *hi; + hash_item_$1 *h; + llist *ll; + llistitem *li; + llistitem *newli; + $2 k1; + + /* allocate the space we will need */ + newli = ll_newitem(sizeof(hash_item_$1)); + hi = (hash_item_$1 *)newli->datum; + + /* fill in the values */ + hi->key = ifelse($5, , key, $5); + hi->value = value; + + /* hash to the bucket */ + bucket = &(ht->buckets[$3]); + + /* walk the list to make sure we do not have a duplicate */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_$1 *)li->datum; + k1 = h->key; + if ($4) + { + /* found one */ + break; + } + li = LL_NEXT(ll, li); + } + li = NULL; + + /* is there already one there? */ + if (li == NULL) + { + /* add the unique element to the buckets list */ + ll_add(&(bucket->list), newli); + return NULL; + } + else + { + /* free the stuff we just allocated */ + ll_freeitem(newli); + return ((hash_item_$1 *)(li->datum))->value; + } +} + +/* + * void *hash_replace_$1(hash_table *ht, $2 key, void *value) + * + * Replace an existing value in the hash table with a new one and + * return the old value. If the key does not already exist in + * the hash table, add a new element and return NULL. + * Key type is $2 + */ + +void * +hash_replace_$1(hash_table *ht, $2 key, void *value) + +{ + bucket_t *bucket; + hash_item_$1 *hi; + llist *ll; + llistitem *li; + void *result = NULL; + $2 k1; + + /* find the bucket */ + bucket = &(ht->buckets[$3]); + + /* walk the list until we find the existing item */ + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + hi = (hash_item_$1 *)li->datum; + k1 = hi->key; + if ($4) + { + /* found it: now replace the value with the new one */ + result = hi->value; + hi->value = value; + break; + } + li = LL_NEXT(ll, li); + } + + /* if the element wasnt found, add it */ + if (result == NULL) + { + li = ll_newitem(sizeof(hash_item_$1)); + hi = (hash_item_$1 *)li->datum; + hi->key = ifelse($5, , key, $5); + hi->value = value; + ll_add(&(bucket->list), li); + } + + /* return the old value (so it can be freed) */ + return result; +} + +/* + * void *hash_lookup_$1(hash_table *ht, $2 key) + * + * Look up "key" in "ht" and return the associated value. If "key" + * is not found, return NULL. Key type is $2 + */ + +void * +hash_lookup_$1(hash_table *ht, $2 key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + hash_item_$1 *h; + void *result; + $2 k1; + + result = NULL; + if ((bucket = &(ht->buckets[$3])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + while (li != NULL) + { + h = (hash_item_$1 *)li->datum; + k1 = h->key; + if ($4) + { + result = h->value; + break; + } + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * void *hash_remove_$1(hash_table *ht, $2 key) + * + * Remove the element associated with "key" from the hash table + * "ht". Return the value or NULL if the key was not found. + */ + +void * +hash_remove_$1(hash_table *ht, $2 key) + +{ + bucket_t *bucket; + llist *ll; + llistitem *li; + llistitem *lilast; + hash_item_$1 *hi; + void *result; + $2 k1; + + result = NULL; + if ((bucket = &(ht->buckets[$3])) != NULL) + { + ll = &(bucket->list); + li = LL_FIRST(ll); + lilast = NULL; + while (li != NULL) + { + hi = (hash_item_$1 *)li->datum; + k1 = hi->key; + if ($4) + { + ll_extract(ll, li, lilast); + result = hi->value; + key = hi->key; + $6; + ll_freeitem(li); + break; + } + lilast = li; + li = LL_NEXT(ll, li); + } + } + return result; +} + +/* + * hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos) + * + * First function to call when iterating through all items in the hash + * table. Returns the first item in "ht" and initializes "*pos" to track + * the current position. + */ + +hash_item_$1 * +hash_first_$1(hash_table *ht, hash_pos *pos) + +{ + /* initialize pos for first item in first bucket */ + pos->num_buckets = ht->num_buckets; + pos->hash_bucket = ht->buckets; + pos->curr = 0; + pos->ll_last = NULL; + + /* find the first non-empty bucket */ + while(pos->hash_bucket->list.count == 0) + { + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + return NULL; + } + } + + /* set and return the first item */ + pos->ll_item = LL_FIRST(&(pos->hash_bucket->list)); + return (hash_item_$1 *)pos->ll_item->datum; +} + + +/* + * hash_item_$1 *hash_next_$1(hash_pos *pos) + * + * Return the next item in the hash table, using "pos" as a description + * of the present position in the hash table. "pos" also identifies the + * specific hash table. Return NULL if there are no more elements. + */ + +hash_item_$1 * +hash_next_$1(hash_pos *pos) + +{ + llistitem *li; + + /* move item to last and check for NULL */ + if ((pos->ll_last = pos->ll_item) == NULL) + { + /* are we really at the end of the hash table? */ + if (pos->curr >= pos->num_buckets) + { + /* yes: return NULL */ + return NULL; + } + /* no: regrab first item in current bucket list (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + else + { + /* get the next item in the llist */ + li = LL_NEXT(&(pos->hash_bucket->list), pos->ll_item); + } + + /* if its NULL we have to find another bucket */ + while (li == NULL) + { + /* locate another bucket */ + pos->ll_last = NULL; + + /* move to the next one */ + pos->hash_bucket++; + if (++pos->curr >= pos->num_buckets) + { + /* at the end of the hash table */ + pos->ll_item = NULL; + return NULL; + } + + /* get the first element (might be NULL) */ + li = LL_FIRST(&(pos->hash_bucket->list)); + } + + /* li is the next element to dish out */ + pos->ll_item = li; + return (hash_item_$1 *)li->datum; +} + +/* + * void *hash_remove_pos_$1(hash_pos *pos) + * + * Remove the hash table entry pointed to by position marker "pos". + * The data from the entry is returned upon success, otherwise NULL. + */ + + +void * +hash_remove_pos_$1(hash_pos *pos) + +{ + llistitem *li; + void *ans; + hash_item_$1 *hi; + $2 key; + + /* sanity checks */ + if (pos == NULL || pos->ll_last == pos->ll_item) + { + return NULL; + } + + /* at this point pos contains the item to remove (ll_item) + and its predecesor (ll_last) */ + /* extract the item from the llist */ + li = pos->ll_item; + ll_extract(&(pos->hash_bucket->list), li, pos->ll_last); + + /* retain the data */ + hi = (hash_item_$1 *)li->datum; + ans = hi->value; + + /* free up the space */ + key = hi->key; + $6; + ll_freeitem(li); + + /* back up to previous item */ + /* its okay for ll_item to be null: hash_next will detect it */ + pos->ll_item = pos->ll_last; + + return ans; +} +') + +dnl # create hash talbe functions for unsigned int and for strings */ + +HASH_TABLE_TMPL(`uint', `unsigned int', `(key % ht->num_buckets)', `key == k1', ,) +HASH_TABLE_TMPL(`pid', `pid_t', `(key % ht->num_buckets)', `key == k1', ,) +HASH_TABLE_TMPL(`string', `char *', `string_hash(ht, key)', `strcmp(key, k1) == 0', `STRDUP(key)', `FREE(key)') +HASH_TABLE_TMPL(`pidthr', `pidthr_t', `((key.k_thr * 10000 + key.k_pid) % ht->num_buckets)', `(key.k_pid == k1.k_pid && key.k_thr == k1.k_thr)', ,) +#if HAVE_LWPID_T +HASH_TABLE_TMPL(`lwpid', `lwpid_t', `(key % ht->num_buckets)', `key == k1', ,) +#endif diff --git a/contrib/top/hash.m4h b/contrib/top/hash.m4h new file mode 100755 index 0000000000..63a386c1fd --- /dev/null +++ b/contrib/top/hash.m4h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* hash.m4h */ + +/* Interface definition for hash.c */ + +/* The file hash.h is generated from hash.m4h via the preprocessor M4 */ + +#ifndef _HASH_H +#define _HASH_H + +#include + +typedef struct pidthr_t { + pid_t k_pid; + id_t k_thr; +} pidthr_t; + +typedef struct llistitem { + void *datum; + struct llistitem *next; +} llistitem; + +typedef struct llist { + llistitem *head; + unsigned int count; +} llist; + +typedef struct bucket { + llist list; +} bucket_t; + +typedef struct hash_table { + int num_buckets; + bucket_t *buckets; +} hash_table; + +typedef struct hash_pos { + int num_buckets; + int curr; + bucket_t *hash_bucket; + llistitem *ll_item; + llistitem *ll_last; +} hash_pos; + +hash_table *hash_create(int num); +void hash_sizeinfo(unsigned int *sizes, int max, hash_table *ht); + +define(`HASH_TYPE_TMPL', ` +typedef struct hash_item_$1 { + $2 key; + void *value; +} hash_item_$1; + +void *hash_add_$1(hash_table *ht, $2 key, void *value); +void *hash_replace_$1(hash_table *ht, $2 key, void *value); +void *hash_lookup_$1(hash_table *ht, $2 key); +void *hash_remove_$1(hash_table *ht, $2 key); +hash_item_$1 *hash_first_$1(hash_table *ht, hash_pos *pos); +hash_item_$1 *hash_next_$1(hash_pos *pos); +void *hash_remove_pos_$1(hash_pos *pos); +') + +HASH_TYPE_TMPL(`uint', `unsigned int') +HASH_TYPE_TMPL(`pid', `pid_t') +HASH_TYPE_TMPL(`string', `char *') +HASH_TYPE_TMPL(`pidthr', `pidthr_t') +#if HAVE_LWPID_T +HASH_TYPE_TMPL(`lwpid', `lwpid_t') +#endif + + +#endif diff --git a/contrib/top/layout.h b/contrib/top/layout.h new file mode 100644 index 0000000000..46bf78e231 --- /dev/null +++ b/contrib/top/layout.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top - a top users display for Unix + * + * This file defines the default locations on the screen for various parts + * of the display. These definitions are used by the routines in "display.c" + * for cursor addressing. + */ + +#define X_LASTPID 10 +#define Y_LASTPID 0 +#define X_LASTPIDWIDTH 13 +#define X_LOADAVE 27 +#define Y_LOADAVE 0 +#define X_LOADAVEWIDTH 7 +#define X_MINIBAR 50 +#define Y_MINIBAR 0 +#define X_UPTIME 48 +#define Y_UPTIME 0 +#define X_PROCSTATE 15 +#define Y_PROCSTATE 1 +#define X_BRKDN 15 +#define Y_BRKDN 1 +#define X_CPUSTATES 0 +#define Y_CPUSTATES 2 +#define X_KERNEL 8 +#define Y_KERNEL 3 +#define X_MEM 8 +#define Y_MEM 3 +#define X_SWAP 6 +#define Y_SWAP 4 +#define Y_MESSAGE 4 +#define X_HEADER 0 +#define Y_HEADER 5 +#define X_IDLECURSOR 0 +#define Y_IDLECURSOR 4 +#define Y_PROCS 6 + diff --git a/contrib/top/loadavg.h b/contrib/top/loadavg.h new file mode 100644 index 0000000000..c73fc7d28d --- /dev/null +++ b/contrib/top/loadavg.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top - a top users display for Berkeley Unix + * + * Defines required to access load average figures. + * + * This include file sets up everything we need to access the load average + * values in the kernel in a machine independent way. First, it sets the + * typedef "load_avg" to be either double or long (depending on what is + * needed), then it defines these macros appropriately: + * + * loaddouble(la) - convert load_avg to double. + * intload(i) - convert integer to load_avg. + */ + +/* + * We assume that if FSCALE is defined, then avenrun and ccpu are type long. + * If your machine is an exception (mips, perhaps?) then make adjustments + * here. + * + * Defined types: load_avg for load averages, pctcpu for cpu percentages. + */ +#if defined(mips) && !defined(NetBSD) +# include +# if defined(FBITS) && !defined(FSCALE) +# define FSCALE (1 << FBITS) /* mips */ +# endif +#endif + +#ifdef FSCALE +# define FIXED_LOADAVG FSCALE +# define FIXED_PCTCPU FSCALE +#endif + +#ifdef ibm032 +# undef FIXED_LOADAVG +# undef FIXED_PCTCPU +# define FIXED_PCTCPU PCT_SCALE +#endif + + +#ifdef FIXED_PCTCPU + typedef long pctcpu; +# define pctdouble(p) ((double)(p) / FIXED_PCTCPU) +#else +typedef double pctcpu; +# define pctdouble(p) (p) +#endif + +#ifdef FIXED_LOADAVG + typedef long load_avg; +# define loaddouble(la) ((double)(la) / FIXED_LOADAVG) +# define intload(i) ((int)((i) * FIXED_LOADAVG)) +#else + typedef double load_avg; +# define loaddouble(la) (la) +# define intload(i) ((double)(i)) +#endif diff --git a/contrib/top/machine.h b/contrib/top/machine.h new file mode 100644 index 0000000000..f727d3b084 --- /dev/null +++ b/contrib/top/machine.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file defines the interface between top and the machine-dependent + * module. It is NOT machine dependent and should not need to be changed + * for any specific machine. + */ + +#ifndef _MACHINE_H_ +#define _MACHINE_H_ + +#include "top.h" + +/* + * The statics struct is filled in by machine_init. Fields marked as + * "optional" are not filled in by every module. + */ +struct statics +{ + char **procstate_names; + char **cpustate_names; + char **memory_names; + char **swap_names; /* optional */ + char **order_names; /* optional */ + char **top_color_names; /* optional */ + char **kernel_names; /* optional */ + time_t boottime; /* optional */ + int modemax; /* optional */ + struct { + unsigned int fullcmds : 1; + unsigned int idle : 1; + unsigned int warmup : 1; + unsigned int threads : 1; + } flags; +}; + +/* + * the system_info struct is filled in by a machine dependent routine. + */ + +#ifdef p_active /* uw7 define macro p_active */ +#define P_ACTIVE p_pactive +#else +#define P_ACTIVE p_active +#endif + +struct system_info +{ + int last_pid; + double load_avg[NUM_AVERAGES]; + int p_total; + int P_ACTIVE; /* number of procs considered "active" */ + int *procstates; + int *cpustates; + int *kernel; + long *memory; + long *swap; +}; + +/* cpu_states is an array of percentages * 10. For example, + the (integer) value 105 is 10.5% (or .105). + */ + +/* + * the process_select struct tells get_process_info what processes we + * are interested in seeing + */ + +struct process_select +{ + int idle; /* show idle processes */ + int system; /* show system processes */ + int fullcmd; /* show full command */ + int usernames; /* show usernames */ + int uid; /* only this uid (unless uid == -1) */ + char *command; /* only this command (unless == NULL) */ + int mode; /* select display mode (0 is default) */ + int threads; /* show threads separately */ +}; + +/* routines defined by the machine dependent module */ +int machine_init(struct statics *); +void get_system_info(struct system_info *); +caddr_t get_process_info(struct system_info *, struct process_select *, int); +char *format_header(char *); +char *format_next_process(caddr_t, char *(*)(int)); +int proc_owner(int); +#ifdef HAVE_FORMAT_PROCESS_HEADER + +#endif /* _MACHINE_H_ */ +char *format_process_header(struct process_select *sel, caddr_t handle, int count); +#endif diff --git a/contrib/top/message.h b/contrib/top/message.h new file mode 100644 index 0000000000..f8a50b2ce3 --- /dev/null +++ b/contrib/top/message.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* interface declaration for display messages */ +/* This is a small subset of the interface from display.c that + just contains the calls for displaying messages. Do not include + this and display.h at the same time. */ + +#ifndef _MESSAGE_H +#define _MESSAGE_H + +void error_message(char *msgfmt, ...); +void clear_message(); + +#endif /* _MESSAGE_H_ */ diff --git a/contrib/top/os.h b/contrib/top/os.h new file mode 100644 index 0000000000..5502df635a --- /dev/null +++ b/contrib/top/os.h @@ -0,0 +1,143 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include + +#ifdef HAVE_LIMITS_H +#include +#endif + +#if TIME_WITH_SYS_TIME +# include +# include +#else +# if HAVE_SYS_TIME_H +# include +# else +# include +# endif +#endif + +#if STDC_HEADERS +#include +#include +#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 +#else +#ifdef HAVE_STRING_H +#include +#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 +#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 +#endif + +#ifdef HAVE_SYSEXITS_H +#include +#else +#define EX_OK 0 /* successful termination */ +#define EX_USAGE 64 /* command line usage error */ +#define EX_DATAERR 65 /* data format error */ +#define EX_NOINPUT 66 /* cannot open input */ +#define EX_NOUSER 67 /* addressee unknown */ +#define EX_NOHOST 68 /* host name unknown */ +#define EX_UNAVAILABLE 69 /* service unavailable */ +#define EX_SOFTWARE 70 /* internal software error */ +#define EX_OSERR 71 /* system error (e.g., can't fork) */ +#define EX_OSFILE 72 /* critical OS file missing */ +#define EX_CANTCREAT 73 /* can't create (user) output file */ +#define EX_IOERR 74 /* input/output error */ +#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */ +#define EX_PROTOCOL 76 /* remote error in protocol */ +#define EX_NOPERM 77 /* permission denied */ +#define EX_CONFIG 78 /* configuration error */ +#endif diff --git a/contrib/top/screen.c b/contrib/top/screen.c new file mode 100644 index 0000000000..398a858356 --- /dev/null +++ b/contrib/top/screen.c @@ -0,0 +1,591 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * 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 interface to termcap and stty/gtty. + * + * Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty. + * + * I put in code to turn on the TOSTOP bit while top was running, but I + * didn't really like the results. If you desire it, turn on the + * preprocessor variable "TOStop". --wnl + */ + +#include "os.h" +#include "top.h" + +#if HAVE_CURSES_H && HAVE_TERM_H +#include +#include +#else +#if HAVE_TERMCAP_H +#include +#else +#if HAVE_CURSES_H +#include +#endif +#endif +#endif + +#if !HAVE_DECL_TPUTS +int tputs(const char *, int, int (*)(int)); +#endif +#if !HAVE_DECL_TGOTO +char *tgoto(const char *, int, int); +#endif +#if !HAVE_DECL_TGETENT +int tgetent(const char *, char *); +#endif +#if !HAVE_DECL_TGETFLAG +int tgetflag(const char *); +#endif +#if !HAVE_DECL_TGETNUM +int tgetnum(const char *); +#endif +#if !HAVE_DECL_TGETSTR +char *tgetstr(const char *, char **); +#endif + +#include +#ifdef CBREAK +# include +# define USE_SGTTY +#else +# ifdef TCGETA +# define USE_TERMIO +# include +# else +# define USE_TERMIOS +# include +# endif +#endif +#if defined(USE_TERMIO) || defined(USE_TERMIOS) +# ifndef TAB3 +# ifdef OXTABS +# define TAB3 OXTABS +# else +# define TAB3 0 +# endif +# endif +#endif + +#include "screen.h" +#include "boolean.h" + +#define putcap(str) (void)((str) != NULL ? tputs(str, 1, putstdout) : 0) + +extern char *myname; + +char ch_erase; +char ch_kill; +char ch_werase; +char smart_terminal; +int screen_length; +int screen_width; + +char PC; + +static int tc_overstrike; +static char termcap_buf[1024]; +static char string_buffer[1024]; +static char home[15]; +static char lower_left[15]; +static char *tc_clear_line; +static char *tc_clear_screen; +static char *tc_clear_to_end; +static char *tc_cursor_motion; +static char *tc_start_standout; +static char *tc_end_standout; +static char *terminal_init; +static char *terminal_end; + +#ifdef USE_SGTTY +static struct sgttyb old_settings; +static struct sgttyb new_settings; +#endif +#ifdef USE_TERMIO +static struct termio old_settings; +static struct termio new_settings; +#endif +#ifdef USE_TERMIOS +static struct termios old_settings; +static struct termios new_settings; +#endif +static char is_a_terminal = No; +#ifdef TOStop +static int old_lword; +static int new_lword; +#endif + +#define STDIN 0 +#define STDOUT 1 +#define STDERR 2 + +/* This has to be defined as a subroutine for tputs (instead of a macro) */ + +static int +putstdout(TPUTS_PUTC_ARGTYPE ch) + +{ + return putchar((int)ch); +} + +void +screen_getsize() + +{ + +#ifdef TIOCGWINSZ + + struct winsize ws; + + if (ioctl (1, TIOCGWINSZ, &ws) != -1) + { + if (ws.ws_row != 0) + { + screen_length = ws.ws_row; + } + if (ws.ws_col != 0) + { + screen_width = ws.ws_col - 1; + } + } + +#else +#ifdef TIOCGSIZE + + struct ttysize ts; + + if (ioctl (1, TIOCGSIZE, &ts) != -1) + { + if (ts.ts_lines != 0) + { + screen_length = ts.ts_lines; + } + if (ts.ts_cols != 0) + { + screen_width = ts.ts_cols - 1; + } + } + +#endif /* TIOCGSIZE */ +#endif /* TIOCGWINSZ */ + + (void) strcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1)); +} + +int +screen_readtermcap(int interactive) + +{ + char *bufptr; + char *PCptr; + char *term_name; + char *getenv(); + int status; + + /* set defaults in case we aren't smart */ + screen_width = MAX_COLS; + screen_length = 0; + + if (interactive == No) + { + /* pretend we have a dumb terminal */ + smart_terminal = No; + return No; + } + + /* assume we have a smart terminal until proven otherwise */ + smart_terminal = Yes; + + /* get the terminal name */ + term_name = getenv("TERM"); + + /* if there is no TERM, assume it's a dumb terminal */ + /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */ + if (term_name == NULL) + { + smart_terminal = No; + return No; + } + + /* now get the termcap entry */ + if ((status = tgetent(termcap_buf, term_name)) != 1) + { + if (status == -1) + { + fprintf(stderr, "%s: can't open termcap file\n", myname); + } + else + { + fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n", + myname, term_name); + } + + /* pretend it's dumb and proceed */ + smart_terminal = No; + return No; + } + + /* "hardcopy" immediately indicates a very stupid terminal */ + if (tgetflag("hc")) + { + smart_terminal = No; + return No; + } + + /* set up common terminal capabilities */ + if ((screen_length = tgetnum("li")) <= 0) + { + screen_length = smart_terminal = 0; + return No; + } + + /* screen_width is a little different */ + if ((screen_width = tgetnum("co")) == -1) + { + screen_width = 79; + } + else + { + screen_width -= 1; + } + + /* terminals that overstrike need special attention */ + tc_overstrike = tgetflag("os"); + + /* initialize the pointer into the termcap string buffer */ + bufptr = string_buffer; + + /* get "ce", clear to end */ + if (!tc_overstrike) + { + tc_clear_line = tgetstr("ce", &bufptr); + } + + /* get necessary capabilities */ + if ((tc_clear_screen = tgetstr("cl", &bufptr)) == NULL || + (tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL) + { + smart_terminal = No; + return No; + } + + /* get some more sophisticated stuff -- these are optional */ + tc_clear_to_end = tgetstr("cd", &bufptr); + terminal_init = tgetstr("ti", &bufptr); + terminal_end = tgetstr("te", &bufptr); + tc_start_standout = tgetstr("so", &bufptr); + tc_end_standout = tgetstr("se", &bufptr); + + /* pad character */ + PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; + + /* set convenience strings */ + (void) strcpy(home, tgoto(tc_cursor_motion, 0, 0)); + /* (lower_left is set in screen_getsize) */ + + /* get the actual screen size with an ioctl, if needed */ + /* This may change screen_width and screen_length, and it always + sets lower_left. */ + screen_getsize(); + + /* if stdout is not a terminal, pretend we are a dumb terminal */ +#ifdef USE_SGTTY + if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1) + { + smart_terminal = No; + } +#endif +#ifdef USE_TERMIO + if (ioctl(STDOUT, TCGETA, &old_settings) == -1) + { + smart_terminal = No; + } +#endif +#ifdef USE_TERMIOS + if (tcgetattr(STDOUT, &old_settings) == -1) + { + smart_terminal = No; + } +#endif + + return smart_terminal; +} + +void +screen_init() + +{ + /* get the old settings for safe keeping */ +#ifdef USE_SGTTY + if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1) + { + /* copy the settings so we can modify them */ + new_settings = old_settings; + + /* turn on CBREAK and turn off character echo and tab expansion */ + new_settings.sg_flags |= CBREAK; + new_settings.sg_flags &= ~(ECHO|XTABS); + (void) ioctl(STDOUT, TIOCSETP, &new_settings); + + /* remember the erase and kill characters */ + ch_erase = old_settings.sg_erase; + ch_kill = old_settings.sg_kill; + ch_werase = old_settings.sg_werase; + +#ifdef TOStop + /* get the local mode word */ + (void) ioctl(STDOUT, TIOCLGET, &old_lword); + + /* modify it */ + new_lword = old_lword | LTOSTOP; + (void) ioctl(STDOUT, TIOCLSET, &new_lword); +#endif + /* remember that it really is a terminal */ + is_a_terminal = Yes; + + /* send the termcap initialization string */ + putcap(terminal_init); + } +#endif +#ifdef USE_TERMIO + if (ioctl(STDOUT, TCGETA, &old_settings) != -1) + { + /* copy the settings so we can modify them */ + new_settings = old_settings; + + /* turn off ICANON, character echo and tab expansion */ + new_settings.c_lflag &= ~(ICANON|ECHO); + new_settings.c_oflag &= ~(TAB3); + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + (void) ioctl(STDOUT, TCSETA, &new_settings); + + /* remember the erase and kill characters */ + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; + + /* remember that it really is a terminal */ + is_a_terminal = Yes; + + /* send the termcap initialization string */ + putcap(terminal_init); + } +#endif +#ifdef USE_TERMIOS + if (tcgetattr(STDOUT, &old_settings) != -1) + { + /* copy the settings so we can modify them */ + new_settings = old_settings; + + /* turn off ICANON, character echo and tab expansion */ + new_settings.c_lflag &= ~(ICANON|ECHO); + new_settings.c_oflag &= ~(TAB3); + new_settings.c_cc[VMIN] = 1; + new_settings.c_cc[VTIME] = 0; + (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); + + /* remember the erase and kill characters */ + ch_erase = old_settings.c_cc[VERASE]; + ch_kill = old_settings.c_cc[VKILL]; + ch_werase = old_settings.c_cc[VWERASE]; + + /* remember that it really is a terminal */ + is_a_terminal = Yes; + + /* send the termcap initialization string */ + putcap(terminal_init); + } +#endif + + if (!is_a_terminal) + { + /* not a terminal at all---consider it dumb */ + smart_terminal = No; + } +} + +void +screen_end() + +{ + /* move to the lower left, clear the line and send "te" */ + if (smart_terminal) + { + putcap(lower_left); + putcap(tc_clear_line); + fflush(stdout); + putcap(terminal_end); + } + + /* if we have settings to reset, then do so */ + if (is_a_terminal) + { +#ifdef USE_SGTTY + (void) ioctl(STDOUT, TIOCSETP, &old_settings); +#ifdef TOStop + (void) ioctl(STDOUT, TIOCLSET, &old_lword); +#endif +#endif +#ifdef USE_TERMIO + (void) ioctl(STDOUT, TCSETA, &old_settings); +#endif +#ifdef USE_TERMIOS + (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings); +#endif + } +} + +void +screen_reinit() + +{ + /* install our settings if it is a terminal */ + if (is_a_terminal) + { +#ifdef USE_SGTTY + (void) ioctl(STDOUT, TIOCSETP, &new_settings); +#ifdef TOStop + (void) ioctl(STDOUT, TIOCLSET, &new_lword); +#endif +#endif +#ifdef USE_TERMIO + (void) ioctl(STDOUT, TCSETA, &new_settings); +#endif +#ifdef USE_TERMIOS + (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); +#endif + } + + /* send init string */ + if (smart_terminal) + { + putcap(terminal_init); + } +} + +void +screen_move(int x, int y) + +{ + tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout); +} + +void +screen_standout(char *msg) + +{ + if (smart_terminal) + { + putcap(tc_start_standout); + fputs(msg, stdout); + putcap(tc_end_standout); + } + else + { + fputs(msg, stdout); + } +} + +void +screen_clear() + +{ + if (smart_terminal) + { + putcap(tc_clear_screen); + } +} + +int +screen_cte() + +{ + if (smart_terminal) + { + if (tc_clear_to_end) + { + putcap(tc_clear_to_end); + return(Yes); + } + } + return(No); +} + +void +screen_cleareol(int len) + +{ + int i; + + if (smart_terminal && !tc_overstrike && len > 0) + { + if (tc_clear_line) + { + putcap(tc_clear_line); + return; + } + else + { + i = 0; + while (i++ < 0) + { + putchar(' '); + } + i = 0; + while (i++ < 0) + { + putchar('\b'); + } + return; + } + } + return; +} + +void +screen_home() + +{ + if (smart_terminal) + { + putcap(home); + } +} + + diff --git a/contrib/top/screen.h b/contrib/top/screen.h new file mode 100644 index 0000000000..640f6f1b44 --- /dev/null +++ b/contrib/top/screen.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * top - a top users display for Unix 4.2 + * + * This file contains all the definitions necessary to use the hand-written + * screen package in "screen.c" + */ + +#ifndef _SCREEN_H_ +#define _SCREEN_H_ + +extern char ch_erase; /* set to the user's erase character */ +extern char ch_kill; /* set to the user's kill character */ +extern char ch_werase; /* set to the user's werase character */ +extern char smart_terminal; /* set if the terminal has sufficient termcap + capabilities for normal operation */ + +/* rows and columns on the screen according to termcap */ +extern int screen_length; +extern int screen_width; + +void screen_getsize(); +int screen_readtermcap(int interactive); +void screen_init(); +void screen_end(); +void screen_reinit(); +void screen_move(int x, int y); +void screen_standout(char *msg); +void screen_clear(); +int screen_cte(); +void screen_cleareol(int len); +void screen_home(); + +#endif /* _SCREEN_H_ */ diff --git a/contrib/top/top.c b/contrib/top/top.c new file mode 100644 index 0000000000..a9dccd8121 --- /dev/null +++ b/contrib/top/top.c @@ -0,0 +1,980 @@ +/* + * 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. + */ + +char *copyright = + "Copyright (c) 1984 through 2008, William LeFebvre"; + +/* + * Changes to other files that we can do at the same time: + * screen.c:init_termcap: get rid of the "interactive" argument and have it + * pass back something meaningful (such as success/failure/error). + */ + +#include "os.h" +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_UTSNAME_H +#include +#endif + +#ifdef HAVE_GETOPT_H +#include +#endif + +/* definitions */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif + +/* determine which type of signal functions to use */ +/* cant have sigaction without sigprocmask */ +#if defined(HAVE_SIGACTION) && !defined(HAVE_SIGPROCMASK) +#undef HAVE_SIGACTION +#endif +/* always use sigaction when it is available */ +#ifdef HAVE_SIGACTION +#undef HAVE_SIGHOLD +#else +/* use sighold/sigrelse, otherwise use old fashioned BSD signals */ +#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE) +#define BSD_SIGNALS +#endif +#endif + +/* if FD_SET and friends aren't present, then fake something up */ +#ifndef FD_SET +typedef int fd_set; +#define FD_ZERO(x) (*(x) = 0) +#define FD_SET(f, x) (*(x) = 1<use_color = !gstate->use_color; + break; +#endif + + case 'D': + debug_set(1); + break; + + case 'v': + fprintf(stderr, "%s: version %s\n", myname, version_string()); + exit(EX_OK); + break; + + case 'b': + case 'n': + gstate->interactive = No; + break; + + case 'a': + gstate->displays = Infinity; + gstate->topn = Infinity; + break; + + case 'i': + gstate->interactive = Yes; + break; + + case 'o': + gstate->order_name = optarg; + break; + + case 'd': + i = atoiwi(optarg); + if (i == Invalid || i == 0) + { + message_error(" Bad display count"); + } + else + { + gstate->displays = i; + } + break; + + case 's': + i = atoi(optarg); + if (i < 0 || (i == 0 && getuid() != 0)) + { + message_error(" Bad seconds delay"); + } + else + { + gstate->delay = i; + } + break; + + case 'u': + gstate->show_usernames = !gstate->show_usernames; + break; + + case 'U': + i = userid(optarg); + if (i == -1) + { + message_error(" Unknown user '%s'", optarg); + } + else + { + gstate->pselect.uid = i; + } + break; + + case 'm': + i = atoi(optarg); + gstate->pselect.mode = i; + break; + + case 'S': + gstate->pselect.system = !gstate->pselect.system; + break; + + case 'I': + gstate->pselect.idle = !gstate->pselect.idle; + break; + +#ifdef ENABLE_COLOR + case 'T': + gstate->show_tags = 1; + break; +#endif + + case 'c': + gstate->pselect.fullcmd = !gstate->pselect.fullcmd; + break; + + case 't': + gstate->pselect.threads = !gstate->pselect.threads; + break; + + case 'q': /* be quick about it */ + /* only allow this if user is really root */ + if (getuid() == 0) + { + /* be very un-nice! */ + (void) nice(-20); + } + else + { + message_error(" Option -q can only be used by root"); + } + break; + + default: + fprintf(stderr, "\ +Top version %s\n\ +Usage: %s [-ISTabcinqu] [-d x] [-s x] [-o field] [-U username] [number]\n", + version_string(), myname); + exit(EX_USAGE); + } + } + + /* get count of top processes to display */ + if (optind < ac && *av[optind]) + { + if ((i = atoiwi(av[optind])) == Invalid) + { + message_error(" Process count not a number"); + } + else + { + gstate->topn = i; + } + } +} + +void +do_display(globalstate *gstate) + +{ + int active_procs; + int i; + time_t curr_time; + caddr_t processes; + struct system_info system_info; + char *hdr; + + /* get the time */ + time_mark(&(gstate->now)); + curr_time = (time_t)(gstate->now.tv_sec); + + /* get the current stats */ + get_system_info(&system_info); + + /* get the current processes */ + processes = get_process_info(&system_info, &(gstate->pselect), gstate->order_index); + + /* determine number of processes to actually display */ + if (gstate->topn > 0) + { + /* this number will be the smallest of: active processes, + number user requested, number current screen accomodates */ + active_procs = system_info.P_ACTIVE; + if (active_procs > gstate->topn) + { + active_procs = gstate->topn; + } + if (active_procs > gstate->max_topn) + { + active_procs = gstate->max_topn; + } + } + else + { + /* dont show any */ + active_procs = 0; + } + +#ifdef HAVE_FORMAT_PROCESS_HEADER + /* get the process header to use */ + hdr = format_process_header(&(gstate->pselect), processes, active_procs); +#else + hdr = gstate->header_text; +#endif + + /* full screen or update? */ + if (gstate->fulldraw) + { + display_clear(); + i_loadave(system_info.last_pid, system_info.load_avg); + i_uptime(&(gstate->statics->boottime), &curr_time); + i_timeofday(&curr_time); + i_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + if (gstate->show_cpustates) + { + i_cpustates(system_info.cpustates); + } + else + { + if (smart_terminal) + { + z_cpustates(); + } + gstate->show_cpustates = Yes; + } + i_kernel(system_info.kernel); + i_memory(system_info.memory); + i_swap(system_info.swap); + i_message(&(gstate->now)); + i_header(hdr); + for (i = 0; i < active_procs; i++) + { + i_process(i, format_next_process(processes, gstate->get_userid)); + } + i_endscreen(); + if (gstate->smart_terminal) + { + gstate->fulldraw = No; + } + } + else + { + u_loadave(system_info.last_pid, system_info.load_avg); + u_uptime(&(gstate->statics->boottime), &curr_time); + i_timeofday(&curr_time); + u_procstates(system_info.p_total, system_info.procstates, gstate->pselect.threads); + u_cpustates(system_info.cpustates); + u_kernel(system_info.kernel); + u_memory(system_info.memory); + u_swap(system_info.swap); + u_message(&(gstate->now)); + u_header(hdr); + for (i = 0; i < active_procs; i++) + { + u_process(i, format_next_process(processes, gstate->get_userid)); + } + u_endscreen(); + } +} + +#ifdef DEBUG +void +timeval_xdprint(char *s, struct timeval tv) + +{ + xdprintf("%s %d.%06d\n", s, tv.tv_sec, tv.tv_usec); +} +#endif + +void +do_wait(globalstate *gstate) + +{ + struct timeval wait; + + wait.tv_sec = gstate->delay; + wait.tv_usec = 0; + select(0, NULL, NULL, NULL, &wait); +} + +void +do_command(globalstate *gstate) + +{ + int status; + struct timeval wait = {0, 0}; + struct timeval now; + fd_set readfds; + unsigned char ch; + + /* calculate new refresh time */ + gstate->refresh = gstate->now; + gstate->refresh.tv_sec += gstate->delay; + time_get(&now); + + /* loop waiting for time to expire */ + do { + /* calculate time to wait */ + if (gstate->delay > 0) + { + wait = gstate->refresh; + wait.tv_usec -= now.tv_usec; + if (wait.tv_usec < 0) + { + wait.tv_usec += 1000000; + wait.tv_sec--; + } + wait.tv_sec -= now.tv_sec; + } + + /* set up arguments for select on stdin (0) */ + FD_ZERO(&readfds); + FD_SET(STDIN_FILENO, &readfds); + + /* wait for something to read or time out */ + if (select(32, &readfds, NULL, NULL, &wait) > 0) + { + /* read it */ + if (read(STDIN_FILENO, &ch, 1) != 1) + { + /* read error */ + message_error(" Read error on stdin"); + quit(EX_DATAERR); + /*NOTREACHED*/ + } + + /* mark pending messages as old */ + message_mark(); + + /* dispatch */ + status = command_process(gstate, (int)ch); + switch(status) + { + case CMD_ERROR: + quit(EX_SOFTWARE); + /*NOTREACHED*/ + + case CMD_REFRESH: + return; + + case CMD_UNKNOWN: + message_error(" Unknown command"); + break; + + case CMD_NA: + message_error(" Command not available"); + } + } + + /* get new time */ + time_get(&now); + } while (timercmp(&now, &(gstate->refresh), < )); +} + +void +do_minidisplay(globalstate *gstate) + +{ + int real_delay; + struct system_info si; + + /* save the real delay and substitute 1 second */ + real_delay = gstate->delay; + gstate->delay = 1; + + /* wait 1 second for a command */ + time_mark(&(gstate->now)); + do_command(gstate); + + /* do a mini update that only updates the cpustates */ + get_system_info(&si); + u_cpustates(si.cpustates); + + /* restore the delay time */ + gstate->delay = real_delay; + + /* done */ + i_endscreen(); +} + +int +main(int argc, char *argv[]) + +{ + char *env_top; + char **preset_argv; + int preset_argc = 0; + void *mask; + int need_mini = 1; + + struct statics statics; + globalstate *gstate; + + /* get our name */ + if (argc > 0) + { + if ((myname = strrchr(argv[0], '/')) == 0) + { + myname = argv[0]; + } + else + { + myname++; + } + } + + /* binary compatibility check */ +#ifdef HAVE_UNAME + { + struct utsname uts; + + if (uname(&uts) == 0) + { + if (strcmp(uts.machine, UNAME_HARDWARE) != 0) + { + fprintf(stderr, "%s: incompatible hardware platform\n", + myname); + exit(EX_UNAVAILABLE); + } + } + } +#endif + + /* initialization */ + gstate = (globalstate *)calloc(1, sizeof(globalstate)); + gstate->statics = &statics; + time_mark(NULL); + + /* preset defaults for various options */ + gstate->show_usernames = Yes; + gstate->topn = DEFAULT_TOPN; + gstate->delay = DEFAULT_DELAY; + gstate->fulldraw = Yes; + gstate->use_color = Yes; + gstate->interactive = Maybe; + + /* preset defaults for process selection */ + gstate->pselect.idle = Yes; + gstate->pselect.system = No; + gstate->pselect.fullcmd = No; + gstate->pselect.command = NULL; + gstate->pselect.uid = -1; + gstate->pselect.mode = 0; + + /* use a large buffer for stdout */ +#ifdef HAVE_SETVBUF + setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE); +#else +#ifdef HAVE_SETBUFFER + setbuffer(stdout, stdoutbuf, BUFFERSIZE); +#endif +#endif + + /* get preset options from the environment */ + if ((env_top = getenv("TOP")) != NULL) + { + preset_argv = argparse(env_top, &preset_argc); + preset_argv[0] = myname; + do_arguments(gstate, preset_argc, preset_argv); + } + + /* process arguments */ + do_arguments(gstate, argc, argv); + +#ifdef ENABLE_COLOR + /* If colour has been turned on read in the settings. */ + env_top = getenv("TOPCOLOURS"); + if (!env_top) + { + env_top = getenv("TOPCOLORS"); + } + /* must do something about error messages */ + color_env_parse(env_top); + color_activate(gstate->use_color); +#endif + + /* in order to support forward compatability, we have to ensure that + the entire statics structure is set to a known value before we call + machine_init. This way fields that a module does not know about + will retain their default values */ + memzero((void *)&statics, sizeof(statics)); + statics.boottime = -1; + + /* call the platform-specific init */ + if (machine_init(&statics) == -1) + { + exit(EX_SOFTWARE); + } + + /* create a helper list of sort order names */ + gstate->order_namelist = string_list(statics.order_names); + + /* look up chosen sorting order */ + if (gstate->order_name != NULL) + { + int i; + + if (statics.order_names == NULL) + { + message_error(" This platform does not support arbitrary ordering"); + } + else if ((i = string_index(gstate->order_name, + statics.order_names)) == -1) + { + message_error(" Sort order `%s' not recognized", gstate->order_name); + message_error(" Recognized sort orders: %s", gstate->order_namelist); + } + else + { + gstate->order_index = i; + } + } + + /* initialize extensions */ + init_username(); + + /* initialize termcap */ + gstate->smart_terminal = screen_readtermcap(gstate->interactive); + + /* determine interactive state */ + if (gstate->interactive == Maybe) + { + gstate->interactive = smart_terminal; + } + + /* if displays were not specified, choose an appropriate default */ + if (gstate->displays == 0) + { + gstate->displays = gstate->smart_terminal ? Infinity: 1; + } + + /* we don't need a mini display when delay is less than 2 + seconds or when we are not on a smart terminal */ + if (gstate->delay <= 1 || !smart_terminal) + { + need_mini = 0; + } + +#ifndef HAVE_FORMAT_PROCESS_HEADER + /* set constants for username/uid display */ + if (gstate->show_usernames) + { + gstate->header_text = format_header("USERNAME"); + gstate->get_userid = username; + } + else + { + gstate->header_text = format_header(" UID "); + gstate->get_userid = itoa7; + } +#endif + gstate->pselect.usernames = gstate->show_usernames; + + /* initialize display */ + if ((gstate->max_topn = display_init(&statics)) == -1) + { + fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); + exit(EX_OSERR); + } + + /* check for infinity and for overflowed screen */ + if (gstate->topn == Infinity) + { + gstate->topn = INT_MAX; + } + else if (gstate->topn > gstate->max_topn) + { + message_error(" This terminal can only display %d processes", + gstate->max_topn); + } + +#ifdef ENABLE_COLOR + /* producing a list of color tags is easy */ + if (gstate->show_tags) + { + color_dump(stdout); + exit(EX_OK); + } +#endif + + /* hold all signals while we initialize the screen */ + mask = hold_signals(); + screen_init(); + + /* set the signal handlers */ + set_signals(); + + /* longjmp re-entry point */ + /* set the jump buffer for long jumps out of signal handlers */ + if (setjmp(jmp_int) != 0) + { + /* this is where we end up after processing sigwinch or sigtstp */ + + /* tell display to resize its buffers, and get the new length */ + if ((gstate->max_topn = display_resize()) == -1) + { + /* thats bad */ + quit(EX_OSERR); + /*NOTREACHED*/ + } + + /* set up for a full redraw, and get the current line count */ + gstate->fulldraw = Yes; + + /* safe to release the signals now */ + release_signals(mask); + } + else + { + /* release the signals */ + release_signals(mask); + + /* some systems require a warmup */ + /* always do a warmup for batch mode */ + if (gstate->interactive == 0 || statics.flags.warmup) + { + struct system_info system_info; + struct timeval timeout; + + time_mark(&(gstate->now)); + get_system_info(&system_info); + (void)get_process_info(&system_info, &gstate->pselect, 0); + timeout.tv_sec = 1; + timeout.tv_usec = 0; + select(0, NULL, NULL, NULL, &timeout); + + /* if we've warmed up, then we can show good states too */ + gstate->show_cpustates = Yes; + need_mini = 0; + } + } + + /* main loop */ + while ((gstate->displays == -1) || (--gstate->displays > 0)) + { + do_display(gstate); + if (gstate->interactive) + { + if (need_mini) + { + do_minidisplay(gstate); + need_mini = 0; + } + do_command(gstate); + } + else + { + do_wait(gstate); + } + } + + /* do one last display */ + do_display(gstate); + + quit(EX_OK); + /* NOTREACHED */ + return 1; /* Keep compiler quiet. */ +} diff --git a/contrib/top/top.h b/contrib/top/top.h new file mode 100644 index 0000000000..24b1abbd0a --- /dev/null +++ b/contrib/top/top.h @@ -0,0 +1,80 @@ +/* + * 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 + * + * General (global) definitions + */ + +#ifndef _TOP_H_ +#define _TOP_H_ + +#include + +/* Maximum number of columns allowed for display */ +#define MAX_COLS 255 + +/* Log base 2 of 1024 is 10 (2^10 == 1024) */ +#define LOG1024 10 + +/* Special atoi routine returns either a non-negative number or one of: */ +#define Infinity -1 +#define Invalid -2 + +/* maximum number we can have */ +#define Largest 0x7fffffff + +/* + * The entire display is based on these next numbers being defined as is. + */ + +#define NUM_AVERAGES 3 + +struct ext_decl { + int (*f_minibar)(char *, int); + int (*f_display)(char *, int); +}; + +/* + * "Table_size" defines the size of the hash tables used to map uid to + * username. Things will work best if the number is a prime number. + * We use a number that should be suitable for most installations. + */ +#ifndef Table_size +#define Table_size 8191 +#endif + +void gettime(struct timeval *); +void quit(int); + +#endif /* _TOP_H_ */ diff --git a/contrib/top/username.c b/contrib/top/username.c new file mode 100644 index 0000000000..9866bffb32 --- /dev/null +++ b/contrib/top/username.c @@ -0,0 +1,150 @@ +/* + * 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 + */ + +/* + * Username translation code for top. + * + * These routines handle uid to username mapping. They use a hash table to + * reduce reading overhead. Entries are refreshed every EXPIRETIME seconds. + * + * The old ad-hoc hash functions have been replaced with something a little + * more formal and (hopefully) more robust (found in hash.c) + */ + +#include "os.h" + +#include + +#include "top.h" +#include "utils.h" +#include "hash.h" + +#define EXPIRETIME (60 * 5) + +/* we need some sort of idea how long usernames can be */ +#ifndef MAXLOGNAME +#ifdef _POSIX_LOGIN_NAME_MAX +#define MAXLOGNAME _POSIX_LOGIN_NAME_MAX +#else +#define MAXLOGNAME 9 +#endif +#endif + +struct hash_data { + int uid; + char name[MAXLOGNAME]; /* big enough? */ + time_t expire; +}; + +hash_table *userhash; + + +void +init_username() + +{ + userhash = hash_create(211); +} + +char * +username(int uid) + +{ + struct hash_data *data; + struct passwd *pw; + time_t now; + + /* what time is it? */ + now = time(NULL); + + /* get whatever is in the cache */ + data = hash_lookup_uint(userhash, (unsigned int)uid); + + /* if we had a cache miss, then create space for a new entry */ + if (data == NULL) + { + /* make space */ + data = (struct hash_data *)malloc(sizeof(struct hash_data)); + + /* fill in some data, including an already expired time */ + data->uid = uid; + data->expire = (time_t)0; + + /* add it to the hash: the rest gets filled in later */ + hash_add_uint(userhash, uid, data); + } + + /* Now data points to the correct hash entry for "uid". If this is + a new entry, then expire is 0 and the next test will be true. */ + if (data->expire <= now) + { + if ((pw = getpwuid(uid)) != NULL) + { + strncpy(data->name, pw->pw_name, MAXLOGNAME-1); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } + else + { + /* username doesnt exist ... so invent one */ + snprintf(data->name, sizeof(data->name), "%d", uid); + data->expire = now + EXPIRETIME; + dprintf("username: updating %d with %s, expires %d\n", + data->uid, data->name, data->expire); + } + } + + /* return what we have */ + return data->name; +} + +int +userid(char *username) + +{ + struct passwd *pwd; + + if ((pwd = getpwnam(username)) == NULL) + { + return(-1); + } + + /* return our result */ + return(pwd->pw_uid); +} + diff --git a/contrib/top/username.h b/contrib/top/username.h new file mode 100644 index 0000000000..3c56399557 --- /dev/null +++ b/contrib/top/username.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* interface for username.c */ + +#ifndef _USERNAME_H_ +#define _USERNAME_H_ + +void init_username(); +char *username(int uid); +int userid(char *username); + +#endif /* _USERNAME_H_ */ diff --git a/contrib/top/utils.c b/contrib/top/utils.c new file mode 100644 index 0000000000..856005af54 --- /dev/null +++ b/contrib/top/utils.c @@ -0,0 +1,745 @@ +/* + * 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 various handy utilities used by top. + */ + +#include "os.h" +#include +#ifdef HAVE_STDARG_H +#include +#else +#undef DEBUG +#endif +#include "top.h" +#include "utils.h" + +static int +alldigits(char *s) + +{ + int ch; + + while ((ch = *s++) != '\0') + { + if (!isdigit(ch)) + { + return 0; + } + } + return 1; +} + +int +atoiwi(char *str) + +{ + register int len; + + len = strlen(str); + if (len != 0) + { + if (strncmp(str, "infinity", len) == 0 || + strncmp(str, "all", len) == 0 || + strncmp(str, "maximum", len) == 0) + { + return(Infinity); + } + else if (alldigits(str)) + { + return(atoi(str)); + } + else + { + return(Invalid); + } + } + return(0); +} + +/* + * itoa - convert integer (decimal) to ascii string for positive numbers + * only (we don't bother with negative numbers since we know we + * don't use them). + */ + + /* + * How do we know that 16 will suffice? + * Because the biggest number that we will + * ever convert will be 2^32-1, which is 10 + * digits. + */ + +char * +itoa(int val) + +{ + register char *ptr; + static char buffer[16]; /* result is built here */ + /* 16 is sufficient since the largest number + we will ever convert will be 2^32-1, + which is 10 digits. */ + + ptr = buffer + sizeof(buffer); + *--ptr = '\0'; + if (val == 0) + { + *--ptr = '0'; + } + else while (val != 0) + { + *--ptr = (val % 10) + '0'; + val /= 10; + } + return(ptr); +} + +/* + * itoa7(val) - like itoa, except the number is right justified in a 7 + * character field. This code is a duplication of itoa instead of + * a front end to a more general routine for efficiency. + */ + +char * +itoa_w(int val, int w) + +{ + char *ptr; + char *eptr; + static char buffer[16]; /* result is built here */ + /* 16 is sufficient since the largest number + we will ever convert will be 2^32-1, + which is 10 digits. */ + + if (w > 15) + { + w = 15; + } + eptr = ptr = buffer + sizeof(buffer); + *--ptr = '\0'; + if (val == 0) + { + *--ptr = '0'; + } + else while (val != 0) + { + *--ptr = (val % 10) + '0'; + val /= 10; + } + while (ptr >= eptr - w) + { + *--ptr = ' '; + } + return(ptr); +} + +char * +itoa7(int val) + +{ + return itoa_w(val, 7); +} + +/* + * digits(val) - return number of decimal digits in val. Only works for + * positive numbers. If val < 0 then digits(val) == 0, but + * digits(0) == 1. + */ + +int +digits(int val) + +{ + register int cnt = 0; + + if (val == 0) + { + return 1; + } + while (val > 0) + { + cnt++; + val /= 10; + } + return(cnt); +} + +/* + * printable(char *str) - make the string pointed to by "str" into one that is + * printable (i.e.: all ascii), by converting all non-printable + * characters into '?'. Replacements are done in place and a pointer + * to the original buffer is returned. + */ + +char * +printable(char *str) + +{ + register char *ptr; + register int ch; + + ptr = str; + while ((ch = *ptr) != '\0') + { + if (!isprint(ch)) + { + *ptr = '?'; + } + ptr++; + } + return(str); +} + +/* + * strcpyend(to, from) - copy string "from" into "to" and return a pointer + * to the END of the string "to". + */ + +char * +strcpyend(char *to, char *from) + +{ + while ((*to++ = *from++) != '\0'); + return(--to); +} + +/* + * char * + * homogenize(char *str) + * + * Remove unwanted characters from "str" and make everything lower case. + * Newly allocated string is returned: the original is not altered. + */ + +char *homogenize(char *str) + +{ + char *ans; + char *fr; + char *to; + int ch; + + to = fr = ans = strdup(str); + while ((ch = *fr++) != '\0') + { + if (isalnum(ch)) + { + *to++ = tolower(ch); + } + } + + *to = '\0'; + return ans; +} + +/* + * string_index(string, array) - find string in array and return index + */ + +int +string_index(char *string, char **array) + +{ + register int i = 0; + + while (*array != NULL) + { + if (strcmp(string, *array) == 0) + { + return(i); + } + array++; + i++; + } + return(-1); +} + +/* + * char *string_list(char **strings) + * + * Create a comma-separated list of the strings in the NULL-terminated + * "strings". Returned string is malloc-ed and should be freed when the + * caller is done. Note that this is not an efficient function. + */ + +char *string_list(char **strings) + +{ + int cnt = 0; + char **pp; + char *p; + char *result = NULL; + char *resp = NULL; + + pp = strings; + while ((p = *pp++) != NULL) + { + cnt += strlen(p) + 2; + } + + if (cnt > 0) + { + resp = result = (char *)malloc(cnt); + pp = strings; + while ((p = *pp++) != NULL) + { + resp = strcpyend(resp, p); + if (*pp != NULL) + { + resp = strcpyend(resp, ", "); + } + } + } + + return result; +} + +/* + * argparse(line, cntp) - parse arguments in string "line", separating them + * out into an argv-like array, and setting *cntp to the number of + * arguments encountered. This is a simple parser that doesn't understand + * squat about quotes. + */ + +char ** +argparse(char *line, int *cntp) + +{ + register char *from; + register char *to; + register int cnt; + register int ch; + int length; + int lastch; + register char **argv; + char **argarray; + char *args; + + /* unfortunately, the only real way to do this is to go thru the + input string twice. */ + + /* step thru the string counting the white space sections */ + from = line; + lastch = cnt = length = 0; + while ((ch = *from++) != '\0') + { + length++; + if (ch == ' ' && lastch != ' ') + { + cnt++; + } + lastch = ch; + } + + /* add three to the count: one for the initial "dummy" argument, + one for the last argument and one for NULL */ + cnt += 3; + + /* allocate a char * array to hold the pointers */ + argarray = (char **)malloc(cnt * sizeof(char *)); + + /* allocate another array to hold the strings themselves */ + args = (char *)malloc(length+2); + + /* initialization for main loop */ + from = line; + to = args; + argv = argarray; + lastch = '\0'; + + /* create a dummy argument to keep getopt happy */ + *argv++ = to; + *to++ = '\0'; + cnt = 2; + + /* now build argv while copying characters */ + *argv++ = to; + while ((ch = *from++) != '\0') + { + if (ch != ' ') + { + if (lastch == ' ') + { + *to++ = '\0'; + *argv++ = to; + cnt++; + } + *to++ = ch; + } + lastch = ch; + } + *to++ = '\0'; + + /* set cntp and return the allocated array */ + *cntp = cnt; + return(argarray); +} + +/* + * percentages(cnt, out, new, old, diffs) - calculate percentage change + * between array "old" and "new", putting the percentages i "out". + * "cnt" is size of each array and "diffs" is used for scratch space. + * The array "old" is updated on each call. + * The routine assumes modulo arithmetic. This function is especially + * useful on BSD mchines for calculating cpu state percentages. + */ + +long +percentages(int cnt, int *out, long *new, long *old, long *diffs) + +{ + register int i; + register long change; + register long total_change; + register long *dp; + long half_total; + + /* initialization */ + total_change = 0; + dp = diffs; + + /* calculate changes for each state and the overall change */ + for (i = 0; i < cnt; i++) + { + if ((change = *new - *old) < 0) + { + /* this only happens when the counter wraps */ + change = (int) + ((unsigned long)*new-(unsigned long)*old); + } + total_change += (*dp++ = change); + *old++ = *new++; + } + + /* avoid divide by zero potential */ + if (total_change == 0) + { + total_change = 1; + } + + /* calculate percentages based on overall change, rounding up */ + half_total = total_change / 2l; + for (i = 0; i < cnt; i++) + { + *out++ = (int)((*diffs++ * 1000 + half_total) / total_change); + } + + /* return the total in case the caller wants to use it */ + return(total_change); +} + +/* + * errmsg(errnum) - return an error message string appropriate to the + * error number "errnum". This is a substitute for the System V + * function "strerror". There appears to be no reliable way to + * determine if "strerror" exists at compile time, so I make do + * by providing something of similar functionality. For those + * systems that have strerror and NOT errlist, define + * -DHAVE_STRERROR in the module file and this function will + * use strerror. + */ + +/* externs referenced by errmsg */ + +#ifndef HAVE_STRERROR +#if !HAVE_DECL_SYS_ERRLIST +extern char *sys_errlist[]; +#endif + +extern int sys_nerr; +#endif + +char * +errmsg(int errnum) + +{ +#ifdef HAVE_STRERROR + char *msg = strerror(errnum); + if (msg != NULL) + { + return msg; + } +#else + if (errnum > 0 && errnum < sys_nerr) + { + return((char *)(sys_errlist[errnum])); + } +#endif + return("No error"); +} + +/* format_percent(v) - format a double as a percentage in a manner that + * does not exceed 5 characters (excluding any trailing + * percent sign). Since it is possible for the value + * to exceed 100%, we format such values with no fractional + * component to fit within the 5 characters. + */ + +char * +format_percent(double v) + +{ + static char result[10]; + + /* enumerate the possibilities */ + if (v < 0 || v >= 100000.) + { + /* we dont want to try extreme values */ + strcpy(result, " ???"); + } + else if (v > 99.99) + { + sprintf(result, "%5.0f", v); + } + else + { + sprintf(result, "%5.2f", v); + } + + return result; +} + +/* format_time(seconds) - format number of seconds into a suitable + * display that will fit within 6 characters. Note that this + * routine builds its string in a static area. If it needs + * to be called more than once without overwriting previous data, + * then we will need to adopt a technique similar to the + * one used for format_k. + */ + +/* Explanation: + We want to keep the output within 6 characters. For low values we use + the format mm:ss. For values that exceed 999:59, we switch to a format + that displays hours and fractions: hhh.tH. For values that exceed + 999.9, we use hhhh.t and drop the "H" designator. For values that + exceed 9999.9, we use "???". + */ + +char * +format_time(long seconds) + +{ + static char result[10]; + + /* sanity protection */ + if (seconds < 0 || seconds > (99999l * 360l)) + { + strcpy(result, " ???"); + } + else if (seconds >= (1000l * 60l)) + { + /* alternate (slow) method displaying hours and tenths */ + sprintf(result, "%5.1fH", (double)seconds / (double)(60l * 60l)); + + /* It is possible that the sprintf took more than 6 characters. + If so, then the "H" appears as result[6]. If not, then there + is a \0 in result[6]. Either way, it is safe to step on. + */ + result[6] = '\0'; + } + else + { + /* standard method produces MMM:SS */ + /* we avoid printf as must as possible to make this quick */ + sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l); + } + return(result); +} + +/* + * format_k(amt) - format a kilobyte memory value, returning a string + * suitable for display. Returns a pointer to a static + * area that changes each call. "amt" is converted to a + * string with a trailing "K". If "amt" is 10000 or greater, + * then it is formatted as megabytes (rounded) with a + * trailing "M". + */ + +/* + * Compromise time. We need to return a string, but we don't want the + * caller to have to worry about freeing a dynamically allocated string. + * Unfortunately, we can't just return a pointer to a static area as one + * of the common uses of this function is in a large call to sprintf where + * it might get invoked several times. Our compromise is to maintain an + * array of strings and cycle thru them with each invocation. We make the + * array large enough to handle the above mentioned case. The constant + * NUM_STRINGS defines the number of strings in this array: we can tolerate + * up to NUM_STRINGS calls before we start overwriting old information. + * Keeping NUM_STRINGS a power of two will allow an intelligent optimizer + * to convert the modulo operation into something quicker. What a hack! + */ + +#define NUM_STRINGS 8 + +char * +format_k(long amt) + +{ + static char retarray[NUM_STRINGS][16]; + static int index = 0; + register char *ret; + register char tag = 'K'; + + ret = retarray[index]; + index = (index + 1) % NUM_STRINGS; + + if (amt >= 10000) + { + amt = (amt + 512) / 1024; + tag = 'M'; + if (amt >= 10000) + { + amt = (amt + 512) / 1024; + tag = 'G'; + } + } + + snprintf(ret, sizeof(retarray[index])-1, "%ld%c", amt, tag); + + return(ret); +} + +/* + * Time keeping functions. + */ + +static struct timeval lasttime = { 0, 0 }; +static unsigned int elapsed_msecs = 0; + +void +time_get(struct timeval *tv) + +{ + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif +} + +void +time_mark(struct timeval *tv) + +{ + struct timeval thistime; + struct timeval timediff; + + /* if the caller didnt provide one then use our own */ + if (tv == NULL) + { + tv = &thistime; + } + + /* get the current time */ +#ifdef HAVE_GETTIMEOFDAY + gettimeofday(tv, NULL); +#else + tv->tv_sec = (long)time(NULL); + tv->tv_usec = 0; +#endif + + /* calculate the difference */ + timediff.tv_sec = tv->tv_sec - lasttime.tv_sec; + timediff.tv_usec = tv->tv_usec - lasttime.tv_usec; + if (timediff.tv_usec < 0) { + timediff.tv_sec--; + timediff.tv_usec += 1000000; + } + + /* convert to milliseconds */ + elapsed_msecs = timediff.tv_sec * 1000 + timediff.tv_usec / 1000; + if (elapsed_msecs == 0) + { + elapsed_msecs = 1; + } + + /* save for next time */ + lasttime = *tv; +} + +unsigned int +time_elapsed() + +{ + return elapsed_msecs; +} + +unsigned int +diff_per_second(unsigned int x, unsigned int y) + +{ + return (y > x ? UINT_MAX - y + x + 1 : x - y) * 1000 / elapsed_msecs; +} + +static int debug_on = 0; + +#ifdef DEBUG +FILE *debugfile; +#endif + +void +debug_set(int i) + +{ + debug_on = i; +#ifdef DEBUG + debugfile = fopen("/tmp/top.debug", "w"); +#endif +} + +#ifdef DEBUG +void +xdprintf(char *fmt, ...) + +{ + va_list argp; + + va_start(argp, fmt); + + if (debug_on) + { + vfprintf(debugfile, fmt, argp); + fflush(debugfile); + } + + va_end(argp); +} +#endif + diff --git a/contrib/top/utils.h b/contrib/top/utils.h new file mode 100644 index 0000000000..b6a29de3b5 --- /dev/null +++ b/contrib/top/utils.h @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + */ + +/* prototypes for functions found in utils.c */ + +#ifndef _UTILS_H +#define _UTILS_H + +int atoiwi(char *); +char *itoa(int); +char *itoa_w(int, int); +char *itoa7(int); +int digits(int); +char *printable(char *); +char *strcpyend(char *, char *); +char *homogenize(char *); +int string_index(char *, char **); +char **argparse(char *, int *); +long percentages(int, int *, long *, long *, long *); +char *errmsg(int); +char *format_percent(double); +char *format_time(long); +char *format_k(long); +char *string_list(char **); +void time_get(struct timeval *); +void time_mark(struct timeval *); +unsigned int time_elapsed(); +unsigned int diff_per_second(unsigned int, unsigned int); +void debug_set(int); +#ifdef DEBUG +#define dprintf xdprintf +void xdprintf(char *fmt, ...); +#else +#ifdef HAVE_C99_VARIADIC_MACROS +#define dprintf(...) +#else +#ifdef HAVE_GNU_VARIADIC_MACROS +#define dprintf(x...) +#else +#define dprintf if (0) +#endif +#endif +#endif + +#endif diff --git a/contrib/top/version.c b/contrib/top/version.c new file mode 100644 index 0000000000..63a6960b94 --- /dev/null +++ b/contrib/top/version.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1984 through 2008, William LeFebvre + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of William LeFebvre nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Top users/processes display for Unix + * Version 3 + */ + +#include "config.h" +#include "top.h" + +char * +version_string() + +{ + return(PACKAGE_VERSION); +} diff --git a/contrib/top/version.h b/contrib/top/version.h new file mode 100644 index 0000000000..f1b5ee6880 --- /dev/null +++ b/contrib/top/version.h @@ -0,0 +1,37 @@ +/* + * 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 + */ + +char *version_string();