]> granicus.if.org Git - procps-ng/commitdiff
procps 010114
authorcsmall <>
Fri, 1 Feb 2002 22:47:29 +0000 (22:47 +0000)
committercsmall <>
Fri, 1 Feb 2002 22:47:29 +0000 (22:47 +0000)
90 files changed:
.cvsignore.patch [new file with mode: 0644]
BUGS [new file with mode: 0644]
COPYING [new file with mode: 0644]
COPYING.LIB [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
NEWS [new file with mode: 0644]
TODO [new file with mode: 0644]
XConsole [new file with mode: 0755]
free.1 [new file with mode: 0644]
free.c [new file with mode: 0644]
gfinger.p [new file with mode: 0644]
glibc.patch [new file with mode: 0644]
kill.1 [new file with mode: 0644]
minimal.c [new file with mode: 0644]
oldps.1 [new file with mode: 0644]
oldps.c [new file with mode: 0644]
pgrep.1 [new file with mode: 0644]
pgrep.c [new file with mode: 0644]
pkill.1 [new file with mode: 0644]
pmap.c [new file with mode: 0644]
proc/.cvsignore.patch [new file with mode: 0644]
proc/COPYING [new file with mode: 0644]
proc/Makefile [new file with mode: 0644]
proc/alloc.c [new file with mode: 0644]
proc/compare.c [new file with mode: 0644]
proc/compare.h [new file with mode: 0644]
proc/devname.c [new file with mode: 0644]
proc/devname.h [new file with mode: 0644]
proc/ksym.c [new file with mode: 0644]
proc/output.c [new file with mode: 0644]
proc/procps.h [new file with mode: 0644]
proc/pwcache.c [new file with mode: 0644]
proc/readproc.c [new file with mode: 0644]
proc/readproc.h [new file with mode: 0644]
proc/sig.c [new file with mode: 0644]
proc/sig.h [new file with mode: 0644]
proc/status.c [new file with mode: 0644]
proc/status.h [new file with mode: 0644]
proc/sysinfo.c [new file with mode: 0644]
proc/sysinfo.h [new file with mode: 0644]
proc/tree.h [new file with mode: 0644]
proc/version.c [new file with mode: 0644]
proc/version.h [new file with mode: 0644]
proc/whattime.c [new file with mode: 0644]
proc/whattime.h [new file with mode: 0644]
procps.lsm [new file with mode: 0644]
procps.spec [new file with mode: 0644]
ps/.cvsignore.patch [new file with mode: 0644]
ps/COPYING [new file with mode: 0644]
ps/HACKING [new file with mode: 0644]
ps/Makefile [new file with mode: 0755]
ps/TRANSLATION [new file with mode: 0644]
ps/common.h [new file with mode: 0644]
ps/display.c [new file with mode: 0644]
ps/escape.c [new file with mode: 0644]
ps/global.c [new file with mode: 0644]
ps/help.c [new file with mode: 0644]
ps/it [new file with mode: 0644]
ps/output.c [new file with mode: 0644]
ps/p [new file with mode: 0755]
ps/parser.c [new file with mode: 0644]
ps/ps.1 [new file with mode: 0644]
ps/regression [new file with mode: 0644]
ps/select.c [new file with mode: 0644]
ps/sortformat.c [new file with mode: 0644]
ps/stacktrace.c [new file with mode: 0644]
skill.1 [new file with mode: 0644]
skill.c [new file with mode: 0644]
snice.1 [new file with mode: 0644]
sysctl.8 [new file with mode: 0644]
sysctl.c [new file with mode: 0644]
sysctl.conf.5 [new file with mode: 0644]
t [new file with mode: 0755]
tload.1 [new file with mode: 0644]
tload.c [new file with mode: 0644]
tmp-junk.c [new file with mode: 0644]
top.1 [new file with mode: 0644]
top.c [new file with mode: 0644]
top.desktop [new file with mode: 0644]
top.h [new file with mode: 0644]
uptime.1 [new file with mode: 0644]
uptime.c [new file with mode: 0644]
utmp.c [new file with mode: 0644]
vmstat.8 [new file with mode: 0644]
vmstat.c [new file with mode: 0644]
w.1 [new file with mode: 0644]
w.c [new file with mode: 0644]
watch.1 [new file with mode: 0644]
watch.c [new file with mode: 0644]

diff --git a/.cvsignore.patch b/.cvsignore.patch
new file mode 100644 (file)
index 0000000..216dc03
--- /dev/null
@@ -0,0 +1,18 @@
+diff -Naur procps-2.0.6/.cvsignore procps-2.0.7/.cvsignore
+--- procps-2.0.6/.cvsignore    Wed Dec 31 19:00:00 1969
++++ procps-2.0.7/.cvsignore    Fri Jul 14 16:45:01 2000
+@@ -0,0 +1,14 @@
++skill
++kill
++oldps
++uptime
++tload
++free
++w
++top
++vmstat
++watch
++snice
++pgrep
++pkill
++sysctl
diff --git a/BUGS b/BUGS
new file mode 100644 (file)
index 0000000..df7ced4
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,81 @@
+BUG REPORTS
+
+Please read this file before sending in a bug report or patch.
+
+Also, PLEASE read the documentation first.  90% of the mail I get
+complaining about procps is due to the sender not having read the
+documentation!
+
+
+Where to send
+=============
+Send comments, bug reports, patches, etc., to acahalan@cs.uml.edu
+
+
+What to send
+============
+It is much more useful to me if a program really crases to recompile it
+with make "CC=gcc -ggdb -O", run it with "gdb prog" and "run" and send
+me a stack trace ('bt' command).  That said, any bug report is still
+better than none.
+
+It might be nice to get rid of miscellaneous compiler warnings, but
+don't bend over backwards to do it.
+
+
+Kernel-Dependent Patches
+========================
+If you send me patches which are specific to *running* with a particular
+kernel version of /proc, please condition them with the runtime determined
+variable `linux_version_code' from libproc/kvers.c.  It is the same
+number as the macro LINUX_VERSION_CODE for which the kernel /proc fs
+code was compiled.
+
+A macro is provide in libproc/version.h to construct the code from its
+components, e.g.
+  if (linux_version_code < LINUX_VERSION(1,1,30))
+     /* tty field is only a minor */
+A startup call to set_linux_version may also be necessary.
+
+Of course, if a bug is due to a change in kernel file formats, it would
+be best to first try to generalize the parsing, since the code is then
+more resilient against future change.
+
+If you send me patches which are specific to *compiling* on a particular
+version of Linux include a "#if LINUX_VERSION_CODE > 1*0x10000+3*0x100+54"
+markup of the patch so that the package may be compiled with older
+kernels as well as the "latest and greatest".  LINUX_VERSION_CODE is
+#define'd in <linux/version.h>.
+
+Note that you should not make patches specific to *compiling* on a
+particular version of Linux unless there is nothing else you can do.
+
+Also unified diffs (diff -u) are my preference, context diffs (diff -c )
+are kind of usable, and standard diffs (diff) are more useless than a
+generic text description of what you did.  Just use
+       diff -u oldfile newfile
+or
+       diff -Naur old-procps-dir new-procps-dir
+to create your diffs and you will make me happy.  Also make sure to
+include a description of what the diff is for or I'm likely to ignore
+it because of general lack of time...
+
+
+Code Structure
+==============
+My ultimate goal for this package is to be compilable with any kernel
+headers and to be able to run under any kernel's /proc.  (Don't bother
+telling me that I'm not especially close to my ultimate goal... who
+is? :-)
+
+Anyhow, another goal is to encapsulate *all* parsing dependent on /proc
+file formats into the libproc library.  If the API is general enough
+it can hopefully stabilize and then /proc changes might only require
+updating libproc.so.  Beyond that having the set of utilities be simple
+command lines parsers and output formatters and encapsulating all kernel
+divergence in libproc is the way to go.
+
+Hence if you are submitting a new program or are fixing an old one, keep
+in mind that adding files to libproc which encapsulate such things is
+more desirable than patching the actual driver program.  (well, except
+to move it toward the API of the library).
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYING.LIB b/COPYING.LIB
new file mode 100644 (file)
index 0000000..92b8903
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                   59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..4be90ec
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,73 @@
+INSTALL for procps version 2.0.1
+================================
+Please read the NEWS and BUGS files, also.
+==========================================
+
+
+Re-compiling the package
+========================
+
+You want to examine the first 30 or so lines of the Makefile (up to the
+configurability note). The destinations of various things attempt FSSTND
+compliance, and are pretty standard.  The CC/LD flags and which libraries you
+use for curses/termcap are here also.  It is all annotated there and in a
+readily understood format: directories, program to installdir mapping,
+sub-packages to build, and compilation options.  A few extra points worth
+mentioning are:
+
+  o  The SUBDIRS variable is essentially just a list of subdirectories to
+     perform a recursive make or make install in.  Right now that's just
+     ps; the old xproc has been removed because it was entirely redundant.
+  o  There is also an option to build and link against a libproc.so which
+     reduces 'ps' and 'top' sizes by a large fraction.  It is on by default,
+     so change the value of SHARED if you want.
+  o  'make libinstall' will install the library and header files into standard
+     system directories for developers of /proc utilities.  There are no
+     library man pages yet, but the headers are fairly well documented. 
+  o  You may need to change the INCDIRS definition if your system is
+     not very standard.  The current definition has been tested to work
+     on several different systems, though.
+
+Once you are satisfied with the top-level Makefile options (and possibly those
+in subdirectories) you compile and install the package like so:
+(ignore innocuous `rcsid' defined-but-not-used warnings.)
+
+    make distclean # clean-out everything to re-make from scratch
+    make           # takes about 0.75 minutes on a PPro 200 with SCSI
+    su             # for write/chown-perms on sys dirs
+    make install
+    ldconfig -v    # update ld.so to use new libproc if SHARED=1
+    exit
+    make distclean # remove anything that can be rebuilt
+
+
+Miscellaneous Notes
+===================
+
+COMPATIBILITY
+
+    This code is intended for use with Linux 2.0.xx and 2.2.xx.
+    Both libc 5 and libc 6 should work.
+
+PS/TOP WCHAN FIELD
+
+    In past releases, in order to get WCHAN output for ps, you had to
+    have a psdatabase for each zImage kernel that you generated from
+    the vmlinux unstripped binary with /sbin/psupdate.  With this release,
+    you should use the System.map automatically generated by Linus' makefile.
+    'ps' and 'top' will first use the contents of an environment variable
+    "PS_SYSTEM_MAP" and then look for System.map files along
+    the followin search path:
+        /boot/System.map-V, /boot/System.map, /lib/modules/V/System.map,
+    Here 'V' is the output of "uname -r" and looks like "2.2.0".
+    Don't forget the dash! In those same locations, remove psdatabase* files.
+
+
+PARTIAL INSTALLATION
+
+    If you just want to install one program use the targets
+    install_progname and install_progname.X (where X is the man section)
+    instead.  E.g.
+        make install_ps install_ps.1
+    Likewise for component packages, e.g.
+        make install_psmisc
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..3cba70f
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,220 @@
+# Makefile for procps.  Chuck Blake.
+# Portions of this are highly dependent upon features specific to GNU make
+
+export PREFIX     =  #proc# prefix for program names
+
+export DESTDIR    =  /
+export MANDIR     =  /usr/man
+export MAN1DIR    =  $(DESTDIR)$(MANDIR)/man1
+export MAN5DIR    =  $(DESTDIR)$(MANDIR)/man5
+export MAN8DIR    =  $(DESTDIR)$(MANDIR)/man8
+export BINDIR     =  $(DESTDIR)/bin
+export SBINDIR    =  $(DESTDIR)/sbin
+export XBINDIR    =  $(DESTDIR)/usr/X11R6/bin
+export USRBINDIR  =  $(DESTDIR)/usr/bin
+export PROCDIR    =  $(DESTDIR)/usr/bin# /usr/proc/bin for Solaris devotees
+export APPLNK     =  $(DESTDIR)/etc/X11/applnk/Utilities
+export OWNERGROUP =  --owner 0 --group 0
+export INSTALLBIN =  install --mode a=rx --strip
+export INSTALLSCT =  install --mode a=rx
+export INSTALLMAN =  install --mode a=r
+
+BPROG      =  kill #                                   -> BINDIR
+UPROG      =  oldps uptime tload free w top vmstat watch skill snice # -> USRBINDIR
+PPROG      =  pgrep pkill# -> PROCDIR
+SPROG      =  sysctl
+MAN1       =  oldps.1 uptime.1 tload.1 free.1 w.1 top.1 watch.1 skill.1 kill.1 snice.1 pgrep.1 pkill.1
+MAN5       =  sysctl.conf.5
+MAN8       =  vmstat.8 sysctl.8
+DESKTOP    =  top.desktop
+XSCPT      =  XConsole # -> XBINDIR
+
+SUBDIRS    =  ps # sub-packages to build/install
+
+# easy to command-line override
+export INCDIRS    =  -I/usr/include/ncurses -I/usr/X11R6/include
+
+export CC         =  gcc #-ggdb # this gets compiling and linking :-)
+export OPT        =  -O2
+export CFLAGS     =  -D_GNU_SOURCE $(OPT) -I$(shell pwd) $(INCDIRS) -W -Wall -Wstrict-prototypes -Wshadow -Wcast-align -Wmissing-prototypes
+
+export SHARED     =  1# build/install both a static and ELF shared library
+export SHLIBDIR   =  $(DESTDIR)/lib# where to install the shared library
+
+export LDFLAGS    =  -Wl,-warn-common #-s      recommended for ELF systems
+#LDFLAGS    =  -qmagic -s#             recommended for a.out systems
+#LDFLAGS    =  -Xlinker -qmagic -s#    older a.out systems may need this
+#LDFLAGS    =  -N -s#                  still older a.out systems use this
+
+#BFD_CAPABLE = -DBFD_CAPABLE
+#AOUT_CAPABLE = #-DAOUT_CAPABLE 
+#ELF_CAPABLE = #-DELF_CAPABLE
+
+#LIBBFD = -lbfd -liberty
+LIBCURSES  =  -lncurses# watch is the only thing that needs this
+#LIBCURSES  =  -lcurses -ltermcap# BSD Curses requires termcap
+LIBTERMCAP =  -lncurses# provides perfectly good termcap support
+#LIBTERMCAP =  -ltermcap
+EXTRALIBS  =  # -lshadow
+
+W_SHOWFROM =  -DW_SHOWFROM# show remote host users are logged in from.
+
+#----------------------------------------------------#
+# End of user-configurable portion of the Makefile.  #
+# You should not need to modify anything below this. #
+#----------------------------------------------------#
+BUILD = $(BPROG) $(UPROG) $(PPROG) $(SPROG) $(SUBDIRS) $(DESKTOP)
+
+# BUILD LIBRARIES + PROGRAMS
+all: $(BUILD)
+
+# INSTALL PROGRAMS + DOCS
+install: $(patsubst %,install_%,$(BUILD) $(XSCPT) $(MAN1) $(MAN5) $(MAN8))
+ifeq ($(SHARED),1)
+       install $(OWNERGROUP) --mode a=rx $(LIB_TGT) $(SHLIBDIR)
+endif
+
+# INSTALL LIBRARIES + HEADERS (OPTIONAL)
+libinstall:
+       $(MAKE) -C proc install $(LIBPROCPASS)
+
+clean:
+       $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(PPROG) $(SPROG)
+       for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done
+
+distclean: clean
+       for i in proc $(SUBDIRS); do $(MAKE) -C $$i clean; done
+       $(RM) -f $(OBJ) $(BPROG) $(UPROG) $(SPROG) \
+             proc/.depend
+
+
+#-----------------------------------------------------#
+# End of user-callable make targets.                  #
+# You should not need to read anything below this.    #
+#-----------------------------------------------------#
+
+.PHONY:        all install libinstall clean distclean
+.PHONY: $(patsubst %,install_%, $(BPROG) $(UPROG) $(SPROG))
+.PHONY: proc ps
+.PHONY: $(patsubst %,build_%, proc ps)
+.PHONY: $(patsubst %,install_%, proc ps)
+
+VERSION      = $(shell awk '/^%define major_version/ { print $$3 }' < procps.spec)
+SUBVERSION   = $(shell awk '/^%define minor_version/ { print $$3 }' < procps.spec)
+MINORVERSION = $(shell awk '/^%define revision/ { print $$3 }' < procps.spec)
+
+# Note: LIBVERSION may be less than $(VERSION).$(SUBVERSION).$(MINORVERSION)
+# LIBVERSION is only set to current $(VERSION).$(SUBVERSION).$(MINORVERSION)
+# when an incompatible change is made in libproc.
+LIBVERSION   =  2.0.7
+ifdef MINORVERSION
+LIBPROCPASS  =  SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) MINORVERSION=$(MINORVERSION) LIBVERSION=$(LIBVERSION)
+else
+LIBPROCPASS  =  SHARED=$(SHARED) SHLIBDIR=$(SHLIBDIR) VERSION=$(VERSION) SUBVERSION=$(SUBVERSION) LIBVERSION=$(LIBVERSION)
+endif
+
+# libproc setup
+
+ifeq ($(SHARED),1)
+    LIB_TGT = proc/libproc.so.$(LIBVERSION)
+else
+    LIB_TGT = proc/libproc.a
+endif
+
+$(LIB_TGT): $(wildcard proc/*.[ch])
+       $(MAKE) -C proc `basename $(LIB_TGT)` $(LIBPROCPASS)
+
+# component package setup -- the pattern should be obvious: A build rule and
+# unified executable+documentation install rule. (An extra makefile rule is
+# needed for those packages which use Imake.)
+
+ps:              build_ps
+build_ps:                              ; $(MAKE) -C ps
+install_ps:      ps            ; $(MAKE) -C ps install
+
+# executable dependencies
+oldps kill skill snice top w uptime tload free vmstat utmp : $(LIB_TGT)
+
+# static pattern build/link rules:
+
+%.o : %.c
+       $(strip $(CC) $(CFLAGS) -c $^)
+
+oldps w uptime tload free vmstat utmp pgrep: % : %.o
+       $(strip $(CC) $(LDFLAGS) -o $@ $< $(LIB_TGT) $(EXTRALIBS))
+
+
+# special instances of link rules (need extra libraries/objects)
+
+top:   % : %.o
+       $(strip $(CC)  $(LDFLAGS) -o $@ $^ $(LIB_TGT) $(LIBTERMCAP) $(EXTRALIBS))
+
+watch: % : %.o
+       $(strip $(CC) $(SLDFLAGS) -o $@ $< $(LIBCURSES) $(EXTRALIBS))
+
+
+# special instances of compile rules (need extra defines)
+w.o:   w.c
+       $(strip $(CC) $(CFLAGS) $(W_SHOWFROM) -c $<)
+
+top.o: top.c
+       $(strip $(CC) $(CFLAGS) -fwritable-strings -c $<)
+
+skill.o:       skill.c
+       $(strip $(CC) $(CFLAGS) -DSYSV -c $<)
+
+snice: skill
+       ln -f skill snice
+
+kill:  skill
+       ln -f skill kill
+
+pkill: pgrep
+       ln -f pgrep pkill
+
+# static pattern installation rules
+
+$(patsubst %,install_%,$(BPROG)): install_%: %
+       $(INSTALLBIN) $< $(BINDIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(SPROG)): install_%: %
+       $(INSTALLBIN) $< $(SBINDIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(UPROG)): install_%: %
+       $(INSTALLBIN) $< $(USRBINDIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(PPROG)): install_%: %
+       $(INSTALLBIN) $< $(PROCDIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(XSCPT)): install_%: %
+       $(INSTALLSCT) $< $(XBINDIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(MAN1)) : install_%: %
+       $(INSTALLMAN) $< $(MAN1DIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(MAN5)) : install_%: %
+       $(INSTALLMAN) $< $(MAN5DIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(MAN8)) : install_%: %
+       $(INSTALLMAN) $< $(MAN8DIR)/$(PREFIX)$<
+$(patsubst %,install_%,$(DESKTOP)) : install_%: %
+       $(INSTALLSCT $< $(APPLNK)/$(PREFIX)$<
+
+# special case install rules
+install_snice: snice install_skill
+       cd $(USRBINDIR) && ln -f skill snice
+install_kill: snice install_skill
+       cd $(USRBINDIR) && ln -f skill kill
+install_pkill: pgrep install_pgrep
+       cd $(USRBINDIR) && ln -f pgrep pkill
+
+# Find all the source and object files in this directory
+
+SRC      =  $(sort $(wildcard *.c))
+OBJ      =  $(SRC:.c=.o)
+
+CVSTAG = ps_$(VERSION)_$(SUBVERSION)_$(MINORVERSION)
+FILEVERSION = $(VERSION).$(SUBVERSION).$(MINORVERSION)
+dist: archive
+archive:
+       @cvs -Q tag -F $(CVSTAG)
+       @rm -rf /tmp/procps
+       @cd /tmp; cvs -Q -d $(CVSROOT) export -r$(CVSTAG) procps || echo GRRRrrrrr -- ignore [export aborted]
+       @mv /tmp/procps /tmp/procps-$(FILEVERSION)
+       @cd /tmp; tar czSpf procps-$(FILEVERSION).tar.gz procps-$(FILEVERSION)
+       @cd /tmp; cp procps-$(FILEVERSION)/procps.lsm procps-$(FILEVERSION).lsm
+       @rm -rf /tmp/procps-$(FILEVERSION)
+       @echo "The final archive is /tmp/procps-$(FILEVERSION).tar.gz"
diff --git a/NEWS b/NEWS
new file mode 100644 (file)
index 0000000..b1bcf5f
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,645 @@
+NEWS: what has changed recently with procps, in reverse cronological order.
+Please send bug reports to acahalan@cs.uml.edu
+
+*** THIS FILE DOES NOT INCLUDE RECENT CHANGES ***
+
+
+NEWS for version 2.0.7 of procps
+
+SMP support has been added to top.  This adds one line to the screen
+for every processor.  There is also an (off by default) field to show
+the last processor that a process used that replaced the long-dead
+LIB field.  Please send feedback, positive and negative, on these
+changes to procps-bugs@redhat.com
+
+The libproc soname has changed to 2.0.7 because of a structure
+change required to add SMP support.
+
+Two programs inspired by Solaris's /usr/proc/bin collection have
+been added: pgrep and pkill.  By default, for FSSTND/FHS/LSB compliance,
+these are installed in /usr/bin; if you want, you can change PROCDIR
+in Makefile to move them.  You can also make a symlink from
+/usr/proc/bin to /usr/bin if you like.
+
+The Makefiles have been sanitized a bit more; they are now less messy
+than they were.  There is less duplication between the Makefiles now.
+
+The man pages that use tables have been fixed to work work broken
+versions of man.
+
+The old wmconfig file (specific to Red Hat) has been replaced with a
+desktop file (common across GNOME and KDE).
+
+sysctl returns an error code in a condition in which it didn't before,
+and handles EOF correctly.
+
+top now only loads System.map when it is actually going to make use
+of it.
+
+vmstat has its buffer size increased and handles page size dynamically.
+
+w has its year display fixed to show the year 2000 as 00 instead of 0,
+and to try harder to find a process to display.
+
+watch has a fix for ncurses in recent versions of glibc, and expands
+tabs so that they display correctly.
+
+libproc tries to get the default page size from the system header
+files, but still has a fallback.  It also has been extended to allow
+applications to handle their own error reporting in some important
+cases.  The pwcache has been expanded in size to correctly handle
+user names longer than 8 characters.  It has been expanded to
+provide the lproc field that shows up as top's LC field.  A segfault
+when /proc is not mounted was fixed.  Missing files will cause
+applications to exit with an error code instead of good exit code.
+A warning when libproc cannot calculate the HZ value (probably due
+to a kernel bug) has been supressed by default because it broke
+people's scripts unnecessarily.  A 64-bit memory size reading
+bug was fixed (/proc/meminfo was read incorrectly).
+
+A couple of error messages in ps had newlines added to them.  ps
+only opens System.map when it is going to make use of it.  The
+full-page error message has been replaced with a shorter usage
+message; the full-page command summary is available with --help,
+and the usage message tells about --help; the full-page summary
+is now no longer an error message and so it is sent to stdout
+insteada of stderr.  Processes with run times longer than a day
+now have their runtime displayed correctly.  ruser output was fixed.
+An attempt was made to support one more piece of BSD syntax in the
+command line arguments, where pids can follow options with no
+intervening space.  The ps man page was made a bit more
+internally consistent and typos fixed.  Fixed a segfault when the
+PS_FORMAT environment variable was set wrong.
+
+
+NEWS for version 2.0.6 of procps
+
+Support for large-memory systems has been added.
+
+LIBVERSION has been incremented because of an incompatible change
+in the libproc interface, necessitated by the large-memory support.
+
+A little more error checking in device idle time calculations.
+
+Fixed an almost-impossible file descriptor leak in libproc that
+occasionally showed up in long-running top sessions.
+
+The fix for top no longer depending on NR_TASKS in 2.0.5 was
+broken; top would die with more than 204 tasks.  This has been
+fixed.
+
+
+NEWS for version 2.0.5 of procps
+
+procps can build against the 2.3.18 kernel source; top no longer
+depends on NR_TASKS.
+
+sysctl no longer segfaults with -A; has a few parsing fixes
+
+
+NEWS for version 2.0.4 of procps
+
+sysctl can save/restore settings using /etc/sysctl.conf file
+
+top has -p option and N and A commands.
+
+vmstat doesn't mind interrupt counter overflow on long-running machines
+
+ps can now sort by PCPU.
+
+
+NEWS for version 2.0.3 of procps
+
+Time calculations fixed (or at least improved...) for SMP machines.
+In particular, hertz is calculated correctly.
+
+ps doesn't mind terminal resizing happening while it runs.
+
+sysctl is a nifty new program!  Try it!  Carefully!  :-)
+
+w PCPU and WHAT output fixed.
+
+top batch mode now works without a tty (for instance, via rsh).
+
+new version of watch has a few new features like --differences.
+
+sessreg removed; it belongs where it has been for a long time, in X.
+
+
+NEWS for version 2.0.2 of procps
+
+Removed xproc entirely; the only thing left there was XConsole, which
+was equivalent to xconsole -file /proc/kmsg.  Added an XConsole shell
+script which does
+exec xconsole -file /proc/kmsg "$@"
+so that folks who depend on XConsole won't be left behind.  This also
+fixes the bug that XConsole effectively removed limitations on which
+users were allowed to read /proc/kmsg without root having much choice
+in the matter other than to remove XConsole...
+
+Removed unused psupdate code (still available as part of procps-1.2.x
+for anyone who wants to play with it).
+
+Removed sessreg, as it is included in XFree86 and there is no reason
+for the duplication.
+
+Fixed version number generation so that it happens in one place.  I'm
+tired of releasing versions that misreport their version number...
+
+
+NEWS for version 2.0.1 of procps
+
+Reverted my changes that had broken Albert's Unix98 compliance.
+
+Major bugs fixed:
+ o  ps now returns failure for "ps <non-existant-pid>"
+ o  ps h has reverted to old linux behaviour except in BSD personality;
+    --headers and --no-headers long options have been added
+ o  watch buffer overrun fixed (no, not a security hole).
+
+top has -b and -n options added.
+
+
+NEWS for version 2.0 of procps
+
+Thanks to Albert Cahalan for his rewrite of ps, and for making
+the time for new development of ps that I just didn't have.  His
+research into how ps worked on lots of different UNIX systems
+really made this version much more usable for a lot of people.
+
+Read ALL of Albert's comments regarding his 1.9.0 release.  If
+you do not, you may be surprised when scripts fail.  I've tried
+to prepare people for the worst of this by giving out the much-detested
+  warning: `-' deprecated; use `ps aux', not `ps -aux'
+message for roughly a year and a half.
+
+
+NEWS for version 1.9.0 of procps
+
+The ps command now supports simultaneous BSD and Unix98 syntax.
+There have been many other changes in ps. (complete rewrite)
+
+Red Hat users should check /etc/sysconfig/network-scripts/ifdown-ppp
+for the bogus command "ps aul" and change it to "ps axl".
+
+Scripts that make assumptions about character position are and
+were broken because fields may overflow. Scripts should parse by
+whitespace and use -o to get the best results. Command names for
+swapped out processes are now shown in square brackets instead of
+parentheses, as required by the Unix98 standard. This problem can
+be avoided entirely by using a SysV format without -f, using the
+BSD "c" option, or using the format specifier "comm" with -o.
+
+Some uses of ps in /etc/rc.d/init.d/functions should probably
+eventually be changed to stuff somewhat like the following, which
+would eliminate the need for awk:
+
+PS_PERSONALITY=linux dead=`ps -p $pid -o pid=`
+PS_PERSONALITY=linux echo -n `ps -C $1 -o pid=`
+PS_PERSONALITY=linux pid=`ps -C $1 -o pid=`
+
+(or use the 'start-stop-daemon' program that Debian has)
+
+Obsolete files you may have: /etc/psdatabase, /etc/psdevtab,
+~/.psdevtab, /usr/man/man8/psupdate.8 and /usr/sbin/psupdate.
+
+
+NEWS for version 1.2.9 of procps
+
+psupdate has been REMOVED from the default install.  You can put it back
+if you want to by removing two comment characters in the Makefile, but
+I'm not going to tell you which ones.  <RANT>If you can't figure it out,
+there's a good chance that it won't build on your machine and I don't
+want the bug reports.  One of the main reasons I have removed it is that
+it is not necessary on a properly-maintained system (that is, System.map
+exists and is correct) and 4/5ths of the bug reports I have fielded in
+the past are from users who are not able to maintain a working system
+complaining that it doesn't work, despite the fact that the INSTALL file
+explains how to build it in different ways and how to disable it.</RANT>
+I will delete without response all email asking me how to build psupdate.
+
+ps now accepts and ignores the g option for those with fingers tied to
+the g key from too much exposure to BSD.  :-)
+
+Should build properly on ultrasparc -- two people sent the patch and
+the second time I didn't notice that the patch went in reverted.  So
+I have now reverted the reverted patch to get the right behavior.
+
+More reliable command-line parsing in vmstat has been added.  If vmstat
+didn't die for you before, you won't notice the difference...
+
+Fixed a bug where arbitrary users might have been able to corrupt the
+screen of arbitrary other users running top by creating processes with
+weird arguments.
+
+"ps ce" was missing a space character; fixed.
+
+
+NEWS for version 1.2.8 of procps
+
+procps.spec now uses buildroot, and can be built by non-root users.
+This means that the default install targets had to change in the
+makefiles so that the default install does not require root privs
+otherwise.  Also changed all "bin" ownership to "root" ownership,
+as kmem-ps has died out and folks won't be automatically doing
+"chmod u+s /bin/ps" and making their systems insecure thereby.
+
+XConsole no longer installed setuid root by default.  This is safer,
+if less convenient.  Change it if you want to use it as non-root.
+
+top had disgustingly buggy ^Z handling: you could only suspend once,
+and if you had suspended, you could no longer read WCHAN information.
+Both bugs are now fixed.
+
+xcpustate removed -- it was a very old, obsolete version.
+Get a new version from ftp://ftp.cs.toronto.edu/pub/jdd/xcpustate
+
+Now looks for kernel symbols in loadable modules.
+
+psupdate no longer included in RPM package; the theory is that
+systems maintained with RPM probably are reasonably recent and
+up-to-date and so psupdate is extraneous on those systems, only
+adding an unhelpful dependency on a particular version of
+binutils.
+
+top printed two spaces instead of just one between command-line arguments.
+
+
+NEWS for version 1.2.7 of procps
+
+Security hole in XConsole fixed.
+
+Works better with very long uptimes.
+
+Fixed RPM spec file to have correct libproc soname.
+
+skill -i works.
+
+Knows about more major numbers for smart serial boards.
+
+Fixed some small problems in top.
+
+
+NEWS for version 1.2.6 of procps
+
+Signal handling in top fixed in several ways.  Main effect: suspending
+works correctly with glibc now.
+
+Patch from Linus for change in some kernel structures to 64-bit.
+This means that libproc has changed soname, as the proc_t data
+structure has changed incompatibly.
+
+File descriptor leaks in libproc were fixed.
+
+
+NEWS for version 1.2.5 of procps
+
+Potential security hole fixed: if there was no /etc/psdevtab on
+a system, a user could put a link from /tmp/psdevtab to a file
+owned by another user, and when that other user ran ps, the
+file it pointed to would be killed.  This wouldn't work for
+root because root would be able to create /etc/psdevtab first,
+so arbitrary system files would not be killable.  This was fixed
+by avoiding psdevtab files that are symlinks or have link counts
+higher than 1, and by not looking for /tmp/psdevtab anymore, as
+the reason for it to exist is really obsolete.
+
+w -s output has been fixed.  Scripts that depend on w -s output
+should be examined.  The output was broken enough to warrant this
+change; it was extremely buggy.
+
+
+NEWS for version 1.2.4 of procps
+
+I_WANT_A_BROKEN_PS environment variable turns off usage warning.
+
+"w <username>" sense corrected.
+
+argument order problem fixed.
+
+XConsole doesn't dump core on exit any more (only fclose if the
+file is open...)
+
+NEWS for version 1.2.3 of procps
+
+psupdate moved to /usr/sbin, since it is no longer necessary.
+
+Added wmconfig for Red Hat systems (others can use it if they
+want; if/when wmconfig becomes standard, I'll move it into the
+install target of the makefile).
+
+Ugly hack to get around a problem some people were getting with
+a too-small variable.
+
+The usage message no longer recommends a deprecated syntax.  :-)
+
+NEWS for version 1.2.2 of procps
+
+Made procps report its version number correctly.
+Fixed typo in w.1 man page.
+
+NEWS for version 1.2.1 of procps
+
+New address for bug reports against procps: procps-bugs@redhat.com
+(That's not an official service of Red Hat Software, just an alias
+for me so that bugs get filed properly where I don't lose them...)
+
+Better memory checks.
+Fixed a file descriptor leak.
+In top, the space key updates immediately.
+Fixed broken signal code in top.
+Fixed broken screen size calculations in top.
+Fixed broken user count calculations in top.
+Fixed broken time difference calculations in w.
+Link libproc against libc explicitly for better co-existance with libc.
+
+NEWS for version 1.2 of procps
+
+Original author/maintainer, Michael K. Johnson <johnsonm@redhat.com>,
+has taken over maintenance again.
+
+psmisc removed, as it is better maintained separately.  xload removed,
+as it is better maintained as part of XFree86.  mknewpty removed, as
+it duplicates /dev/MAKEDEV and MAKEDEV is maintained.
+
+Support for 2.1.x kernels and for glibc 2.0.5.
+
+Lots of documentation updates.
+
+BFD support and shared library enabled by default.
+
+Binaries are no longer included in the archive -- get them from your
+favorite Linux distributor.
+
+free's display fixed in several ways: no such thing as cached swap,
+buffer+ display was incorrectly calculated, and buffer calculations
+didn't include cached memory.
+
+Removed mknewpty, because it wasn't perfect and collides with MAKEDEV.
+
+NEWS for version 1.01 of procps
+
+top bugfix release.  Fixes memory leak, extra line after loadavg and broken
+no-idle mode.  Also be a little more aggressive utmp 'from host' filtering in w.
+
+NEWS for version 1.00 of procps
+
+Fixed the ps -t without a -a segfault bug.  Royal screwup on my part.
+Updated libproc parsing routine for the new vsize output as of 1.3.91-ish.
+Expanded the room for the FLAGS in ps -l due to high order bits now being set.
+
+Added a brand new top to the distribution.  Highly run-time configurable.  The
+old top is available for at least a release or so as "top.old".  See the new man
+page for details, or just run it and type 'h'.  This is courtesy of Helmut
+Geyer.  Thanks Helmut!
+
+egrep -n '\<(tgetent|cm|top_clrtoeol|top_clrtobot|cl|ho|me|md|mr|tgoto)\>' on
+top.c and cleaned up all terminal strings being just dumped to stdout.  All such
+strings are now tputs'd out with putchar.  This should fix a lot of problems
+people have been having.
+
+Also cleaned up non-HZ based references to jiffies -> real time conversion. I
+may have missed one or two, though...
+
+Cleaned up some Makefile things.  make distclean; make should really work now.
+removed function pointer warnings in xcpustate.c.  .depend is properly removed
+as are the imake generated makefiles.  Shared lib generation bug for libproc
+fixed.  In general the build should be a lot cleaner, but may still have a glitch
+or two.
+
+Fixed a few (but probably not all) Alpha compatibility bugs dealing with memory
+alignment.  Please let me know if I missed any, or if you like give me a test
+account on some Alpha.  I don't have access to any Linux other than x86 which is
+notoriously forgiving about unaligned memory accesses.  Thanks to Alfred Arnold
+(a.arnold@kfa-juelich.de) and Harald Koenig for their help so far with this.
+
+Completely (well almost) re-wrote 'w'.  From on by default, J/PCPU display is
+accurate to 0.01s.  w and top use readproc, so snap.c has gone away completely.
+
+'skill' should actually work now, but you may have to use '-c', '-u', etc. to
+actually get it to parse the command line correctly.
+
+*** Significant changes in psdevtab inverse device name resolution
+------------------------------------------------------------------
+Extended semantics of the -n (numeric) option to output the full device number
+in hexadecimal for the tty field.  That way if /etc/psdevtab cannot be created
+rescanning the /dev directory can be short-circuited.  Fixed incorrect file
+creation mode for /etc/psdevtab.  I just forgot to alter my original 0 mode to
+something reasonable like 0664.  Added fallback locations for psdevtab.  First
+it tries /etc/psdevtab, then /tmp/psdevtab and then $HOME/.psdevtab.  Also,
+decided to go ahead and create the devtab file with regular old write instead of
+the rw mmap.  I still read it with mmap, but there should be no trouble with
+that even in ancient kernels.  Also changed the semantics such that if any
+devtab file is found, it is assumed to be correct regardless of the relative
+timestamps of /dev and the file.  Hopefully all this will avoid any unnecessary
+slowness.  I'm still willing to reactivate the older somewhat broken code to
+do the mapping without any file as a fallback if the file doesn't exist.
+
+*** Significant changes in psdatabase/WCHAN inverse name resolution
+-------------------------------------------------------------------
+libproc reads directly from System.map, so psupdate and /etc/psdatabase are no
+longer necessary.  Hopefully this will make kernel configuration management
+simpler requiring only the zImage and System.map, which being the stripped
+(compressed) kernel and the symbol table before stripping are natural
+complements.  This is probably how things should have been all along even going
+back to kmem ps days four years ago.  Oh well. This change should make dealing
+with x86, AXP, Sparc, etc binary formats for kernels a lot easier.  All that is
+required of System.map is that there be exactly 3 space delimited columns:
+   "address[single space]anything_with_no_spaces[single space]symbol[newline]"
+[ Well, ok, the addresses have to be zero padded so that lexicographic order is
+the same as numerical order and the addresses all have to be the same ascii
+length and I haven't tested to see how resilient it is to bogus internal records
+like multiple newlines in a row.  Hopefully aren't editting and commenting their
+System.map files. ;-)  This could actually work on "sort < /proc/ksyms" also if
+we generalize the behavior to work with either two or three columns. ]
+
+We take advantage of the following files in this order:
+    PS_SYSTEM_MAP                 # may only point to System.map, not psdatabase
+    /boot/System.map-`uname -r`,  # Note: this is the preferred path
+    /boot/System.map
+    /lib/modules/`uname -r`/System.map
+    /etc/psdatabase
+    /boot/psdatabase-`uname -r`,
+    /boot/psdatabase,
+    /lib/modules/`uname -r`/psdatabase
+
+The reason for the /lib/modules/*/file location is that I imagine many people
+have enough trouble keeping track of kernel version-dependent files in the
+filesystem as it is, so I didn't want to invent a new place.  I keep things in
+/boot myself and don't usually have more than 8 or so kernel versions, so this
+works for me.  I know not everyone uses modules, but it's just a search path
+folks...  In case it isn't obvious from all of the above, this means that
+psupdate is no longer necessary.  I include it in this release and include
+recognition of it because I realize that some distributions may have scripts
+which depend on the old semantics.  I doubt there will ever be a compelling
+reason to not include support for generating or reading the old psdatabase
+files, but they aren't necessary anymore if you have the System.map files from
+the kernel builds.
+
+I'm not 100% sold on the ordering of the search path, but I think it makes
+sense.  If you have reasons why it should be different I may be convincable.
+Also I'm interested in exactly what the output of 'nm' looks like on Alpha,
+Sparc, etc., architectures.  It should be easy enough to adapt the code if it is
+basically the same format of <0 padded hex> ... <symbol>\n.
+
+Thanks to Helmut Geyer for the idea of living off the System.map file natively
+and Mike Dean for the idea of using an approximate binary search instead of
+padding out the lines of System.map with tons of spaces to get equal record
+lengths.  The cost of doing it approximately is mild, mostly consisting of lots
+of scans to get to the next or last newline and a logarithmically few extra
+steps to get to the desired record.  Only an order of magnitude guess for the
+record length is necessary.  The code is all mine, though, so all bugs are due
+to me alone.
+
+NEWS for version 0.99a of procps
+================================
+This is a quick bug-fix release to solve a few thorny problems with my probably
+overzealous attempt to use the inline-assembly string.h and a Makefile bug or
+two.  It also fixes free to +/- the cached column too and makes it ignore the
+new extra-pretty non-numeric lines that /proc/meminfo is spitting out.  It also
+fixes the lack of set_linux_verion() in 'w' that caused no command line to be
+displayed.  I am still working on a much condensed 'w' that should be a lot
+easier to maintain and a readproctree that should be usable by both 'ps',
+'pstree', and 'w'.
+
+Apparently memory-mapped files are pretty broken before the early 1.3.X kernels
+so I may have to rewrite devname.c to not use MAP_WRITE to create psdevtab. Some
+people have been claiming that /dev changes at boot-up in some rc scripts. I'm
+not really sure why they would want to do that.  Seems kind of paranoid to
+continually re-make /dev/log.  Anyhow, I'm open to suggestions for psdevtab
+behaviors.  I've been thinking a /tmp/psdevtab fallback (with a careful world
+readable umask to avoid repetition) or maybe a $HOME/.psdevtab fallback too.
+
+NEWS for version 0.99 of procps
+===============================
+This file is a brief catalog of new features or developments in the package.
+For general information about using the programs see their man pages.
+
+NEW PACKAGES OR PROGRAMS
+========================
+
+LIBPROC
+    I've modularized some routines and fixed some long standing bugs.  Replaced
+    the regex() recognition of /proc/PID with a simple check of the first
+    character of the filename being a digit which should be just as safe.
+
+    Added an opendir/readdir/closedir style interface to the process table.  The
+    new interface seems cleaner, more intuitive and generally more applicable
+    (to me anyway).  The only program which uses the new interface is ps.  'w'
+    will follow soon.  'top' may take a while longer...  openproc,readproc, and
+    closeproc are implemented.  I still want to do readproctab and rewindproc,
+    too though.
+
+    Added some kernel and package versioning things to make it easier to be run
+    time adaptable.  Also updated sysinfo to understand any /proc/meminfo.  A
+    /proc/stat parser should probably be in there as well with the appropriate
+    updates to vmstat and xcpustate.
+
+    The general direction procps should move in is lightweight command-line or
+    X11/Motif display/format programs and compartmentalized libproc routines to
+    parse all of the /proc files.  This isolates the utilities from kernel
+    versioning.
+
+TTY DEVICE NUMBER TO NAME RESOLUTION
+    Tty device name <-> number mapping has been completely generalized.  It now
+    stat's every character special file in /dev and builds a memory mapped table
+    of device names indexed in a way that makes lookup of name from number a
+    fast, constant time process.  The extra overhead incurred by building
+    /etc/psdevtab is non-negligible if you have a large /dev and permissions to
+    write the file (or its directory) are required to update the file (which is
+    done if it does not exist or if /dev is newer than /etc/psdevtab).
+
+    Hence `root' should `ps' shortly after any modification to /dev (or chmod
+    666 /etc/psdevtab :-) to avoid ordinary users rebuilding it over and over.
+    Since such modifications are rare, hopefully having a fallback $HOME
+    location will not be necessary.  If the file is up to date, the overhead
+    incurred is very small.  The generality bought is essentially optimal since
+    `ps' tailors its notion of name<->devno mapping according to the /dev of the
+    local system which is the canonical repository of this information.
+
+    In principle the name database could encompass all device majors. The file
+    would be large, but since I use mmap to access it, only the pages with the
+    major of interest are ever actually read off the disk.  Right now I just use
+    the majors 2,3,4,5,19,20 which should cover both old and new systems with
+    both master and slave devices (I know... no reason for the masters... :-)
+    and the multiport serial devices.  Also the 'mknewpty' script is provided
+    to update your /dev directory to the new pty master/slave devices.
+
+    The tty abbreviation scheme has been rationalized to match device special
+    files.  The leading "tty" or "cu" is stripped, so cua3 -> a3, tty1 -> 1 and
+    ttyp9 -> p9.  The t flag in ps now works with a full device names and to
+    pick up processes even if they aren't owned by the owner of ps, e.g.
+    "ps tcua0" picks up gpm for everyone.  This seems desirable.
+
+WATCH
+    A little program similar to another called 'vis' which simply re-displays
+    in a polling fashion the output of other programs.  "watch ps --sort:utime"
+    might be dubbed a poor man's 'top'.  Though this has been included in procps
+    for some time it hasn't been built or installed by default.  It is now.
+
+SKILL/SNICE
+    I have written the necessary machine-dependant file for 'skill' and tested
+    it somewhat under Linux.  It seems to mostly work, but there are probably a
+    few glitches.  This is a generalization of the 'killall' concept.  You can
+    send signals or change priority based upon user, command basename (the same
+    that 'ps c' gives), terminal, etc.  If you have a user named 'satan' "skill
+    -u satan" will kill all their processes. :-) See the man page for more
+    details.
+
+    An annoyance of the current implementation is that although permission to
+    send signals is based upon the real user id, /proc only gives the effective
+    uids of processes.  Hence processes which you *could* kill because they're
+    suid-root (X say) won't be detected as kill-able.  Either /proc + readproc
+    need to be updated to report the *real* uid to skil or skill needs to try to
+    send the signal even if the uid doesn't match.
+
+
+CHANGES TO OLD PROGRAMS
+=======================
+
+MAKEFILE
+    The directory hierarchy has been restructured.  It is now easier to have the
+    multiple components to the suite under nearly autonomous administration.
+    The library code has also been moved to a subdirectory.  The best thing
+    about the new setup is that things like Imake generated Makefiles with
+    preconceived notions of 'make install' can be used without getting into the
+    business of re-writing component package makefiles.
+
+    There is now an option to build a shared libproc.a which reduces 'ps' and
+    'top' sizes by about 10K apiece.  Simply change the value of SHARED.  Also,
+    one may optionally install the library header files and archive/shared
+    object files into standard system directories.  There are no library man
+    pages yet, but the headers are fairly descriptive.
+
+PS
+    Several long standing bugs have been fixed and much of the internal code was
+    re-written to use my new directory-style interface to the process table.  In
+    particular if sorting is disabled (with '-o') the process entries are output
+    to the terminal as soon as possible (making it more helpful under heavy
+    system load).
+
+    I am considering several new additional features to `ps' including
+        regex filtering of which processes to list,
+        "grep -s" silent testing for existing of processes matching criteria,
+        run-time/user-defined output formats.
+    I would also like to completely phase the w,top code which uses the snapshot
+    interface instead of the 'readproc' interface.  And of course adding long
+    options for the rest of the options would be nice too (I may not get around
+    to doing this anytime really soon so patches which implement any of these
+    things would most likely be gleefully accepted).
+
+PSUPDATE
+    psupdate has been updated to work with ELF kernels.  If you compile it as
+    an ELF binary it will handle both a.out and ELF kernels.  If you compile it
+    as an a.out binary it will only handle a.out kernels.  Many thanks to Jeff
+    Uphoff.
+
+    psupdate has also been updated to work with any binary type supported
+    by libbfd.  This is the default configuration.
+    Many thanks to David Mossberger-Tang.
+
+TOP
+    A user-defined format would be nice here too.  Alternate sorting criteria
+    (top memory users instead of top CPU users, etc.) may be another interesting
+    alternative.  Of course the sorting in ps can do all of that, but it doesn't
+    have any optimal screen update action going down... :-)
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..a66d295
--- /dev/null
+++ b/TODO
@@ -0,0 +1,176 @@
+-------------------------- general ------------------------
+
+Implement /usr/proc/bin tools like Solaris has.
+
+The main Makefile is broken. Try 'touch top.h' to see.
+The Makefile is too clever, so it's still not fixed.
+
+Don't these really belong in the procps package?
+         killall   pstree   fuser   lsof   who
+(they are maintained elsewhere, which causes version problems)
+
+Cache results of dev_to_tty.
+
+---------------------- kernel -------------------------
+
+Add an "adopted child" flag to mark processes that are not
+natural children of init. This can make --forest work better.
+
+Add a thread group ID, to be shared by all tasks that are related by
+the clone() system call. This ID might be made unique from boot to
+shutdown, perhaps being a 16-bit CPU number and 48-bit per-CPU
+serial number.
+
+Make the kernel group /proc listing output by thread group.
+Without this, a thread-aware ps must always sort processes.
+
+Supply the task ID (the "PID"/"TID") of the thread group leader.
+I define "leader" as the first process of a thread group.
+
+Don't reuse the task ID of a thread group leader until all threads
+are dead. Better yet, don't let the leader exit.
+
+Supply better data for top's CPU state display. Currently top has
+to subtract old numbers from new numbers and divide that result by
+the number of processors. The kernel won't even supply the number
+of processors in a portable way.
+
+Mark threads, and supply a list of other threads.
+
+Supply data for the ADDR and JOBC fields.
+
+Support & supply data for SL and RE.
+
+Add a /proc/*/tty symlink to eliminate guessing when /proc/*/fd is
+not accessable.
+
+Put unique ID at the top of System.map and in /proc, to make sure
+there is never a mismatch.
+
+Maybe just put the symbols in the kernel like some other systems do.
+Raul Miller writes: You might want to implement this as user data which just
+happens to come from a wierd source -- instead of just freeing the memory
+(like happens with init code), you want it to wind up in the buffer cache.
+
+Add /proc/*/.bindata files to avoid string parsing. It should be an array
+of 64-bit values on all machines. New entries go on the end and obsolete
+ones get filled in with something logical -- entries must never be deleted!
+
+Add all the stuff Solaris has. This would also replace ptrace.
+
+The kernel should report these task_struct members:
+    has_cpu
+    processor
+
+---------------------- w --------------------------
+
+The LOGIN@ column sometimes has a space in it. This makes correct
+scripting difficult.
+
+Verify that DNS control does not give a user the power to specify
+arbitrary data for the FROM column. (could set root's VGA color map!)
+
+---------------------- vmstat --------------------------
+
+Extract /proc/stat parsing from vmstat into libproc somewhere.
+
+--------------------- libproc ----------------------
+
+Stop storing fields with duplicate info (often different
+units: kB and pages, seconds and jiffies) in the proc_t struct.
+
+---------------------- top -------------------------
+
+Share more stuff with ps.
+
+---------------- ps for now, maybe move to libproc ------------------
+
+With forest output and a tty named /dev/this_is_my_tty, the position
+of the command name gets messed up. (we print too many spaces) (fixed?)
+
+Fix missing stuff for these formats: FB_j FB_l FB_v HP_f HP_*_ HP_fl JFMT OL_m
+(jobc,cpu,sl,re,cpu,prmgrp,cp,m_size,m_swap,m_share,vm_lib,m_dt)
+Note that "cpu" has two meanings.
+
+Add Beowulf support. This is ugly, since the current patches use a
+daemon to collect info and add a HOST field after the PID field.
+
+Query optimizer, put cheap/required process selection first.
+
+Avoid reading both /proc/*/status and /proc/*/stat. Actually, avoid
+reading lots of stuff! Avoidance got disabled. (at least don't read the
+environment!!!)
+
+Maybe ps should put a 'C' in front of fields when they are affected
+by cumulative mode. Debian ps did that. (move flag to common.h...)
+
+Support printing the client hostname (the FROM that w(1) uses) in place
+of a pty. Maybe do this when PS_PERSONALITY=linux.
+
+Disgusting color support, for completeness. (Debian ps had it)
+
+Disambiguate narrow tty info. (/dev/tty7 == /dev/pts/7 now)
+1------8     1--4
+ttyS2        S2
+ttyI31       I31
+pts/7        7        Short form could be /999.
+pts/9999     9999     Short form could just be trunctuated to /999.
+tty7         7        Short form could be vc-7.
+tty63        63       Short form could be vc63.
+
+Internationalization, as specified by XPG3, Volume 1, Commands and Utilities.
+(and suggested by Unix98) LC_TIME affects date format.
+
+----------------------- ps -----------------------
+
+Solve the "ps reports wrong date" bug. (recent processes on an SMP system
+are reported as being 5 days old, while boot processes look normal)
+(reported for procps-2.0.2 in May 1999)
+
+Show real-time priorities. (type & number)
+
+Add an option to select all processes that a user can kill.
+(related to RUID, EUID, tty, etc. -- but maybe ignore root power)
+
+Add a nice display option for killing things.
+ruser,euser,ppid,pid,pmem,stime,args isn't too bad
+
+Make the column alignment algorithm support this:
+    FOO BAR
+    8 44444
+    453  45
+    45 2989
+    63666 0
+    34  333
+(useful for the UNIX tty and time values, since the time might look
+like 100-10:40:32 for old processes and the tty might have extra room)
+
+Improve long sort/format specifiers documentation and fill in the missing
+code as much as the kernel can support. Make sure that memory amounts are in
+pages when they should be and in kB when they should be, not backwards.
+
+output encoding: UTF8 --nul --null
+
+Make BSD formats use non-standard BSD time format, at least when it
+doesn't violate the "no whitespace" rule.
+
+Try to make -jl fit in 80 columns. Do we need more than 1000 pty devices,
+9 flag bits, etc.? (hmmm, Linux supports 2048 pty devices now, and we
+might also want to steal whitespace there when the time column overflows)
+
+When not in strict Unix98 mode, let foo=8 specify that foo is 8
+characters wide. Debian did that. Then foo=8=bar and foo=bar=8
+could change both header and width.
+
+Better unmangling of '?' as a tty. The shell destroys '?' when there
+is a filename that matches. If the argument seems like garbage,
+check for a file that might have screwed up the '?'.
+
+If the 'O' option is given something already implied by 'O',
+assume the user wanted a sorting option.
+
+Conflict:
+Digital THREAD is user,pcpu,pri,scnt,wchan,usertime,systime
+AIX     THREAD is uname,pid,ppid,tid,S,C,PRI,scount,WCHAN,F,tty,bnd,comm
+AIX looks like this:
+         USER   PID  PPID  TID S  C PRI SC   WCHAN   FLAG   TTY BND  CMD
diff --git a/XConsole b/XConsole
new file mode 100755 (executable)
index 0000000..b4ecf22
--- /dev/null
+++ b/XConsole
@@ -0,0 +1,5 @@
+#!/bin/sh
+# xconsole is capable of doing everything XConsole did, and is maintained...
+# it is not setuid root, so we just exit if called as non-root
+[ $(id -u) = 0 ] || exit 0
+exec xconsole -file /proc/kmsg "$@"
diff --git a/free.1 b/free.1
new file mode 100644 (file)
index 0000000..f33b8d1
--- /dev/null
+++ b/free.1
@@ -0,0 +1,45 @@
+.\"             -*-Nroff-*-
+.\"  This page Copyright (C) 1993 Matt Welsh, mdw@sunsite.unc.edu.
+.\"  Freely distributable under the terms of the GPL
+.TH FREE 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+free \- Display amount of free and used memory in the system
+.SH SYNOPSIS
+.BR "free " [ "\-b" " | " "\-k" " | " "\-m" "] [" "\-o" "] [" "\-s"
+.I delay
+.RB "] [" "\-t" "] [" "\-V" ]
+.SH DESCRIPTION
+\fBfree\fP displays the total amount of free and used physical and swap 
+memory in the system, as well as the buffers used by the kernel.
+The shared memory column should be ignored; it is obsolete.
+.SS Options
+The \fB-b\fP switch displays the amount of memory in bytes; the 
+\fB-k\fP switch (set by default) displays it in kilobytes; the \fB-m\fP
+switch displays it in megabytes.
+.PP
+The \fB-t\fP switch displays a line containing the totals.
+.PP
+The \fB-o\fP switch disables the display of a "buffer adjusted" line. Unless
+specified \fBfree\fP subtracts/adds buffer memory from/to the used/free memory
+reports (respectively!).
+.PP
+The \fB-s\fP switch activates continuous polling \fIdelay\fP seconds apart. You
+may actually specify any floating point number for \fIdelay\fP, 
+.BR usleep (3)
+is used for microsecond resolution delay times.
+.PP
+The \fB\-V\fP displays version information.
+.SH FILES
+.ta
+.IR /proc/meminfo "\-\- memory information"
+.fi
+
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top(1)
+
+.SH AUTHORS
+Written by Brian Edmonds. 
+
+Send bug reports to <acahalan@cs.uml.edu>
+
diff --git a/free.c b/free.c
new file mode 100644 (file)
index 0000000..c788d79
--- /dev/null
+++ b/free.c
@@ -0,0 +1,80 @@
+/* free.c - a /proc implementation of free */
+/* Dec14/92 by Brian Edmonds */
+/* Thanks to Rafal Maszkowski for the Total line */
+
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#define S(X) ( ((unsigned long long)(X) << 10) >> byteshift)
+
+static int byteshift = 10;
+static int total = 0;
+
+int main(int argc, char *argv[]){
+    int i;
+    int old_fmt = 0;
+    int rtime = 0;
+
+    /* check startup flags */
+    while( (i = getopt(argc, argv, "bkmos:tV") ) != -1 )
+        switch (i) {
+        case 'b': byteshift = 0;  break;
+        case 'k': byteshift = 10; break;
+        case 'm': byteshift = 20; break;
+        case 'o': old_fmt = 1; break;
+        case 's': rtime = 1000000 * atof(optarg); break;
+        case 't': total = 1; break;
+       case 'V': display_version(); exit(0);
+        default:
+         fprintf(stderr, "usage: %s [-b|-k|-m] [-o] [-s delay] [-t] [-V]\n", argv[0]);
+         return 1;
+    }
+
+    do {
+        meminfo();
+        printf("             total       used       free     shared    buffers     cached\n");
+        printf(
+            "%-7s %10Ld %10Ld %10Ld %10Ld %10Ld %10Ld\n", "Mem:",
+            S(kb_main_total),
+            S(kb_main_used),
+            S(kb_main_free),
+            S(kb_main_shared),
+            S(kb_main_buffers),
+            S(kb_main_cached)
+        );
+        if(!old_fmt){
+            printf(
+                "-/+ buffers/cache: %10Ld %10Ld\n", 
+                S(kb_main_used-kb_main_buffers-kb_main_cached),
+                S(kb_main_free+kb_main_buffers+kb_main_cached)
+            );
+        }
+        printf(
+            "%-7s %10Ld %10Ld %10Ld\n", "Swap:",
+            S(kb_swap_total),
+            S(kb_swap_used),
+            S(kb_swap_free)
+        );
+        if(total == 1){
+            printf(
+                "%-7s %10Ld %10Ld %10Ld\n", "Total:",
+                S(kb_main_total + kb_swap_total),
+                S(kb_main_used  + kb_swap_used),
+                S(kb_main_free  + kb_swap_free)
+            );
+        }
+        if(rtime){
+           fputc('\n', stdout);
+           fflush(stdout);
+           usleep(rtime);
+       }
+    } while(rtime);
+
+    return 0;
+}
diff --git a/gfinger.p b/gfinger.p
new file mode 100644 (file)
index 0000000..9ff9b31
--- /dev/null
+++ b/gfinger.p
@@ -0,0 +1,216 @@
+Return-Path: <qpliu@ulch.Princeton.EDU>
+Date: Wed, 24 Feb 93 23:37:45 -0500
+From: qpliu@ulch.Princeton.EDU (Quowong P Liu)
+To: johnsonm@stolaf.edu
+Subject: Suggestions for procps
+Reply-To: qpliu@princeton.edu
+
+[...]
+
+Since you are collecting programs that use the proc-fs, I have included
+some patches to GNU finger that allow it to read the proc information
+that it uses.
+
+[...]
+
+Here the diffs for GNU finger begin.
+-------------BEGIN---------------
+diff -ru finger-1.37.orig/lib/os.c finger-1.37/lib/os.c
+--- finger-1.37.orig/lib/os.c  Thu Oct 22 17:01:10 1992
++++ finger-1.37/lib/os.c       Mon Feb  8 00:56:32 1993
+@@ -23,7 +23,9 @@
+ #include <sys/types.h>
+ #include <sys/stat.h>
+ #include <sys/file.h>
++#ifndef linux
+ #include <sys/acct.h>
++#endif
+ #include <time.h>
+ #include <packet.h>
+@@ -43,9 +45,11 @@
+ #ifdef HAVE_PROC_FS
+ #include <sys/signal.h>
++#ifndef linux
+ #include <sys/fault.h>
+ #include <sys/syscall.h>
+ #include <sys/procfs.h>
++#endif /*linux*/
+ #endif
+ #include <general.h>
+@@ -193,6 +197,7 @@
+   long idle, current_time, get_last_access ();
+   char *hostname, *ttyloc, *tty_location ();
+ #ifdef HAVE_PROC_FS
++#ifndef linux
+   prpsinfo_t procinfo;
+   struct procent
+     {
+@@ -202,6 +207,16 @@
+       char fname[sizeof (procinfo.pr_fname)];
+     }
+   *proctable, *tableptr;
++#else
++  struct procent
++    {
++      time_t start;
++      int uid;
++      dev_t tty;
++      char fname[15];
++    }
++  *proctable, *tableptr;
++#endif /* linux */
+   int nprocs = 0;
+ #endif /* HAVE_PROC_FS */
+       
+@@ -235,6 +250,7 @@
+      searching through user list. */
+ #ifdef HAVE_PROC_FS
++#ifndef linux
+   {
+     extern char *savedir ();
+     char *dirstring = savedir ("/proc", 2*1024, &nprocs);
+@@ -280,6 +296,64 @@
+       free (dirstring);
+       }
+   }
++#else /* linux */
++  {
++
++    extern char *savedir ();
++    char *dirstring = savedir ("/proc", 2*1024, &nprocs);
++    char *proc;
++    int procfd;
++    char procfile[17], procstatbuffer[1024];
++    struct stat proc_fs_stat;
++    char procstate;
++    int tmp_tty; /* dev_t is unsigned, -1 means no controlling tty */
++    
++    /* Allocate process table */
++    if (nprocs)
++      proctable = (struct procent *) xmalloc (sizeof *proctable * nprocs);
++
++    /* Walk processes */
++    if (dirstring && nprocs)
++      {
++      for (tableptr = proctable, proc = dirstring;
++           *proc; proc += strlen (proc) + 1)
++        {
++          sprintf (procfile, "/proc/%.5s/stat", proc);
++
++          if ((procfd = open (procfile, O_RDONLY)) >= 0
++              && read (procfd, procstatbuffer, 1024))
++            {
++              /* see /usr/src/linux/fs/proc/array.c
++               * this is for 0.99.4
++               */
++              sscanf (procstatbuffer, "%*d (%14s %c %*d %*d %*d %d "
++                      "%*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u "
++                      "%*u %hd" /* " %*u %*u %*u %*u %*u %*u %*u %*u %*d %*d "
++                      "%*d %*d %*u" */,
++                      tableptr->fname, &procstate, &tmp_tty,
++                      &tableptr->start);
++              tableptr->fname[strlen(tableptr->fname)-1] = 0; /*trailing )*/
++              tableptr->tty = tmp_tty;
++              if (fstat (procfd, &proc_fs_stat) == 0)
++                      tableptr->uid = proc_fs_stat.st_uid;
++
++              /* Only collect non-zombies with controlling tty */
++              if (tmp_tty > 0 && procstate != 'Z')
++                {
++                  tableptr++;
++                }
++              else
++                nprocs--;
++              
++              close (procfd);
++            }
++          else
++            nprocs--;
++        }
++      free (dirstring);
++      }
++  }
++#endif /* linux */
+ #endif /* HAVE_PROC_FS */
+@@ -332,10 +406,14 @@
+           if (stat (device_name, &finfo) != -1)
+             {
++#ifdef linux
++              dev_t tty = finfo.st_rdev&0xff;
++#else
+ #ifdef HAVE_ST_RDEV
+               dev_t tty = finfo.st_rdev;
+ #else
+               dev_t tty = finfo.st_dev;
++#endif
+ #endif
+               int file;
+diff -ru finger-1.37.orig/lib/packet.c finger-1.37/lib/packet.c
+--- finger-1.37.orig/lib/packet.c      Wed Oct 21 18:04:38 1992
++++ finger-1.37/lib/packet.c   Mon Feb  8 01:10:00 1993
+@@ -348,7 +348,9 @@
+         perror (filename);
+     }
++#ifndef linux /* doesn't have fsync (as of .99.4) */
+   fsync (file);
++#endif
+   close (file);
+ }
+diff -ru finger-1.37.orig/lib/savedir.c finger-1.37/lib/savedir.c
+--- finger-1.37.orig/lib/savedir.c     Fri Oct 16 21:52:40 1992
++++ finger-1.37/lib/savedir.c  Mon Feb  8 01:06:47 1993
+@@ -132,7 +132,7 @@
+ char *
+ stpcpy (dest, source)
+      char *dest;
+-     char *source;
++     const char *source; /* gcc insists on prototype consistency */
+ {
+   while ((*dest++ = *source++) != '\0')
+     /* Do nothing. */ ;
+diff -ru finger-1.37.orig/src/finger.c finger-1.37/src/finger.c
+--- finger-1.37.orig/src/finger.c      Wed Oct 21 20:41:13 1992
++++ finger-1.37/src/finger.c   Mon Feb  8 00:56:32 1993
+@@ -192,6 +192,9 @@
+   long addr;
+   char *finger_server = NULL;
+   int suppress_hostname = 0;
++#if defined(linux) /* gethostbyaddr evidently returns pointer to static area */
++  int do_hostfree = 0;
++#endif /* linux */
+   username = savestring (arg);
+@@ -237,6 +240,9 @@
+       {
+         host = (struct hostent *) xmalloc (sizeof (struct hostent));
+         host->h_name = hostname;
++#if defined (linux)
++        do_hostfree = 1;
++#endif /* linux */
+       }
+     }
+   else
+@@ -296,6 +302,11 @@
+   if (finger_server)
+     free (finger_server);
++#if !defined (linux)
+   if (host)
+     free (host);
++#else
++  if (host && do_hostfree)
++    free (host);
++#endif
+ }
+-------------END-------------
diff --git a/glibc.patch b/glibc.patch
new file mode 100644 (file)
index 0000000..5ac7f14
--- /dev/null
@@ -0,0 +1,32 @@
+From: Nix <nix@esperi.demon.co.uk>
+Date: Tue, 2 Mar 1999 05:25:08 +0000 (GMT)
+Subject: Re: [PATCH] Re: that fputs() thing
+
+
+1999-03-02  Nix  <nix@esperi.demon.co.uk>
+
+       * stdlib/fputs.c (fputs): Return `len' rather than 0 on success.
+
+*** 2.1/stdio/fputs.c.orig      Tue Mar  2 05:17:12 1999
+--- 2.1/stdio/fputs.c   Tue Mar  2 05:18:07 1999
+***************
+*** 27,35 ****
+  {
+    const size_t len = strlen (s);
+    if (len == 1)
+!     return putc (*s, stream) == EOF ? EOF : 0;
+    if (fwrite ((void *) s, 1, len, stream) != len)
+      return EOF;
+!   return 0;
+  }
+  weak_alias (fputs, fputs_unlocked)
+--- 27,35 ----
+  {
+    const size_t len = strlen (s);
+    if (len == 1)
+!     return putc (*s, stream) == EOF ? EOF : len;
+    if (fwrite ((void *) s, 1, len, stream) != len)
+      return EOF;
+!   return len;
+  }
+  weak_alias (fputs, fputs_unlocked)
diff --git a/kill.1 b/kill.1
new file mode 100644 (file)
index 0000000..66197df
--- /dev/null
+++ b/kill.1
@@ -0,0 +1,96 @@
+,\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for kill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan; converted to a man page by
+.\" Michael K. Johnson
+.TH KILL 1 "November 21, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+kill \- report process status
+
+.SH SYNOPSIS
+.TS
+l l.
+kill pid ...   Send SIGTERM to every process listed.
+kill signal pid ...    Send a signal to every process listed.
+kill -s signal pid ... Send a signal to every process listed.
+kill -l        List all signal names.
+kill -L        List all signal names in a nice table.
+kill -l signal Convert a signal number into a name.
+.TE
+
+.SH DESCRIPTION
+The default signal for kill is TERM. Use -l or -L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
+
+.SH SIGNALS
+The signals listed below may be available for use with kill.
+When known constant, numbers and default behavior are shown.
+
+.TS
+lB rB lB lB
+lfCW r l l.
+Name   Num     Action  Description
+.TH
+ALRM   14      exit
+HUP    1       exit
+INT    2       exit
+KILL   9       exit    this signal may not be blocked
+PIPE   13      exit
+POLL           exit
+PROF           exit
+TERM   15      exit
+USR1           exit
+USR2           exit
+VTALRM         exit
+STKFLT         exit    may not be implemented
+PWR            ignore  may exit on some systems
+WINCH          ignore
+CHLD           ignore
+URG            ignore
+TSTP           stop    may interact with the shell
+TTIN           stop    may interact with the shell
+TTOU           stop    may interact with the shell
+STOP           stop    this signal may not be blocked
+CONT           restart continue if stopped, otherwise ignore
+ABRT   6       core
+FPE    8       core
+ILL    4       core
+QUIT   3       core
+SEGV   11      core
+TRAP   5       core
+SYS            core    may not be implemented
+EMT            core    may not be implemented
+BUS            core    core dump may fail
+XCPU           core    core dump may fail
+XFSZ           core    core dump may fail
+.TE
+
+.SH NOTES
+Your shell (command line interpreter) may have a built-in kill command.
+You may need to run the command described here as /bin/kill to solve
+the conflict.
+
+.SH EXAMPLES
+kill -9 -1
+.br
+kill -l 11
+.br
+kill -L
+.br
+kill 123 543 2341 3453
+
+.SH "SEE ALSO"
+top(1) skill(1) kill(2) renice(1) nice(1)
+
+.SH STANDARDS
+This command meets appropriate standards. The -L flag is Linux-specific.
+
+.SH AUTHOR
+Albert Cahalan <acahalan@cs.uml.edu> wrote kill in 1999 to replace the
+version that was not standards compliant. Michael K. Johnson
+<johnsonm@redhat.com> is the current maintainer of the procps collection.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/minimal.c b/minimal.c
new file mode 100644 (file)
index 0000000..0582d34
--- /dev/null
+++ b/minimal.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* This is a minimal /bin/ps, designed to be smaller than the old ps
+ * while still supporting some of the more important features of the
+ * new ps. (for total size, note that this ps does not need libproc)
+ * It is suitable for Linux-on-a-floppy systems only.
+ *
+ * Maintainers: do not compile or install for normal systems.
+ * Anyone needing this will want to tweak their compiler anyway.
+ */
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/dir.h>
+
+#include <asm/param.h>  /* HZ */
+#include <asm/page.h>   /* PAGE_SIZE */
+
+static int P_euid;
+static int P_pid;
+static char P_cmd[16];
+static char P_state;
+static int P_ppid, P_pgrp, P_session, P_tty, P_tpgid;
+static unsigned long P_flags, P_min_flt, P_cmin_flt, P_maj_flt, P_cmaj_flt, P_utime, P_stime;
+static long P_cutime, P_cstime, P_priority, P_nice, P_timeout, P_it_real_value;
+static unsigned long P_start_time, P_vsize;
+static long P_rss;
+static unsigned long P_rss_rlim, P_start_code, P_end_code, P_start_stack, P_kstk_esp, P_kstk_eip;
+static unsigned P_signal, P_blocked, P_sigignore, P_sigcatch;
+static unsigned long P_wchan, P_nswap, P_cnswap;
+
+
+#if 0
+static int screen_cols = 80;
+static int w_count;
+#endif
+
+static int want_one_pid;
+static const char *want_one_command;
+static int select_notty;
+static int select_all;
+
+static int ps_format;
+static int old_h_option;
+
+/* we only pretend to support this */
+static int show_args;    /* implicit with -f and all BSD options */
+static int bsd_c_option; /* this option overrides the above */
+
+static int ps_argc;    /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg;    /* index into ps_argv */
+static char *flagptr;  /* current location in ps_argv[thisarg] */
+
+
+#ifndef PAGE_SIZE
+#warning PAGE_SIZE not defined, assuming it is 4096
+#define PAGE_SIZE 4096
+#endif
+
+#ifndef HZ
+#warning HZ not defined, assuming it is 100
+#define HZ 100
+#endif
+
+
+
+static void usage(void){
+  fprintf(stderr,
+    "-C   select by command name (minimal ps only accepts one)\n"
+    "-p   select by process ID (minimal ps only accepts one)\n"
+    "-e   all processes (same as ax)\n"
+    "a    all processes w/ tty, including other users\n"
+    "x    processes w/o controlling ttys\n"
+    "-f   full format\n"
+    "-j,j job control format\n"
+    "v    virtual memory format\n"
+    "-l,l long format\n"
+    "u    user-oriented format\n"
+    "-o   user-defined format (limited support, only \"ps -o pid=\")\n"
+    "h    no header\n"
+/*
+    "-A   all processes (same as ax)\n"
+    "c    true command name\n"
+    "-w,w wide output\n"
+*/
+  );
+  exit(1);
+}
+
+/*
+ * Return the next argument, or call the usage function.
+ * This handles both:   -oFOO   -o FOO
+ */
+static const char *get_opt_arg(void){
+  const char *ret;
+  ret = flagptr+1;    /* assume argument is part of ps_argv[thisarg] */
+  if(*ret) return ret;
+  if(++thisarg >= ps_argc) usage();   /* there is nothing left */
+  /* argument is the new ps_argv[thisarg] */
+  ret = ps_argv[thisarg];
+  if(!ret || !*ret) usage();
+  return ret;
+}
+
+
+/* return the PID, or 0 if nothing good */
+static void parse_pid(const char *str){
+  char *endp;
+  int num;
+  if(!str)            goto bad;
+  num = strtol(str, &endp, 0);
+  if(*endp != '\0')   goto bad;
+  if(num>0x7fff)      goto bad;  /* Linux PID limit */
+  if(num<1)           goto bad;
+  if(want_one_pid)    goto bad;
+  want_one_pid = num;
+  return;
+bad:
+  usage();
+}
+
+/***************** parse SysV options, including Unix98  *****************/
+static void parse_sysv_option(void){
+  do{
+    switch(*flagptr){
+    /**** selection ****/
+    case 'C': /* end */
+      if(want_one_command) usage();
+      want_one_command = get_opt_arg();
+      return; /* can't have any more options */
+    case 'p': /* end */
+      parse_pid(get_opt_arg());
+      return; /* can't have any more options */
+    case 'A':
+    case 'e':
+      select_all++;
+      select_notty++;
+case 'w':    /* here for now, since the real one is not used */
+      break;
+    /**** output format ****/
+    case 'f':
+      show_args = 1;
+      /* FALL THROUGH */
+    case 'j':
+    case 'l':
+      if(ps_format) usage();
+      ps_format = *flagptr;
+      break;
+    case 'o': /* end */
+      /* We only support a limited form: "ps -o pid="  (yes, just "pid=") */
+      if(strcmp(get_opt_arg(),"pid=")) usage();
+      if(ps_format) usage();
+      ps_format = 'o';
+      old_h_option++;
+      return; /* can't have any more options */
+    /**** other stuff ****/
+#if 0
+    case 'w':
+      w_count++;
+      break;
+#endif
+    default:
+      usage();
+    } /* switch */
+  }while(*++flagptr);
+}
+
+/************************* parse BSD options **********************/
+static void parse_bsd_option(void){
+  do{
+    switch(*flagptr){
+    /**** selection ****/
+    case 'a':
+      select_all++;
+      break;
+    case 'x':
+      select_notty++;
+      break;
+    case 'p': /* end */
+      parse_pid(get_opt_arg());
+      return; /* can't have any more options */
+    /**** output format ****/
+    case 'j':
+    case 'l':
+    case 'u':
+    case 'v':
+      if(ps_format) usage();
+      ps_format = 0x80 | *flagptr;  /* use 0x80 to tell BSD from SysV */
+      break;
+    /**** other stuff ****/
+    case 'c':
+      bsd_c_option++;
+#if 0
+      break;
+#endif
+    case 'w':
+#if 0
+      w_count++;
+#endif
+      break;
+    case 'h':
+      old_h_option++;
+      break;
+    default:
+      usage();
+    } /* switch */
+  }while(*++flagptr);
+}
+
+#if 0
+/* not used yet */
+static void choose_dimensions(void){
+  struct winsize ws;
+  char *columns;
+  /* screen_cols is 80 by default */
+  if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>30) screen_cols = ws.ws_col;
+  columns = getenv("COLUMNS");
+  if(columns && *columns){
+    long t;
+    char *endptr;
+    t = strtol(columns, &endptr, 0);
+    if(!*endptr && (t>30) && (t<(long)999999999)) screen_cols = (int)t;
+  }
+  if(w_count && (screen_cols<132)) screen_cols=132;
+  if(w_count>1) screen_cols=999999999;
+}
+#endif
+
+static void arg_parse(int argc, char *argv[]){
+  int sel = 0;  /* to verify option sanity */
+  ps_argc = argc;
+  ps_argv = argv;
+  thisarg = 0;
+  /**** iterate over the args ****/
+  while(++thisarg < ps_argc){
+    flagptr = ps_argv[thisarg];
+    switch(*flagptr){
+    case '0' ... '9':
+      show_args = 1;
+      parse_pid(flagptr);
+      break;
+    case '-':
+      flagptr++;
+      parse_sysv_option();
+      break;
+    default:
+      show_args = 1;
+      parse_bsd_option();
+      break;
+    }
+  }
+  /**** sanity check and clean-up ****/
+  if(want_one_pid) sel++;
+  if(want_one_command) sel++;
+  if(select_notty || select_all) sel++;
+  if(sel>1 || select_notty>1 || select_all>1 || bsd_c_option>1 || old_h_option>1) usage();
+  if(bsd_c_option) show_args = 0;
+}
+
+/* return 1 if it works, or 0 for failure */
+static int stat2proc(int pid) {
+    char buf[800]; /* about 40 fields, 64-bit decimal is about 20 chars */
+    int num;
+    int fd;
+    char* tmp;
+    struct stat sb; /* stat() used to get EUID */
+    snprintf(buf, 32, "/proc/%d/stat", pid);
+    if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return 0;
+    num = read(fd, buf, sizeof buf - 1);
+    fstat(fd, &sb);
+    P_euid = sb.st_uid;
+    close(fd);
+    if(num<80) return 0;
+    buf[num] = '\0';
+    tmp = strrchr(buf, ')');      /* split into "PID (cmd" and "<rest>" */
+    *tmp = '\0';                  /* replace trailing ')' with NUL */
+    /* parse these two strings separately, skipping the leading "(". */
+    memset(P_cmd, 0, sizeof P_cmd);          /* clear */
+    sscanf(buf, "%d (%15c", &P_pid, P_cmd);  /* comm[16] in kernel */
+    num = sscanf(tmp + 2,                    /* skip space after ')' too */
+       "%c "
+       "%d %d %d %d %d "
+       "%lu %lu %lu %lu %lu %lu %lu "
+       "%ld %ld %ld %ld %ld %ld "
+       "%lu %lu "
+       "%ld "
+       "%lu %lu %lu %lu %lu %lu "
+       "%u %u %u %u " /* no use for RT signals */
+       "%lu %lu %lu",
+       &P_state,
+       &P_ppid, &P_pgrp, &P_session, &P_tty, &P_tpgid,
+       &P_flags, &P_min_flt, &P_cmin_flt, &P_maj_flt, &P_cmaj_flt, &P_utime, &P_stime,
+       &P_cutime, &P_cstime, &P_priority, &P_nice, &P_timeout, &P_it_real_value,
+       &P_start_time, &P_vsize,
+       &P_rss,
+       &P_rss_rlim, &P_start_code, &P_end_code, &P_start_stack, &P_kstk_esp, &P_kstk_eip,
+       &P_signal, &P_blocked, &P_sigignore, &P_sigcatch,
+       &P_wchan, &P_nswap, &P_cnswap
+    );
+/*    fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+    P_vsize /= 1024;
+    P_rss *= (PAGE_SIZE/1024);
+    if(num < 30) return 0;
+    if(P_pid != pid) return 0;
+    return 1;
+}
+
+static const char *do_time(unsigned long t){
+  int hh,mm,ss;
+  static char buf[32];
+  int cnt = 0;
+  t /= HZ;
+  ss = t%60;
+  t /= 60;
+  mm = t%60;
+  t /= 60;
+  hh = t%24;
+  t /= 24;
+  if(t) cnt = snprintf(buf, sizeof buf, "%d-", (int)t);
+  snprintf(cnt + buf, sizeof(buf)-cnt, "%02d:%02d:%02d", hh, mm, ss);
+  return buf;
+}
+
+static const char *do_user(void){
+  static char buf[32];
+  static struct passwd *p;
+  static int lastuid = -1;
+  if(P_euid != lastuid){
+    p = getpwuid(P_euid);
+    if(p) snprintf(buf, sizeof buf, "%-8.8s", p->pw_name);
+    else  snprintf(buf, sizeof buf, "%5d   ", P_euid);
+  }
+  return buf;
+}
+
+static const char *do_cpu(int longform){
+  static char buf[8];
+  strcpy(buf," -  ");
+  if(!longform) buf[2] = '\0';
+  return buf;
+}
+
+static const char *do_mem(int longform){
+  static char buf[8];
+  strcpy(buf," -  ");
+  if(!longform) buf[2] = '\0';
+  return buf;
+}
+
+static const char *do_stime(void){
+  static char buf[32];
+  strcpy(buf,"  -  ");
+  return buf;
+}
+
+static void print_proc(void){
+  char tty[16];
+  snprintf(tty, sizeof tty, "%3d,%-3d", (P_tty>>8)&0xff, P_tty&0xff);
+  switch(ps_format){
+  case 0:
+    printf("%5d %s %s", P_pid, tty, do_time(P_utime+P_stime));
+    break;
+  case 'o':
+    printf("%d\n", P_pid);
+    return; /* don't want the command */
+  case 'l':
+    printf(
+      "%03x %c %5d %5d %5d %s %3d %3d - "
+      "%5ld %06x %s %s",
+      (unsigned)P_flags&0x777, P_state, P_euid, P_pid, P_ppid, do_cpu(0),
+      (int)P_priority, (int)P_nice, P_vsize/(PAGE_SIZE/1024),
+      (unsigned)(P_wchan&0xffffff), tty, do_time(P_utime+P_stime)
+    );
+    break;
+  case 'f':
+    printf(
+      "%8s %5d %5d %s %s %s %s",
+      do_user(), P_pid, P_ppid, do_cpu(0), do_stime(), tty, do_time(P_utime+P_stime)
+    );
+    break;
+  case 'j':
+    printf(
+      "%5d %5d %5d %s %s",
+      P_pid, P_pgrp, P_session, tty, do_time(P_utime+P_stime)
+    );
+    break;
+  case 'u'|0x80:
+    printf(
+      "%8s %5d %s %s %5ld %4ld %s %c %s %s",
+      do_user(), P_pid, do_cpu(1), do_mem(1), P_vsize, P_rss, tty, P_state,
+      do_stime(), do_time(P_utime+P_stime)
+    );
+    break;
+  case 'v'|0x80:
+    printf(
+      "%5d %s %c %s %6d   -   - %5d %s",
+      P_pid, tty, P_state, do_time(P_utime+P_stime), (int)P_maj_flt,
+      (int)P_rss, do_mem(1)
+    );
+    break;
+  case 'j'|0x80:
+    printf(
+      "%5d %5d %5d %5d %s %5d %c %5d %s",
+      P_ppid, P_pid, P_pgrp, P_session, tty, P_tpgid, P_state, P_euid, do_time(P_utime+P_stime)
+    );
+    break;
+  case 'l'|0x80:
+    printf(
+      "%03x %5d %5d %5d %3d %3d "
+      "%5ld %4ld %06x %c %s %s",
+      (unsigned)P_flags&0x777, P_euid, P_pid, P_ppid, (int)P_priority, (int)P_nice,
+      P_vsize, P_rss, (unsigned)(P_wchan&0xffffff), P_state, tty, do_time(P_utime+P_stime)
+    );
+    break;
+  default:
+  }
+  if(show_args) printf(" [%s]\n", P_cmd);
+  else          printf(" %s\n", P_cmd);
+}
+
+
+int main(int argc, char *argv[]){
+  arg_parse(argc, argv);
+#if 0
+  choose_dimensions();
+#endif
+  if(!old_h_option){
+    const char *head;
+    switch(ps_format){
+    default: /* can't happen */
+    case 0:        head = "  PID TTY         TIME CMD"; break;
+    case 'l':      head = "  F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN    TTY       TIME CMD"; break;
+    case 'f':      head = "USER       PID  PPID  C STIME   TTY       TIME CMD"; break;
+    case 'j':      head = "  PID  PGID   SID TTY         TIME CMD"; break;
+    case 'u'|0x80: head = "USER       PID %CPU %MEM   VSZ  RSS   TTY   S START     TIME COMMAND"; break;
+    case 'v'|0x80: head = "  PID   TTY   S     TIME  MAJFL TRS DRS   RSS %MEM COMMAND"; break;
+    case 'j'|0x80: head = " PPID   PID  PGID   SID   TTY   TPGID S   UID     TIME COMMAND"; break;
+    case 'l'|0x80: head = "  F   UID   PID  PPID PRI  NI   VSZ  RSS WCHAN  S   TTY       TIME COMMAND"; break;
+    }
+    printf("%s\n",head);
+  }
+  if(want_one_pid){
+    if(stat2proc(want_one_pid)) print_proc();
+    else exit(1);
+  }else{
+    struct direct *ent;          /* dirent handle */
+    DIR *dir;
+    int ouruid;
+    int found_a_proc;
+    found_a_proc = 0;
+    ouruid = getuid();
+    dir = opendir("/proc");
+    while(( ent = readdir(dir) )){
+      if(*ent->d_name<'0' || *ent->d_name>'9') continue;
+      if(!stat2proc(atoi(ent->d_name))) continue;
+      if(want_one_command){
+        if(strcmp(want_one_command,P_cmd)) continue;
+      }else{
+        if(!select_notty && P_tty==-1) continue;
+        if(!select_all && P_euid!=ouruid) continue;
+      }
+      found_a_proc++;
+      print_proc();
+    }
+    closedir(dir);
+    exit(!found_a_proc);
+  }
+  return 0;
+}
diff --git a/oldps.1 b/oldps.1
new file mode 100644 (file)
index 0000000..69e6f74
--- /dev/null
+++ b/oldps.1
@@ -0,0 +1,356 @@
+.\" This file Copyright 1992, 1997 Michael K. Johnson <johnsonm@redhat.com>
+.\" and 1996 Charles L. Blake <cblake@bbn.com>
+.\" It may be distributed under the GNU Public License, version 2, or
+.\" any higher version.  See section COPYING of the GNU Public license
+.\" for conditions under which this file may be redistributed.
+.TH PS 1 "3 Sep 1997" "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+ps \- report process status
+.SH SYNOPSIS
+ps [\fBlujsvmaxScewhrnu\fR] [\fBt\fIxx\fR] \
+[\fBO\fR[\fB+\fR|\fB-\fR]\fIk1\fR[[\fB+\fR|\fB-\fR]\fIk2\fR...]] \
+[\fIpids\fR]
+
+there are also three long options:
+
+.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]]
+
+.B "\-\-help"
+
+.B "\-\-version"
+
+More long options are on the way...
+.SH DESCRIPTION
+.B "ps "
+gives a snapshot of the current processes.  If you want a repetitive
+update of this status, use
+.BR top .
+This man page documents the
+.IR /proc -based
+version of
+.BR ps ,
+or tries to.
+.PP
+.SH "COMMAND-LINE OPTIONS"
+The command-line options for this version of
+.B ps
+are derived from the BSD version of
+.BR ps ,
+not the System V version.
+
+The command-line arguments should \fBnot\fP be preceeded by a `\-' character,
+because in the future, a `\-' will be used to indicate Unix98-standard
+command-line arguments, while no `\-' will indicate the current
+``extended BSD'' style of command line arguments.
+
+For now, ps will give you a warning if you use a `\-' for a short option,
+but it will still work.  If you have shell scripts which use BSD-style
+arguments to ps, take heed of the warning and fix them, or else your
+scripts will fail to function correctly at some point in the future.
+If you want to turn off the warnings, set the
+.B I_WANT_A_BROKEN_PS
+environment variable. 
+
+There are also some ``long options'' in GNU style; see below for those.
+.PP
+.PD 0
+.TP 0.5i
+.B "l "
+long format
+.TP 0.5i
+.B "u "
+user format: gives user name and start time
+.TP 0.5i
+.B "j "
+jobs format: pgid sid
+.TP 0.5i
+.B "s"
+signal format
+.TP 0.5i
+.B "v "
+vm format
+.TP 0.5i
+.B "m "
+displays memory info (combine with
+.B p
+flag to get number of pages).
+.TP 0.5i
+.B "f "
+"forest" family tree format for command line
+.TP 0.5i
+.B "a "
+show processes of other users too
+.TP 0.5i
+.B "x "
+show processes without controlling terminal
+.TP 0.5i
+.B "S "
+add child cpu time and page faults
+.TP 0.5i
+.B "c "
+command name from task_struct
+.TP 0.5i
+.B "e "
+show environment after command line and ` + '
+.TP 0.5i
+.B "w "
+wide output: don't truncate command lines to fit on one line.
+To be exact, every w that is specified will add another possible
+line to the output. If the space isn't needed it isn't used. You
+may up to 100
+.BR w 's.
+.TP 0.5i
+.B "h "
+no header
+.TP 0.5i
+.B "r "
+running procs only
+.TP 0.5i
+.B "n "
+numeric output for
+.BR USER " and " WCHAN .
+.PD 1
+.TP 0.5i
+.BI t xx
+only procs with controlling tty \fIxx\fR; for \fIxx\fR you may use either the
+name of a device file under "/dev" or that name with either
+.IR tty " or " cu
+sliced off.  This is the reverse heuristic that ps uses to print out the
+abbreviated tty name in the \fBTT\fR field, e.g.
+.BR "ps t1" .
+.TP 0.5i
+.BR O [ + | - ] \fIk1 [,[ + | - ] \fIk2 [, ... ]]
+Order the process listing according to the multi-level sort specified by
+the sequence of \fIshort\fR keys from \fBSORT KEYS\fR, \fIk1\fR, \fIk2\fR, ...
+Default order specifications exist for each of the various formats of \fBps\fR.
+These are over-ridden by a user specified ordering.  The `+' is quite optional,
+merely re-iterating the default direction on a key.  `-' reverses direction only
+on the key it precedes.  As with \fBt\fR and \fIpids\fR, the O option must be
+the last option in a single command argument, but specifications in successive
+arguments are catenated.
+.TP 0.5i
+.I pids
+List only the specified processes; they are comma-delimited.  The 
+list must be given immediately after the last option in a single command-line
+argument, with no intervening space, e.g.
+.BR "ps j1,4,5" .
+Lists specified in subsequent arguments are catenated, e.g.
+.B ps l 1,2 3,4 5 6
+will list all of the processes 1-6 in long format.  If pids are given, they
+are listed no matter what.  If a tty is given matching processes are listed
+no matter what.  These two features override the 'a' and 'x' flags.
+.SH "LONG COMMAND\-LINE OPTIONS"
+These options are preceeded by a double\-hyphen.
+.TP 0.5i
+.BR \-\-sort\fIX [ + | - ] \fIkey [,[ + | - ] \fIkey [, ... ]]
+Choose a \fImulti-letter key\fR from the \fBSORT KEYS\fR section. \fIX\fR may be
+any convenient separator character.  To be GNU-ish use `='.  The `+' is really
+optional since default direction is increasing numerical or lexicographic order.
+E.g.:
+.B ps jax --sort=uid,-ppid,+pid
+.TP 0.5i
+.B "\-\-help"
+Get a help message that summarizes the usage and gives a list of
+supported sort keys.  This list may be more up to date than this man
+page.
+.TP 0.5i
+.B "\-\-version"
+Display version and source of this program.
+.SH "SORT KEYS"
+Note that the values used in sorting are the internal values \fBps\fR uses and
+\fInot\fR the `cooked' values used in some of the output format fields.  If
+someone wants to volunteer to write special comparison functions for the cooked
+values, ... ;-)
+
+SHORT   LONG            DESCRIPTION
+.PD 0
+.TP 0.5i
+c       cmd             simple name of executable
+.TP 0.5i
+C       cmdline         full command line
+.TP 0.5i
+f       flags           flags as in long format F field
+.TP 0.5i
+g       pgrp            process group ID
+.TP 0.5i
+G       tpgid           controlling tty process group ID
+.TP 0.5i
+j       cutime          cumulative user time
+.TP 0.5i
+J       cstime          cumulative system time
+.TP 0.5i
+k       utime           user time
+.TP 0.5i
+K       stime           system time
+.TP 0.5i
+m       min_flt         number of minor page faults
+.TP 0.5i
+M       maj_flt         number of major page faults
+.TP 0.5i
+n       cmin_flt        cumulative minor page faults
+.TP 0.5i
+N       cmaj_flt        cumulative major page faults
+.TP 0.5i
+o       session         session ID
+.TP 0.5i
+p       pid             process ID
+.TP 0.5i
+P       ppid            parent process ID
+.TP 0.5i
+r       rss             resident set size
+.TP 0.5i
+R       resident        resident pages
+.TP 0.5i
+s       size            memory size in kilobytes
+.TP 0.5i
+S       share           amount of shared pages
+.TP 0.5i
+t       tty             the minor device number of tty
+.TP 0.5i
+T       start_time      time process was started
+.TP 0.5i
+U       uid             user ID number
+.TP 0.5i
+u       user            user name
+.TP 0.5i
+v       vsize           total VM size in bytes
+.TP 0.5i
+y       priority        kernel scheduling priority
+.PD 1
+.SH "FIELD DESCRIPTIONS"
+.TP 0.5i
+.B "PRI "
+This is the counter field in the task struct.  It is the time in
+.B HZ
+of the process's possible timeslice.
+.TP 0.5i
+.B "NI "
+Standard unix nice value; a positive value means less cpu time.
+.TP 0.5i
+.B "SIZE "
+Virtual image size; size of text+data+stack.
+.TP 0.5i
+.B "RSS "
+Resident set size; kilobytes of program in memory.
+.TP 0.5i
+.B "WCHAN "
+Name of the kernel function where the process is sleeping, with the
+.RB ` sys_ '
+stripped from the function name.  If
+.I /etc/psdatabase
+does not exist, it is just a hex number instead.
+.TP 0.5i
+.B "STAT "
+Information about the status of the process.  The first field is
+.B R
+for runnable,
+.B S
+for sleeping,
+.B D
+for uninterruptible sleep,
+.B T
+for stopped or traced, or
+.B Z
+for a zombie process.  The second field contains
+.B W
+if the process has no resident pages.  The third field is
+.B N
+if the process has a positive nice value
+.RB ( NI
+field).
+.TP 0.5i
+.B "TT "
+Controlling tty.
+.TP 0.5i
+.B "PAGEIN "
+Number of major page faults (page faults that cause pages to be read
+from disk, including pages read from the buffer cache).
+.TP 0.5i
+.B "TRS "
+Text resident size.
+.TP 0.5i
+.B "SWAP "
+Kilobytes (or pages if
+.B p
+is used) on swap device.
+.TP 0.5i
+.B "SHARE "
+Shared memory.
+.SH UPDATING
+This
+.BR proc -based
+.B ps
+works by reading the files in the
+.B proc
+filesystem, mounted on
+.BR /proc .
+This
+.B ps
+does not need to be suid
+.B kmem
+or have any privileges to run.
+.I "Do not give this ps any special permissions."
+.PP
+You will need to put in place the appropriate System.map file
+when you install a new kernel in order
+to get meaningful information from the
+.B WCHAN
+field.  This should be done every time you compile a new kernel.  You should
+also run 'ps' as root once and then any time the tty devices in the "/dev"
+directory change.
+
+As of procps-1.00, ps/top read System.map directly if it is available.  The
+search path for kernel address-to-symbol resolution is:
+.nf
+            $PS_SYSTEM_MAP
+            /boot/System.map-`uname -r`
+            /boot/System.map
+            /lib/modules/`uname -r`/System.map
+            /etc/psdatabase
+            /boot/psdatabase-`uname -r`
+            /boot/psdatabase,
+            /lib/modules/`uname -r`/psdatabase
+.fi 
+.PP
+.SH NOTES
+The member
+.B used_math
+of
+.B task_struct
+is not shown, since
+.B crt0.s
+checks to see if math is present.  This causes the math flag to be set
+for all processes, and so it is worthless.
+.PP
+Programs swapped out to disk will be shown without command line
+arguments, and unless the
+.B c
+option is given, in parentheses.
+.PP
+.B %CPU
+shows the cputime/realtime percentage.  It will not add up to 100%
+unless you are lucky.  It is time used divided by the time the process
+has been running.
+.PP
+The
+.B SIZE
+and
+.B RSS
+fields don't count the page tables and the
+.B task_struct
+of a proc; this is at least 12k of memory that is always resident.
+.B SIZE
+is the virtual size of the proc (code+data+stack).
+.SH AUTHOR
+.B ps
+was originally written by Branko Lankester <lankeste@fwi.uva.nl>. Michael K.
+Johnson <johnsonm@redhat.com> re-wrote it significantly to use the proc
+filesystem, changing a few things in the process.  Michael Shields
+<mjshield@nyx.cs.du.edu> added the pid-list feature.  Charles
+Blake <cblake@bbn.com> added multi-level sorting, the dirent-style library, the
+device name-to-number mmaped database, the approximate binary search directly
+on System.map, and many code and documentation cleanups.  David Mossberger-Tang
+wrote the generic BFD support for psupdate.  Michael K. Johnson
+<johnsonm@redhat.com> is the current maintainer.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/oldps.c b/oldps.c
new file mode 100644 (file)
index 0000000..2ed865f
--- /dev/null
+++ b/oldps.c
@@ -0,0 +1,660 @@
+/*
+ * ps.c                - show process status
+ *
+ * Copyright (c) 1992 Branko Lankester
+ *
+ * Snarfed and HEAVILY modified for procps by Michael K. Johnson,
+ * (johnsonm@sunsite.unc.edu).  What is used is what is required to have a
+ *  common interface.
+ *
+ * Massive modifications by Charles Blake (cblake@bbn.com).  Rewrite
+ * of the system process table code, multilevel sorting, device number
+ * database, forest feature (contributed by ...), environment variables, GNU
+ * style long options, pid list filtering (contributed by Michael Shields).
+ *
+ * Michael K. Johnson <johnsonm@redhat.com> again in charge, merging
+ * patches that have accumulated over a long time.
+ *
+ * Changes Copyright (C) 1993, 1994, 1997 Michael K. Johnson,
+ *   and   Copyright (C) 1995, 1996 Charles Blake
+ * See file COPYING for copyright details.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/param.h>
+
+#include <proc/version.h>
+#include <proc/readproc.h>
+#include <proc/procps.h>
+#include <proc/devname.h>
+#include <proc/tree.h>
+#include <proc/sysinfo.h>
+#include <proc/status.h>
+#include <proc/compare.h>
+
+static void show_short(char*, proc_t*);
+static void show_long (char*, proc_t*);
+static void show_user (char*, proc_t*);
+static void show_jobs (char*, proc_t*);
+static void show_sig  (char*, proc_t*);
+static void show_vm   (char*, proc_t*);
+static void show_m    (char*, proc_t*);
+static void show_regs (char*, proc_t*);
+
+/* this struct replaces the previously parallel fmt_fnc and hdrs */
+static struct {
+    void      (*format)(char*,proc_t*);
+    char        CL_option_char;
+    const char*        default_sort;
+    const char*        header;
+} mode[] = {
+    { show_short,  0 , "Up", "  PID TTY STAT TIME COMMAND" },
+    { show_long,  'l', "Pp", " FLAGS   UID   PID  PPID PRI  NI   SIZE   RSS WCHAN       STA TTY TIME COMMAND" },
+    { show_user,  'u', "up", "USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND" },
+    { show_jobs,  'j', "gPp"," PPID   PID  PGID   SID TTY TPGID  STAT  UID   TIME COMMAND" },
+    { show_sig,   's', "p",  "  UID   PID SIGNAL   BLOCKED  IGNORED  CATCHED  STAT TTY   TIME COMMAND" },
+    { show_vm,    'v', "r",  "  PID TTY STAT TIME  PAGEIN TSIZ DSIZ  RSS   LIM %MEM COMMAND" },
+    { show_m,     'm', "r",  "  PID TTY MAJFLT MINFLT   TRS   DRS  SIZE  SWAP   RSS  SHRD   LIB  DT COMMAND" },
+    { show_regs,  'X', "p",  "NR   PID    STACK      ESP      EIP TMOUT ALARM STAT TTY   TIME COMMAND" },
+    { NULL, 0, NULL, NULL }
+};
+
+/* some convenient integer constants corresponding to the above rows. */
+enum PS_MODALITY { PS_D = 0, PS_L, PS_U, PS_J, PS_S, PS_V, PS_M, PS_X };
+
+static char * prtime         (char *s, unsigned long t, unsigned long rel);
+static void   usage          (char* context);
+static void   set_cmdspc     (int w_opts);
+static void   show_procs     (unsigned maxcmd, int do_header, int, void*,int);
+static void   show_cmd_env   (char* tskcmd, char** cmd, char** env, unsigned maxch);
+static void   show_a_proc    (proc_t* the_proc, unsigned maxcmd);
+static void   show_time      (char *s, proc_t * p);
+static void   add_node       (char *s, proc_t *task);
+static int    node_cmp       (const void *s1, const void *s2);
+static void   show_tree      (int n, int depth, char *continued);
+static void   show_forest    (void);
+
+static int CL_fmt       = 0;
+
+/* process list filtering command line options */
+
+static int CL_all,
+    CL_kern_comm,
+    CL_no_ctty,
+    CL_run_only;
+static char  * CL_ctty;
+static pid_t * CL_pids;          /* zero-terminated list, dynamically allocated */
+
+/* process display modification command line options */
+
+static int CL_show_env,
+    CL_num_outp,          /* numeric fields for user, wchan, tty */
+    CL_sort     = 1,
+    CL_forest,
+    CL_Sum,
+    CL_pg_shift = (PAGE_SHIFT - 10);   /* default: show k instead of pages */
+
+/* Globals */
+
+static unsigned cmdspc = 80;     /* space left for cmd+env after table per row */
+static int      GL_current_time; /* some global system parameters */
+static long     GL_time_now;
+static int      GL_wchan_nout;   /* this is can also be set on the command-line  */
+
+
+/*****************************/
+int main(int argc, char **argv) {
+    char *p;
+    int width = 0,
+       do_header = 1,
+       user_ord = 0,
+       next_arg = 0,
+       toppid = 0,
+       pflags, N = 1;
+    void* args = NULL;
+    dev_t tty[2] = { 0 };
+    uid_t uid[1];
+    
+    do {
+        --argc;                /* shift to next arg. */
+        ++argv;
+        for (p = *argv; p && *p; ++p) {
+            switch (*p) {
+             case '-':               /* "--" ==> long name options */
+               if (*(p+1) == '-') {
+                    if (strncmp(p+2,"sort",4)==0) {
+                       if (parse_long_sort(p+7))
+                           usage("unrecognized long sort option\n");
+                       user_ord = 1;
+                       next_arg = 1;
+                       break;
+                    } else if (strncmp(p+2, "help", 4) == 0) {
+                       usage(NULL);
+                       return 0;
+                    } else if (strncmp(p+2, "version", 6) == 0) {
+                       display_version();
+                       return 0;
+                    } else if (*(p+2) != '\0') /* just '-- '; not an error */
+                       usage("ps: unknown long option\n");
+               }
+               if (!getenv("I_WANT_A_BROKEN_PS")) {
+                   fprintf(stderr,
+                           "warning: `-' deprecated; use `ps %s', not `ps %s'\n",
+                           (p+1), p);
+               }
+               break;
+             case 'l': CL_fmt = PS_L;   /* replaceable by a */ break;
+             case 'u': CL_fmt = PS_U;   /* loop over mode[] */ break;
+             case 'j': CL_fmt = PS_J;                          break;
+             case 's': CL_fmt = PS_S;                          break;
+             case 'v': CL_fmt = PS_V;                          break;
+             case 'm': CL_fmt = PS_M;                          break;
+             case 'X': CL_fmt = PS_X;   /* regs */             break;
+
+             case 'r': CL_run_only = 1; /* list filters */     break;
+             case 'a': CL_all = 1;                             break;
+             case 'x': CL_no_ctty = 1;                         break;
+             case 't': CL_ctty = p + 1;
+               next_arg = 1;                           break;
+
+             case 'e': CL_show_env = 1; /* output modifiers */ break;
+             case 'f': CL_forest = 1;
+               CL_kern_comm = 0;                       break;
+             case 'c': CL_kern_comm = 1;                       break;
+             case 'w': ++width;                                break;
+             case 'h': do_header = 0;                          break;
+             case 'n': CL_num_outp = 1;
+               GL_wchan_nout = 1;                      break;
+             case 'S': CL_Sum = 1;                             break;
+             case 'p': CL_pg_shift = 0;                        break;
+             case 'o': CL_sort = !CL_sort;                     break;
+             case 'O':
+               if (parse_sort_opt(p+1))
+                   usage("short form sort flag parse error\n");
+               user_ord = 1;
+               next_arg = 1;
+               break;
+             case 'V': display_version(); exit(0);
+             default:
+                /* Step through, reading+alloc space for comma-delim pids */
+               if (isdigit(*p)) {
+                   while (isdigit(*p)) {
+                       CL_pids = xrealloc(CL_pids, (toppid + 2)*sizeof(pid_t));
+                       CL_pids[toppid++] = atoi(p);
+                       while (isdigit(*p))
+                           p++;
+                       if (*p == ',')
+                           p++;
+                   }
+                   CL_pids[toppid] = 0;
+                   next_arg = 1;
+               }
+               if (*p)
+                   usage("unrecognized option or trailing garbage\n");
+            }
+            if (next_arg) {
+                next_arg = 0;
+                break;       /* end loop over chars in this argument */
+            }
+        }
+    } while (argc > 1);
+
+    if (!CL_sort)      /* since the unsorted mode is intended to be speedy */
+       CL_forest = 0;  /* turn off the expensive forest option as well. */
+
+    if (CL_fmt == PS_L) {
+       if (open_psdb(NULL)) GL_wchan_nout = 1;
+    }
+
+    set_cmdspc(width);
+
+    if (!(GL_current_time = uptime(0,0)))
+       return 1;
+    GL_time_now = time(0L);
+
+    if (CL_sort && !user_ord)
+        parse_sort_opt(mode[CL_fmt].default_sort);
+
+    /* NOTE:  all but option parsing has really been done to enable
+     * multiple uid/tty/state filtering as well as multiple pid filtering
+     */
+    pflags = PROC_ANYTTY;      /* defaults */
+
+    if (!CL_kern_comm) pflags |= PROC_FILLCMD;          /* verbosity flags */
+    if (CL_fmt == PS_M) pflags |= PROC_FILLMEM;
+    if (CL_show_env)   pflags |= PROC_FILLENV;
+    if (!CL_num_outp)  pflags |= PROC_FILLUSR;
+
+    if (CL_no_ctty)    pflags &= ~PROC_ANYTTY;         /* filter flags */
+    if (CL_run_only)  { pflags |= PROC_STAT; args = "RD"; }
+    else if (!CL_all)      { pflags |= PROC_UID;  args = uid; uid[0] = getuid(); pflags &= ~PROC_STAT; }
+    if (CL_pids)      { pflags |= PROC_PID;  args = CL_pids; pflags &= ~PROC_UID; pflags &= ~PROC_STAT; }
+    if (CL_ctty) {
+       if ((tty[0] = tty_to_dev(CL_ctty)) == (dev_t)-1) {
+           fprintf(stderr, "the name `%s' is not a tty\n", CL_ctty);
+           exit(1);
+       }
+       pflags = (pflags | PROC_TTY) & ~(PROC_ANYTTY|PROC_STAT|PROC_UID|PROC_PID);
+       args = tty;
+    }
+    show_procs(cmdspc, do_header, pflags, args, N);
+    return 0;
+}
+
+/***************************/
+/* print a context dependent usage message and maybe exit */
+static void usage(char* context) {
+    fprintf(stderr,
+           "%s"
+            "usage:  ps acehjlnrsSuvwx{t<tty>|#|O[-]u[-]U..} \\\n"
+            "           --sort:[-]key1,[-]key2,...\n"
+            "           --help gives you this message\n"
+            "           --version prints version information\n",
+           context ? context : "");
+    if (context)
+       exit(1);        /* prevent bad exit status by calling usage(NULL) */
+}
+
+/*****************************/
+/* set maximum chars displayed on a line based on screen size.
+ * Always allow for the header, with n+1 lines of output per row.
+ */
+static void set_cmdspc(int n) {
+    struct winsize win;
+    int h = strlen(mode[CL_fmt].header),
+       c = strlen("COMMAND");
+
+    if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+       cmdspc = win.ws_col;
+    if (n > 100) n = 100;      /* max of 100 'w' options */
+    if (cmdspc > h)
+       cmdspc = cmdspc*(n+1) - h + c;
+    else
+       cmdspc = cmdspc*n + c;
+}
+
+/*****************************/
+static const char *ttyc(int tty, int pid){
+  static char outbuf[8];
+  if(CL_num_outp)
+    snprintf(outbuf, sizeof outbuf, "%4.4x", tty);
+  else
+    dev_to_tty(outbuf, 4, tty, pid, ABBREV_TTY|ABBREV_DEV|ABBREV_PTS);
+  return outbuf;
+}
+
+/*****************************/
+/* This is the main driver routine that iterates over the process table.
+ */
+static void show_procs(unsigned maxcmd, int do_header, int pflags, void* args, int N) {
+    static proc_t buf; /* less dynamic memory allocation when not sorting */
+    PROCTAB* tab;
+    proc_t **ptable = NULL, *next, *retbuf = NULL;
+    int n = 0;
+
+    /* initiate process table scan */
+    tab = openproc(pflags, args, N);
+    if (!tab) {
+        fprintf(stderr, "Error: can not access /proc.\n");
+        exit(1);
+    }
+
+    if (do_header) puts(mode[CL_fmt].header);  /* print header */
+
+    if (!(CL_sort || CL_forest))       /* when sorting and forest are both */
+       retbuf = &buf;                  /* off we can use a static buffer */
+
+    while ((next = readproc(tab,retbuf))) {    /* read next process */
+       n++;                                    /* Now either: */
+       if (CL_forest) {                        /*    add process to tree */
+           /* FIXME: this static is disgusting, but to keep binary
+            * compatibility within the 1.2 series I'll just go from
+            * 256 bytes to 1024 bytes, which should be good enough
+            * for most people....  :-(  In the future, the format
+            * format function should probably dynamically allocate
+            * strings.
+            */
+           static char s[1024];
+           (mode[CL_fmt].format)(s, next);
+           if (CL_fmt != PS_V && CL_fmt != PS_M)
+               show_time(s+strlen(s), next);
+           add_node(s, next);
+       } else if (CL_sort) {                   /*    add process to table */
+           ptable = xrealloc(ptable, n*sizeof(proc_t*));
+           ptable[n-1] = next;
+       } else {                                /*    or show it right away */
+           show_a_proc(&buf, maxcmd);
+           if (buf.cmdline) free((void*)(buf.cmdline[0]));
+           if (buf.environ) free((void*)(buf.environ[0]));
+       }
+    }
+    if (!n) {
+       fprintf(stderr, "No processes available.\n");
+       exit(1);
+    }
+    if (CL_sort && !CL_forest) {       /* just print sorted table */
+       int i;
+       qsort(ptable, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+       for (i = 0; i < n; i++) {
+           show_a_proc(ptable[i], maxcmd);
+           freeproc(ptable[i]);
+       }
+       free(ptable);
+    } else if (CL_forest)
+       show_forest();
+}
+
+/*****************************/
+/* show the trailing command and environment in available space.
+ * use abbreviated cmd if requested, NULL list, or singleton NULL string
+ */
+static void show_cmd_env(char* tskcmd, char** cmd, char** env, unsigned maxch) {
+    if (CL_kern_comm)          /* no () when explicit request for tsk cmd */
+       maxch = print_str(stdout, tskcmd, maxch);
+    else if (!cmd || !*cmd || (!cmd[1] && !*cmd)) {
+       /* no /proc//cmdline ==> bounding () */
+       if (maxch) {
+           fputc('(', stdout);
+           maxch--;
+       }
+       maxch = print_str(stdout, tskcmd, maxch);
+       if (maxch) {
+           fputc(')', stdout);
+           maxch--;
+       }
+    } else
+       maxch = print_strlist(stdout, cmd, " ", maxch);
+    if (CL_show_env && env)
+       print_strlist(stdout, env, " ", maxch);
+    fputc('\n', stdout);
+}
+
+
+/*****************************/
+/* format a single process for output.
+ */
+static void show_a_proc(proc_t* p, unsigned maxch) {
+    static char s[2048];
+    (mode[CL_fmt].format)(s, p);
+    if (CL_fmt != PS_V && CL_fmt != PS_M)
+       show_time(s+strlen(s), p);
+    printf("%s", s);
+    show_cmd_env(p->cmd, p->cmdline, p->environ, maxch);
+}
+
+/****** The format functions for the various formatting modes follow *****/
+
+/*****************************/
+static void show_short(char *s, proc_t *p) {
+    sprintf(s, "%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p));
+}
+
+
+/*****************************/
+static void show_long(char *s, proc_t *p) {
+    char wchanb[32];
+    
+    if (GL_wchan_nout)
+       sprintf(wchanb, " %-9lx ", p->wchan & 0xffffffff);
+    else
+       sprintf(wchanb, "%-11.11s", wchan(p->wchan));
+    sprintf(s, "%6lx %5d %5d %5d %3ld %3ld %6ld %5ld %-11.11s %s%3s",
+           p->flags, p->euid, p->pid, p->ppid, p->priority, p->nice,
+           p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), wchanb, status(p),
+           ttyc(p->tty,p->pid));
+}
+
+
+/*****************************/
+static void show_jobs(char *s, proc_t *p) {
+    sprintf(s, "%5d %5d %5d %5d %3s %5d  %s %5d ",
+           p->ppid, p->pid, p->pgrp, p->session, ttyc(p->tty,p->pid), p->tpgid, status(p),
+           p->euid);
+}
+
+
+/*****************************/
+static void show_user(char *s, proc_t *p) {
+    long pmem, total_time, seconds;
+    time_t start;
+    unsigned int pcpu;
+
+    if (CL_num_outp)
+       s += sprintf(s, "%5d    ", p->euid);
+    else
+       s += sprintf(s, "%-8s ", p->euser);
+    /* Hertz, being UL, forces 64-bit calculation on 64-bit machines */
+    seconds = (((GL_current_time * Hertz) - p->start_time) / Hertz);
+    start = GL_time_now - seconds;
+    total_time = (p->utime + p->stime +
+                 (CL_Sum ? p->cutime + p->cstime : 0));
+    pcpu = seconds ?
+       (total_time * 10 * 100 / Hertz) / seconds :
+       0;
+    if (pcpu > 999) pcpu = 999;
+    pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10));
+    sprintf(s, "%5d %2u.%u %2ld.%ld %5ld %5ld %3s %s %.6s ",
+           p->pid,  pcpu / 10, pcpu % 10,  pmem / 10, pmem % 10,
+           p->vsize >> 10, p->rss << (PAGE_SHIFT - 10), ttyc(p->tty,p->pid), status(p),
+           ctime(&start) + (GL_time_now - start > 3600*24 ? 4 : 10));
+}
+
+
+/*****************************/
+/* TODO: this is broken... but does anyone care? */
+static void show_sig(char *s, proc_t *p) {
+#undef SIGNAL_STRING /* not filled in by the function oldps calls */
+#ifdef SIGNAL_STRING
+#define TAIL(s) ((s)+(long)strlen(s)-8)
+    sprintf(s, "%5d %5d %s %s %s %s %s  %3s ",
+       p->euid, p->pid,
+       TAIL(p->signal), TAIL(p->blocked), TAIL(p->sigignore), TAIL(p->sigcatch),
+       status(p), ttyc(p->tty,p->pid)
+    );
+#else
+    sprintf(s, "%5d %5d %08x %08x %08x %08x %s  %3s ",
+       p->euid, p->pid,
+       (unsigned)p->signal, (unsigned)p->blocked, (unsigned)p->sigignore, (unsigned)p->sigcatch,
+       status(p), ttyc(p->tty,p->pid)
+    );
+#endif
+}
+
+
+/*****************************/
+static void show_vm(char *s, proc_t *p) {
+    int pmem;
+
+    s += sprintf(s,"%5d %3s %s", p->pid, ttyc(p->tty,p->pid), status(p));
+    show_time(s, p);
+    s += strlen(s);
+    s += sprintf(s, " %6ld %4ld %4ld %4ld ",
+                p->maj_flt + (CL_Sum ? p->cmaj_flt : 0),
+                p->vsize ? (p->end_code - p->start_code) >> 10 : 0,
+                p->vsize ? (p->vsize - p->end_code + p->start_code) >> 10 : 0,
+                p->rss << (PAGE_SHIFT - 10));
+    if(p->rss_rlim == RLIM_INFINITY)
+       s += sprintf(s, "   xx ");
+    else
+       s += sprintf(s, "%5ld ", p->rss_rlim >> 10);
+    pmem = p->rss * 1000 / (kb_main_total >> (PAGE_SHIFT-10));
+    sprintf(s, "%2d.%d ", pmem / 10, pmem % 10);
+}
+
+
+
+/*****************************/
+static void show_m(char *s, proc_t *p) {
+    sprintf(s, "%5d %3s %6ld %6ld %5ld %5ld %5ld %5ld %5ld %5ld %5ld %3ld ", 
+           p->pid, ttyc(p->tty,p->pid),
+           p->maj_flt + (CL_Sum ? p->cmaj_flt : 0),
+           p->min_flt + (CL_Sum ? p->cmin_flt : 0),
+           p->trs << CL_pg_shift,
+           p->drs << CL_pg_shift,
+           p->size << CL_pg_shift,
+           (p->size - p->resident) << CL_pg_shift,
+           p->resident << CL_pg_shift,
+           p->share << CL_pg_shift,
+           p->lrs << CL_pg_shift,
+           p->dt);
+}
+
+
+/*****************************/
+static void show_regs(char *s, proc_t *p) {
+    char time1[16], time2[16];
+
+    sprintf(s, "%2ld %5d %8lx %8lx %8lx %s %s %s  %3s ",
+           p->start_code >> 26, p->pid, p->start_stack,
+           p->kstk_esp, p->kstk_eip,
+           prtime(time1, p->timeout, GL_current_time*Hertz),
+           prtime(time2, p->it_real_value, 0),
+           status(p), ttyc(p->tty,p->pid));
+}
+
+
+/*****************************/
+static char *prtime(char *s, unsigned long t, unsigned long rel) {
+    if (t == 0) {
+        sprintf(s, "     ");
+        return s;
+    }
+    if ((long) t == -1) {
+        sprintf(s, "   xx");
+        return s;
+    }
+    if ((long) (t -= rel) < 0)
+        t = 0;
+    if (t > 9999)
+        sprintf(s, "%5lu", t / 100);
+    else
+        sprintf(s, "%2lu.%02lu", t / 100, t % 100);
+    return s;
+}
+
+
+/*****************************/
+static void show_time(char *s, proc_t * p) {
+    unsigned t;
+    t = (p->utime + p->stime) / Hertz;
+    if (CL_Sum) t += (p->cutime + p->cstime) / Hertz;
+    sprintf(s, "%3d:%02d ", t / 60, t % 60);
+}
+
+
+
+/*****************************/
+/* fancy process family tree based cmdline printing.  Building the tree
+   should be relegated to libproc and only the printing logic should
+   remain here.
+*/
+static struct tree_node * node;  /* forest mode globals */
+static int      nodes = 0;
+static int      maxnodes = 0;
+
+/*****************************/
+
+static void add_node(char *s, proc_t *task) {
+    if (maxnodes == 0) {
+       maxnodes = 64;
+        node = (struct tree_node *)
+            xmalloc(sizeof(struct tree_node) * maxnodes);
+    }
+    if (nodes >= maxnodes) {
+       maxnodes *= 2;
+        node = (struct tree_node *)
+            xrealloc(node, sizeof(struct tree_node) * maxnodes);
+    }
+    node[nodes].proc        = task;
+    node[nodes].pid         = task->pid;
+    node[nodes].ppid        = task->ppid;
+    node[nodes].line        = strdup(s);
+    node[nodes].cmd         = task->cmd;
+    node[nodes].cmdline     = task->cmdline;
+    node[nodes].environ     = task->environ;
+    node[nodes].children    = 0;
+    node[nodes].have_parent = 0;
+    nodes++;
+}
+
+
+/*****************************/
+static int node_cmp(const void *s1, const void *s2) {
+    struct tree_node *n1 = (struct tree_node *) s1;
+    struct tree_node *n2 = (struct tree_node *) s2;
+    return n1->pid - n2->pid;
+}
+
+
+/*****************************/
+static void show_tree(int n, int depth, char *continued) {
+    int i, cols = 0;
+
+    fprintf(stdout, "%s", node[n].line);
+    for (i = 0; i < depth; i++) {
+        if (cols + 4 >= cmdspc - 1)
+            break; 
+        if (i == depth - 1)
+            printf(" \\_ ");
+        else if (continued[i])
+            printf(" |  ");
+        else
+            printf("    ");
+        cols += 4;
+    }
+    show_cmd_env(node[n].cmd, node[n].cmdline, node[n].environ, cmdspc - cols);
+    for (i = 0; i < node[n].children; i++) {
+        continued[depth] = i != node[n].children - 1;
+        show_tree(node[n].child[i], depth + 1, continued);
+    }
+}
+
+
+/*****************************/
+static void show_forest() {
+    register int i, j;
+    int parent;
+    char continued[1024];
+
+    if (CL_sort)
+       qsort((void*)node, nodes, sizeof(struct tree_node), (void*)node_mult_lvl_cmp);
+
+    for (i = 0; i < nodes; i++) {
+        if (node[i].ppid > 1 && node[i].pid != node[i].ppid) {
+           parent = -1;
+           for (j=0; j<nodes; j++)
+               if (node[j].pid==node[i].ppid)
+                   parent = j;
+        } else
+            parent = -1;
+        if (parent >= 0) {
+            node[i].have_parent++;
+            if (node[parent].children == 0) {
+                node[parent].child = (int*)xmalloc(16 * sizeof(int*));
+                node[parent].maxchildren = 16;
+            }
+            else if (node[parent].children == node[parent].maxchildren) {
+                node[parent].maxchildren *= 2;
+                node[parent].child = (int*)xrealloc(node[parent].child,
+                                                   node[parent].maxchildren
+                                                   * sizeof(int*));
+            }
+            node[parent].child[node[parent].children++] = i;
+        }
+    }
+
+    for (i = 0; i < nodes; i++) {
+        if (!node[i].have_parent)
+            show_tree(i, 0, continued);
+    }
+}
diff --git a/pgrep.1 b/pgrep.1
new file mode 100644 (file)
index 0000000..346c5d0
--- /dev/null
+++ b/pgrep.1
@@ -0,0 +1,158 @@
+.\" Manual page for pgrep / pkill.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Copyright 2000 Kjetil Torgrim Homme
+.\"
+.TH PGREP 1 "June 25, 2000" "Linux" "Linux User's Manual"
+.SH NAME
+pgrep, pkill \- look up or signal processes based on name and other attributes
+
+.SH SYNOPSIS
+pgrep [\-flnvx] [\-d \fIdelimiter\fP] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+.br
+       [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+.br
+       [\-t \fIterm\fP,...] [\fIpattern\fP]
+
+pkill [\-\fIsignal\fP] [\-fnvx] [\-P \fIppid\fP,...] [\-g \fIpgrp\fP,...]
+.br
+       [\-s \fIsid\fP,...] [\-u \fIeuid\fP,...] [\-U \fIuid\fP,...] [\-G \fIgid\fP,...]
+.br
+       [\-t \fIterm\fP,...] [\fIpattern\fP]
+
+.SH DESCRIPTION
+\fBpgrep\fP looks through the currently running processes and lists the
+process IDs which matches the selection criteria to stdout.  All
+the criteria have to match.  For example,
+
+pgrep -u root sshd
+
+will only list the processes called \fBsshd\fP AND owned by \fBroot\fP.
+On the other hand,
+
+pgrep -u root,daemon
+
+will list the processes owned by \fBroot\fP OR \fBdaemon\fP.
+
+\fBpkill\fP will send the specified signal (by default \fBSIGTERM\fP)
+to each process instead of listing them on stdout.
+
+.SH OPTIONS
+.TP
+\-d \fIdelimiter\fP
+Sets the string used to delimit each process ID in the output (by
+default a newline).  (\fBpgrep\fP only.)
+.TP
+\-f
+The \fIpattern\fP is normally only matched against the process name.
+When \-f is set, the full command line is used.
+.TP
+\-g \fIpgrp\fP,...
+Only match processes in the process group IDs listed.  Process group 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own process group.
+.TP
+\-G \fIgid\fP,...
+Only match processes whose real group ID is listed.  Either the
+numerical or symbolical value may be used.
+.TP
+\-l
+List the process name as well as the process ID. (\fBpgrep\fP only.)
+.TP
+\-n
+Select only the newest (most recently started) of the matching
+processes.
+.TP
+\-P \fIppid\fP,...
+Only match processes whose parent process ID is listed.
+.TP
+\-s \fIsid\fP,...
+Only match processes whose process session ID is listed.  Session ID 0
+is translated into \fBpgrep\fP's or \fBpkill\fP's own session ID.
+.TP
+\-t \fIterm\fP,...
+Only match processes whose controlling terminal is listed.  The
+terminal name should be specified without the "/dev/" prefix.
+.TP
+\-u \fIeuid\fP,...
+Only match processes whose effective user ID is listed.  Either the
+numerical or symbolical value may be used.
+.TP
+\-U \fIuid\fP,...
+Only match processes whose real user ID is listed.  Either the
+numerical or symbolical value may be used.
+.TP
+\-v
+Negates the matching.
+.TP
+\-x
+Only match processes whose name (or command line if \-f is specified)
+\fBexactly\fP match the \fIpattern\fP.
+.TP
+\-\fIsignal\fP
+Defines the signal to send to each matched process.  Either the
+numeric or the symbolic signal name can be used.  (\fBpkill\fP only.)
+
+.SH OPERANDS
+.TP
+\fIpattern\fP
+Specifies an Extended Regular Expression for matching against the
+process names or command lines.
+
+.SH EXAMPLES
+Example 1: Find the process ID of the \fBnamed\fP daemon:
+
+unix$ pgrep \-u root named
+
+Example 2: Make \fBsyslog\fP reread its configuration file:
+
+unix$ pkill \-HUP syslogd
+
+Example 3: Give detailed information on all \fBxterm\fP processes:
+
+unix$ ps \-fp $(pgrep \-d, \-x xterm)
+
+Example 4: Make all \fBnetscape\fP processes run nicer:
+
+unix$ renice +4 `pgrep netscape`
+
+.SH "EXIT STATUS"
+.TP
+.I "0"
+One or more processes matched the criteria.
+.TP
+.I "1"
+No processes matched.
+.TP
+.I "2"
+Syntax error in the command line.
+.TP
+.I "3"
+Fatal error: out of memory etc.
+
+.SH NOTES
+The process name used for matching is limited to the 15 characters
+present in the output of /proc/\fIpid\fP/stat.  Use the \-f option to
+match against the complete command line, /proc/\fIpid\fP/cmdline.
+
+The running \fBpgrep\fP or \fBpkill\fP process will never report
+itself as a match.
+
+.SH BUGS
+The options \-n and \-v can not be combined.  Let me know if you need
+to do this.
+
+Defunct processes are reported.
+
+.SH "SEE ALSO"
+ps(1) proc(5) regex(5)
+
+.SH STANDARDS
+\fBpkill\fP and \fBpgrep\fP were introduced in Sun's Solaris 7.  This
+implementation is fully compatible.
+
+.SH AUTHOR
+Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+
+Michael K. Johnson <johnsonm@redhat.com> is the current maintainer of
+the procps package.
+
+Please send bug reports to <procps-bugs@redhat.com>
diff --git a/pgrep.c b/pgrep.c
new file mode 100644 (file)
index 0000000..91f6a3a
--- /dev/null
+++ b/pgrep.c
@@ -0,0 +1,605 @@
+/* emacs settings:  -*- c-basic-offset: 8 tab-width: 8 -*-
+ *
+ * pgrep/pkill -- utilities to filter the process table
+ *
+ * Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+ *
+ * May be distributed under the conditions of the
+ * GNU General Public License; a copy is in COPYING
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+#include <errno.h>
+
+#include "proc/readproc.h"
+#include "proc/sig.h"
+#include "proc/devname.h"
+#include "proc/sysinfo.h"
+
+static int i_am_pkill = 0;
+char *progname = "pgrep";
+
+union el {
+       long    num;
+       char *  str;
+};
+
+/* User supplied arguments */
+
+static int opt_full = 0;
+static int opt_long = 0;
+static int opt_newest = 0;
+static int opt_negate = 0;
+static int opt_exact = 0;
+static int opt_signal = SIGTERM;
+
+static char *opt_delim = "\n";
+static union el *opt_pgrp = NULL;
+static union el *opt_gid = NULL;
+static union el *opt_ppid = NULL;
+static union el *opt_sid = NULL;
+static union el *opt_term = NULL;
+static union el *opt_euid = NULL;
+static union el *opt_uid = NULL;
+static char *opt_pattern = NULL;
+
+/* Prototypes */
+
+static union el *split_list (const char *, char, int (*)(const char *, union el *));
+static int conv_uid (const char *, union el *);
+static int conv_gid (const char *, union el *);
+static int conv_sid (const char *, union el *);
+static int conv_pgrp (const char *, union el *);
+static int conv_num (const char *, union el *);
+static int conv_str (const char *, union el *);
+static int match_numlist (long, const union el *);
+static int match_strlist (const char *, const union el *);
+
+
+static int
+usage (int opt)
+{
+       if (i_am_pkill)
+               fprintf (stderr, "Usage: pgrep [-flnvx] [-d DELIM] ");
+       else
+               fprintf (stderr, "Usage: pkill [-SIGNAL] [-fnvx] ");
+       fprintf (stderr, "[-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
+                "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
+                "[PATTERN]\n");
+       exit (opt == '?' ? 0 : 2);
+}
+
+
+static void
+parse_opts (int argc, char **argv)
+{
+       char opts[32] = "";
+       int opt;
+       int criteria_count = 0;
+
+       if (strstr (argv[0], "pkill")) {
+               i_am_pkill = 1;
+               progname = "pkill";
+               /* Look for a signal name or number as first argument */
+               if (argc > 1 && argv[1][0] == '-') {
+                       int sig;
+                       sig = signal_name_to_number (argv[1] + 1);
+                       if (sig == -1 && isdigit (argv[1][1]))
+                               sig = atoi (argv[1] + 1);
+                       if (sig != -1) {
+                               int i;
+                               for (i = 2; i < argc; i++)
+                                       argv[i-1] = argv[i];
+                               --argc;
+                               opt_signal = sig;
+                       }
+               }
+       } else {
+               /* These options are for pgrep only */
+               strcat (opts, "ld:");
+       }
+                       
+       strcat (opts, "fnvxP:g:s:u:U:G:t:?");
+       
+       while ((opt = getopt (argc, argv, opts)) != -1) {
+               switch (opt) {
+               case 'f':
+                       opt_full = 1;
+                       break;
+               case 'l':
+                       opt_long = 1;
+                       break;
+               case 'n':
+                       opt_newest = 1;
+                       ++criteria_count;
+                       break;
+               case 'v':
+                       opt_negate = 1;
+                       break;
+               case 'x':
+                       opt_exact = 1;
+                       break;
+               case 'd':
+                       opt_delim = strdup (optarg);
+                       break;
+               case 'P':
+                       opt_ppid = split_list (optarg, ',', conv_num);
+                       if (opt_ppid == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case 'g':
+                       opt_pgrp = split_list (optarg, ',', conv_pgrp);
+                       if (opt_pgrp == NULL)
+                               usage (opt);
+                       break;
+               case 's':
+                       opt_sid = split_list (optarg, ',', conv_sid);
+                       if (opt_sid == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case 'u':
+                       opt_euid = split_list (optarg, ',', conv_uid);
+                       if (opt_euid == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case 'U':
+                       opt_uid = split_list (optarg, ',', conv_uid);
+                       if (opt_uid == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case 'G':
+                       opt_gid = split_list (optarg, ',', conv_gid);
+                       if (opt_gid == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case 't':
+                       opt_term = split_list (optarg, ',', conv_str);
+                       if (opt_term == NULL)
+                               usage (opt);
+                       ++criteria_count;
+                       break;
+               case '?':
+                       usage (opt);
+                       break;
+               }
+       }
+        if (argc - optind == 1)
+               opt_pattern = argv[optind];
+       else if (argc - optind > 1)
+               usage (0);
+       else if (criteria_count == 0) {
+               fprintf (stderr, "%s: No matching criteria specified\n",
+                        progname);
+               usage (0);
+       }
+}
+
+
+static union el *
+split_list (const char *str, char sep, int (*convert)(const char *, union el *))
+{
+       char *copy = strdup (str);
+       char *ptr = copy;
+       char *sep_pos;
+       int i = 1, size = 32;
+       union el *list;
+
+       list = malloc (size * sizeof (union el));
+       if (list == NULL)
+               exit (3);
+
+       do {
+               sep_pos = strchr (ptr, sep);
+               if (sep_pos)
+                       *sep_pos = 0;
+               if (convert (ptr, &list[i]))
+                       ++i;
+               else
+                       exit (2);
+               if (i == size) {
+                       size *= 2;
+                       list = realloc (list, size * sizeof (union el));
+                       if (list == NULL)
+                               exit (3);
+               }
+               if (sep_pos)
+                       ptr = sep_pos + 1;
+       } while (sep_pos);
+
+       free (copy);
+       if (i == 1) {
+               free (list);
+               list = NULL;
+       } else {
+               list[0].num = i - 1;
+       }
+       return (list);
+}
+
+/* strict_atol returns a Boolean: TRUE if the input string contains a
+   plain number, FALSE if there are any non-digits. */
+
+static int
+strict_atol (const char *str, long *value)
+{
+       int res = 0;
+       int sign = 1;
+
+       if (*str == '+')
+               ++str;
+       else if (*str == '-') {
+               ++str;
+               sign = -1;
+       }
+
+       for ( ; *str; ++str) {
+               if (! isdigit (*str))
+                       return (0);
+               res *= 10;
+               res += *str - '0';
+       }
+       *value = sign * res;
+       return (1);
+}
+
+static int
+conv_uid (const char *name, union el *e)
+{
+       struct passwd *pwd;
+
+       if (strict_atol (name, &e->num))
+               return (1);
+
+       pwd = getpwnam (name);
+       if (pwd == NULL) {
+               fprintf (stderr, "%s: invalid user name: %s\n",
+                        progname, name);
+               return (0);
+       }
+       e->num = pwd->pw_uid;
+       return (1);
+}
+
+
+static int
+conv_gid (const char *name, union el *e)
+{
+       struct group *grp;
+
+       if (strict_atol (name, &e->num))
+               return (1);
+
+       grp = getgrnam (name);
+       if (grp == NULL) {
+               fprintf (stderr, "%s: invalid group name: %s\n",
+                        progname, name);
+               return (0);
+       }
+       e->num = grp->gr_gid;
+       return (1);
+}
+
+
+static int
+conv_pgrp (const char *name, union el *e)
+{
+       if (! strict_atol (name, &e->num)) {
+               fprintf (stderr, "%s: invalid process group: %s\n",
+                        progname, name);
+               return (0);
+       }
+       if (e->num == 0)
+               e->num = getpgrp ();
+       return (1);
+}
+
+
+static int
+conv_sid (const char *name, union el *e)
+{
+       if (! strict_atol (name, &e->num)) {
+               fprintf (stderr, "%s: invalid session id: %s\n",
+                        progname, name);
+               return (0);
+       }
+       if (e->num == 0)
+               e->num = getsid (0);
+       return (1);
+}
+
+
+static int
+conv_num (const char *name, union el *e)
+{
+       if (! strict_atol (name, &e->num)) {
+               fprintf (stderr, "%s: not a number: %s\n",
+                        progname, name);
+               return (0);
+       }
+       return (1);
+}
+
+
+static int
+conv_str (const char *name, union el *e)
+{
+       e->str = strdup (name);
+       return (1);
+}
+
+
+static int
+match_numlist (long value, const union el *list)
+{
+       int found = 0;
+       if (list == NULL)
+               found = 0;
+       else {
+               int i;
+               for (i = list[0].num; i > 0; i--) {
+                       if (list[i].num == value)
+                               found = 1;
+               }
+       }
+       return (found);
+}
+
+static int
+match_strlist (const char *value, const union el *list)
+{
+       int found = 0;
+       if (list == NULL)
+               found = 0;
+       else {
+               int i;
+               for (i = list[0].num; i > 0; i--) {
+                       if (! strcmp (list[i].str, value))
+                               found = 1;
+               }
+       }
+       return (found);
+}
+
+static void
+output_numlist (const union el *list)
+{
+       int i;
+       for (i = 1; i < list[0].num; i++)
+               printf ("%ld%s", list[i].num, opt_delim);
+
+       if (list[0].num)
+               printf ("%ld\n", list[i].num);
+}
+
+static void
+output_strlist (const union el *list)
+{
+       int i;
+       for (i = 1; i < list[0].num; i++)
+               printf ("%s%s", list[i].str, opt_delim);
+
+       if (list[0].num)
+               printf ("%s\n", list[i].str);
+}
+
+static PROCTAB *
+do_openproc ()
+{
+       PROCTAB *ptp;
+       int flags = PROC_FILLANY;
+
+       if (opt_pattern || opt_full)
+               flags |= PROC_FILLCMD;
+       if (opt_uid)
+               flags |= PROC_FILLSTATUS;
+       if (opt_euid && !opt_negate) {
+               int num = opt_euid[0].num;
+               int i = num;
+               uid_t *uids = malloc (num * sizeof (uid_t));
+               if (uids == NULL)
+                       exit (3);
+               while (i-- > 0) {
+                       uids[i] = opt_euid[i+1].num;
+               }
+               flags |= PROC_UID;
+               ptp = openproc (flags, uids, num);
+       } else {
+               ptp = openproc (flags);
+       }
+       return (ptp);
+}
+
+static regex_t *
+do_regcomp ()
+{
+       regex_t *preg = NULL;
+
+       if (opt_pattern) {
+               char *re;
+               char errbuf[256];
+               int re_err;
+
+               preg = malloc (sizeof (regex_t));
+               if (preg == NULL)
+                       exit (3);
+               if (opt_exact) {
+                       re = malloc (strlen (opt_pattern) + 5);
+                       if (re == NULL)
+                               exit (3);
+                       sprintf (re, "^(%s)$", opt_pattern);
+               } else {
+                       re = opt_pattern;
+               }
+
+               re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB);
+               if (re_err) {
+                       regerror (re_err, preg, errbuf, sizeof(errbuf));
+                       fprintf (stderr, errbuf);
+                       exit (2);
+               }
+       }
+       return preg;
+}
+
+#ifdef NOT_USED
+static time_t
+jiffies_to_time_t (long jiffies)
+{
+       static time_t time_of_boot = 0;
+       if (time_of_boot == 0) {
+               time_of_boot = time (NULL) - uptime (0, 0);
+       }
+       return (time_of_boot + jiffies / Hertz);
+}
+#endif
+
+static union el *
+select_procs ()
+{
+       PROCTAB *ptp;
+       proc_t task;
+       long newest_start_time = 0;
+       pid_t newest_pid = 0;
+       int matches = 0;
+       int size = 32;
+       regex_t *preg;
+       pid_t myself = getpid();
+       union el *list;
+       char cmd[4096];
+
+       list = malloc (size * sizeof (union el));
+       if (list == NULL)
+               exit (3);
+
+       ptp = do_openproc ();
+       preg = do_regcomp ();
+       
+       memset (&task, 0, sizeof (task));
+       while (readproc (ptp, &task)) {
+               int match = 1;
+
+               if (task.pid == myself)
+                       continue;
+               else if (opt_newest && task.start_time < newest_start_time)
+                       match = 0;
+               else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid))
+                       match = 0;
+               else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp))
+                       match = 0;
+               else if (opt_euid && ! match_numlist (task.euid, opt_euid))
+                       match = 0;
+               else if (opt_uid && ! match_numlist (task.ruid, opt_uid))
+                       match = 0;
+               else if (opt_gid && ! match_numlist (task.rgid, opt_gid))
+                       match = 0;
+               else if (opt_sid && ! match_numlist (task.session, opt_sid))
+                       match = 0;
+               else if (opt_term) {
+                       if (task.tty == -1) {
+                               match = 0;
+                       } else {
+                               char tty[256];
+                               dev_to_tty (tty, sizeof(tty) - 1,
+                                           task.tty, task.pid, ABBREV_DEV);
+                               match = match_strlist (tty, opt_term);
+                       }
+               }
+               if (opt_long || (match && opt_pattern)) {
+                       if (opt_full && task.cmdline) {
+                               int i = 0;
+                               int bytes = sizeof (cmd) - 1;
+
+                               /* make sure it is always NUL-terminated */
+                               cmd[bytes] = 0;
+                               /* make room for SPC in loop below */
+                               --bytes;
+
+                               strncpy (cmd, task.cmdline[i], bytes);
+                               bytes -= strlen (task.cmdline[i++]);
+                               while (task.cmdline[i] && bytes > 0) {
+                                       strncat (cmd, " ", bytes);
+                                       strncat (cmd, task.cmdline[i], bytes);
+                                       bytes -= strlen (task.cmdline[i++]) + 1;
+                               }
+                       } else {
+                               strcpy (cmd, task.cmd);
+                       }
+               }
+
+               if (match && opt_pattern) {
+                       if (regexec (preg, cmd, 0, NULL, 0) != 0)
+                               match = 0;
+               }
+
+               if (match ^ opt_negate) {       /* Exclusive OR is neat */
+                       if (opt_newest) {
+                               if (newest_start_time == task.start_time &&
+                                   newest_pid > task.pid)
+                                       continue;
+                               newest_start_time = task.start_time;
+                               newest_pid = task.pid;
+                               matches = 0;
+                       }
+                       if (opt_long) {
+                               char buff[4096];
+                               sprintf (buff, "%d %s", task.pid, cmd);
+                               list[++matches].str = strdup (buff);
+                       } else {
+                               list[++matches].num = task.pid;
+                       }
+                       if (matches == size) {
+                               size *= 2;
+                               list = realloc (list,
+                                               size * sizeof (union el));
+                               if (list == NULL)
+                                       exit (3);
+                       }
+               }
+               
+               memset (&task, 0, sizeof (task));
+       }
+       closeproc (ptp);
+
+       list[0].num = matches;
+       return (list);
+}
+
+
+int
+main (int argc, char **argv)
+{
+       union el *procs;
+
+       parse_opts (argc, argv);
+
+       procs = select_procs ();
+       if (i_am_pkill) {
+               int i;
+               for (i = 1; i <= procs[0].num; i++) {
+                       if (kill (procs[i].num, opt_signal) == -1)
+                               fprintf (stderr, "pkill: %ld - %s\n",
+                                        procs[i].num, strerror (errno));
+               }
+       } else {
+               if (opt_long)
+                       output_strlist (procs);
+               else
+                       output_numlist (procs);
+       }
+       return ((procs[0].num) == 0 ? 1 : 0);
+}
diff --git a/pkill.1 b/pkill.1
new file mode 100644 (file)
index 0000000..0e94b52
--- /dev/null
+++ b/pkill.1
@@ -0,0 +1 @@
+.so man1/pgrep.1
diff --git a/pmap.c b/pmap.c
new file mode 100644 (file)
index 0000000..ad01e1e
--- /dev/null
+++ b/pmap.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+void usage(void){
+  fprintf(stderr,
+    "Usage: pmap [-r] [-x] pid...\n"
+    "-r  ignored (compatibility option)\n"
+    "-x  show details\n"
+  );
+  exit(1);
+}
+
+int one_proc(unsigned pid){
+  char cmdbuf[64];
+  char buf[32];
+  int fd;
+  sprintf(buf,"/proc/%u/cmdline",pid);
+  if( ((fd=open(buf)) == -1) return 1;
+  count = read(fd, cmdbuf, sizeof(cmdbuf)-1);
+  if(count<1) return 1;
+  cmdbuf[count] = '\0';
+  while(count--) if(!isprint(cmdbuf[count])) cmdbuf[count]=' ';
+  close(fd);
+  sprintf(buf,"/proc/%u/maps",pid);
+  if( ((fd=open(buf)) == -1) return 1;
+  printf("%u:   %s\n", pid, cmdbuf);
+  if(x_option)
+    printf("Address   kB     Resident Shared Private Permissions       Name\n");
+@@@ FIXME FIXME FIXME @@@  
+}
+
+int main(int argc, char *argv[]){
+}
diff --git a/proc/.cvsignore.patch b/proc/.cvsignore.patch
new file mode 100644 (file)
index 0000000..f12054b
--- /dev/null
@@ -0,0 +1,7 @@
+diff -Naur procps-2.0.6/proc/.cvsignore procps-2.0.7/proc/.cvsignore
+--- procps-2.0.6/proc/.cvsignore       Wed Dec 31 19:00:00 1969
++++ procps-2.0.7/proc/.cvsignore       Fri Jul 14 16:45:01 2000
+@@ -0,0 +1,3 @@
++.depend
++signames.h
++libproc.so.*
diff --git a/proc/COPYING b/proc/COPYING
new file mode 100644 (file)
index 0000000..92b8903
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                   59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/proc/Makefile b/proc/Makefile
new file mode 100644 (file)
index 0000000..bc41bf1
--- /dev/null
@@ -0,0 +1,98 @@
+# Auto-adaptive C library Makefile adapted for libproc, Chuck Blake.
+# Assumptions are basically that all the .c files in the CWD are modules
+# for the library and that all .h files are the interface to the library.
+
+# PROJECT SPECIFIC MACROS
+NAME       =  proc
+
+# INSTALLATION OPTIONS
+TOPDIR     = /usr
+HDRDIR     = $(TOPDIR)/include/$(NAME)#        where to put .h files
+LIBDIR     = $(TOPDIR)/lib#            where to put library files
+SHLIBDIR   = /lib#                     where to put shared library files
+HDROWN     = $(OWNERGROUP) #           owner of header files
+LIBOWN     = $(OWNERGROUP) #           owner of library files
+INSTALL    = install
+
+# ----------------------------------------------------------------#
+# The rest is the auto-magic section -- highly GNU make dependent #
+# You should never need to edit this.                             #
+# ----------------------------------------------------------------#
+
+VC_SUF     =  ,v
+VC_PFX     =  RCS/
+RCSFILES   =  $(patsubst $(VC_PFX)%$(VC_SUF),%,$(wildcard $(VC_PFX)*$(VC_SUF)))
+
+# We take the union of RCS files and other files in CWD so that new files do
+# not need to alter this makefile.  'sort' removes duplicates.  This allows the
+# convenience of compiling and testing new files before the initial check-in.
+
+SRC        =  $(sort $(wildcard *.c) $(filter %.c,$(RCSFILES)))
+HDR        =  $(sort $(wildcard *.h) $(filter %.h,$(RCSFILES)))
+
+OBJ        =  $(SRC:.c=.o)
+SONAME     =  lib$(NAME).so.$(LIBVERSION)
+
+ifeq ($(SHARED),1)
+CFLAGS += -fpic
+all: lib$(NAME).a $(SONAME)
+else
+all: lib$(NAME).a
+endif
+
+lib$(NAME).a: $(OBJ)
+       $(AR) rcs $@ $^
+
+$(SONAME): $(OBJ)
+       gcc -shared -Wl,-soname,$(SONAME) -o $@ $^ -lc
+       ln -sf $(SONAME) lib$(NAME).so
+
+# AUTOMATIC DEPENDENCY GENERATION -- GCC AND GNUMAKE DEPENDENT
+
+.depend:
+       $(strip $(CC) $(CFLAGS) -MM -MG $(SRC) > .depend)
+-include .depend
+
+# INSTALLATION
+
+install: all
+       if ! [ -d $(HDRDIR) ] ; then mkdir $(HDRDIR) ; fi
+       $(INSTALL) $(HDROWN) $(HDR) $(TOPDIR)/include/$(NAME)
+       $(INSTALL) $(LIBOWN) lib$(NAME).a $(LIBDIR)
+ifeq ($(SHARED),1)
+       $(INSTALL) $(LIBOWN) $(SONAME) $(SHLIBDIR)
+       cd $(SHLIBDIR) && ln -sf $(SONAME) lib$(NAME).so
+       ldconfig
+endif
+
+# VARIOUS SHORT CUT TARGETS
+.PHONY:        all install dep clean distclean checkout checkclean
+
+dep:   .depend
+
+clean:
+       $(RM) lib$(NAME).* *.o DEADJOE
+
+distclean:     clean
+       $(RM) .depend *~
+
+checkout:
+       $(CO) $(RCSFILES)
+
+checkclean:
+       $(RM) $(RCSFILES)
+
+# CUSTOM c -> o rule so that command-line has minimal whitespace
+
+%.o : %.c
+       $(strip $(CC) $(CFLAGS) -c $<)
+
+# PROJECT SPECIFIC DEPENDENCIES/BUILD RULES
+
+
+version.o:     version.c version.h
+ifdef MINORVERSION
+       $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -DMINORVERSION=\"$(MINORVERSION)\" -c version.c)
+else
+       $(strip $(CC) $(CFLAGS) -DVERSION=\"$(VERSION)\" -DSUBVERSION=\"$(SUBVERSION)\" -c version.c)
+endif
diff --git a/proc/alloc.c b/proc/alloc.c
new file mode 100644 (file)
index 0000000..0a616ce
--- /dev/null
@@ -0,0 +1,49 @@
+/***********************************************************************\
+*   Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com  *
+*                                                                      *
+*      This file is placed under the conditions of the GNU Library     *
+*      General Public License, version 2, or any later version.        *
+*      See file COPYING for information on distribution conditions.    *
+\***********************************************************************/
+#include <stdlib.h>
+#include <stdio.h>
+
+void *xcalloc(void *pointer, int size) {
+    void * ret;
+    if (pointer)
+        free(pointer);
+    if (!(ret = calloc(1, size))) {
+        fprintf(stderr, "xcalloc: allocation error, size = %d\n", size);
+        exit(1);
+    } else {
+        return ret;
+    }
+}
+
+void *xmalloc(unsigned int size) {
+    void *p;
+
+    if (size == 0)
+        ++size;
+    p = malloc(size);
+    if (!p) {
+       fprintf(stderr, "xmalloc: malloc(%d) failed", size);
+       perror(NULL);
+       exit(1);
+    }
+    return(p);
+}
+
+void *xrealloc(void *oldp, unsigned int size) {
+    void *p;
+
+    if (size == 0)
+        ++size;
+    p = realloc(oldp, size);
+    if (!p) {
+       fprintf(stderr, "xrealloc: realloc(%d) failed", size);
+       perror(NULL);
+       exit(1);
+    }
+    return(p);
+}
diff --git a/proc/compare.c b/proc/compare.c
new file mode 100644 (file)
index 0000000..46695ac
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *
+ * Copyright 1994 Charles Blake and Michael K. Johnson
+ * This file is a part of procps, which is distributable
+ * under the conditions of the GNU Library General Public License.
+ * See the file COPYING for details.
+ *
+ */
+
+#include <string.h>            /* for strcmp */
+#include <stdio.h>             /* for parse error output */
+
+#include "proc/readproc.h"     /* for proc_t */
+#include "proc/tree.h"         /* for struct tree_node */
+
+#include "proc/compare.h"      /* for this code */
+
+
+/*
+  This module was written by Charles Blake for procps.
+
+mult_lvl_cmp:
+    slick general purpose multi-level compare function I invented.
+sort_depth:
+    the number of levels of functions *to use*.  This means many more levels
+    can be defined than mult_lvl_cmp tres out.  If this is 1 then mult_lvl_cmp
+    is just a trivial wrapper around (*sort_function[0]).
+sort_direction:
+    multiplicative factor for the output of cmp_whatever.
+    1 ==> default order, -1 ==> reverse order, 0 ==> forced equality
+    The 0 bit is the neat part.  Since a value of zero is the code for equality
+    multiplying the output of cmp_foo(a,b) forces a==b to be true.  This is a
+    convenient way to turn sorting off in middle levels of a multi-level sort.
+    If time is a problem, reforming the whole sort_function array to not include
+    these unsorted middle levels will be faster since then cmp_foo won't even
+    be called.  It might simplify some code depending upon how you organize it.
+sort_function[]:
+    array of function pointers that points to our family of comparison functions
+    (I have named them cmp_* but mult_lvl_cmp doesn't care what they're named).
+    This may be declared and initialized like so:
+       int (*sort_function[])(void* a, void* b)={&cmp_foo, &cmp_bar, &cmp_hiho};
+    You could also use my command line '-O' parser below.
+
+Note that we only descend levels until the order is determined.  If we descend
+all levels, that means that the items are equal at all levels, so we return 0.
+Otherwise we return whatever the level's cmp_foo function would have returned.
+This allows whatever default behavior you want for cmp_foo.  sort_direction[]
+reverses this default behavior, but mult_lvl_cmp doesn't decide that ascending
+or descending is the default.  That is the job of your cmp_foo's.
+*/
+
+/* the only reason these are global is because qsort(3) likes it that way.
+   It's also a little more efficient if mult_lvl_cmp() is called many times.
+*/
+
+static int sort_depth = 0;
+static int sort_direction[10];     /* storage for 10 levels, but 4 would be plenty!*/
+static int (*sort_function[10])(void* a, void* b);
+
+int mult_lvl_cmp(void* a, void* b) {
+    int i, cmp_val;
+    for(i = 0; i < sort_depth; i++) {
+        cmp_val = sort_direction[i] * (*sort_function[i])(a,b);
+        if (cmp_val != 0)
+            return cmp_val;
+    }
+    return 0;
+}
+
+int node_mult_lvl_cmp(void* a, void* b) {
+    int i, cmp_val;
+    for(i = 0; i < sort_depth; i++) {
+        cmp_val = sort_direction[i] * (*sort_function[i])(&(((struct tree_node *)a)->proc),&(((struct tree_node *)b)->proc));
+        if (cmp_val != 0)
+            return cmp_val;
+    }
+    return 0;
+}
+
+/* qsort(3) compliant comparison functions for all members of the ps_proc
+   structure (in the same order in which they appear in the proc_t declaration)
+   return is {-1,0,1} as {a<b, a==b, a>b}
+   default ordering is ascending for all members. (flip 1,-1 to reverse)
+*/
+/* pre-processor macros to cut down on source size (and typing!)
+   Note the use of the string concatenation operator ##
+*/
+#define CMP_STR(NAME) \
+static int cmp_ ## NAME(proc_t** P, proc_t** Q) { \
+    return strcmp((*P)->NAME, (*Q)->NAME); \
+}
+
+#define CMP_INT(NAME) \
+static int cmp_ ## NAME (proc_t** P, proc_t** Q) { \
+    if ((*P)->NAME < (*Q)->NAME) return -1; \
+    if ((*P)->NAME > (*Q)->NAME) return  1; \
+    return 0; \
+}
+
+/* Define the (46!) cmp_ functions with the above macros for every element
+   of proc_t.  If the binary gets too big, we could nuke inessentials.
+*/
+
+/* CMP_STR(cmdline) */
+CMP_STR(ruser)
+CMP_STR(euser)
+CMP_STR(cmd)
+/* CMP_INT(state) */
+/* CMP_STR(ttyc) */
+CMP_INT(euid)
+CMP_INT(pid)
+CMP_INT(ppid)
+CMP_INT(pgrp)
+CMP_INT(session)
+CMP_INT(tty)
+CMP_INT(tpgid)
+CMP_INT(utime)
+CMP_INT(stime)
+CMP_INT(cutime)
+CMP_INT(cstime)
+/* CMP_INT(priority) */
+CMP_INT(nice)
+CMP_INT(start_time)
+/* CMP_INT(signal) */
+/* CMP_INT(blocked) */
+/* CMP_INT(sigignore) */
+/* CMP_INT(sigcatch) */
+CMP_INT(flags)
+CMP_INT(min_flt)
+CMP_INT(cmin_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmaj_flt)
+/* CMP_INT(timeout) */
+CMP_INT(vsize)
+CMP_INT(rss)
+/* CMP_INT(rss_rlim) */
+/* CMP_INT(start_code) */
+/* CMP_INT(end_code) */
+/* CMP_INT(start_stack) */
+/* CMP_INT(kstk_esp) */
+/* CMP_INT(kstk_eip) */
+/* CMP_INT(wchan) */
+CMP_INT(pcpu)
+CMP_INT(size)
+CMP_INT(resident)
+CMP_INT(share)
+/* CMP_INT(trs) */
+/* CMP_INT(lrs) */
+/* CMP_INT(drs) */
+/* CMP_INT(dt) */
+
+/* define user interface to sort keys.  Fairly self-explanatory. */
+
+static struct cmp_fun_struct {
+    char letter;                           /* single option-letter for key */
+    char name[15];                             /* long option name for key */
+    int (*fun)(proc_t**, proc_t**);  /* pointer to cmp_key */
+} cmp[] = {
+/*  { '?', "cmdline",       &cmp_cmdline       }, */
+    { 'u', "user",          &cmp_euser         },
+/*  { '?', "ruser",         &cmp_ruser         }, */
+    { 'c', "cmd",           &cmp_cmd           },
+/*  { '?', "state",         &cmp_state         }, */
+/*  { '?', "ttyc",          &cmp_ttyc          }, */
+    { 'U', "uid",           &cmp_euid          },
+    { 'p', "pid",           &cmp_pid           },
+    { 'P', "ppid",          &cmp_ppid          },
+    { 'g', "pgrp",          &cmp_pgrp          },
+    { 'o', "session",       &cmp_session       },
+    { 't', "tty",           &cmp_tty           },
+    { 'G', "tpgid",         &cmp_tpgid         },
+    { 'k', "utime",         &cmp_utime         },
+    { 'K', "stime",         &cmp_stime         },
+    { 'j', "cutime",        &cmp_cutime        },
+    { 'J', "cstime",        &cmp_cstime        },
+/*  { '?', "counter",       &cmp_counter       }, */
+    { 'y', "priority",      &cmp_nice          },
+    { 'T', "start_time",    &cmp_start_time    },
+/*  { '?', "signal",        &cmp_signal        }, */
+/*  { '?', "blocked",       &cmp_blocked       }, */
+/*  { '?', "sigignore",     &cmp_sigignore     }, */
+/*  { '?', "sigcatch",      &cmp_sigcatch      }, */
+    { 'f', "flags",         &cmp_flags         },
+    { 'm', "min_flt",       &cmp_min_flt       },
+    { 'n', "cmin_flt",      &cmp_cmin_flt      },
+    { 'M', "maj_flt",       &cmp_maj_flt       },
+    { 'N', "cmaj_flt",      &cmp_cmaj_flt      },
+/*  { 'C', "timeout",       &cmp_timeout       }, */
+    { 'v', "vsize",         &cmp_vsize         },
+    { 'r', "rss",           &cmp_rss           },
+/*  { '?', "rss_rlim",      &cmp_rss_rlim      }, */
+/*  { '?', "start_code",    &cmp_start_code    }, */
+/*  { '?', "end_code",      &cmp_end_code      }, */
+/*  { '?', "start_stack",   &cmp_start_stack   }, */
+/*  { '?', "kstk_esp",      &cmp_kstk_esp      }, */
+/*  { '?', "kstk_eip",      &cmp_kstk_eip      }, */
+/*  { '?', "wchan",         &cmp_wchan         }, */
+    { 'C', "pcpu",          &cmp_pcpu          },
+    { 's', "size",          &cmp_size          },
+    { 'R', "resident",      &cmp_resident      },
+    { 'S', "share",         &cmp_share         },
+/*  { '?', "trs",           &cmp_trs           }, */
+/*  { '?', "lrs",           &cmp_lrs           }, */
+/*  { '?', "drs",           &cmp_drs           }, */
+/*  { '?', "dt",            &cmp_dt            }, */
+    { '\0',"terminator",    NULL               }
+};
+
+/* command line option parsing.  Assign sort_{depth,direction[],function[]}
+   based upon a string of the form:
+        [+-]a[+-]b[+-]c...
+   with a,b,c,... being letter flags corresponding to a particular sort
+   key and the optional '-' specifying a reverse sort on that key.  + doesn't
+   mean anything, but it keeps things looking balanced...
+*/
+const char *parse_sort_opt(const char* opt) {
+    int i, next_dir=1;
+    for(; *opt ; ++opt) {
+        if (*opt == '-' || *opt == '+') {
+            if (*opt == '-')
+                next_dir = -1;
+           opt++;
+            continue;
+        }
+        for (i = 0; cmp[i].letter; i++)
+            if (*opt == cmp[i].letter)
+                break;
+        if (!cmp[i].letter) { /* failed, clear and return */
+            sort_depth=0;
+            for (i=0;i<10;i++){
+              sort_direction[i]=0;
+              sort_function[i]=(cmp_t)NULL;
+            }
+            return "Unknown sort key.";
+        } else {
+#ifdef DEBUG
+           fprintf(stderr,
+                   "sort level %d: key %s, direction % d\n",
+                   sort_depth, cmp[i].name, next_dir);
+#endif
+            sort_function[sort_depth] = (cmp_t)cmp[i].fun;
+            sort_direction[sort_depth++] = next_dir;
+            next_dir = 1;
+        }
+    }
+    return NULL;
+}
+
+const char *parse_long_sort(const char* opt) {
+    char* comma;
+    int i, more_keys, next_dir=1;
+    do {
+        if (*opt == '-' || *opt == '+') {
+            if (*opt == '-')
+                next_dir = -1;
+            more_keys = 1;
+            opt++;
+            continue;
+        }
+        more_keys = ((comma=index(opt,',')) != NULL);
+                             /* keys are ',' delimited */
+        if (more_keys)
+            *comma='\0';      /* terminate for strcmp() */
+        for(i = 0; cmp[i].letter; ++i)
+            if (strcmp(opt, cmp[i].name) == 0)
+                break;
+        if (!cmp[i].letter) { /* failed, clear and return */
+            sort_depth=0;
+            for (i=0;i<10;i++){
+              sort_direction[i]=0;
+              sort_function[i]=(cmp_t)NULL;
+            }
+            return "Unknown sort key.";
+        } else {
+#ifdef DEBUG
+           fprintf(stderr,
+                   "sort level %d: key %s, direction % d\n",
+                   sort_depth, cmp[i].name, next_dir);
+#endif
+            sort_function[sort_depth] = (cmp_t)cmp[i].fun;
+            sort_direction[sort_depth++] = next_dir;
+            next_dir = 1;
+        }
+        opt = comma + 1; /* do next loop on next key, if more keys, else done*/
+    } while (more_keys);
+    return NULL;
+}
+
+void reset_sort_options (void)
+{
+  int i;
+
+  sort_depth=0;
+  for (i=0;i<10;i++){
+    sort_direction[i]=0;
+    sort_function[i]=(cmp_t)NULL;
+  }
+}
+
+void register_sort_function (int dir, cmp_t func)
+{
+    sort_function[sort_depth] = func;
+    sort_direction[sort_depth++] = dir;
+}
diff --git a/proc/compare.h b/proc/compare.h
new file mode 100644 (file)
index 0000000..153717a
--- /dev/null
@@ -0,0 +1,9 @@
+typedef int (*cmp_t)(void*,void*);       /* for function pointer casts */
+
+extern void register_sort_function (int dir, cmp_t func);
+extern void reset_sort_options(void);
+extern int mult_lvl_cmp(void* a, void* b);
+extern int node_mult_lvl_cmp(void* a, void* b);
+extern const char *parse_sort_opt(const char* opt);
+extern const char *parse_long_sort(const char* opt);
+
diff --git a/proc/devname.c b/proc/devname.c
new file mode 100644 (file)
index 0000000..0a2520d
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                 
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/sysmacros.h>
+#include "devname.h"
+
+#include <asm/page.h>
+#ifndef PAGE_SIZE
+#define PAGE_SIZE (sizeof(long)*1024)
+#endif
+
+/* Who uses what:
+ *
+ * tty_to_dev   oldps, w (there is a fancy version in ps)
+ * dev_to_tty   oldps, top, ps
+ */
+
+typedef struct tty_map_node {
+  struct tty_map_node *next;
+  int major_number; /* not unsigned! Ugh... */
+  char name[4];
+} tty_map_node;
+
+static tty_map_node *tty_map = NULL;
+
+/* Load /proc/tty/drivers for device name mapping use. */
+static void load_drivers(void){
+  char buf[10000];
+  char *p;
+  int fd;
+  int bytes;
+  fd = open("/proc/tty/drivers",O_RDONLY);
+  if(fd == -1) goto fail;
+  bytes = read(fd, buf, 9999);
+  if(bytes == -1) goto fail;
+  buf[bytes] = '\0';
+  p = buf;
+  while(( p = strstr(p, " /dev/tty") )){
+    tty_map_node *tmn;
+    int len;
+    p += 9;
+    len = strspn(p, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+    if(!len) continue;
+    if(len>3) continue;
+    if((len=1) && (*p=='S')) continue;
+    tmn = malloc(sizeof(tty_map_node));
+    tmn->next = tty_map;
+    tty_map = tmn;
+    memset(tmn->name, '\0', 4);
+    strncpy(tmn->name, p, len);
+    p += len;
+    tmn->major_number = atoi(p);
+  }
+fail:
+  if(fd != -1) close(fd);
+  if(!tty_map) tty_map = (tty_map_node *)-1;
+}
+
+/* Try to guess the device name from /proc/tty/drivers info. */
+static int driver_name(char * const buf, int maj, int min){
+  struct stat sbuf;
+  tty_map_node *tmn;
+  if(!tty_map) load_drivers();
+  if(tty_map == (tty_map_node *)-1) return 0;
+  tmn = tty_map;
+  for(;;){
+    if(!tmn) return 0;
+    if(tmn->major_number == maj) break;
+    tmn = tmn->next;
+  }
+  sprintf(buf, "/dev/tty%s%d", tmn->name, min);  /* like "/dev/ttyZZ255" */
+  if(stat(buf, &sbuf) < 0) return 0;
+  if(min != minor(sbuf.st_rdev)) return 0;
+  if(maj != major(sbuf.st_rdev)) return 0;
+  return 1;
+}
+
+/* Try to guess the device name (useful until /proc/PID/tty is added) */
+static int guess_name(char * const buf, int maj, int min){
+  struct stat sbuf;
+  int t0, t1;
+  int tmpmin = min;
+  switch(maj){
+  case   4:
+    if(min<64){
+      sprintf(buf, "/dev/tty%d", min);
+      break;
+    }
+    if(min<128){  /* to 255 on newer systems */
+      sprintf(buf, "/dev/ttyS%d", min-64);
+      break;
+    }
+    tmpmin = min & 0x3f;  /* FALL THROUGH */
+  case   3:      /* /dev/[pt]ty[p-za-o][0-9a-z] is 936 */
+    t0 = "pqrstuvwxyzabcde"[tmpmin>>4];
+    t1 = "0123456789abcdef"[tmpmin&0x0f];
+    sprintf(buf, "/dev/tty%c%c", t0, t1);
+    break;
+  case  17:  sprintf(buf, "/dev/ttyH%d",  min); break;
+  case  19:  sprintf(buf, "/dev/ttyC%d",  min); break;
+  case  22:  sprintf(buf, "/dev/ttyD%d",  min); break; /* devices.txt */
+  case  23:  sprintf(buf, "/dev/ttyD%d",  min); break; /* driver code */
+  case  24:  sprintf(buf, "/dev/ttyE%d",  min); break;
+  case  32:  sprintf(buf, "/dev/ttyX%d",  min); break;
+  case  43:  sprintf(buf, "/dev/ttyI%d",  min); break;
+  case  46:  sprintf(buf, "/dev/ttyR%d",  min); break;
+  case  48:  sprintf(buf, "/dev/ttyL%d",  min); break;
+  case  57:  sprintf(buf, "/dev/ttyP%d",  min); break;
+  case  71:  sprintf(buf, "/dev/ttyF%d",  min); break;
+  case  75:  sprintf(buf, "/dev/ttyW%d",  min); break;
+  case  78:  sprintf(buf, "/dev/ttyM%d",  min); break; /* conflict */
+  case 105:  sprintf(buf, "/dev/ttyV%d",  min); break;
+  case 112:  sprintf(buf, "/dev/ttyM%d",  min); break; /* conflict */
+  /* 136 ... 143 are /dev/pts/0, /dev/pts/1, /dev/pts/2 ... */
+  case 136 ... 143:  sprintf(buf, "/dev/pts/%d",  min+(maj-136)*256); break;
+  case 148:  sprintf(buf, "/dev/ttyT%d",  min); break;
+  case 154:  sprintf(buf, "/dev/ttySR%d", min); break;
+  case 156:  sprintf(buf, "/dev/ttySR%d", min+256); break;
+  case 164:  sprintf(buf, "/dev/ttyCH%d",  min); break;
+  case 166:  sprintf(buf, "/dev/ttyACM%d", min); break; /* bummer, 9-char */
+  case 172:  sprintf(buf, "/dev/ttyMX%d",  min); break;
+  case 174:  sprintf(buf, "/dev/ttySI%d",  min); break;
+  case 188:  sprintf(buf, "/dev/ttyUSB%d", min); break; /* bummer, 9-char */
+  default: return 0;
+  }
+  if(stat(buf, &sbuf) < 0) return 0;
+  if(min != minor(sbuf.st_rdev)) return 0;
+  if(maj != major(sbuf.st_rdev)) return 0;
+  return 1;
+}
+
+/* Linux 2.2 can give us filenames that might be correct.
+ * Useful names could be in /proc/PID/fd/2 (stderr, seldom redirected)
+ * and in /proc/PID/fd/255 (used by bash to remember the tty).
+ */
+static int link_name(char * const buf, int maj, int min, int pid, char *name){
+  struct stat sbuf;
+  char path[32];
+  int count;
+  sprintf(path, "/proc/%d/%s", pid, name);  /* often permission denied */
+  count = readlink(path,buf,PAGE_SIZE-1);
+  if(count == -1) return 0;
+  buf[count] = '\0';
+  if(stat(buf, &sbuf) < 0) return 0;
+  if(min != minor(sbuf.st_rdev)) return 0;
+  if(maj != major(sbuf.st_rdev)) return 0;
+  return 1;
+}
+
+/* number --> name */
+int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags) {
+  static char buf[PAGE_SIZE];
+  char *tmp = buf;
+  int i = 0;
+  int c;
+  if((short)dev == (short)-1) goto fail;
+  if(  link_name(tmp, major(dev), minor(dev), pid, "tty"   )) goto abbrev;
+  if(  link_name(tmp, major(dev), minor(dev), pid, "fd/2"  )) goto abbrev;
+  if( guess_name(tmp, major(dev), minor(dev)               )) goto abbrev;
+  if(  link_name(tmp, major(dev), minor(dev), pid, "fd/255")) goto abbrev;
+  if(driver_name(tmp, major(dev), minor(dev)               )) goto abbrev;
+fail:
+  strcpy(ret, "?");
+  return 1;
+abbrev:
+  if((flags&ABBREV_DEV) && !strncmp(tmp,"/dev/",5) && tmp[5]) tmp += 5;
+  if((flags&ABBREV_TTY) && !strncmp(tmp,"tty",  3) && tmp[3]) tmp += 3;
+  if((flags&ABBREV_PTS) && !strncmp(tmp,"pts/", 4) && tmp[4]) tmp += 4;
+  tmp[chop] = '\0';
+  for(;;){
+    c = *tmp;
+    tmp++;
+    if(!c) break;
+    i++;
+    if(c<=' ') c = '?';
+    if(c>126)  c = '?';
+    *ret = c;
+    ret++;
+  }
+  *ret = '\0';
+  return i;
+}
+
+/* name --> number */
+int tty_to_dev(char *name) {
+  struct stat sbuf;
+  static char buf[32];
+  if(stat(name, &sbuf) >= 0) return sbuf.st_rdev;
+  snprintf(buf,32,"/dev/%s",name);
+  if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+  snprintf(buf,32,"/dev/tty%s",name);
+  if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+  snprintf(buf,32,"/dev/pts/%s",name);
+  if(stat(buf, &sbuf) >= 0) return sbuf.st_rdev;
+  return -1;
+}
diff --git a/proc/devname.h b/proc/devname.h
new file mode 100644 (file)
index 0000000..f7b60f1
--- /dev/null
@@ -0,0 +1,7 @@
+#define ABBREV_DEV  1     /* remove /dev/         */
+#define ABBREV_TTY  2     /* remove tty           */
+#define ABBREV_PTS  4     /* remove pts/          */
+
+int dev_to_tty(char *ret, int chop, int dev, int pid, unsigned int flags);
+
+int tty_to_dev(char *name);
diff --git a/proc/ksym.c b/proc/ksym.c
new file mode 100644 (file)
index 0000000..8925bd5
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/utsname.h>
+#include "proc/procps.h"
+#include "proc/version.h"
+#include "proc/sysinfo.h" /* smp_num_cpus */
+
+#define KSYMS_FILENAME "/proc/ksyms"
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME  "/would/be/nice/to/have/this/file"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-hacked"
+#define linux_version_code 131598 /* ? */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME  "/home/albert/ps/45621/ksyms-2.3.12"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.12"
+#define linux_version_code 131852 /* 2.3.12 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME  "/home/albert/ps/45621/ksyms-2.3.18ac8-MODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-MODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+#if 0
+#undef KSYMS_FILENAME
+#define KSYMS_FILENAME  "/home/albert/ps/45621/ksyms-2.3.18ac8-NOMODVERS"
+#define SYSMAP_FILENAME "/home/albert/ps/45621/System.map-2.3.18ac8-NOMODVERS"
+#define linux_version_code 131858 /* 2.3.18ac8 */
+#define smp_num_cpus 2
+#endif
+
+/* These are the symbol types, with relative popularity:
+ *     ? w  machine type junk for Alpha -- odd syntax
+ *     ? S  not for i386
+ *     4 W  not for i386
+ *    60 R
+ *   100 A
+ *   125 r
+ *   363 s  not for i386
+ *   858 B
+ *   905 g  generated by modutils?
+ *   929 G  generated by modutils?
+ *  1301 b
+ *  2750 D
+ *  4481 d
+ * 11417 ?
+ * 13666 t
+ * 15442 T
+ *
+ * For i386, that is: "RArBbDd?tT"
+ */
+
+#define SYMBOL_TYPE_CHARS "Tt?dDbBrARGgsWS"
+
+/*
+ * '?' is a symbol type
+ * '.' is part of a name (versioning?)
+ * "\t[]" are for the module name in /proc/ksyms
+ */
+#define LEGAL_SYSMAP_CHARS "0123456789_ ?.\n\t[]" \
+                     "abcdefghijklmnopqrstuvwxyz" \
+                     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+/* System.map lines look like:
+ * hex num, space, one of SYMBOL_TYPE_CHARS, space, LEGAL_SYSMAP_CHARS, \n
+ *
+ * Alpha systems can start with a few lines that have the address replaced
+ * by space padding and a 'w' for the type. For those lines, the last space
+ * is followed by something like: mikasa_primo_mv p2k_mv sable_gamma_mv
+ * (just one of those, always with a "_mv", then the newline)
+ *
+ * The /proc/ksyms lines are like System.map lines w/o the symbol type char.
+ * When odd features are used, the name part contains:
+ * "(.*)_R(smp_|smp2gig_|2gig_)?[0-9a-fA-F]{8,}"
+ * It is likely that more crap will be added...
+ */
+
+typedef struct symb {
+  const char *name;
+  unsigned long addr;
+} symb;
+
+static const symb fail = { "?", 0 };
+static const char *dash = "-";
+
+/* These mostly rely on POSIX to make them zero. */
+
+static const symb hashtable[256];
+
+static char       *sysmap_data;
+static unsigned    sysmap_room;
+static symb       *sysmap_index;
+static unsigned    sysmap_count;
+
+static char       *ksyms_data;
+static unsigned    ksyms_room     = 4096;
+static symb       *ksyms_index;
+static unsigned    ksyms_count;
+static int         idx_room;
+
+/*********************************/
+
+/* Kill this:  _R(smp_?|smp2gig_?|2gig_?)?[0-9a-f]{8,}$
+ * We kill:    (_R[^A-Z]*[0-9a-f]{8,})+$
+ *
+ * The loop should almost never be taken, but it has to be there.
+ * It gets rid of anything that _looks_ like a version code, even
+ * if a real version code has already been found. This is because
+ * the inability to perfectly recognize a version code may lead to
+ * symbol mangling, which in turn leads to mismatches between the
+ * /proc/ksyms and System.map data files.
+ */
+#if 0
+static void chop_version(char *arg){
+  char *cp;
+  cp = strchr(arg,'\t');
+  if(cp) *cp = '\0';  /* kill trailing module name first */
+  for(;;){
+    char *p;
+    int len = 0;
+    cp = strrchr(arg, 'R');
+    if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+    for(p=cp; *++p; ){
+      switch(*p){
+      default:
+        return;
+      case '0' ... '9':
+      case 'a' ... 'f':
+        len++;
+        continue;
+      case 'g' ... 'z':
+      case '_':
+        len=0;
+        continue;
+      }
+    }
+    if(len<8) break;
+    cp[-1] = '\0';
+  }
+}
+#endif
+static void chop_version(char *arg){
+  char *cp;
+  cp = strchr(arg,'\t');
+  if(cp) *cp = '\0';  /* kill trailing module name first */
+  for(;;){
+    int len;
+    cp = strrchr(arg, 'R');
+    if(!cp || cp<=arg+1 || cp[-1]!='_') break;
+    len=strlen(cp);
+    if(len<9) break;
+    if(strpbrk(cp+1,"ABCDEFGHIJKLMNOPQRSTUVWXYZ")) break;
+    if(strspn(cp+len-8,"0123456789abcdef")!=8) break;
+    cp[-1] = '\0';
+  }
+}
+
+/***********************************/
+
+static const symb *search(unsigned long address, symb *idx, unsigned count){
+  unsigned left;
+  unsigned mid;
+  unsigned right;
+  if(!idx) return NULL;   /* maybe not allocated */
+  if(address < idx[0].addr) return NULL;
+  if(address >= idx[count-1].addr) return idx+count-1;
+  left  = 0;
+  right = count-1;
+  for(;;){
+    mid = (left + right) / 2;
+    if(address >= idx[mid].addr) left  = mid;
+    if(address <= idx[mid].addr) right = mid;
+    if(right-left <= 1) break;
+  }
+  if(address == idx[right].addr) return idx+right;
+  return idx+left;
+}
+
+/*********************************/
+
+/* allocate if needed, read, and return buffer size */
+static void read_file(const char *filename, char **bufp, unsigned *roomp) {
+  int fd = 0;
+  ssize_t done;
+  char *buf;
+  ssize_t total = 0;
+  unsigned room = *roomp;
+  buf = *bufp;
+  if(!room) goto hell;     /* failed before */
+  if(!buf) buf = malloc(room);
+  if(!buf) goto hell;
+open_again:
+  fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+  if(fd<0){
+    switch(errno){
+    case EINTR:  goto open_again;
+    default:     _exit(101);
+    case EACCES:   /* somebody screwing around? */
+      /* FIXME: set a flag to disable symbol lookup? */
+    case ENOENT:   /* no module support */
+    }
+    goto hell;
+  }
+  for(;;){
+    done = read(fd, buf+total, room-total-1);
+    if(done==0) break;  /* nothing left */
+    if(done==-1){
+      if(errno==EINTR) continue;  /* try again */
+      perror("");
+      goto hell;
+    }
+    if(done==(ssize_t)room-total-1){
+      char *tmp;
+      total += done;
+      /* more to go, but no room in buffer */
+      room *= 2;
+      tmp = realloc(buf, room);
+      if(!tmp) goto hell;
+      buf = tmp;
+      continue;
+    }
+    if(done>0 && done<(ssize_t)room-total-1){
+      total += done; 
+      continue;   /* OK, we read some. Go do more. */
+    }
+    fprintf(stderr,"%ld can't happen\n", (long)done);
+    /* FIXME: memory leak */
+    _exit(42);
+  }
+  *bufp = buf;
+  *roomp = room;
+  close(fd);
+  return;
+hell:
+  if(buf) free(buf);
+  *bufp = NULL;
+  *roomp = 0;   /* this function will never work again */
+  total = 0;
+  close(fd);
+  return;
+}
+
+/*********************************/
+
+static int parse_ksyms(void) {
+  char *endp;
+  if(!ksyms_room || !ksyms_data) goto quiet_goodbye;
+  endp = ksyms_data;
+  ksyms_count = 0;
+  if(idx_room) goto bypass;  /* some space already allocated */
+  idx_room = 512;
+  for(;;){
+    void *vp;
+    idx_room *= 2;
+    vp = realloc(ksyms_index, sizeof(symb)*idx_room);
+    if(!vp) goto bad_alloc;
+    ksyms_index = vp;
+bypass:
+    for(;;){
+      char *saved;
+      if(!*endp) return 1;
+      saved = endp;
+      ksyms_index[ksyms_count].addr = strtoul(endp, &endp, 16);
+      if(endp==saved || *endp != ' ') goto bad_parse;
+      endp++;
+      ksyms_index[ksyms_count].name = endp;
+      saved = endp;
+      endp = strchr(endp,'\n');
+      if(!endp) goto bad_parse;   /* no newline */
+      *endp = '\0';
+      chop_version(saved);
+      ++endp;
+      if(++ksyms_count >= idx_room) break;  /* need more space */
+    }
+  }
+
+  if(0){
+bad_alloc:
+    fprintf(stderr, "Warning: not enough memory available\n");
+  }
+  if(0){
+bad_parse:
+    fprintf(stderr, "Warning: "KSYMS_FILENAME" not normal\n");
+  }
+quiet_goodbye:
+  idx_room = 0;
+  if(ksyms_data) free(ksyms_data) , ksyms_data = NULL;
+  ksyms_room = 0;
+  if(ksyms_index) free(ksyms_index) , ksyms_index = NULL;
+  ksyms_count = 0;
+  return 0;
+}
+
+/*********************************/
+
+#define VCNT 16
+
+static int sysmap_mmap(const char *filename, void (*message)(const char *, ...)) {
+  struct stat sbuf;
+  char *endp;
+  int fd;
+  char Version[32];
+  fd = open(filename, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+  if(fd<0) return 0;
+  if(fstat(fd, &sbuf) < 0) goto bad_open;
+  if(!S_ISREG(sbuf.st_mode)) goto bad_open;
+  if(sbuf.st_size < 5000) goto bad_open;  /* if way too small */
+  /* Would be shared read-only, but we want '\0' after each name. */
+  endp = mmap(0, sbuf.st_size + 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
+  sysmap_data = endp;
+  while(*endp==' '){  /* damn Alpha machine types */
+    if(strncmp(endp,"                 w ", 19)) goto bad_parse;
+    endp += 19;
+    endp = strchr(endp,'\n');
+    if(!endp) goto bad_parse;   /* no newline */
+    if(strncmp(endp-3, "_mv\n", 4)) goto bad_parse;
+    endp++;
+  }
+  if(sysmap_data == (caddr_t) -1) goto bad_open;
+  close(fd);
+  fd = -1;
+  sprintf(Version, "Version_%d", linux_version_code);
+  sysmap_room = 512;
+  for(;;){
+    void *vp;
+    sysmap_room *= 2;
+    vp = realloc(sysmap_index, sizeof(symb)*sysmap_room);
+    if(!vp) goto bad_alloc;
+    sysmap_index = vp;
+    for(;;){
+      char *vstart;
+      if(!*endp){                /* if we reached the end */
+        int i = VCNT;            /* check VCNT times to verify this file */
+        if(*Version) goto bad_version;
+        if(!ksyms_index) return 1; /* if can not verify, assume success */
+        while(i--){
+#if 1
+          const symb *findme;
+          const symb *map_symb;
+          /* Choose VCNT entries from /proc/ksyms to test */
+          findme = ksyms_index + (ksyms_count*i/VCNT);
+          /* Search for them in the System.map */
+          map_symb = search(findme->addr, sysmap_index, sysmap_count);
+          if(map_symb){
+            if(map_symb->addr != findme->addr) continue;
+            /* backup to first matching address */
+            while (map_symb != sysmap_index){
+              if (map_symb->addr != (map_symb-1)->addr) break;
+              map_symb--;
+            }
+            /* search for name in symbols with same address */
+            while (map_symb != (sysmap_index+sysmap_count)){
+              if (map_symb->addr != findme->addr) break;
+              if (!strcmp(map_symb->name,findme->name)) goto good_match;
+              map_symb++;
+            }
+            map_symb--; /* backup to last symbol with matching address */
+            message("{%s} {%s}\n",map_symb->name,findme->name);
+            goto bad_match;
+          }
+good_match:;
+#endif
+        }
+        return 1; /* success */
+      }
+      sysmap_index[sysmap_count].addr = strtoul(endp, &endp, 16);
+      if(*endp != ' ') goto bad_parse;
+      endp++;
+      if(!strchr(SYMBOL_TYPE_CHARS, *endp)) goto bad_parse;
+      endp++;
+      if(*endp != ' ') goto bad_parse;
+      endp++;
+      sysmap_index[sysmap_count].name = endp;
+      vstart = endp;
+      endp = strchr(endp,'\n');
+      if(!endp) goto bad_parse;   /* no newline */
+      *endp = '\0';
+      ++endp;
+      chop_version(vstart);
+      if(*vstart=='V' && *Version && !strcmp(Version,vstart)) *Version='\0';
+      if(++sysmap_count >= sysmap_room) break;  /* need more space */
+    }
+  }
+
+  if(0){
+bad_match:
+    message("Warning: %s does not match kernel data.\n", filename);
+  }
+  if(0){
+bad_version:
+    message("Warning: %s has an incorrect kernel version.\n", filename);
+  }
+  if(0){
+bad_alloc:
+    message("Warning: not enough memory available\n");
+  }
+  if(0){
+bad_parse:
+    message("Warning: %s not parseable as a System.map\n", filename);
+  }
+  if(0){
+bad_open:
+    message("Warning: %s could not be opened as a System.map\n", filename);
+  }
+
+  sysmap_room=0;
+  sysmap_count=0;
+  if(sysmap_index) free(sysmap_index);
+  sysmap_index = NULL;
+  if(fd>=0) close(fd);
+  if(sysmap_data) munmap(sysmap_data, sbuf.st_size + 1);
+  sysmap_data = NULL;
+  return 0;
+}
+
+/*********************************/
+
+static void read_and_parse(void){
+  static time_t stamp;    /* after data gets old, load /proc/ksyms again */
+  if(time(NULL) != stamp){
+    read_file(KSYMS_FILENAME, &ksyms_data, &ksyms_room);
+    parse_ksyms();
+    memset((void*)hashtable,0,sizeof(hashtable)); /* invalidate cache */
+    stamp = time(NULL);
+  }
+}
+
+/*********************************/
+
+static void default_message(const char *format, ...) {
+    va_list arg;
+
+    va_start (arg, format);
+    vfprintf (stderr, format, arg);
+    va_end (arg);
+}
+
+/*********************************/
+
+int open_psdb_message(const char *override, void (*message)(const char *, ...)) {
+  static const char *sysmap_paths[] = {
+    "/boot/System.map-%s",
+    "/boot/System.map",
+    "/lib/modules/%s/System.map",
+    "/usr/src/linux/System.map",
+    "/System.map",
+    NULL
+  };
+  struct utsname uts;
+  char path[64];
+  const char **fmt = sysmap_paths;
+  const char *env;
+  read_and_parse();
+#ifdef SYSMAP_FILENAME    /* debug feature */
+  override = SYSMAP_FILENAME;
+#endif
+  if(override){           /* ought to search some path */
+    if(sysmap_mmap(override, message)) return 0;
+    return -1;           /* ought to return "Namelist not found." */
+    /* failure is better than ignoring the user & using bad data */
+  }
+  /* Arrrgh, the old man page and code did not match. */
+  if ((env = getenv("PS_SYSMAP"))     && sysmap_mmap(env, message)) return 0;
+  if ((env = getenv("PS_SYSTEM_MAP")) && sysmap_mmap(env, message)) return 0;
+  uname(&uts);
+  do{
+    snprintf(path, sizeof path, *fmt, uts.release);
+    if (sysmap_mmap(path, message)) return 0;
+  }while(*++fmt);
+  /* TODO: Without System.map, no need to keep ksyms loaded. */
+  return -1;
+}
+
+/***************************************/
+
+int open_psdb(const char *override) {
+    return open_psdb_message(override, default_message);
+}
+
+/***************************************/
+
+#define MAX_OFFSET (0x1000*sizeof(long))  /* past this is generally junk */
+
+/* return pointer to temporary static buffer with function name */
+const char * wchan(unsigned long address) {
+  const symb *mod_symb;
+  const symb *map_symb;
+  const symb *good_symb;
+  const char *ret;
+  unsigned hash = (address >> 4) & 0xff;  /* got 56/63 hits & 7/63 misses */
+  if(!address) return dash;
+  read_and_parse();
+
+  if(hashtable[hash].addr == address) return hashtable[hash].name;
+  mod_symb = search(address, ksyms_index,  ksyms_count);
+  if(!mod_symb) mod_symb = &fail;
+  map_symb = search(address, sysmap_index, sysmap_count);
+  if(!map_symb) map_symb = &fail;
+
+  /* which result is closest? */
+  good_symb = (mod_symb->addr > map_symb->addr)
+            ? mod_symb
+            : map_symb
+  ;
+  if(address > good_symb->addr + MAX_OFFSET) good_symb = &fail;
+
+  /* good_symb->name has the data, but needs to be trimmed */
+  ret = good_symb->name;
+  switch(*ret){
+    case 's': if(!strncmp(ret, "sys_", 4)) ret += 4;   break;
+    case 'd': if(!strncmp(ret, "do_",  3)) ret += 3;   break;
+    case '_': while(*ret=='_') ret++;                  break;
+  }
+  /* if(!*ret) ret = fail.name; */  /* not likely (name was "sys_", etc.) */
+
+  /* cache name after abbreviation */
+  hashtable[hash].addr = address;
+  hashtable[hash].name = ret;
+
+  return ret;
+}
diff --git a/proc/output.c b/proc/output.c
new file mode 100644 (file)
index 0000000..66e4e40
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  Some output conversion routines for libproc
+  Copyright (C) 1996, Charles Blake.  See COPYING for details.
+*/
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+/* output a string, converting unprintables to octal as we go, and stopping after
+   processing max chars of output (accounting for expansion due to octal rep).
+*/
+unsigned print_str(FILE* file, char *s, unsigned max) {
+    int i;
+    for (i=0; s[i] && i < max; i++)
+       if (isprint(s[i]) || s[i] == ' ')
+           fputc(s[i], file);
+       else {
+           if (max - i > 3) {
+               fprintf(file, "\\%03o", s[i]);
+               i += 3; /* 4 printed, but i counts one */
+           } else
+               return max - i;
+       }
+    return max - i;
+}
+
+/* output an argv style NULL-terminated string list, converting unprintables
+   to octal as we go, separating items of the list by 'sep' and stopping after
+   processing max chars of output (accounting for expansion due to octal rep).
+*/
+unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max) {
+    int i, n, seplen = strlen(sep);
+    for (n=0; *strs && n < max; strs++) {
+       for (i=0; strs[0][i] && n+i < max; i++)
+           if (isprint(strs[0][i]) || strs[0][i] == ' ')
+               fputc(strs[0][i], file);
+           else {
+               if (max-(n+i) > 3) {
+                   fprintf(file, "\\%03o", strs[0][i]);
+                   n += 3; /* 4 printed, but i counts one */
+               } else
+                   return max - n;
+           }
+       n += i;
+       if (n + seplen < max) {
+           fputs(sep, file);
+           n += seplen;
+       } else
+           return max - n;
+    }
+    return max - n;
+}
diff --git a/proc/procps.h b/proc/procps.h
new file mode 100644 (file)
index 0000000..4449750
--- /dev/null
@@ -0,0 +1,27 @@
+/* The shadow of the original with only common prototypes now. */
+#include <stdio.h>
+#include <sys/types.h>
+
+/* The HZ constant from <asm/param.h> is replaced by the Hertz variable
+ * available from "proc/sysinfo.h".
+ */
+
+/* get page info */
+#include <asm/page.h>
+
+void *xrealloc(void *oldp, unsigned int size);
+void *xmalloc(unsigned int size);
+void *xcalloc(void *pointer, int size);
+       
+int   mult_lvl_cmp(void* a, void* b);
+int   node_mult_lvl_cmp(void* a, void* b);
+       
+char *user_from_uid(uid_t uid);
+char *group_from_gid(gid_t gid);
+
+const char * wchan(unsigned long address);
+int   open_psdb(const char *override);
+int   open_psdb_message(const char *override, void (*message)(const char *, ...));
+
+unsigned print_str    (FILE* file, char *s, unsigned max);
+unsigned print_strlist(FILE* file, char **strs, char* sep, unsigned max);
diff --git a/proc/pwcache.c b/proc/pwcache.c
new file mode 100644 (file)
index 0000000..8fff60b
--- /dev/null
@@ -0,0 +1,74 @@
+/***********************************************************************\
+*   Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
+*                                                                      *
+*      This file is placed under the conditions of the GNU Library     *
+*      General Public License, version 2, or any later version.        *
+*      See file ../COPYING for information on distribution conditions. *
+\***********************************************************************/
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include "proc/procps.h"
+#include <grp.h>
+
+#define        HASHSIZE        16                      /* power of 2 */
+#define        HASH(x)         ((x) & (HASHSIZE - 1))
+
+#define NAMESIZE       16
+#define NAMELENGTH     "15"
+
+static struct pwbuf {
+    uid_t uid;
+    char name[NAMESIZE];
+    struct pwbuf *next;
+} *pwhash[HASHSIZE];
+
+char *user_from_uid(uid_t uid)
+{
+    struct pwbuf **p;
+    struct passwd *pw;
+
+    p = &pwhash[HASH(uid)];
+    while (*p) {
+       if ((*p)->uid == uid)
+           return((*p)->name);
+       p = &(*p)->next;
+    }
+    *p = (struct pwbuf *) xmalloc(sizeof(struct pwbuf));
+    (*p)->uid = uid;
+    if ((pw = getpwuid(uid)) == NULL)
+       sprintf((*p)->name, "#%d", uid);
+    else
+       sprintf((*p)->name, "%-." NAMELENGTH "s", pw->pw_name);
+    (*p)->next = NULL;
+    return((*p)->name);
+}
+
+static struct grpbuf {
+    gid_t gid;
+    char name[NAMESIZE];
+    struct grpbuf *next;
+} *grphash[HASHSIZE];
+
+char *group_from_gid(gid_t gid)
+{
+    struct grpbuf **g;
+    struct group *gr;
+
+    g = &grphash[HASH(gid)];
+    while (*g) {
+       if ((*g)->gid == gid)
+           return((*g)->name);
+       g = &(*g)->next;
+    }
+    *g = (struct grpbuf *) malloc(sizeof(struct grpbuf));
+    (*g)->gid = gid;
+    if ((gr = getgrgid(gid)) == NULL)
+       sprintf((*g)->name, "#%d", gid);
+    else
+       sprintf((*g)->name, "%-." NAMELENGTH "s", gr->gr_name);
+    (*g)->next = NULL;
+    return((*g)->name);
+}
diff --git a/proc/readproc.c b/proc/readproc.c
new file mode 100644 (file)
index 0000000..21a3996
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright (C) 1996 Charles L. Blake.
+ * Copyright (C) 1998 Michael K. Johnson
+ * May be distributed under the conditions of the
+ * GNU Library General Public License; a copy is in COPYING
+ */
+#include "proc/version.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/procps.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/dir.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define Do(x) (flags & PROC_ ## x)     /* convenient shorthand */
+
+/* initiate a process table scan
+ */
+PROCTAB* openproc(int flags, ...) {
+    va_list ap;
+    PROCTAB* PT = xmalloc(sizeof(PROCTAB));
+    
+    if (Do(PID))
+      PT->procfs = NULL;
+    else if (!(PT->procfs = opendir("/proc")))
+      return NULL;
+    PT->flags = flags;
+    va_start(ap, flags);               /*  Init args list */
+    if (Do(PID))
+       PT->pids = va_arg(ap, pid_t*);
+    else if (Do(TTY))
+       PT->ttys = va_arg(ap, dev_t*);
+    else if (Do(UID)) {
+       PT->uids = va_arg(ap, uid_t*);
+       PT->nuid = va_arg(ap, int);
+    } else if (Do(STAT))
+       PT->stats = va_arg(ap, char*);
+    va_end(ap);                                /*  Clean up args list */
+    if (Do(ANYTTY) && Do(TTY))
+       PT->flags = PT->flags & ~PROC_TTY; /* turn off TTY flag */
+    return PT;
+}
+
+/* terminate a process table scan
+ */
+void closeproc(PROCTAB* PT) {
+    if (PT){
+        if (PT->procfs) closedir(PT->procfs);
+        free(PT);
+    }
+}
+
+/* deallocate the space allocated by readproc if the passed rbuf was NULL
+ */
+void freeproc(proc_t* p) {
+    if (!p)    /* in case p is NULL */
+       return;
+    /* ptrs are after strings to avoid copying memory when building them. */
+    /* so free is called on the address of the address of strvec[0]. */
+    if (p->cmdline)
+       free((void*)*p->cmdline);
+    if (p->environ)
+       free((void*)*p->environ);
+    free(p);
+}
+
+
+
+static void status2proc (char* S, proc_t* P, int fill) {
+    char* tmp;
+    if (fill == 1) {
+        memset(P->cmd, 0, sizeof P->cmd);
+        sscanf (S, "Name:\t%15c", P->cmd);
+        tmp = strchr(P->cmd,'\n');
+        *tmp='\0';
+        tmp = strstr (S,"State");
+        sscanf (tmp, "State:\t%c", &P->state);
+    }
+
+    tmp = strstr (S,"Pid:");
+    if(tmp) sscanf (tmp,
+        "Pid:\t%d\n"
+        "PPid:\t%d\n",
+        &P->pid,
+        &P->ppid
+    );
+    else fprintf(stderr, "Internal error!\n");
+
+    tmp = strstr (S,"Uid:");
+    if(tmp) sscanf (tmp,
+        "Uid:\t%d\t%d\t%d\t%d",
+        &P->ruid, &P->euid, &P->suid, &P->fuid
+    );
+    else fprintf(stderr, "Internal error!\n");
+
+    tmp = strstr (S,"Gid:");
+    if(tmp) sscanf (tmp,
+        "Gid:\t%d\t%d\t%d\t%d",
+        &P->rgid, &P->egid, &P->sgid, &P->fgid
+    );
+    else fprintf(stderr, "Internal error!\n");
+
+    tmp = strstr (S,"VmSize:");
+    if(tmp) sscanf (tmp,
+        "VmSize: %lu kB\n"
+        "VmLck: %lu kB\n"
+        "VmRSS: %lu kB\n"
+        "VmData: %lu kB\n"
+        "VmStk: %lu kB\n"
+        "VmExe: %lu kB\n"
+        "VmLib: %lu kB\n",
+        &P->vm_size, &P->vm_lock, &P->vm_rss, &P->vm_data,
+        &P->vm_stack, &P->vm_exe, &P->vm_lib
+    );
+    else /* looks like an annoying kernel thread */
+    {
+        P->vm_size  = 0;
+        P->vm_lock  = 0;
+        P->vm_rss   = 0;
+        P->vm_data  = 0;
+        P->vm_stack = 0;
+        P->vm_exe   = 0;
+        P->vm_lib   = 0;
+    }
+
+    tmp = strstr (S,"SigPnd:");
+    if(tmp) sscanf (tmp,
+#ifdef SIGNAL_STRING
+        "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
+        P->signal, P->blocked, P->sigignore, P->sigcatch
+#else
+        "SigPnd: %Lx SigBlk: %Lx SigIgn: %Lx %*s %Lx",
+        &P->signal, &P->blocked, &P->sigignore, &P->sigcatch
+#endif
+    );
+    else fprintf(stderr, "Internal error!\n");
+}
+
+
+
+/* stat2proc() makes sure it can handle arbitrary executable file basenames
+ * for `cmd', i.e. those with embedded whitespace or embedded ')'s.
+ * Such names confuse %s (see scanf(3)), so the string is split and %39c
+ * is used instead. (except for embedded ')' "(%[^)]c)" would work.
+ */
+static void stat2proc(char* S, proc_t* P) {
+    int num;
+    char* tmp = strrchr(S, ')');       /* split into "PID (cmd" and "<rest>" */
+    *tmp = '\0';                       /* replace trailing ')' with NUL */
+    /* fill in default values for older kernels */
+    P->exit_signal = SIGCHLD;
+    P->processor = 0;
+    /* parse these two strings separately, skipping the leading "(". */
+    memset(P->cmd, 0, sizeof P->cmd);  /* clear even though *P xcalloc'd ?! */
+    sscanf(S, "%d (%15c", &P->pid, P->cmd);   /* comm[16] in kernel */
+    num = sscanf(tmp + 2,                      /* skip space after ')' too */
+       "%c "
+       "%d %d %d %d %d "
+       "%lu %lu %lu %lu %lu %lu %lu "
+       "%ld %ld %ld %ld %ld %ld "
+       "%lu %lu "
+       "%ld "
+       "%lu %lu %lu %lu %lu %lu "
+       "%*s %*s %*s %*s " /* discard, no RT signals & Linux 2.1 used hex */
+       "%lu %lu %lu "
+       "%d %d",
+       &P->state,
+       &P->ppid, &P->pgrp, &P->session, &P->tty, &P->tpgid,
+       &P->flags, &P->min_flt, &P->cmin_flt, &P->maj_flt, &P->cmaj_flt, &P->utime, &P->stime,
+       &P->cutime, &P->cstime, &P->priority, &P->nice, &P->timeout, &P->it_real_value,
+       &P->start_time, &P->vsize,
+       &P->rss,
+       &P->rss_rlim, &P->start_code, &P->end_code, &P->start_stack, &P->kstk_esp, &P->kstk_eip,
+/*     P->signal, P->blocked, P->sigignore, P->sigcatch,   */ /* can't use */
+       &P->wchan, &P->nswap, &P->cnswap,
+/* -- Linux 2.0.35 ends here -- */
+       &P->exit_signal, &P->processor  /* 2.2.1 ends with "exit_signal" */
+/* -- Linux 2.2.8 and 2.3.47 end here -- */
+    );
+    
+    /* fprintf(stderr, "stat2proc converted %d fields.\n",num); */
+    if (P->tty == 0)
+       P->tty = -1;  /* the old notty val, update elsewhere bef. moving to 0 */
+}
+
+static void statm2proc(char* s, proc_t* P) {
+    int num;
+    num = sscanf(s, "%ld %ld %ld %ld %ld %ld %ld",
+          &P->size, &P->resident, &P->share,
+          &P->trs, &P->lrs, &P->drs, &P->dt);
+/*    fprintf(stderr, "statm2proc converted %d fields.\n",num); */
+}
+
+static int file2str(char *directory, char *what, char *ret, int cap) {
+    static char filename[80];
+    int fd, num_read;
+
+    sprintf(filename, "%s/%s", directory, what);
+    if ( (fd       = open(filename, O_RDONLY, 0)) == -1 ) return -1;
+    if ( (num_read = read(fd, ret, cap - 1))      <= 0 ) num_read = -1;
+    else ret[num_read] = 0;
+    close(fd);
+    return num_read;
+}
+
+static char** file2strvec(char* directory, char* what) {
+    char buf[2048];    /* read buf bytes at a time */
+    char *p, *rbuf = 0, *endbuf, **q, **ret;
+    int fd, tot = 0, n, c, end_of_file = 0;
+    int align;
+
+    sprintf(buf, "%s/%s", directory, what);
+    if ( (fd = open(buf, O_RDONLY, 0) ) == -1 ) return NULL;
+
+    /* read whole file into a memory buffer, allocating as we go */
+    while ((n = read(fd, buf, sizeof buf - 1)) > 0) {
+       if (n < sizeof buf - 1)
+           end_of_file = 1;
+       if (n == 0 && rbuf == 0)
+           return NULL;        /* process died between our open and read */
+       if (n < 0) {
+           if (rbuf)
+               free(rbuf);
+           return NULL;        /* read error */
+       }
+       if (end_of_file && buf[n-1])            /* last read char not null */
+           buf[n++] = '\0';                    /* so append null-terminator */
+       rbuf = xrealloc(rbuf, tot + n);         /* allocate more memory */
+       memcpy(rbuf + tot, buf, n);             /* copy buffer into it */
+       tot += n;                               /* increment total byte ctr */
+       if (end_of_file)
+           break;
+    }
+    close(fd);
+    if (n <= 0 && !end_of_file) {
+       if (rbuf) free(rbuf);
+       return NULL;            /* read error */
+    }
+    endbuf = rbuf + tot;                       /* count space for pointers */
+    align = (sizeof(char*)-1) - ((tot + sizeof(char*)-1) & (sizeof(char*)-1));
+    for (c = 0, p = rbuf; p < endbuf; p++)
+       if (!*p)
+           c += sizeof(char*);
+    c += sizeof(char*);                                /* one extra for NULL term */
+
+    rbuf = xrealloc(rbuf, tot + c + align);    /* make room for ptrs AT END */
+    endbuf = rbuf + tot;                       /* addr just past data buf */
+    q = ret = (char**) (endbuf+align);         /* ==> free(*ret) to dealloc */
+    *q++ = p = rbuf;                           /* point ptrs to the strings */
+    endbuf--;                                  /* do not traverse final NUL */
+    while (++p < endbuf) 
+       if (!*p)                                /* NUL char implies that */
+           *q++ = p+1;                         /* next string -> next char */
+
+    *q = 0;                                    /* null ptr list terminator */
+    return ret;
+}
+
+
+/* These are some nice GNU C expression subscope "inline" functions.
+ * The can be used with arbitrary types and evaluate their arguments
+ * exactly once.
+ */
+
+/* Test if item X of type T is present in the 0 terminated list L */
+#   define XinL(T, X, L) ( {                   \
+           T  x = (X), *l = (L);               \
+           while (*l && *l != x) l++;          \
+           *l == x;                            \
+       } )
+
+/* Test if item X of type T is present in the list L of length N */
+#   define XinLN(T, X, L, N) ( {               \
+           T x = (X), *l = (L);                \
+           int i = 0, n = (N);                 \
+           while (i < n && l[i] != x) i++;     \
+           i < n && l[i] == x;                 \
+       } )
+
+/* readproc: return a pointer to a proc_t filled with requested info about the
+ * next process available matching the restriction set.  If no more such
+ * processes are available, return a null pointer (boolean false).  Use the
+ * passed buffer instead of allocating space if it is non-NULL.  */
+
+/* This is optimized so that if a PID list is given, only those files are
+ * searched for in /proc.  If other lists are given in addition to the PID list,
+ * the same logic can follow through as for the no-PID list case.  This is
+ * fairly complex, but it does try to not to do any unnecessary work.
+ * Unfortunately, the reverse filtering option in which any PID *except* the
+ * ones listed is pursued.
+ */
+proc_t* readproc(PROCTAB* PT, proc_t* rbuf) {
+    static struct direct *ent;         /* dirent handle */
+    static struct stat sb;             /* stat buffer */
+    static char path[32], sbuf[512];   /* bufs for stat,statm */
+    int allocated = 0, matched = 0;    /* flags */
+    proc_t *p = NULL;
+
+    /* loop until a proc matching restrictions is found or no more processes */
+    /* I know this could be a while loop -- this way is easier to indent ;-) */
+next_proc:                             /* get next PID for consideration */
+
+/*printf("PT->flags is 0x%08x\n", PT->flags);*/
+#define flags (PT->flags)
+
+    if (Do(PID)) {
+       if (!*PT->pids)                 /* set to next item in pids */
+           return NULL;
+       sprintf(path, "/proc/%d", *(PT->pids)++);
+       matched = 1;
+    } else {                                   /* get next numeric /proc ent */
+       while ((ent = readdir(PT->procfs)) &&
+              (*ent->d_name < '0' || *ent->d_name > '9'))
+           ;
+       if (!ent || !ent->d_name)
+           return NULL;
+       sprintf(path, "/proc/%s", ent->d_name);
+    }
+    if (stat(path, &sb) == -1)         /* no such dirent (anymore) */
+       goto next_proc;
+    if (Do(UID) && !XinLN(uid_t, sb.st_uid, PT->uids, PT->nuid))
+       goto next_proc;                 /* not one of the requested uids */
+
+    if (!allocated) {                           /* assign mem for return buf */
+       p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */
+       allocated = 1;                           /* remember space is set up */
+    }
+    p->euid = sb.st_uid;                       /* need a way to get real uid */
+
+    if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
+       goto next_proc;                 /* error reading /proc/#/stat */
+    stat2proc(sbuf, p);                                /* parse /proc/#/stat */
+
+    if (!matched && Do(TTY) && !XinL(dev_t, p->tty, PT->ttys))
+       goto next_proc;                 /* not one of the requested ttys */
+
+    if (!matched && Do(ANYTTY) && p->tty == -1)
+       goto next_proc;                 /* no controlling terminal */
+
+    if (!matched && Do(STAT) && !strchr(PT->stats,p->state))
+       goto next_proc;                 /* not one of the requested states */
+
+    if (Do(FILLMEM)) {                         /* read, parse /proc/#/statm */
+       if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 )
+           statm2proc(sbuf, p);                /* ignore statm errors here */
+    }                                          /* statm fields just zero */
+
+    if (Do(FILLSTATUS)) {         /* read, parse /proc/#/status */
+       if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){
+           status2proc(sbuf, p, 0 /*FIXME*/);
+       }
+    }
+
+    /* some number->text resolving which is time consuming */
+    if (Do(FILLUSR)){
+       strncpy(p->euser,   user_from_uid(p->euid), sizeof p->euser);
+        strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
+        if(Do(FILLSTATUS)) {
+            strncpy(p->ruser,   user_from_uid(p->ruid), sizeof p->ruser);
+            strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
+            strncpy(p->suser,   user_from_uid(p->suid), sizeof p->suser);
+            strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
+            strncpy(p->fuser,   user_from_uid(p->fuid), sizeof p->fuser);
+            strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
+        }
+    }
+
+    if (Do(FILLCMD))                           /* read+parse /proc/#/cmdline */
+       p->cmdline = file2strvec(path, "cmdline");
+    if (Do(FILLENV))                           /* read+parse /proc/#/environ */
+       p->environ = file2strvec(path, "environ");
+    
+    if (p->state == 'Z')                       /* fixup cmd for zombies */
+       strncat(p->cmd," <defunct>", sizeof p->cmd);
+
+    return p;
+}
+#undef flags
+
+/* ps_readproc: return a pointer to a proc_t filled with requested info about the
+ * next process available matching the restriction set.  If no more such
+ * processes are available, return a null pointer (boolean false).  Use the
+ * passed buffer instead of allocating space if it is non-NULL.  */
+
+/* This is optimized so that if a PID list is given, only those files are
+ * searched for in /proc.  If other lists are given in addition to the PID list,
+ * the same logic can follow through as for the no-PID list case.  This is
+ * fairly complex, but it does try to not to do any unnecessary work.
+ * Unfortunately, the reverse filtering option in which any PID *except* the
+ * ones listed is pursued.
+ */
+proc_t* ps_readproc(PROCTAB* PT, proc_t* rbuf) {
+    static struct direct *ent;         /* dirent handle */
+    static struct stat sb;             /* stat buffer */
+    static char path[32], sbuf[512];   /* bufs for stat,statm */
+    int allocated = 0 /* , matched = 0 */ ;    /* flags */
+    proc_t *p = NULL;
+
+    /* loop until a proc matching restrictions is found or no more processes */
+    /* I know this could be a while loop -- this way is easier to indent ;-) */
+next_proc:                             /* get next PID for consideration */
+
+/*printf("PT->flags is 0x%08x\n", PT->flags);*/
+#define flags (PT->flags)
+
+       while ((ent = readdir(PT->procfs)) &&
+              (*ent->d_name < '0' || *ent->d_name > '9'))
+           ;
+       if (!ent || !ent->d_name)
+           return NULL;
+       sprintf(path, "/proc/%s", ent->d_name);
+
+    if (stat(path, &sb) == -1)         /* no such dirent (anymore) */
+       goto next_proc;
+
+    if (!allocated) {                           /* assign mem for return buf */
+       p = rbuf ? rbuf : xcalloc(p, sizeof *p); /* passed buf or alloced mem */
+       allocated = 1;                           /* remember space is set up */
+    }
+    p->euid = sb.st_uid;                       /* need a way to get real uid */
+
+    if ((file2str(path, "stat", sbuf, sizeof sbuf)) == -1)
+       goto next_proc;                 /* error reading /proc/#/stat */
+    stat2proc(sbuf, p);                                /* parse /proc/#/stat */
+
+/*    if (Do(FILLMEM)) {*/                             /* read, parse /proc/#/statm */
+       if ((file2str(path, "statm", sbuf, sizeof sbuf)) != -1 )
+           statm2proc(sbuf, p);                /* ignore statm errors here */
+/*    }                        */                      /* statm fields just zero */
+
+  /*  if (Do(FILLSTATUS)) { */        /* read, parse /proc/#/status */
+       if ((file2str(path, "status", sbuf, sizeof sbuf)) != -1 ){
+           status2proc(sbuf, p, 0 /*FIXME*/);
+       }
+/*    }*/
+
+    /* some number->text resolving which is time consuming */
+ /*   if (Do(FILLUSR)){ */
+       strncpy(p->euser,   user_from_uid(p->euid), sizeof p->euser);
+        strncpy(p->egroup, group_from_gid(p->egid), sizeof p->egroup);
+/*        if(Do(FILLSTATUS)) { */
+            strncpy(p->ruser,   user_from_uid(p->ruid), sizeof p->ruser);
+            strncpy(p->rgroup, group_from_gid(p->rgid), sizeof p->rgroup);
+            strncpy(p->suser,   user_from_uid(p->suid), sizeof p->suser);
+            strncpy(p->sgroup, group_from_gid(p->sgid), sizeof p->sgroup);
+            strncpy(p->fuser,   user_from_uid(p->fuid), sizeof p->fuser);
+            strncpy(p->fgroup, group_from_gid(p->fgid), sizeof p->fgroup);
+/*        }*/
+/*    }*/
+
+/*    if (Do(FILLCMD)) */                      /* read+parse /proc/#/cmdline */
+       p->cmdline = file2strvec(path, "cmdline");
+/*    if (Do(FILLENV)) */                      /* read+parse /proc/#/environ */
+       p->environ = file2strvec(path, "environ");
+    
+    if (p->state == 'Z')                       /* fixup cmd for zombies */
+       strncat(p->cmd," <defunct>", sizeof p->cmd);
+
+    return p;
+}
+#undef flags
+
+
+void look_up_our_self(proc_t *p) {
+    static char path[32], sbuf[512];   /* bufs for stat,statm */
+    sprintf(path, "/proc/%d", getpid());
+    file2str(path, "stat", sbuf, sizeof sbuf);
+    stat2proc(sbuf, p);                                /* parse /proc/#/stat */
+    file2str(path, "statm", sbuf, sizeof sbuf);
+    statm2proc(sbuf, p);               /* ignore statm errors here */
+    file2str(path, "status", sbuf, sizeof sbuf);
+    status2proc(sbuf, p, 0 /*FIXME*/);
+}
+
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * tree subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctree().  The tree structure is a classic
+ * left-list children + right-list siblings.  The algorithm is a two-pass of the
+ * process table.  Since most process trees will have children with strictly
+ * increasing PIDs, most of the structure will be picked up in the first pass.
+ * The second loop then cleans up any nodes which turn out to have preceeded
+ * their parent in /proc order.
+ */
+
+/* Traverse tree 't' breadth-first looking for a process with pid p */
+static proc_t* LookupPID(proc_t* t, pid_t p) {
+    proc_t* tmp = NULL;
+    if (!t)
+       return NULL;
+    if (t->pid == p)                           /* look here/terminate recursion */
+       return t;
+    if ((tmp = LookupPID(t->l, p)))            /* recurse over children */
+       return tmp;
+    for (; t; t=t->r)                          /* recurse over siblings */
+       if ((tmp = LookupPID(tmp, p)))
+           return tmp;
+    return NULL;
+}
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctab().  Access via tab[N]->member.  The
+ * pointer list is NULL terminated.
+ */
+proc_t** readproctab(int flags, ...) {
+    PROCTAB* PT = NULL;
+    proc_t** tab = NULL;
+    int n = 0;
+    va_list ap;
+
+    va_start(ap, flags);               /* pass through args to openproc */
+    if (Do(UID)) {
+       /* temporary variables to ensure that va_arg() instances
+        * are called in the right order
+        */
+       uid_t* u;
+       int i;
+
+       u = va_arg(ap, uid_t*);
+       i = va_arg(ap, int);
+       PT = openproc(flags, u, i);
+    }
+    else if (Do(PID) || Do(TTY) || Do(STAT))
+       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+    else
+       PT = openproc(flags);
+    va_end(ap);
+    do {                                       /* read table: */
+       tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+       tab[n] = readproc(PT, NULL);              /* final null to terminate */
+    } while (tab[n++]);                                  /* stop when NULL reached */
+    closeproc(PT);
+    return tab;
+}
diff --git a/proc/readproc.h b/proc/readproc.h
new file mode 100644 (file)
index 0000000..8119594
--- /dev/null
@@ -0,0 +1,200 @@
+#ifndef PROCPS_PROC_READPROC_H
+#define PROCPS_PROC_READPROC_H
+/*
+ * New Interface to Process Table -- PROCTAB Stream (a la Directory streams)
+ * Copyright (C) 1996 Charles L. Blake.
+ * Copyright (C) 1998 Michael K. Johnson
+ * May be distributed under the terms of the
+ * GNU Library General Public License, a copy of which is provided
+ * in the file COPYING
+ */
+
+
+#define SIGNAL_STRING
+
+
+/*
+ ld    cutime, cstime, priority, nice, timeout, it_real_value, rss,
+ c     state,
+ d     ppid, pgrp, session, tty, tpgid,
+ s     signal, blocked, sigignore, sigcatch,
+ lu    flags, min_flt, cmin_flt, maj_flt, cmaj_flt, utime, stime,
+ lu    rss_rlim, start_code, end_code, start_stack, kstk_esp, kstk_eip,
+ lu    start_time, vsize, wchan, nswap, cnswap,
+*/
+
+/* Basic data structure which holds all information we can get about a process.
+ * (unless otherwise specified, fields are read from /proc/#/stat)
+ *
+ * Most of it comes from task_struct in linux/sched.h
+ */
+typedef struct proc_s {
+#ifdef SIGNAL_STRING
+    char
+       /* Linux 2.1.7x and up have more signals. This handles 88. */
+       signal[24],     /* mask of pending signals */
+       blocked[24],    /* mask of blocked signals */
+       sigignore[24],  /* mask of ignored signals */
+       sigcatch[24];   /* mask of caught  signals */
+#else
+    long long
+       /* Linux 2.1.7x and up have more signals. This handles 64. */
+       signal,         /* mask of pending signals */
+       blocked,        /* mask of blocked signals */
+       sigignore,      /* mask of ignored signals */
+       sigcatch;       /* mask of caught  signals */
+#endif
+    long
+       cutime,         /* cumulative utime of process and reaped children */
+       cstime,         /* cumulative stime of process and reaped children */
+       priority,       /* kernel scheduling priority */
+       timeout,        /* ? */
+       nice,           /* standard unix nice level of process */
+       rss,            /* resident set size from /proc/#/stat (pages) */
+       it_real_value,  /* ? */
+    /* the next 7 members come from /proc/#/statm */
+       size,           /* total # of pages of memory */
+       resident,       /* number of resident set (non-swapped) pages (4k) */
+       share,          /* number of pages of shared (mmap'd) memory */
+       trs,            /* text resident set size */
+       lrs,            /* shared-lib resident set size */
+       drs,            /* data resident set size */
+       dt;             /* dirty pages */
+    unsigned long
+       /* FIXME: are these longs? Maybe when the alpha does PCI bounce buffers */
+       vm_size,        /* same as vsize in kb */
+       vm_lock,        /* locked pages in kb */
+       vm_rss,         /* same as rss in kb */
+       vm_data,        /* data size */
+       vm_stack,       /* stack size */
+       vm_exe,         /* executable size */
+       vm_lib,         /* library size (all pages, not just used ones) */
+       vsize,          /* number of pages of virtual memory ... */
+       rss_rlim,       /* resident set size limit? */
+       flags,          /* kernel flags for the process */
+       min_flt,        /* number of minor page faults since process start */
+       maj_flt,        /* number of major page faults since process start */
+       cmin_flt,       /* cumulative min_flt of process and child processes */
+       cmaj_flt,       /* cumulative maj_flt of process and child processes */
+       nswap,          /* ? */
+       cnswap,         /* cumulative nswap ? */
+       utime,          /* user-mode CPU time accumulated by process */
+       stime,          /* kernel-mode CPU time accumulated by process */
+       start_code,     /* address of beginning of code segment */
+       end_code,       /* address of end of code segment */
+       start_stack,    /* address of the bottom of stack for the process */
+       kstk_esp,       /* kernel stack pointer */
+       kstk_eip,       /* kernel instruction pointer */
+       start_time,     /* start time of process -- seconds since 1-1-70 */
+       wchan;          /* address of kernel wait channel proc is sleeping in */
+    struct proc_s *l,  /* ptrs for building arbitrary linked structs */
+                  *r;  /* (i.e. singly/doubly-linked lists and trees */
+    char
+       **environ,      /* environment string vector (/proc/#/environ) */
+       **cmdline;      /* command line string vector (/proc/#/cmdline) */
+    char
+       /* Be compatible: Digital allows 16 and NT allows 14 ??? */
+       ruser[16],      /* real user name */
+       euser[16],      /* effective user name */
+       suser[16],      /* saved user name */
+       fuser[16],      /* filesystem user name */
+       rgroup[16],     /* real group name */
+       egroup[16],     /* effective group name */
+       sgroup[16],     /* saved group name */
+       fgroup[16],     /* filesystem group name */
+       cmd[16];        /* basename of executable file in call to exec(2) */
+    int
+        ruid, rgid,     /* real      */
+        euid, egid,     /* effective */
+        suid, sgid,     /* saved     */
+        fuid, fgid,     /* fs (used for file access only) */
+       pid,            /* process id */
+       ppid,           /* pid of parent process */
+       pgrp,           /* process group id */
+       session,        /* session id */
+       tty,            /* full device number of controlling terminal */
+       tpgid,          /* terminal process group id */
+       exit_signal,    /* might not be SIGCHLD */
+       processor;      /* current (or most recent?) CPU */
+    unsigned
+        pcpu;           /* %CPU usage (is not filled in by readproc!!!) */
+    char
+       state;          /* single-char code for process state (S=sleeping) */
+} proc_t;
+
+/* PROCTAB: data structure holding the persistent information readproc needs
+ * from openproc().  The setup is intentionally similar to the dirent interface
+ * and other system table interfaces (utmp+wtmp come to mind).
+ */
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+typedef struct {
+    DIR*       procfs;
+    int                flags;
+    pid_t*     pids;   /* pids of the procs */
+    dev_t*     ttys;   /* devnos of the cttys */
+    uid_t*     uids;   /* uids of procs */
+    int                nuid;   /* cannot really sentinel-terminate unsigned short[] */
+    char*      stats;  /* status chars (actually output into /proc//stat) */
+} PROCTAB;
+
+/* initialize a PROCTAB structure holding needed call-to-call persistent data
+ */
+PROCTAB* openproc(int flags, ... /* pid_t*|uid_t*|dev_t*|char* [, int n] */ );
+
+
+/* Convenient wrapper around openproc and readproc to slurp in the whole process
+ * table subset satisfying the constraints of flags and the optional PID list.
+ * Free allocated memory with freeproctab().  Access via tab[N]->member.  The
+ * pointer list is NULL terminated.
+ */
+proc_t** readproctab(int flags, ... /* same as openproc */ );
+
+/* clean-up open files, etc from the openproc()
+ */
+void closeproc(PROCTAB* PT);
+
+/* retrieve the next process matching the criteria set by the openproc()
+ */
+proc_t* readproc(PROCTAB* PT, proc_t* return_buf);
+proc_t* ps_readproc(PROCTAB* PT, proc_t* return_buf);
+
+void look_up_our_self(proc_t *p);
+
+/* deallocate space allocated by readproc
+ */
+void freeproc(proc_t* p);
+
+/* openproc/readproctab:
+ *   
+ * Return PROCTAB* / *proc_t[] or NULL on error ((probably) "/proc" cannot be
+ * opened.)  By default readproc will consider all processes as valid to parse
+ * and return, but not actually fill in the cmdline, environ, and /proc/#/statm
+ * derived memory fields.
+ *
+ * `flags' (a bitwise-or of PROC_* below) modifies the default behavior.  The
+ * "fill" options will cause more of the proc_t to be filled in.  The "filter"
+ * options all use the second argument as the pointer to a list of objects:
+ * process status', process id's, user id's, and tty device numbers.  The third
+ * argument is the length of the list (currently only used for lists of user
+ * id's since unsigned short[] supports no convenient termination sentinel.)
+ */
+#define PROC_FILLANY    0x00 /* either stat or status will do */
+#define PROC_FILLMEM    0x01 /* read statm into the appropriate proc_t entries */
+#define PROC_FILLCMD    0x02 /* alloc and fill in `cmdline' part of proc_t */
+#define PROC_FILLENV    0x04 /* alloc and fill in `environ' part of proc_t */
+#define PROC_FILLUSR    0x08 /* resolve user id number -> user name via passwd */
+#define PROC_FILLSTATUS 0x10
+#define PROC_FILLSTAT   0x20
+#define PROC_FILLBUG    0x3f    /* No idea what we need */
+
+
+/* Obsolete, consider only processes with one of the passed: */
+#define PROC_PID     0x0100  /* process id numbers ( 0   terminated) */
+#define PROC_TTY     0x0200  /* ctty device nos.   ( 0   terminated) */
+#define PROC_UID     0x0400  /* user id numbers    ( length needed ) */
+#define PROC_STAT    0x0800  /* status fields      ('\0' terminated) */
+#define PROC_ANYTTY  0x1000  /* proc must have a controlling terminal */
+
+#endif
diff --git a/proc/sig.c b/proc/sig.c
new file mode 100644 (file)
index 0000000..46dd847
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* Linux signals:
+ *
+ * SIGSYS is required by Unix98.
+ * SIGEMT is part of SysV, BSD, and ancient UNIX tradition.
+ *
+ * They are provided by these Linux ports: alpha, mips, sparc, and sparc64.
+ * You get SIGSTKFLT and SIGUNUSED instead on i386, m68k, ppc, and arm.
+ * (this is a Linux & libc bug -- both must be fixed)
+ *
+ * Total garbage: SIGIO SIGINFO SIGIOT SIGLOST SIGCLD
+ *                 (popular ones are handled as aliases)
+ * Nearly garbage: SIGSTKFLT SIGUNUSED (nothing else to fill slots)
+ */
+
+/* Linux 2.3.29 replaces SIGUNUSED with the standard SIGSYS signal */
+#ifndef SIGSYS
+#  warning Standards require that <signal.h> define SIGSYS
+#  define SIGSYS SIGUNUSED
+#endif
+
+/* If we see both, it is likely SIGSTKFLT (junk) was replaced. */
+#ifdef SIGEMT
+#  undef SIGSTKFLT
+#endif
+
+#ifndef SIGRTMIN
+#  warning Standards require that <signal.h> define SIGRTMIN; assuming 32
+#  define SIGRTMIN 32
+#endif
+
+/* It seems the SPARC libc does not know the kernel supports SIGPWR. */
+#ifndef SIGPWR
+#  warning Your header files lack SIGPWR. (assuming it is number 29)
+#  define SIGPWR 29
+#endif
+
+typedef struct mapstruct {
+  char *name;
+  int num;
+} mapstruct;
+
+
+static mapstruct sigtable[] = {
+  {"ABRT",   SIGABRT},  /* IOT */
+  {"ALRM",   SIGALRM},
+  {"BUS",    SIGBUS},
+  {"CHLD",   SIGCHLD},  /* CLD */
+  {"CONT",   SIGCONT},
+#ifdef SIGEMT
+  {"EMT",    SIGEMT},
+#endif
+  {"FPE",    SIGFPE},
+  {"HUP",    SIGHUP},
+  {"ILL",    SIGILL},
+  {"INT",    SIGINT},
+  {"KILL",   SIGKILL},
+  {"PIPE",   SIGPIPE},
+  {"POLL",   SIGPOLL},  /* IO */
+  {"PROF",   SIGPROF},
+  {"PWR",    SIGPWR},
+  {"QUIT",   SIGQUIT},
+  {"SEGV",   SIGSEGV},
+#ifdef SIGSTKFLT
+  {"STKFLT", SIGSTKFLT},
+#endif
+  {"STOP",   SIGSTOP},
+  {"SYS",    SIGSYS},   /* UNUSED */
+  {"TERM",   SIGTERM},
+  {"TRAP",   SIGTRAP},
+  {"TSTP",   SIGTSTP},
+  {"TTIN",   SIGTTIN},
+  {"TTOU",   SIGTTOU},
+  {"URG",    SIGURG},
+  {"USR1",   SIGUSR1},
+  {"USR2",   SIGUSR2},
+  {"VTALRM", SIGVTALRM},
+  {"WINCH",  SIGWINCH},
+  {"XCPU",   SIGXCPU},
+  {"XFSZ",   SIGXFSZ}
+};
+
+static const int number_of_signals = sizeof(sigtable)/sizeof(mapstruct);
+
+static int compare_signal_names(const void *a, const void *b){
+  return strcasecmp( ((mapstruct*)a)->name, ((mapstruct*)b)->name );
+}
+
+/* return -1 on failure */
+int signal_name_to_number(char *name){
+  const mapstruct *ptr;
+  mapstruct ms;
+  long val;
+  char *endp;
+  int offset;
+
+  /* clean up name */
+  if(!strncasecmp(name,"SIG",3)) name += 3;
+
+  if(!strcasecmp(name,"CLD")) return SIGCHLD;
+  if(!strcasecmp(name,"IO"))  return SIGPOLL;
+  if(!strcasecmp(name,"IOT")) return SIGABRT;
+
+  /* search the table */
+  ms.name = name;
+  ptr = bsearch(&ms, sigtable, number_of_signals,
+     sizeof(mapstruct), compare_signal_names
+  );
+  if(ptr) return ptr->num;
+
+  if(!strcasecmp(name,"RTMIN")) return SIGRTMIN;
+  if(!strcasecmp(name,"EXIT"))  return 0;
+  if(!strcasecmp(name,"NULL"))  return 0;
+
+  offset = 0;
+  if(!strncasecmp(name,"RTMIN+",6)){
+    name += 6;
+    offset = SIGRTMIN;
+  }
+
+  /* not found, so try as a number */
+  val = strtol(name,&endp,10);
+  if(*endp) return -1; /* not valid */
+  if(val+SIGRTMIN>127) return -1; /* not valid */
+  return val+offset;
+}
+
+static const char *signal_number_to_name(int signo){
+  static char buf[32];
+  int n = number_of_signals;
+  signo &= 0x7f; /* need to process exit values too */
+  while(n--){
+    if(sigtable[n].num==signo) return sigtable[n].name;
+  }
+  if(signo == SIGRTMIN) return "RTMIN";
+  if(signo) sprintf(buf, "RTMIN+%d", signo-SIGRTMIN);
+  else      strcpy(buf,"0");  /* AIX has NULL; Solaris has EXIT */
+  return buf;
+}
+
+int print_given_signals(int argc, char *argv[], int max_line){
+  char buf[1280]; /* 128 signals, "RTMIN+xx" is largest */
+  int ret = 0;  /* to be used as exit code by caller */
+  int place = 0; /* position on this line */
+  int amt;
+  if(argc > 128) return 1;
+  while(argc--){
+    char tmpbuf[16];
+    char *txt; /* convenient alias */
+    txt = *argv;
+    if(*txt >= '0' && *txt <= '9'){
+      long val;
+      char *endp;
+      val = strtol(txt,&endp,10);
+      if(*endp){
+        fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+        ret = 1;
+        goto end;
+      }
+      amt = sprintf(tmpbuf, "%s", signal_number_to_name(val));
+    }else{
+      int sno;
+      sno = signal_name_to_number(txt);
+      if(sno == -1){
+        fprintf(stderr, "Signal \"%s\" not known.\n", txt);
+        ret = 1;
+        goto end;
+      }
+      amt = sprintf(tmpbuf, "%d", sno);
+    }
+
+    if(!place){
+      strcpy(buf,tmpbuf);
+      place = amt;
+      goto end;
+    }
+    if(amt+place+1 > max_line){
+      printf("%s\n", buf);
+      strcpy(buf,tmpbuf);
+      place = amt;
+      goto end;
+    }
+    sprintf(buf+place, " %s", tmpbuf);
+    place += amt+1;
+end:
+    argv++;
+  }
+  if(place) printf("%s\n", buf);
+  return ret;
+}
+
+void pretty_print_signals(void){
+  int i = 0;
+  while(++i <= number_of_signals){
+    int n;
+    n = printf("%2d %s", i, signal_number_to_name(i));
+    if(i%7) printf("           \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + n);
+    else printf("\n");
+  }
+  if((i-1)%7) printf("\n");
+}
+
+void unix_print_signals(void){
+  int pos = 0;
+  int i = 0;
+  while(++i <= number_of_signals){
+    if(i-1) printf("%c", (pos>73)?(pos=0,'\n'):(pos++,' ') );
+    pos += printf("%s", signal_number_to_name(i));
+  }
+  printf("\n");
+}
+
+/* sanity check */
+static int init_signal_list(void) __attribute__((constructor));
+static int init_signal_list(void){
+  if(number_of_signals != 31){
+    fprintf(stderr, "WARNING: %d signals -- adjust and recompile.\n", number_of_signals);
+  }
+  return 0;
+}
diff --git a/proc/sig.h b/proc/sig.h
new file mode 100644 (file)
index 0000000..3f232c5
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* return -1 on failure */
+extern int signal_name_to_number(char *name);
+
+extern int print_given_signals(int argc, char *argv[], int max_line);
+
+extern void pretty_print_signals(void);
+
+extern void unix_print_signals(void);
diff --git a/proc/status.c b/proc/status.c
new file mode 100644 (file)
index 0000000..712743a
--- /dev/null
@@ -0,0 +1,29 @@
+/***********************************************************************\
+*   Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
+*                                                                      *
+*      This file is placed under the conditions of the GNU Library     *
+*      General Public License, version 2, or any later version.        *
+*      See file COPYING for information on distribution conditions.    *
+\***********************************************************************/
+
+
+#include "proc/procps.h"
+#include "proc/readproc.h"
+
+char * status(proc_t* task) {
+    static char buf[4] = "   ";
+
+    buf[0] = task->state;
+    if (task->rss == 0 && task->state != 'Z')
+        buf[1] = 'W';
+    else
+        buf[1] = ' ';
+    if (task->nice < 0)
+       buf[2] = '<';
+    else if (task->nice > 0)
+       buf[2] = 'N';
+    else
+       buf[2] = ' ';
+
+    return(buf);
+}
diff --git a/proc/status.h b/proc/status.h
new file mode 100644 (file)
index 0000000..562efae
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __PROC_STATUS_H
+#define __PROC_STATUS_H
+extern char *status(proc_t* task);
+#endif
diff --git a/proc/sysinfo.c b/proc/sysinfo.c
new file mode 100644 (file)
index 0000000..fbb14da
--- /dev/null
@@ -0,0 +1,325 @@
+/***********************************************************************\
+*   Copyright (C) 1992-1998 by Michael K. Johnson, johnsonm@redhat.com *
+*                                                                      *
+*      This file is placed under the conditions of the GNU Library     *
+*      General Public License, version 2, or any later version.        *
+*      See file COPYING for information on distribution conditions.    *
+\***********************************************************************/
+
+/* File for parsing top-level /proc entities. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include "proc/version.h"
+#include "proc/sysinfo.h" /* include self to verify prototypes */
+
+#ifndef HZ
+#include <netinet/in.h>  /* htons */
+#endif
+
+long smp_num_cpus;     /* number of CPUs */
+
+#define BAD_OPEN_MESSAGE                                       \
+"Error: /proc must be mounted\n"                               \
+"  To mount /proc at boot you need an /etc/fstab line like:\n" \
+"      /proc   /proc   proc    defaults\n"                     \
+"  In the meantime, mount /proc /proc -t proc\n"
+
+#define STAT_FILE    "/proc/stat"
+static int stat_fd = -1;
+#define UPTIME_FILE  "/proc/uptime"
+static int uptime_fd = -1;
+#define LOADAVG_FILE "/proc/loadavg"
+static int loadavg_fd = -1;
+#define MEMINFO_FILE "/proc/meminfo"
+static int meminfo_fd = -1;
+
+static char buf[1024];
+
+/* This macro opens filename only if necessary and seeks to 0 so
+ * that successive calls to the functions are more efficient.
+ * It also reads the current contents of the file into the global buf.
+ */
+#define FILE_TO_BUF(filename, fd) do{                          \
+    static int local_n;                                                \
+    if (fd == -1 && (fd = open(filename, O_RDONLY)) == -1) {   \
+       fprintf(stderr, BAD_OPEN_MESSAGE);                      \
+       fflush(NULL);                                           \
+       _exit(102);                                             \
+    }                                                          \
+    lseek(fd, 0L, SEEK_SET);                                   \
+    if ((local_n = read(fd, buf, sizeof buf - 1)) < 0) {       \
+       perror(filename);                                       \
+       fflush(NULL);                                           \
+       _exit(103);                                             \
+    }                                                          \
+    buf[local_n] = '\0';                                       \
+}while(0)
+
+/* evals 'x' twice */
+#define SET_IF_DESIRED(x,y) do{  if(x) *(x) = (y); }while(0)
+
+
+/***********************************************************************/
+int uptime(double *uptime_secs, double *idle_secs) {
+    double up=0, idle=0;
+
+    FILE_TO_BUF(UPTIME_FILE,uptime_fd);
+    if (sscanf(buf, "%lf %lf", &up, &idle) < 2) {
+       fprintf(stderr, "bad data in " UPTIME_FILE "\n");
+       return 0;
+    }
+    SET_IF_DESIRED(uptime_secs, up);
+    SET_IF_DESIRED(idle_secs, idle);
+    return up; /* assume never be zero seconds in practice */
+}
+
+/***********************************************************************
+ * Some values in /proc are expressed in units of 1/HZ seconds, where HZ
+ * is the kernel clock tick rate. One of these units is called a jiffy.
+ * The HZ value used in the kernel may vary according to hacker desire.
+ * According to Linus Torvalds, this is not true. He considers the values
+ * in /proc as being in architecture-dependant units that have no relation
+ * to the kernel clock tick rate. Examination of the kernel source code
+ * reveals that opinion as wishful thinking.
+ *
+ * In any case, we need the HZ constant as used in /proc. (the real HZ value
+ * may differ, but we don't care) There are several ways we could get HZ:
+ *
+ * 1. Include the kernel header file. If it changes, recompile this library.
+ * 2. Use the sysconf() function. When HZ changes, recompile the C library!
+ * 3. Ask the kernel. This is obviously correct...
+ *
+ * Linus Torvalds won't let us ask the kernel, because he thinks we should
+ * not know the HZ value. Oh well, we don't have to listen to him.
+ * Someone smuggled out the HZ value. :-)
+ *
+ * This code should work fine, even if Linus fixes the kernel to match his
+ * stated behavior. The code only fails in case of a partial conversion.
+ *
+ */
+unsigned long Hertz;
+static void init_Hertz_value(void) __attribute__((constructor));
+static void init_Hertz_value(void){
+  unsigned long user_j, nice_j, sys_j, other_j;  /* jiffies (clock ticks) */
+  double up_1, up_2, seconds;
+  unsigned long jiffies, h;
+  smp_num_cpus = sysconf(_SC_NPROCESSORS_CONF);
+  if(smp_num_cpus==-1) smp_num_cpus=1;
+  do{
+    FILE_TO_BUF(UPTIME_FILE,uptime_fd);  sscanf(buf, "%lf", &up_1);
+    /* uptime(&up_1, NULL); */
+    FILE_TO_BUF(STAT_FILE,stat_fd);
+    sscanf(buf, "cpu %lu %lu %lu %lu", &user_j, &nice_j, &sys_j, &other_j);
+    FILE_TO_BUF(UPTIME_FILE,uptime_fd);  sscanf(buf, "%lf", &up_2);
+    /* uptime(&up_2, NULL); */
+  } while((long)( (up_2-up_1)*1000.0/up_1 )); /* want under 0.1% error */
+  jiffies = user_j + nice_j + sys_j + other_j;
+  seconds = (up_1 + up_2) / 2;
+  h = (unsigned long)( (double)jiffies/seconds/smp_num_cpus );
+  /* actual values used by 2.4 kernels: 32 64 100 128 1000 1024 1200 */
+  switch(h){
+  case   30 ...   34 :  Hertz =   32; break; /* ia64 emulator */
+  case   48 ...   52 :  Hertz =   50; break;
+  case   58 ...   62 :  Hertz =   60; break;
+  case   63 ...   65 :  Hertz =   64; break; /* StrongARM /Shark */
+  case   95 ...  105 :  Hertz =  100; break; /* normal Linux */
+  case  124 ...  132 :  Hertz =  128; break; /* MIPS, ARM */
+  case  195 ...  204 :  Hertz =  200; break; /* normal << 1 */
+  case  253 ...  260 :  Hertz =  256; break;
+  case  393 ...  408 :  Hertz =  400; break; /* normal << 2 */
+  case  790 ...  808 :  Hertz =  800; break; /* normal << 3 */
+  case  990 ... 1010 :  Hertz = 1000; break; /* ARM */
+  case 1015 ... 1035 :  Hertz = 1024; break; /* Alpha, ia64 */
+  case 1180 ... 1220 :  Hertz = 1200; break; /* Alpha */
+  default:
+#ifdef HZ
+    Hertz = (unsigned long)HZ;    /* <asm/param.h> */
+#else
+    /* If 32-bit or big-endian (not Alpha or ia64), assume HZ is 100. */
+    Hertz = (sizeof(long)==sizeof(int) || htons(999)==999) ? 100UL : 1024UL;
+#endif
+    fprintf(stderr, "Unknown HZ value! (%ld) Assume %ld.\n", h, Hertz);
+  }
+}
+
+/***********************************************************************
+ * The /proc filesystem calculates idle=jiffies-(user+nice+sys) and we
+ * recover jiffies by adding up the 4 numbers we are given. SMP kernels
+ * (as of pre-2.4 era) can report idle time going backwards, perhaps due
+ * to non-atomic reads and updates. There is no locking for these values.
+ */
+#ifndef NAN
+#define NAN (-0.0)
+#endif
+#define JT unsigned long
+void four_cpu_numbers(double *uret, double *nret, double *sret, double *iret){
+    double tmp_u, tmp_n, tmp_s, tmp_i;
+    double scale;  /* scale values to % */
+    static JT old_u, old_n, old_s, old_i;
+    JT new_u, new_n, new_s, new_i;
+    JT ticks_past; /* avoid div-by-0 by not calling too often :-( */
+    FILE_TO_BUF(STAT_FILE,stat_fd);
+    sscanf(buf, "cpu %lu %lu %lu %lu", &new_u, &new_n, &new_s, &new_i);
+    ticks_past = (new_u+new_n+new_s+new_i)-(old_u+old_n+old_s+old_i);
+    if(ticks_past){
+      scale = 100.0 / (double)ticks_past;
+      tmp_u = ( (double)new_u - (double)old_u ) * scale;
+      tmp_n = ( (double)new_n - (double)old_n ) * scale;
+      tmp_s = ( (double)new_s - (double)old_s ) * scale;
+      tmp_i = ( (double)new_i - (double)old_i ) * scale;
+    }else{
+      tmp_u = NAN;
+      tmp_n = NAN;
+      tmp_s = NAN;
+      tmp_i = NAN;
+    }
+    SET_IF_DESIRED(uret, tmp_u);
+    SET_IF_DESIRED(nret, tmp_n);
+    SET_IF_DESIRED(sret, tmp_s);
+    SET_IF_DESIRED(iret, tmp_i);
+    old_u=new_u;
+    old_n=new_n;
+    old_s=new_s;
+    old_i=new_i;
+}
+#undef JT
+
+/***********************************************************************/
+void loadavg(double *av1, double *av5, double *av15) {
+    double avg_1=0, avg_5=0, avg_15=0;
+    
+    FILE_TO_BUF(LOADAVG_FILE,loadavg_fd);
+    if (sscanf(buf, "%lf %lf %lf", &avg_1, &avg_5, &avg_15) < 3) {
+       fprintf(stderr, "bad data in " LOADAVG_FILE "\n");
+       exit(1);
+    }
+    SET_IF_DESIRED(av1,  avg_1);
+    SET_IF_DESIRED(av5,  avg_5);
+    SET_IF_DESIRED(av15, avg_15);
+}
+
+/***********************************************************************/
+/*
+ * Copyright 1999 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+typedef struct mem_table_struct {
+  const char *name;     /* memory type name */
+  const unsigned *slot; /* slot in return struct */
+} mem_table_struct;
+
+static int compare_mem_table_structs(const void *a, const void *b){
+  return strcmp(((mem_table_struct*)a)->name,((mem_table_struct*)b)->name);
+}
+
+/* example data, following junk, with comments added:
+ *
+ * MemTotal:        61768 kB    old
+ * MemFree:          1436 kB    old
+ * MemShared:           0 kB    old (now always zero; not calculated)
+ * Buffers:          1312 kB    old
+ * Cached:          20932 kB    old
+ * Active:          12464 kB    new
+ * Inact_dirty:      7772 kB    new
+ * Inact_clean:      2008 kB    new
+ * Inact_target:        0 kB    new
+ * HighTotal:           0 kB
+ * HighFree:            0 kB
+ * LowTotal:        61768 kB
+ * LowFree:          1436 kB
+ * SwapTotal:      122580 kB    old
+ * SwapFree:        60352 kB    old
+ */
+
+/* obsolete */
+unsigned kb_main_shared;
+/* old but still kicking -- the important stuff */
+unsigned kb_main_buffers;
+unsigned kb_main_cached;
+unsigned kb_main_free;
+unsigned kb_main_total;
+unsigned kb_swap_free;
+unsigned kb_swap_total;
+/* recently introduced */
+unsigned kb_high_free;
+unsigned kb_high_total;
+unsigned kb_low_free;
+unsigned kb_low_total;
+/* 2.4.xx era */
+unsigned kb_active;
+unsigned kb_inact_dirty;
+unsigned kb_inact_clean;
+unsigned kb_inact_target;
+/* derived values */
+unsigned kb_swap_used;
+unsigned kb_main_used;
+
+void meminfo(void){
+  char namebuf[16]; /* big enough to hold any row name */
+  mem_table_struct findme = { namebuf, NULL};
+  mem_table_struct *found;
+  char *head;
+  char *tail;
+  static const mem_table_struct mem_table[] = {
+  {"Active",       &kb_active},
+  {"Buffers",      &kb_main_buffers},
+  {"Cached",       &kb_main_cached},
+  {"HighFree",     &kb_high_free},
+  {"HighTotal",    &kb_high_total},
+  {"Inact_clean",  &kb_inact_clean},
+  {"Inact_dirty",  &kb_inact_dirty},
+  {"Inact_target", &kb_inact_target},
+  {"LowFree",      &kb_low_free},
+  {"LowTotal",     &kb_low_total},
+  {"MemFree",      &kb_main_free},
+  {"MemShared",    &kb_main_shared},
+  {"MemTotal",     &kb_main_total},
+  {"SwapFree",     &kb_swap_free},
+  {"SwapTotal",    &kb_swap_total}
+  };
+  const int mem_table_count = sizeof(mem_table)/sizeof(mem_table_struct);
+
+  FILE_TO_BUF(MEMINFO_FILE,meminfo_fd);
+
+  head = buf;
+  for(;;){
+    tail = strchr(head, ':');
+    if(!tail) break;
+    *tail = '\0';
+    if(strlen(head) >= sizeof(namebuf)){
+      head = tail+1;
+      goto nextline;
+    }
+    strcpy(namebuf,head);
+    found = bsearch(&findme, mem_table, mem_table_count,
+        sizeof(mem_table_struct), compare_mem_table_structs
+    );
+    head = tail+1;
+    if(!found) goto nextline;
+    *(found->slot) = strtoul(head,&tail,10);
+nextline:
+    tail = strchr(head, '\n');
+    if(!tail) break;
+    head = tail+1;
+  }
+  if(!kb_low_total){  /* low==main except with large-memory support */
+    kb_low_total = kb_main_total;
+    kb_low_free  = kb_main_free;
+  }
+  kb_swap_used = kb_swap_total - kb_swap_free;
+  kb_main_used = kb_main_total - kb_main_free;
+}
diff --git a/proc/sysinfo.h b/proc/sysinfo.h
new file mode 100644 (file)
index 0000000..50064da
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef SYSINFO_H
+#define SYSINFO_H
+
+extern unsigned long Hertz;   /* clock tick frequency */
+extern long smp_num_cpus;     /* number of CPUs */
+
+#define JT double
+extern void four_cpu_numbers(JT *uret, JT *nret, JT *sret, JT *iret);
+#undef JT
+
+extern int        uptime (double *uptime_secs, double *idle_secs);
+extern void       loadavg(double *av1, double *av5, double *av15);
+
+
+/* obsolete */
+extern unsigned kb_main_shared;
+/* old but still kicking -- the important stuff */
+extern unsigned kb_main_buffers;
+extern unsigned kb_main_cached;
+extern unsigned kb_main_free;
+extern unsigned kb_main_total;
+extern unsigned kb_swap_free;
+extern unsigned kb_swap_total;
+/* recently introduced */
+extern unsigned kb_high_free;
+extern unsigned kb_high_total;
+extern unsigned kb_low_free;
+extern unsigned kb_low_total;
+/* 2.4.xx era */
+extern unsigned kb_active;
+extern unsigned kb_inact_dirty;
+extern unsigned kb_inact_clean;
+extern unsigned kb_inact_target;
+/* derived values */
+extern unsigned kb_swap_used;
+extern unsigned kb_main_used;
+
+extern void meminfo(void);
+
+#endif /* SYSINFO_H */
diff --git a/proc/tree.h b/proc/tree.h
new file mode 100644 (file)
index 0000000..f834fa2
--- /dev/null
@@ -0,0 +1,15 @@
+/* for oldps.c and proc/compare.c */
+struct tree_node {
+    proc_t *proc;
+    pid_t pid;
+    pid_t ppid;
+    char *line;
+    char *cmd;
+    char **cmdline;
+    char **environ;
+    int children;
+    int maxchildren;
+    int *child;
+    int have_parent;
+};
+
diff --git a/proc/version.c b/proc/version.c
new file mode 100644 (file)
index 0000000..aee0bc3
--- /dev/null
@@ -0,0 +1,43 @@
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Ammended by cblake to only export the function symbol.
+ * Redistributable under the terms of the
+ * GNU Library General Public License; see COPYING
+ */
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef MINORVERSION
+char procps_version[] = "procps version " VERSION "." SUBVERSION "." MINORVERSION;
+#else
+char procps_version[] = "procps version " VERSION "." SUBVERSION;
+#endif
+
+void display_version(void) {
+    fprintf(stdout, "%s\n", procps_version);
+}
+
+/* Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ */
+#include <sys/utsname.h>
+
+#define LINUX_VERSION(x,y,z)   (0x10000*(x) + 0x100*(y) + z)
+
+int linux_version_code = 0;
+
+static void init_Linux_version(void) __attribute__((constructor));
+static void init_Linux_version(void) {
+    static struct utsname uts;
+    int x = 0, y = 0, z = 0;   /* cleared in case sscanf() < 3 */
+    
+    if (linux_version_code) return;
+    if (uname(&uts) == -1)     /* failure implies impending death */
+       exit(1);
+    if (sscanf(uts.release, "%d.%d.%d", &x, &y, &z) < 3)
+       fprintf(stderr,         /* *very* unlikely to happen by accident */
+               "Non-standard uts for running kernel:\n"
+               "release %s=%d.%d.%d gives version code %d\n",
+               uts.release, x, y, z, LINUX_VERSION(x,y,z));
+    linux_version_code = LINUX_VERSION(x, y, z);
+}
diff --git a/proc/version.h b/proc/version.h
new file mode 100644 (file)
index 0000000..7b58cdb
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef PROC_VERSION_H
+#define PROC_VERSION_H
+
+/* Suite version information for procps utilities
+ * Copyright (c) 1995 Martin Schulze <joey@infodrom.north.de>
+ * Linux kernel version information for procps utilities
+ * Copyright (c) 1996 Charles Blake <cblake@bbn.com>
+ * Distributable under the terms of the GNU Library General Public License
+ */
+
+extern void display_version(void);     /* display suite version */
+extern char procps_version[];          /* global buf for suite version */
+
+extern int linux_version_code;         /* runtime version of LINUX_VERSION_CODE
+                                          in /usr/include/linux/version.h */
+
+/* Convenience macros for composing/decomposing version codes */
+#define LINUX_VERSION(x,y,z)   (0x10000*(x) + 0x100*(y) + z)
+#define LINUX_VERSION_MAJOR(x) (((x)>>16) & 0xFF)
+#define LINUX_VERSION_MINOR(x) (((x)>> 8) & 0xFF)
+#define LINUX_VERSION_PATCH(x) ( (x)      & 0xFF)
+
+#endif /* PROC_VERSION_H */
diff --git a/proc/whattime.c b/proc/whattime.c
new file mode 100644 (file)
index 0000000..61e2601
--- /dev/null
@@ -0,0 +1,89 @@
+/* This is a trivial uptime program.  I hereby release this program
+ * into the public domain.  I disclaim any responsibility for this
+ * program --- use it at your own risk.  (as if there were any.. ;-)
+ * -michaelkjohnson (johnsonm@sunsite.unc.edu)
+ *
+ * Modified by Larry Greenfield to give a more traditional output,
+ * count users, etc.  (greenfie@gauss.rutgers.edu)
+ *
+ * Modified by mkj again to fix a few tiny buglies.
+ *
+ * Modified by J. Cowley to add printing the uptime message to a
+ * string (for top) and to optimize file handling.  19 Mar 1993.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+#include <utmp.h>
+#include <sys/ioctl.h>
+#include "proc/whattime.h"
+#include "proc/sysinfo.h"
+
+static char buf[128];
+static double av[3];
+
+char *sprint_uptime(void) {
+  struct utmp *utmpstruct;
+  int upminutes, uphours, updays;
+  int pos;
+  struct tm *realtime;
+  time_t realseconds;
+  int numuser;
+  double uptime_secs, idle_secs;
+
+/* first get the current time */
+
+  time(&realseconds);
+  realtime = localtime(&realseconds);
+  pos = sprintf(buf, " %2d:%02d%s  ",
+               realtime->tm_hour%12 ? realtime->tm_hour%12 : 12,
+               realtime->tm_min, realtime->tm_hour > 11 ? "pm" : "am");
+
+/* read and calculate the amount of uptime */
+
+  uptime(&uptime_secs, &idle_secs);
+
+  updays = (int) uptime_secs / (60*60*24);
+  strcat (buf, "up ");
+  pos += 3;
+  if (updays)
+    pos += sprintf(buf + pos, "%d day%s, ", updays, (updays != 1) ? "s" : "");
+  upminutes = (int) uptime_secs / 60;
+  uphours = upminutes / 60;
+  uphours = uphours % 24;
+  upminutes = upminutes % 60;
+  if(uphours)
+    pos += sprintf(buf + pos, "%2d:%02d, ", uphours, upminutes);
+  else
+    pos += sprintf(buf + pos, "%d min, ", upminutes);
+
+/* count the number of users */
+
+  numuser = 0;
+  setutent();
+  while ((utmpstruct = getutent())) {
+    if ((utmpstruct->ut_type == USER_PROCESS) &&
+       (utmpstruct->ut_name[0] != '\0'))
+      numuser++;
+  }
+  endutent();
+
+  pos += sprintf(buf + pos, "%2d user%s, ", numuser, numuser == 1 ? "" : "s");
+
+  loadavg(&av[0], &av[1], &av[2]);
+
+  pos += sprintf(buf + pos, " load average: %.2f, %.2f, %.2f",
+                av[0], av[1], av[2]);
+
+  return buf;
+}
+
+void print_uptime(void)
+{
+  printf("%s\n", sprint_uptime());
+}
diff --git a/proc/whattime.h b/proc/whattime.h
new file mode 100644 (file)
index 0000000..3d37bfa
--- /dev/null
@@ -0,0 +1,9 @@
+/* whattime.h --- see whattime.c for explanation */
+
+#ifndef __WHATTIME_H
+#define __WHATTIME_H
+
+extern void print_uptime(void);
+extern char *sprint_uptime(void);
+
+#endif
diff --git a/procps.lsm b/procps.lsm
new file mode 100644 (file)
index 0000000..4e76e73
--- /dev/null
@@ -0,0 +1,16 @@
+Begin3
+Title: procps
+Version: 010114
+Entered-date: 14JAN01
+Description: Procps is a library which parses the textual /proc filesystem
+       and a suite of utilites which use the library.
+Keywords: procps /proc libproc
+       ps uptime tload free w top vmstat watch skill snice kill pgrep pkill
+Author: Michael K. Johnson, Charles Blake, Albert Cahalan, many others.
+Maintained-by: various <acahalan@cs.uml.edu>
+Primary-site: http://www.cs.uml.edu/~acahalan/procps/
+       185kB procps-010114.tar.gz
+Alternate-site: http://www.debian.org/Packages/unstable/base/procps.html
+       185kB procps-010114.tar.gz
+Copying-policy: mixed
+End
diff --git a/procps.spec b/procps.spec
new file mode 100644 (file)
index 0000000..a3b212d
--- /dev/null
@@ -0,0 +1,105 @@
+Summary: Utilities for monitoring your system and processes on your system.
+Name: procps
+%define major_version 2
+%define minor_version 0
+%define revision 7
+%define version %{major_version}.%{minor_version}.%{revision}
+Version: %{version}
+Release: 1
+Copyright: GPL
+Group: Applications/System
+Source: ftp://people.redhat.com/johnsonm/procps/procps-%{version}.tar.gz
+BuildRoot: /var/tmp/procps-root
+
+%package X11
+Group: Applications/System
+Summary: X based process monitoring utilities.
+
+%description
+The procps package contains a set of system utilities which provide
+system information.  Procps includes ps, free, sysctl, skill, snice,
+tload, top, uptime, vmstat, w, and watch.  The ps command displays
+a snapshot of running processes.  The top command provides a
+repetitive update of the statuses of running processes.  The free
+command displays the amounts of free and used memory on your system.
+Sysctl is a simple program for managing system configuration entries.
+The skill command sends a terminate command (or another
+specified signal) to a specified set of processes.  The snice
+command is used to change the scheduling priority of specified
+processes.  The tload command prints a graph of the current system
+load average to a specified tty.  The uptime command displays the
+current time, how long the system has been running, how many users
+are logged on and system load averages for the past one, five and
+fifteen minutes.  The w command displays a list of the users who
+are currently logged on and what they're running.  The watch program
+watches a running program.  The vmstat command displays virtual
+memory statistics about processes, memory, paging, block I/O, traps
+and CPU activity.
+
+%description X11
+The procps-X11 package contains the XConsole shell script, a backwards
+compatibility wrapper for the xconsole program.
+
+%prep
+%setup
+
+%build
+PATH=/usr/X11R6/bin:$PATH
+
+make CC="gcc $RPM_OPT_FLAGS" LDFLAGS=-s
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/bin $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/sbin
+mkdir -p $RPM_BUILD_ROOT/usr/man/man1 $RPM_BUILD_ROOT/usr/man/man8
+mkdir -p $RPM_BUILD_ROOT/lib $RPM_BUILD_ROOT/usr/X11R6/bin
+
+make DESTDIR=$RPM_BUILD_ROOT OWNERGROUP= MANDIR=/usr/share/man install
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+# add libproc to the cache
+/sbin/ldconfig
+# remove obsolete files
+rm -f /etc/psdevtab /etc/psdatabase
+
+%files
+%defattr(0644,root,root,755)
+%attr(0644,root,root) %config(missingok) /etc/X11/applnk/Utilities/top.desktop
+%doc NEWS BUGS TODO
+%attr(755,root,root) /lib/libproc.so.2.0.7
+%attr(555,root,root) /bin/ps
+%attr(555,root,root) /sbin/sysctl
+%attr(555,root,root) /usr/bin/oldps
+%attr(555,root,root) /usr/bin/uptime
+%attr(555,root,root) /usr/bin/tload
+%attr(555,root,root) /usr/bin/free
+%attr(555,root,root) /usr/bin/w
+%attr(555,root,root) /usr/bin/top
+%attr(555,root,root) /usr/bin/vmstat
+%attr(555,root,root) /usr/bin/watch
+%attr(555,root,root) /usr/bin/skill
+%attr(555,root,root) /usr/bin/snice
+%attr(555,root,root) /usr/bin/pgrep
+%attr(555,root,root) /usr/bin/pkill
+
+%attr(0644,root,root) /usr/man/man1/free.1
+%attr(0644,root,root) /usr/man/man1/ps.1
+%attr(0644,root,root) /usr/man/man1/oldps.1
+%attr(0644,root,root) /usr/man/man1/sessreg.1
+%attr(0644,root,root) /usr/man/man1/skill.1
+%attr(0644,root,root) /usr/man/man1/snice.1
+%attr(0644,root,root) /usr/man/man1/tload.1
+%attr(0644,root,root) /usr/man/man1/top.1
+%attr(0644,root,root) /usr/man/man1/uptime.1
+%attr(0644,root,root) /usr/man/man1/w.1
+%attr(0644,root,root) /usr/man/man1/watch.1
+%attr(0644,root,root) /usr/man/man1/pgrep.1
+%attr(0644,root,root) /usr/man/man1/pkill.1
+%attr(0644,root,root) /usr/man/man8/vmstat.8
+%attr(0644,root,root) /usr/man/man8/sysctl.8
+
+%files X11
+%attr(0755,root,root) /usr/X11R6/bin/XConsole
diff --git a/ps/.cvsignore.patch b/ps/.cvsignore.patch
new file mode 100644 (file)
index 0000000..7c386a2
--- /dev/null
@@ -0,0 +1,5 @@
+diff -Naur procps-2.0.6/ps/.cvsignore procps-2.0.7/ps/.cvsignore
+--- procps-2.0.6/ps/.cvsignore Wed Dec 31 19:00:00 1969
++++ procps-2.0.7/ps/.cvsignore Fri Jul 14 16:45:01 2000
+@@ -0,0 +1 @@
++ps
diff --git a/ps/COPYING b/ps/COPYING
new file mode 100644 (file)
index 0000000..92b8903
--- /dev/null
@@ -0,0 +1,481 @@
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+                   59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL.  It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it.  You can use it for
+your libraries, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library.  If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software.  To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+  Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs.  This
+license, the GNU Library General Public License, applies to certain
+designated libraries.  This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+  The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it.  Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program.  However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+  Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries.  We
+concluded that weaker conditions might promote sharing better.
+
+  However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves.  This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them.  (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.)  The hope is that this
+will lead to faster development of free libraries.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+  Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+                 GNU LIBRARY GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License").  Each licensee is
+addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+\f
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+  6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    c) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    d) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Library General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                           NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Library General Public
+    License as published by the Free Software Foundation; either
+    version 2 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Library General Public License for more details.
+
+    You should have received a copy of the GNU Library General Public
+    License along with this library; if not, write to the Free
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/ps/HACKING b/ps/HACKING
new file mode 100644 (file)
index 0000000..ffae92c
--- /dev/null
@@ -0,0 +1,53 @@
+Warning:
+
+This code must corrctly handle lots of picky little details to meet
+the Unix98 standard while simultaneously being as compatible as
+possible with the original Linux ps. Don't "fix" something without
+considering the impact on all the special-case code. For example,
+the "tty" format _must_ use "TT" as the header, even though the SysV
+output formats _must_ use "TTY".
+
+File overview:
+
+display.c     main(), debug code, iterates over processes
+escape.c      Does stuff like \202 and &lt; to command and environment.
+global.c      Data + code to init it.
+help.c        Help message.
+output.c      Giant tables and lots of output functions.
+parser.c      Initial command parsing.
+select.c      want_this_proc() checks a process against flags & lists
+sortformat.c  Parses sort & format specifier lists. Picks output format.
+stacktrace.c  Debug code, not normally used.
+../proc/*     Library used to gather data.
+regression    Regression tests that ought to be run.
+common.h      Lots of interesting stuff.
+Makefile      Makefile
+p             Script used to test ps when the library is not installed.
+utf           Empty file used to test "ps ut?" unmangling behavior.
+ps.1          Man page.
+
+Compiling:
+
+Whatever you do, don't trust the Makefiles. They are severely broken.
+You can touch top.h and top won't be recompiled, or you can change
+library files and discover that the library and programs won't be
+recompiled.
+
+Operation:
+
+Unless the personality forces BSD parsing, parser.c tries to parse the
+command line as a mixed BSD+SysV+Gnu mess. On failure, BSD parsing is
+attempted. If BSD parsing fails _after_ SysV parsing has been attempted,
+the error message comes from the original SysV parse.
+
+Control goes to sortformat.c, which must pick apart ambiguous options
+like "O". Failure can reset the whole program and set PER_FORCE_BSD,
+which means a second trip through parser.c and sortformat.c.
+
+The choice of output format happens in sortformat.c. There is a switch()
+with all the valid format_flags combinations. The SysV and default
+options are NULL (unless overridden by personality), which causes a
+trip through SysV output format generation hackery. Note that the
+default format always goes through there, even if it is for BSD.
+Formats that came from the switch() (generally BSD, plus overrides)
+get mangled a bit to support various SysV output modifiers.
diff --git a/ps/Makefile b/ps/Makefile
new file mode 100755 (executable)
index 0000000..ec04f2f
--- /dev/null
@@ -0,0 +1,35 @@
+all: ps
+
+ps: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o
+       $(CC) -o ps   escape.o global.o help.o select.o sortformat.o output.o parser.o display.o -L../proc -lproc
+
+# This just adds the stacktrace code
+debug: escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o
+       $(CC) -o ps   escape.o global.o help.o select.o sortformat.o output.o parser.o display.o stacktrace.o -L../proc -lproc -lefence
+
+sortformat.o: sortformat.c common.h
+
+global.o: global.c common.h
+
+escape.o: escape.c
+
+help.o: help.c
+
+select.o: select.c common.h
+
+output.o: output.c common.h
+
+parser.o: parser.c common.h
+
+display.o: display.c common.h
+
+stacktrace.o: stacktrace.c
+
+
+install: ps
+       install $(OWNERGROUP) --mode a=rx --strip ps $(BINDIR)/ps
+       install $(OWNERGROUP) --mode a=r ps.1 $(MAN1DIR)/ps.1
+       -rm -f $(DESTDIR)/var/catman/cat1/ps.1.gz $(DESTDIR)/var/man/cat1/ps.1.gz
+
+clean:
+       rm -f *.o DEADJOE *~ core ps gmon.out
diff --git a/ps/TRANSLATION b/ps/TRANSLATION
new file mode 100644 (file)
index 0000000..788aa6f
--- /dev/null
@@ -0,0 +1,28 @@
+Initially I only want to translate the --help output and man page.
+Common error messages would be next on the list. I want to avoid
+run-time overhead and bloat.
+
+Translations of the --help output should not be longer than 22 lines long.
+Feel free to leave out the less useful options to save space. (not even
+the English help text has all the options)
+
+I think these are the most important options:
+
+*** selection ***
+-C  by command name list
+-G  by real group ID list (supports names)
+-U  by real user ID list (supports names)
+-u  by effective user ID list (supports names)
+-e  all processes
+-p  by process ID list
+
+*** output ***
+--no-heading  No header line.
+-o,o          user-defined output
+-j,j          job control format
+-l,l          long format
+-f            full format
+s             signal format
+u             user-oriented format
+--forest      ASCII art forest (process heirarchy)
+c             show true command name
diff --git a/ps/common.h b/ps/common.h
new file mode 100644 (file)
index 0000000..d2bf76f
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+#ifndef PROCPS_PS_H
+#define PROCPS_PS_H
+
+#include "../proc/readproc.h"
+#include <asm/page.h>  /* looks safe for glibc, we need PAGE_SIZE */
+
+#if 0
+#define trace(args...) printf(## args)
+#else
+#define trace(args...)
+#endif
+
+
+/***************** GENERAL DEFINE ********************/
+
+
+/* selection list */
+#define SEL_RUID 1
+#define SEL_EUID 2
+#define SEL_SUID 3
+#define SEL_FUID 4
+#define SEL_RGID 5
+#define SEL_EGID 6
+#define SEL_SGID 7
+#define SEL_FGID 8
+#define SEL_PGRP 9
+#define SEL_PID  10
+#define SEL_TTY  11
+#define SEL_SESS 12
+#define SEL_COMM 13
+
+/* Since an enum could be smashed by a #define, it would be bad. */
+#define U98  0 /* Unix98 standard */    /* This must be 0 */
+#define XXX  1 /* Common extension */
+#define DEC  2 /* Digital Unix */
+#define AIX  3 /* AIX */
+#define SCO  4 /* SCO */
+#define LNX  5 /* Linux original :-) */
+#define BSD  6 /* FreeBSD and OpenBSD */
+#define SUN  7 /* SunOS 5 (Solaris) */
+#define HPU  8 /* HP-UX */
+#define SGI  9 /* Irix */
+#define SOE 10 /* IBM's S/390 OpenEdition */
+
+/*
+ * Must not overflow the output buffer:
+ *    32 pages for env+cmd
+ *    8 kB pages on the Alpha
+ *    5 chars for "\001 "
+ *    plus some slack for other stuff
+ * That is about 1.3 MB on the Alpha
+ *
+ * This isn't good enough for setuid. If anyone cares, mmap() over the
+ * last page with something unwriteable.
+ */
+
+/* maximum escape expansion is 6, for &quot; */
+#define ESC_STRETCH 6
+/* output buffer size */
+#define OUTBUF_SIZE (32*PAGE_SIZE*ESC_STRETCH + 8*PAGE_SIZE)
+/* spaces used to right-justify things */
+#define SPACE_AMOUNT (int)(PAGE_SIZE)
+
+/******************* PS DEFINE *******************/
+
+/* personality control flags */
+#define PER_BROKEN_o      0x0001
+#define PER_BSD_h         0x0002
+#define PER_BSD_m         0x0004
+#define PER_CUMUL_MARKED  0x0008
+#define PER_FORCE_BSD     0x0010
+#define PER_GOOD_o        0x0020
+#define PER_OLD_m         0x0040
+#define PER_NO_DEFAULT_g  0x0080
+#define PER_ZAP_ADDR      0x0100
+#define PER_SANE_USER     0x0200
+#define PER_IRIX_l        0x0400
+
+/* Simple selections by bit mask */
+#define SS_B_x 0x01
+#define SS_B_g 0x02
+#define SS_U_d 0x04
+#define SS_U_a 0x08
+#define SS_B_a 0x10
+
+/* predefined format flags such as:  -l -f l u s -j */
+#define FF_Uf 0x0001 /* -f */
+#define FF_Uj 0x0002 /* -j */
+#define FF_Ul 0x0004 /* -l */
+#define FF_Bj 0x0008 /* j */
+#define FF_Bl 0x0010 /* l */
+#define FF_Bs 0x0020 /* s */
+#define FF_Bu 0x0040 /* u */
+#define FF_Bv 0x0080 /* v */
+#define FF_LX 0x0100 /* X */
+#define FF_Lm 0x0200 /* m */  /* overloaded: threads, sort, format */
+
+/* predefined format modifier flags such as:  -l -f l u s -j */
+#define FM_c 0x0001 /* -c */
+#define FM_j 0x0002 /* -j */  /* only set when !sysv_j_format */
+#define FM_y 0x0004 /* -y */
+#define FM_L 0x0008 /* -L */
+#define FM_P 0x0010 /* -P */
+#define FM_M 0x0020 /* -M */
+#define FM_T 0x0040 /* -T */
+#define FM_F 0x0080 /* -F */  /* -F also sets the regular -f flags */
+
+/* sorting & formatting */
+/* U,B,G is Unix,BSD,Gnu and then there is the option itself */
+#define SF_U_O      1
+#define SF_U_o      2
+#define SF_B_O      3
+#define SF_B_o      4
+#define SF_B_m      5       /* overloaded: threads, sort, format */
+#define SF_G_sort   6
+#define SF_G_format 7
+
+/* headers */
+#define HEAD_SINGLE 0  /* default, must be 0 */
+#define HEAD_NONE   1
+#define HEAD_MULTI  2
+
+
+/********************** GENERAL TYPEDEF *******************/
+
+/* Other fields that might be useful:
+ *
+ * char *name;     user-defined column name (format specification)
+ * int reverse;    sorting in reverse (sort specification)
+ *
+ * name in place of u
+ * reverse in place of n
+ */
+
+typedef union sel_union {
+  pid_t pid;
+  uid_t uid;
+  gid_t gid;
+  dev_t tty;
+  char  cmd[8];  /* this is _not_ \0 terminated */
+} sel_union;
+
+typedef struct selection_node {
+  struct selection_node *next;
+  sel_union *u;  /* used if selection type has a list of values */
+  int n;         /* used if selection type has a list of values */
+  int typecode;
+} selection_node;
+
+typedef struct sort_node {
+  struct sort_node *next;
+  int (*sr)(const proc_t* P, const proc_t* Q); /* sort function */
+  int reverse;   /* can sort backwards */
+  int typecode;
+} sort_node;
+
+typedef struct format_node {
+  struct format_node *next;
+  char *name;                             /* user can override default name */
+  int (*pr)(void);                         /* print function */
+/*  int (* const sr)(const proc_t* P, const proc_t* Q); */ /* sort function */
+  int width;
+  int pad;
+  int vendor;                             /* Vendor that invented this */
+  int flags;
+  int typecode;
+} format_node;
+
+typedef struct format_struct {
+  const char *spec; /* format specifier */
+  const char *head; /* default header in the POSIX locale */
+  int (* const pr)(void); /* print function */
+  int (* const sr)(const proc_t* P, const proc_t* Q); /* sort function */
+  const int width;
+  const int pad;    /* could be second width */
+  const int vendor; /* Where does this come from? */
+  const int flags;
+} format_struct;
+
+/* though ps-specific, needed by general file */
+typedef struct macro_struct {
+  const char *spec; /* format specifier */
+  const char *head; /* default header in the POSIX locale */
+} macro_struct;
+
+/**************** PS TYPEDEF ***********************/
+
+typedef struct aix_struct {
+  const int   desc; /* 1-character format code */
+  const char *spec; /* format specifier */
+  const char *head; /* default header in the POSIX locale */
+} aix_struct;
+
+typedef struct shortsort_struct {
+  const int   desc; /* 1-character format code */
+  const char *spec; /* format specifier */
+} shortsort_struct;
+
+/* Save these options for later: -o o -O O --format --sort */
+typedef struct sf_node {
+  struct sf_node *next;  /* next arg */
+  format_node *f_cooked;  /* convert each arg alone, then merge */
+  sort_node   *s_cooked;  /* convert each arg alone, then merge */
+  char *sf;
+  int sf_code;
+} sf_node;
+
+
+/*********************** GENERAL GLOBALS *************************/
+
+/* escape.c */
+extern int escape_strlist(char *dst, const char **src, size_t n);
+extern int escape_str(char *dst, const char *src, size_t n);
+extern int octal_escape_str(char *dst, const char *src, size_t n);
+extern int simple_escape_str(char *dst, const char *src, size_t n);
+
+/********************* UNDECIDED GLOBALS **************/
+
+/*
+ * fputs(3) should (as in "good behavior") return the number of
+ * characters written as it does on Digital Unix, AIX, Irix, and SunOS.
+ * I'll assume glibc 2.1 has this extremely useful feature.
+ *
+ * Note: code ported from other systems will keep breaking until
+ * the library is updated. You should patch the library itself if
+ * at all possible. (for example, distributers who build libc from
+ * source with automatic patching as part of the build process)
+ */
+
+#if defined __GLIBC__ && ((__GLIBC__ == 2 && __GLIBC_MINOR__ > 2) || __GLIBC__ > 2)
+#warning Hopefully fputs(3) has been modernized...
+#else
+#define EMULATE_FPUTS
+#define fputs something_to_avoid_libc_troubles
+static inline int fputs(const char *s, FILE *fp){
+  return fwrite(s,1,strlen(s),fp);
+  /* return fprintf(fp, "%s", s); */
+}
+#endif
+
+/* output.c */
+extern void show_one_proc(proc_t* p);
+extern void print_format_specifiers(void);
+extern const aix_struct *search_aix_array(const int findme);
+extern const shortsort_struct *search_shortsort_array(const int findme);
+extern const format_struct *search_format_array(const char *findme);
+extern const macro_struct *search_macro_array(const char *findme);
+extern void init_output(void);
+
+/* global.c */
+extern void reset_global(void);
+
+/* global.c */
+extern int             all_processes;
+extern char           *bsd_j_format;
+extern char           *bsd_l_format;
+extern char           *bsd_s_format;
+extern char           *bsd_u_format;
+extern char           *bsd_v_format;
+extern int             bsd_c_option;
+extern int             bsd_e_option;
+extern uid_t           cached_euid;
+extern dev_t           cached_tty;
+extern char            forest_prefix[4 * 32*1024 + 100];
+extern int             forest_type;
+extern unsigned        format_flags;     /* -l -f l u s -j... */
+extern format_node    *format_list; /* digested formatting options */
+extern unsigned        format_modifiers; /* -c -j -y -P -L... */
+extern int             header_gap;
+extern int             header_type; /* none, single, multi... */
+extern int             include_dead_children;
+extern int             lines_to_next_header;
+extern int             max_line_width;
+extern const char     *namelist_file;
+extern int             negate_selection;
+extern unsigned        personality;
+extern int             prefer_bsd_defaults;
+extern int             running_only;
+extern int             screen_cols;
+extern int             screen_rows;
+extern unsigned long   seconds_since_boot;
+extern selection_node *selection_list;
+extern unsigned        simple_select;
+extern sort_node      *sort_list;
+extern char           *sysv_f_format;
+extern char           *sysv_fl_format;
+extern char           *sysv_j_format;
+extern char           *sysv_l_format;
+extern int             unix_f_option;
+extern int             user_is_number;
+extern int             wchan_is_number;
+
+/************************* PS GLOBALS *********************/
+
+/* sortformat.c */
+extern int defer_sf_option(const char *arg, int source);
+extern const char *process_sf_options(int localbroken);
+extern void reset_sortformat(void);
+
+/* select.c */
+extern int want_this_proc(proc_t *buf);
+extern const char *select_bits_setup(void);
+
+/* help.c */
+extern const char *help_message;
+
+/* global.c */
+extern void self_info(void);
+
+/* parser.c */
+extern int arg_parse(int argc, char *argv[]);
+
+#endif
diff --git a/ps/display.c b/ps/display.c
new file mode 100644 (file)
index 0000000..6824b41
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                                                     
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* major/minor number */
+#include <sys/sysmacros.h>
+
+#include <signal.h>   /* catch signals */
+
+#include "common.h"
+#include "../proc/procps.h"
+#include "../proc/version.h"
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+
+#ifndef SIGCHLD
+#define SIGCHLD SIGCLD
+#endif
+
+/* just reports a crash */
+static void signal_handler(int signo){
+  if(signo==SIGPIPE) _exit(0);  /* "ps | head" will cause this */
+  /* fprintf() is not reentrant, but we _exit() anyway */
+  fprintf(stderr,
+    "\n\n"
+    "Signal %d caught by ps (%s).\n"
+    "Please send bug reports to <acahalan@cs.uml.edu>\n",
+    signo,
+    procps_version
+  );
+  _exit(signo+128);
+}
+
+
+#undef DEBUG
+#ifdef DEBUG
+void init_stack_trace(char *prog_name);
+
+#include <ctype.h>
+
+void hex_dump(void *vp){
+  char *charlist;
+  int i = 0;
+  int line = 45;
+  char *cp = (char *)vp;
+
+  while(line--){
+      printf("%8lx  ", (unsigned long)cp);
+      charlist = cp;
+      cp += 16;
+      for(i=0; i<16; i++){
+        if((charlist[i]>31) && (charlist[i]<127)){
+          printf("%c", charlist[i]);
+        }else{
+          printf(".");
+        }
+      }
+      printf(" ");
+      for(i=0; i<16; i++) printf(" %2x",(unsigned int)((unsigned char)(charlist[i])));
+      printf("\n");
+      i=0;
+  }
+}
+
+static void show_pid(char *s, int n, sel_union *data){
+  printf("%s  ", s);
+  while(--n){
+    printf("%d,", data[n].pid);
+  }
+  printf("%d\n", data[0].pid);
+}
+
+static void show_uid(char *s, int n, sel_union *data){
+  struct passwd *pw_data;
+  printf("%s  ", s);
+  while(--n){
+    pw_data = getpwuid(data[n].uid);
+    if(pw_data) printf("%s,", pw_data->pw_name);
+    else        printf("%d,", data[n].uid);
+  }
+  pw_data = getpwuid(data[n].uid);
+  if(pw_data) printf("%s\n", pw_data->pw_name);
+  else        printf("%d\n", data[n].uid);
+}
+
+static void show_gid(char *s, int n, sel_union *data){
+  struct group *gr_data;
+  printf("%s  ", s);
+  while(--n){
+    gr_data = getgrgid(data[n].gid);
+    if(gr_data) printf("%s,", gr_data->gr_name);
+    else        printf("%d,", data[n].gid);
+  }
+  gr_data = getgrgid(data[n].gid);
+  if(gr_data) printf("%s\n", gr_data->gr_name);
+  else        printf("%d\n", data[n].gid);
+}
+
+static void show_tty(char *s, int n, sel_union *data){
+  printf("%s  ", s);
+  while(--n){
+    printf("%d:%d,", (int)major(data[n].tty), (int)minor(data[n].tty));
+  }
+  printf("%d:%d\n", (int)major(data[n].tty), (int)minor(data[n].tty));
+}
+
+static void show_cmd(char *s, int n, sel_union *data){
+  printf("%s  ", s);
+  while(--n){
+    printf("%.8s,", data[n].cmd);
+  }
+  printf("%.8s\n", data[0].cmd);
+}
+
+static void arg_show(void){
+  selection_node *walk = selection_list;
+  while(walk){
+    switch(walk->typecode){
+    case SEL_RUID: show_uid("RUID", walk->n, walk->u); break;
+    case SEL_EUID: show_uid("EUID", walk->n, walk->u); break;
+    case SEL_SUID: show_uid("SUID", walk->n, walk->u); break;
+    case SEL_FUID: show_uid("FUID", walk->n, walk->u); break;
+    case SEL_RGID: show_gid("RGID", walk->n, walk->u); break;
+    case SEL_EGID: show_gid("EGID", walk->n, walk->u); break;
+    case SEL_SGID: show_gid("SGID", walk->n, walk->u); break;
+    case SEL_FGID: show_gid("FGID", walk->n, walk->u); break;
+    case SEL_PGRP: show_pid("PGRP", walk->n, walk->u); break;
+    case SEL_PID : show_pid("PID ", walk->n, walk->u); break;
+    case SEL_TTY : show_tty("TTY ", walk->n, walk->u); break;
+    case SEL_SESS: show_pid("SESS", walk->n, walk->u); break;
+    case SEL_COMM: show_cmd("COMM", walk->n, walk->u); break;
+    default: printf("Garbage typecode value!\n");
+    }
+    walk = walk->next;
+  }
+}
+
+#endif
+
+
+/***** check the header */
+/* Unix98: must not print empty header */
+static void check_headers(void){
+  format_node *walk = format_list;
+  int head_normal = 0;
+  if(header_type==HEAD_MULTI){
+    header_gap = screen_rows-1;  /* true BSD */
+    return;
+  }
+  if(header_type==HEAD_NONE){
+    lines_to_next_header = -1;  /* old Linux */
+    return;
+  }
+  while(walk){
+    if(!*(walk->name)){
+      walk = walk->next;
+      continue;
+    }
+    if(walk->pr){
+      head_normal++;
+      walk = walk->next;
+      continue;
+    }
+    walk = walk->next;
+  }
+  if(!head_normal) lines_to_next_header = -1; /* how UNIX does --noheader */
+}
+
+/***** fill in %CPU; not in libproc because of include_dead_children */
+static void fill_pcpu(proc_t *buf){
+  unsigned long total_time;
+  unsigned long pcpu = 0;
+  unsigned long seconds;
+
+  total_time = buf->utime + buf->stime;
+  if(include_dead_children) total_time += (buf->cutime + buf->cstime);
+  seconds = (seconds_since_boot - ((unsigned long)buf->start_time) / Hertz);
+  if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+  buf->pcpu = (pcpu > 999) ? 999 : pcpu;
+}
+
+/***** just display */
+static void simple_spew(void){
+  proc_t buf;
+  PROCTAB* ptp;
+  ptp = openproc(PROC_FILLBUG);
+  if(!ptp) {
+    fprintf(stderr, "Error: can not access /proc.\n");
+    exit(1);
+  }
+  memset(&buf, '#', sizeof(proc_t));
+  /* use "ps_" prefix to catch library mismatch */
+  while(ps_readproc(ptp,&buf)){
+    fill_pcpu(&buf);
+    if(want_this_proc(&buf)) show_one_proc(&buf);
+    /* if(buf.cmdline) free(buf.cmdline); */   /* these crash */
+    /* if(buf.environ) free(buf.environ); */
+    memset(&buf, '#', sizeof(proc_t));
+  }
+  closeproc(ptp);
+}
+
+/***** forest output requires sorting by ppid; add start_time by default */
+static void prep_forest_sort(void){
+  sort_node *tmp_list = sort_list;
+  const format_struct *incoming;
+
+  if(!sort_list) {     /* assume start time order */
+    incoming = search_format_array("start_time");
+    if(!incoming) fprintf(stderr, "Could not find start_time!\n");
+    tmp_list = malloc(sizeof(sort_node));
+    tmp_list->reverse = 0;
+    tmp_list->typecode = '?'; /* what was this for? */
+    tmp_list->sr = incoming->sr;
+    tmp_list->next = sort_list;
+    sort_list = tmp_list;
+  }
+  /* this is required for the forest option */
+  incoming = search_format_array("ppid");
+  if(!incoming) fprintf(stderr, "Could not find ppid!\n");
+  tmp_list = malloc(sizeof(sort_node));
+  tmp_list->reverse = 0;
+  tmp_list->typecode = '?'; /* what was this for? */
+  tmp_list->sr = incoming->sr;
+  tmp_list->next = sort_list;
+  sort_list = tmp_list;
+}
+
+/* we rely on the POSIX requirement for zeroed memory */
+static proc_t *processes[32*1024];
+
+/***** compare function for qsort */
+static int compare_two_procs(const void *a, const void *b){
+  sort_node *tmp_list = sort_list;
+  while(tmp_list){
+    int result;
+    result = (*tmp_list->sr)(*(const proc_t **)a, *(const proc_t **)b);
+    if(result) return (tmp_list->reverse) ? -result : result;
+    tmp_list = tmp_list->next;
+  }
+  return 0; /* no conclusion */
+}
+
+/***** show pre-sorted array of process pointers */
+static void show_proc_array(int n){
+  proc_t **p = processes;
+  while(n--){
+    show_one_proc(*p);
+    /* if(p->cmdline) free(p->cmdline); */   /* this crashes */
+    /* if(p->environ) free(p->environ); */   /* this crashes */
+    /* memset(*p, '%', sizeof(proc_t)); */ /* debug */
+    free(*p);
+    p++;
+  }
+}
+
+/***** show tree */
+/* this needs some optimization work */
+#define ADOPTED(x) 1
+static void show_tree(const int self, const int n, const int level, const int have_sibling){
+  int i = 0;
+  if(level){
+    /* add prefix of "+" or "L" */
+    if(have_sibling) forest_prefix[level-1] = '+';
+    else             forest_prefix[level-1] = 'L';
+    forest_prefix[level] = '\0';
+  }
+  show_one_proc(processes[self]);  /* first show self */
+  /* if(p->cmdline) free(p->cmdline); */   /* this crashes */
+  /* if(p->environ) free(p->environ); */   /* this crashes */
+  /* memset(*p, '%', sizeof(proc_t)); */ /* debug */
+  for(;;){  /* look for children */
+    if(i >= n) return; /* no children */
+    if(processes[i]->ppid == processes[self]->pid) break;
+    i++;
+  }
+  if(level){
+    /* change our prefix to "|" or " " for the children */
+    if(have_sibling) forest_prefix[level-1] = '|';
+    else             forest_prefix[level-1] = ' ';
+    forest_prefix[level] = '\0';
+  }
+  for(;;){
+    int self_pid;
+    int more_children = 1;
+    if(i >= n) break; /* over the edge */
+    self_pid=processes[self]->pid;
+    if(i+1 >= n)
+      more_children = 0;
+    else
+      if(processes[i+1]->ppid != self_pid) more_children = 0;
+    if(self_pid==1 && ADOPTED(processes[i]) && forest_type!='u')
+      show_tree(i++, n, level,   more_children);
+    else
+      show_tree(i++, n, level+1, more_children);
+    if(!more_children) break;
+  }
+  /* chop prefix that children added -- do we need this? */
+  forest_prefix[level] = '\0';
+}
+
+/***** show forest */
+static void show_forest(const int n){
+  int i = n;
+  int j;
+  while(i--){   /* cover whole array looking for trees */
+    j = n;
+    while(j--){   /* search for parent: if none, i is a tree! */
+      if(processes[j]->pid == processes[i]->ppid) goto not_root;
+    }
+    show_tree(i,n,0,0);
+not_root:
+  }
+  /* don't free the array because it takes time and ps will exit anyway */
+}
+
+/***** sorted or forest */
+static void fancy_spew(void){
+  proc_t *retbuf;
+  PROCTAB* ptp;
+  int n = 0;  /* number of processes & index into array */
+  ptp = openproc(PROC_FILLBUG);
+  if(!ptp) {
+    fprintf(stderr, "Error: can not access /proc.\n");
+    exit(1);
+  }
+  while((retbuf = ps_readproc(ptp,NULL))){
+    fill_pcpu(retbuf);
+    if(want_this_proc(retbuf))  processes[n++] = retbuf;
+    else free(retbuf);
+  }
+  closeproc(ptp);
+  if(!n) return;  /* no processes */
+  if(forest_type) prep_forest_sort();
+  qsort(processes, n, sizeof(proc_t*), compare_two_procs);
+  if(forest_type) show_forest(n);
+  else show_proc_array(n);
+}
+
+
+/***** no comment */
+int main(int argc, char *argv[]){
+#ifdef DEBUG
+  init_stack_trace(argv[0]);
+#else
+  do {
+    struct sigaction sa;
+    int i = 32;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = signal_handler;
+    sigfillset(&sa.sa_mask);
+    while(i--) switch(i){
+    default:
+      sigaction(i,&sa,NULL);
+    case 0:
+    case SIGINT:   /* ^C */
+    case SIGQUIT:  /* ^\ */
+    case SIGPROF:  /* profiling */
+    case SIGKILL:  /* can not catch */
+    case SIGSTOP:  /* can not catch */
+    case SIGWINCH: /* don't care if window size changes */
+    }
+  } while (0);
+#endif
+
+  reset_global();  /* must be before parser */
+  arg_parse(argc,argv);
+
+/*  arg_show(); */
+  trace("screen is %ux%u\n",screen_cols,screen_rows);
+/*  printf("sizeof(proc_t) is %d.\n", sizeof(proc_t)); */
+  trace("======= ps output follows =======\n");
+
+  init_output(); /* must be between parser and output */
+  check_headers();
+  if (open_psdb(namelist_file)) wchan_is_number = 1;
+  if(forest_type || sort_list) fancy_spew(); /* sort or forest */
+  else simple_spew(); /* no sort, no forest */
+  show_one_proc((proc_t *)-1); /* no output yet? */
+  return 0;
+}
diff --git a/ps/escape.c b/ps/escape.c
new file mode 100644 (file)
index 0000000..0c613cc
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                 
+#include <sys/types.h>
+
+/* sanitize a string, without the nice BSD library function:     */
+/* strvis(vis_args, k->ki_args, VIS_TAB | VIS_NL | VIS_NOSLASH)  */
+int octal_escape_str(char *dst, const char *src, size_t n){
+  unsigned char c;
+  char d;
+  size_t i;
+  const char *codes =
+  "Z------abtnvfr-------------e----"
+  " *******************************"  /* better: do not print any space */
+  "****************************\\***"
+  "*******************************-"
+  "--------------------------------"
+  "********************************"
+  "********************************"
+  "********************************";
+  for(i=0; i<n;){
+    c = (unsigned char) *(src++);
+    d = codes[c];
+    switch(d){
+    case 'Z':
+      goto leave;
+    case '*':
+      i++;
+      *(dst++) = c;
+      break;
+    case '-':
+      if(i+4 > n) goto leave;
+      i += 4;
+      *(dst++) = '\\';
+      *(dst++) = "01234567"[c>>6];
+      *(dst++) = "01234567"[(c>>3)&07];
+      *(dst++) = "01234567"[c&07];
+      break;
+    default:
+      if(i+2 > n) goto leave;
+      i += 2;
+      *(dst++) = '\\';
+      *(dst++) = d;
+      break;
+    }
+  }
+leave:
+  *(dst++) = '\0';
+  return i;
+}
+
+/* sanitize a string via one-way mangle */
+int simple_escape_str(char *dst, const char *src, size_t n){
+  unsigned char c;
+  size_t i;
+  const char *codes =
+  "Z-------------------------------"
+  "********************************"
+  "********************************"
+  "*******************************-"
+  "--------------------------------"
+  "********************************"
+  "********************************"
+  "********************************";
+  for(i=0; i<n;){
+    c = (unsigned char) *(src++);
+    switch(codes[c]){
+    case 'Z':
+      goto leave;
+    case '*':
+      i++;
+      *(dst++) = c;
+      break;
+    case '-':
+      i++;
+      *(dst++) = '?';
+      break;
+    }
+  }
+leave:
+  *(dst++) = '\0';
+  return i;
+}
+
+/* escape a string as desired */
+int escape_str(char *dst, const char *src, size_t n){
+  return simple_escape_str(dst, src, n);
+}
+
+/* escape an argv or environment string array */
+int escape_strlist(char *dst, const char **src, size_t n){
+  size_t i = 0;
+  while(*src){
+    i += simple_escape_str(dst+i, *src, n-i);
+    if((n-i > 1) && (*(src+1))) dst[i++] = ' ';
+    src++;
+  }
+  return i;
+}
diff --git a/ps/global.c b/ps/global.c
new file mode 100644 (file)
index 0000000..640978f
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                 
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <string.h>
+
+/*#undef __GLIBC_MINOR__
+#define __GLIBC_MINOR__ 1 */
+#include "common.h"
+/*#undef __GLIBC_MINOR__
+#define __GLIBC_MINOR__ 0 */
+
+#include <sys/sysmacros.h>
+#include "../proc/version.h"
+#include "../proc/sysinfo.h"
+
+
+#ifndef __GNU_LIBRARY__
+#define __GNU_LIBRARY__ -1
+#endif
+#ifndef __GLIBC__
+#define __GLIBC__ -1
+#endif
+#ifndef __GLIBC_MINOR__
+#define __GLIBC_MINOR__ -1
+#endif
+
+
+static char *saved_personality_text = "You found a bug!";
+
+int             all_processes = -1;
+char           *bsd_j_format = (char *)0xdeadbeef;
+char           *bsd_l_format = (char *)0xdeadbeef;
+char           *bsd_s_format = (char *)0xdeadbeef;
+char           *bsd_u_format = (char *)0xdeadbeef;
+char           *bsd_v_format = (char *)0xdeadbeef;
+int             bsd_c_option = -1;
+int             bsd_e_option = -1;
+uid_t           cached_euid = -1;
+dev_t           cached_tty = -1;
+char            forest_prefix[4 * 32*1024 + 100];
+int             forest_type = -1;
+unsigned        format_flags = 0xffffffff;   /* -l -f l u s -j... */
+format_node    *format_list = (format_node *)0xdeadbeef; /* digested formatting options */
+unsigned        format_modifiers = 0xffffffff;   /* -c -j -y -P -L... */
+int             header_gap = -1;
+int             header_type = -1;
+int             include_dead_children = -1;
+int             lines_to_next_header = -1;
+const char     *namelist_file = (const char *)0xdeadbeef;
+int             negate_selection = -1;
+int             running_only = -1;
+unsigned        personality = 0xffffffff;
+int             prefer_bsd_defaults = -1;
+int             screen_cols = -1;
+int             screen_rows = -1;
+unsigned long   seconds_since_boot = -1;
+selection_node *selection_list = (selection_node *)0xdeadbeef;
+unsigned        simple_select = 0xffffffff;
+sort_node      *sort_list = (sort_node *)0xdeadbeef; /* ready-to-use sort list */
+char           *sysv_f_format = (char *)0xdeadbeef;
+char           *sysv_fl_format = (char *)0xdeadbeef;
+char           *sysv_j_format = (char *)0xdeadbeef;
+char           *sysv_l_format = (char *)0xdeadbeef;
+int             unix_f_option = -1;
+int             user_is_number = -1;
+int             wchan_is_number = -1;
+
+
+static void reset_selection_list(void){
+  selection_node *old;
+  selection_node *walk = selection_list;
+  if(selection_list == (selection_node *)0xdeadbeef){
+    selection_list = NULL;
+    return;
+  }
+  while(walk){
+    old = walk;
+    walk = old->next;
+    free(old->u);
+    free(old);
+  }
+  selection_list = NULL;
+}
+
+/* The rules:
+ * 1. Defaults are implementation-specific. (ioctl,termcap,guess)
+ * 2. COLUMNS and LINES override the defaults. (standards compliance)
+ * 3. Command line options override everything else.
+ * 4. Actual output may be more if the above is too narrow.
+ */
+static void set_screen_size(void){
+  struct winsize ws;
+  char *columns; /* Unix98 environment variable */
+  char *lines;   /* Unix98 environment variable */
+  if(ioctl(1, TIOCGWINSZ, &ws) != -1 && ws.ws_col>0 && ws.ws_row>0){
+    screen_cols = ws.ws_col;
+    screen_rows = ws.ws_row;
+  }else{  /* TODO: ought to do tgetnum("co") and tgetnum("li") now */
+    screen_cols = 80;
+    screen_rows = 24;
+  }
+  if(!isatty(STDOUT_FILENO)) screen_cols = OUTBUF_SIZE;
+  columns = getenv("COLUMNS");
+  if(columns && *columns){
+    long t;
+    char *endptr;
+    t = strtol(columns, &endptr, 0);
+    if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_cols = (int)t;
+  }
+  lines   = getenv("LINES");
+  if(lines && *lines){
+    long t;
+    char *endptr;
+    t = strtol(lines, &endptr, 0);
+    if(!*endptr && (t>0) && (t<(long)OUTBUF_SIZE)) screen_rows = (int)t;
+  }
+  if((screen_cols<9) || (screen_rows<2))
+    fprintf(stderr,"Your %dx%d screen size is bogus. Expect trouble.\n",
+      screen_cols, screen_rows
+    );
+}
+
+/**************** personality control **************/
+
+typedef struct personality_table_struct {
+  const char *name; /* personality name */
+  const void *jump; /* See gcc extension info.   :-)   */
+} personality_table_struct;
+
+static int compare_personality_table_structs(const void *a, const void *b){
+  return strcasecmp(((personality_table_struct*)a)->name,((personality_table_struct*)b)->name);
+}
+
+static const char *set_personality(void){
+  char *s;
+  size_t sl;
+  char buf[16];
+  personality_table_struct findme = { buf, NULL};
+  personality_table_struct *found;
+  static const personality_table_struct personality_table[] = {
+  {"390",      &&case_390},
+  {"aix",      &&case_aix},
+  {"bsd",      &&case_bsd},
+  {"compaq",   &&case_compaq},
+  {"debian",   &&case_debian},
+  {"default",  &&case_default},
+  {"digital",  &&case_digital},
+  {"gnu",      &&case_gnu},
+  {"hp",       &&case_hp},
+  {"hpux",     &&case_hpux},
+  {"irix",     &&case_irix},
+  {"linux",    &&case_linux},
+  {"old",      &&case_old},
+  {"os390",    &&case_os390},
+  {"posix",    &&case_posix},
+  {"s390",     &&case_s390},
+  {"sco",      &&case_sco},
+  {"sgi",      &&case_sgi},
+  {"solaris2", &&case_solaris2},
+  {"sunos4",   &&case_sunos4},
+  {"sysv",     &&case_sysv},
+  {"tru64",    &&case_tru64},
+  {"unix",     &&case_unix},
+  {"unix95",   &&case_unix95},
+  {"unix98",   &&case_unix98},
+  {"unknown",  &&case_unknown}
+  };
+  const int personality_table_count = sizeof(personality_table)/sizeof(personality_table_struct);
+
+  personality = 0;
+  prefer_bsd_defaults = 0;
+
+  bsd_j_format = "OL_j";
+  bsd_l_format = "OL_l";
+  bsd_s_format = "OL_s";
+  bsd_u_format = "OL_u";
+  bsd_v_format = "OL_v";
+
+  /* When these are NULL, the code does SysV output modifier logic */
+  sysv_f_format  = NULL;
+  sysv_fl_format = NULL;
+  sysv_j_format  = NULL;
+  sysv_l_format  = NULL;
+
+  s = getenv("PS_PERSONALITY");
+  if(!s || !*s) s = getenv("CMD_ENV");
+  if(!s || !*s) s="unknown";   /* "Do The Right Thing[tm]" */
+  if(getenv("I_WANT_A_BROKEN_PS")) s="old";
+  sl = strlen(s);
+  if(sl > 15) return "Environment specified an unknown personality.";
+  strncpy(buf, s, sl);
+  buf[sl] = '\0';
+  saved_personality_text = strdup(buf);
+
+  found = bsearch(&findme, personality_table, personality_table_count,
+      sizeof(personality_table_struct), compare_personality_table_structs
+  );
+
+  if(!found) return "Environment specified an unknown personality.";
+
+  goto *(found->jump);    /* See gcc extension info.  :-)   */
+
+  case_bsd:
+    personality = PER_FORCE_BSD | PER_BSD_h | PER_BSD_m;
+    prefer_bsd_defaults = 1;
+    bsd_j_format = "FB_j";
+    bsd_l_format = "FB_l";
+    /* bsd_s_format not used */
+    bsd_u_format = "FB_u";
+    bsd_v_format = "FB_v";
+    return NULL;
+
+  case_old:
+    personality = PER_FORCE_BSD | PER_OLD_m;
+    prefer_bsd_defaults = 1;
+    return NULL;
+
+  case_debian:  /* Toss this? They don't seem to care much. */
+  case_gnu:
+    personality = PER_GOOD_o | PER_CUMUL_MARKED | PER_OLD_m;
+    prefer_bsd_defaults = 1;
+    sysv_f_format  = "RD_f";
+    /* sysv_fl_format = "RD_fl"; */   /* Debian can't do this! */
+    sysv_j_format  = "RD_j";
+    sysv_l_format  = "RD_l";
+    return NULL;
+
+  case_linux:
+    personality = PER_GOOD_o | PER_ZAP_ADDR | PER_SANE_USER;
+    return NULL;
+
+  case_default: /* use defaults for ps, ignoring other environment variables */
+    return NULL;
+
+  case_unknown: /* defaults, but also check inferior environment variables */
+    if(
+      getenv("UNIX95")     /* Irix */
+      || getenv("POSIXLY_CORRECT")  /* most gnu stuff */
+      || (getenv("POSIX2") && !strcmp(getenv("POSIX2"), "on")) /* Unixware 7 */
+    ) personality = PER_BROKEN_o;
+    return NULL;
+
+  case_aix:
+    bsd_j_format = "FB_j";
+    bsd_l_format = "FB_l";
+    /* bsd_s_format not used */
+    bsd_u_format = "FB_u";
+    bsd_v_format = "FB_v";
+    return NULL;
+
+  case_tru64:
+  case_compaq:
+  case_digital:
+    personality = PER_GOOD_o | PER_BSD_h;
+    prefer_bsd_defaults = 1;
+    sysv_f_format  = "F5FMT";
+    sysv_fl_format = "FL5FMT";
+    sysv_j_format  = "JFMT";
+    sysv_l_format  = "L5FMT";
+    bsd_j_format = "JFMT";
+    bsd_l_format = "LFMT";
+    bsd_s_format = "SFMT";
+    bsd_u_format = "UFMT";
+    bsd_v_format = "VFMT";
+    return NULL;
+
+  case_sunos4:
+    personality = PER_NO_DEFAULT_g;
+    prefer_bsd_defaults = 1;
+    bsd_j_format = "FB_j";
+    bsd_l_format = "FB_l";
+    /* bsd_s_format not used */
+    bsd_u_format = "FB_u";
+    bsd_v_format = "FB_v";
+    return NULL;
+
+  case_irix:
+  case_sgi:
+    s = getenv("_XPG");
+    if(s && s[0]>'0' && s[0]<='9') personality = PER_BROKEN_o;
+    else personality = PER_IRIX_l;
+    return NULL;
+
+  case_os390:  /* IBM's OS/390 OpenEdition on the S/390 mainframe */
+  case_s390:
+  case_390:
+    sysv_j_format  = "J390";  /* don't know what -jl and -jf do */
+    return NULL;
+
+  case_hp:
+  case_hpux:
+  case_posix:
+  case_sco:
+  case_solaris2:
+  case_sysv:
+  case_unix95:
+  case_unix98:
+  case_unix:
+    personality = PER_BROKEN_o;
+    return NULL;
+}
+
+
+/************ Call this to reinitialize everything ***************/
+void reset_global(void){
+  static proc_t p;
+  reset_selection_list();
+  look_up_our_self(&p);
+  set_screen_size();
+  set_personality();
+  
+  all_processes         = 0;
+  bsd_c_option          = 0;
+  bsd_e_option          = 0;
+  cached_euid           = geteuid();
+  cached_tty            = p.tty;
+/* forest_prefix must be all zero because of POSIX */
+  forest_type           = 0;
+  format_flags          = 0;   /* -l -f l u s -j... */
+  format_list           = NULL; /* digested formatting options */
+  format_modifiers      = 0;   /* -c -j -y -P -L... */
+  header_gap            = -1;  /* send lines_to_next_header to -infinity */
+  header_type           = HEAD_SINGLE;
+  include_dead_children = 0;
+  lines_to_next_header  = 1;
+  namelist_file         = NULL;
+  negate_selection      = 0;
+  running_only          = 0;
+  seconds_since_boot    = uptime(0,0);
+  selection_list        = NULL;
+  simple_select         = 0;
+  sort_list             = NULL;
+  unix_f_option         = 0;
+  user_is_number        = 0;
+  wchan_is_number       = 0;
+}
+
+/*********** spew variables ***********/
+void self_info(void){
+#ifndef EMULATE_FPUTS
+  int count;
+#endif
+  fprintf(stderr,
+    "BSD j    %s\n"
+    "BSD l    %s\n"
+    "BSD s    %s\n"
+    "BSD u    %s\n"
+    "BSD v    %s\n"
+    "SysV -f  %s\n"
+    "SysV -fl %s\n"
+    "SysV -j  %s\n"
+    "SysV -l  %s\n"
+    "\n",
+    bsd_j_format   ? bsd_j_format   : "(none)",
+    bsd_l_format   ? bsd_l_format   : "(none)",
+    bsd_s_format   ? bsd_s_format   : "(none)",
+    bsd_u_format   ? bsd_u_format   : "(none)",
+    bsd_v_format   ? bsd_v_format   : "(none)",
+    sysv_f_format  ? sysv_f_format  : "(none)",
+    sysv_fl_format ? sysv_fl_format : "(none)",
+    sysv_j_format  ? sysv_j_format  : "(none)",
+    sysv_l_format  ? sysv_l_format  : "(none)"
+  );
+
+  display_version();
+  fprintf(stderr, "Linux version %d.%d.%d\n",
+    LINUX_VERSION_MAJOR(linux_version_code),
+    LINUX_VERSION_MINOR(linux_version_code),
+    LINUX_VERSION_PATCH(linux_version_code)
+  );
+  /* __libc_print_version(); */  /* how can we get the run-time version? */
+  fprintf(stderr, "Compiled with: libc %d, internal version %d.%d\n\n",
+    __GNU_LIBRARY__, __GLIBC__, __GLIBC_MINOR__
+  );
+
+#ifdef EMULATE_FPUTS
+  fprintf(stderr, "libc assumed lame, using fprintf to emulate fputs.\n\n");
+#else
+  fprintf(stderr, "fputs(\"");
+  count = fputs("123456789", stderr);
+  fprintf(stderr, "\", stderr) gives %d, which is %s.\n",
+    count, count==9?"good":"BAD!\nAdjust ps/common.h or libc, then recompile"
+  );
+  if(count!=9){
+    fprintf(stderr, "(procps includes a libc patch called glibc.patch)\n");
+  }
+  fprintf(stderr, "\n");
+#endif
+
+  fprintf(stderr,
+    "header_gap=%d lines_to_next_header=%d\n"
+    "screen_cols=%d screen_rows=%d\n"
+    "\n",
+    header_gap, lines_to_next_header,
+    screen_cols, screen_rows
+  );
+
+/*  open_psdb(namelist_file); */
+  fprintf(stderr,
+    "personality=0x%08x (from \"%s\")\n"
+    "EUID=%d TTY=%d,%d Hertz=%ld\n"
+/*    "namelist_file=\"%s\"\n" */
+    ,
+    personality, saved_personality_text,
+    cached_euid, (int)major(cached_tty), (int)minor(cached_tty), Hertz   /* ,
+    namelist_file?namelist_file:"<no System.map file>"  */
+  );
+}
diff --git a/ps/help.c b/ps/help.c
new file mode 100644 (file)
index 0000000..8e6bc0d
--- /dev/null
+++ b/ps/help.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * The help message must not become longer, because it must fit
+ * on an 80x24 screen _with_ the error message and command prompt.
+ */
+
+
+const char *help_message =
+"********* simple selection *********  ********* selection by list *********\n"
+"-A all processes                      -C by command name\n"
+"-N negate selection                   -G by real group ID (supports names)\n"
+"-a all w/ tty except session leaders  -U by real user ID (supports names)\n"
+"-d all except session leaders         -g by session leader OR by group name\n"
+"-e all processes                      -p by process ID\n"
+"T  all processes on this terminal     -s processes in the sessions given\n"
+"a  all w/ tty, including other users  -t by tty\n"
+"g  all, even group leaders!           -u by effective user ID (supports names)\n"
+"r  only running processes             U  processes for specified users\n"
+"x  processes w/o controlling ttys     t  by tty\n"
+"*********** output format **********  *********** long options ***********\n"
+"-o,o user-defined  -f full            --Group --User --pid --cols\n"
+"-j,j job control   s  signal          --group --user --sid --rows\n"
+"-O,O preloaded -o  v  virtual memory  --cumulative --format --deselect\n"
+"-l,l long          u  user-oriented   --sort --tty --forest --version\n"
+"                   X  registers       --heading --no-heading\n"
+"                    ********* misc options *********\n"
+"-V,V show version       L  list format codes  f  ASCII art forest\n"
+"-m,m show threads       S  children in sum    -y change -l format\n"
+"-n,N set namelist file  c  true command name  n  numeric WCHAN,UID\n"
+"-w,w wide output        e  show environment   -H process heirarchy\n"
+;
+
+
+
+/* Missing:
+ *
+ * -c -L -P -M --info
+ *
+ */
diff --git a/ps/it b/ps/it
new file mode 100644 (file)
index 0000000..07fd6dc
--- /dev/null
+++ b/ps/it
@@ -0,0 +1,35 @@
+From ddainese@dsi.unive.it Sun Apr 18 14:12:27 1999
+
+here is a first translation of the text:
+---------------------------------------------------------------------
+const char *help_message =
+"****** seleziona i processi *******   * seleziona una lista specificando: *\n"
+"-A tutti                              -C il nome del comando\n"
+"-N nega la selezione                  -G il real group ID (supporta i nomi)\n"
+"-a con tty, tranne i session leader   -U il real user ID (supporta i nomi)\n"
+"-d tutti, tranne i session leader     -g il session leader OPPURE il gruppo\n"
+"-e tutti                              -p l'ID del processo\n"
+"T  su questo terminale                -s la sessione\n"
+"a  con tty, di tutti gli utenti       -t il tty\n"
+"g  tutti, anche i leader di gruppo    -u l'effective user ID (supporta i nomi)\n"
+"r  in stato running                   U  una lista di utenti\n"
+"x  senza tty                          t  il tty\n"
+"******** formato dell'output ******   ********** opzioni lunghe **********\n"
+"-o,o definito dall'utente                --Group --User --pid --cols\n"
+"-j,j job              s segnali          --group --user --sid --rows\n"
+"-O,O -o preimpostato  v memoria virtuale --cumulative --format --deselect\n"
+"-l,l lungo            u utenti           --sort --tty --forest --version\n"
+"-f   completo         X registri         --heading --no-heading\n"
+"                    ******** opzioni varie *********\n"
+"-V,V versione           L  codici di formato       f  foresta di ASCII\n"
+"-m,m vista ad albero    S  figli in sum           -y  cambia il formato -l\n"
+"-n,N namelist file      c  nome reale del comando  n  WCHAN,UID numerici\n"
+"-w,w output ampio       e  mostra l'environment   -H  gerarchia dei processi\n"
+;
+---------------------------------------------------------------------
+
+Unfortunately it isn't really understandable for a newbie, because
+there is too little space for a good translation; to make it more
+meaningful, I would need about an entire line for every option, thus
+if you really want the help text stays under 22 lines, it must
+contains only 22 options. What do you think about it?
diff --git a/ps/output.c b/ps/output.c
new file mode 100644 (file)
index 0000000..152ad64
--- /dev/null
@@ -0,0 +1,1608 @@
+/*
+ * Copyright 1999 by Albert Cahalan; all rights resered.
+ *
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/*
+ * This file is really gross, and I know it. I looked into several
+ * alternate ways to deal with the mess, and they were all ugly.
+ *
+ * FreeBSD has a fancy hack using offsets into a struct -- that
+ * saves code but it is _really_ gross. See the PO macro below.
+ *
+ * We could have a second column width for wide output format.
+ * For example, Digital prints the real-time signals.
+ */
+
+
+/*
+ * Data table idea:
+ *
+ * table 1 maps aix to specifier
+ * table 2 maps shortsort to specifier
+ * table 3 maps macro to specifiers
+ * table 4 maps specifier to title,datatype,offset,vendor,helptext
+ * table 5 maps datatype to justification,width,widewidth,sorting,printing
+ *
+ * Here, "datatype" could be user,uid,u16,pages,deltaT,signals,tty,longtty...
+ * It must be enough to determine printing and sorting.
+ *
+ * After the tables, increase width as needed to fit the header.
+ *
+ * Table 5 could go in a file with the output functions.
+ */
+
+/* proc_t offset macro */
+#define PO(q) ((unsigned long)(&(((proc_t*)0)->q)))
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "../proc/readproc.h"
+#include "../proc/sysinfo.h"
+#include "../proc/status.h"
+#include "../proc/procps.h"
+#include "../proc/devname.h"
+#include "common.h"
+
+
+/* TODO:
+ * Stop assuming system time is local time.
+ */
+
+#define COLWID 240 /* satisfy snprintf, which is faster than sprintf */
+
+static char  whitespace_and_outbuf[OUTBUF_SIZE + SPACE_AMOUNT + PAGE_SIZE*2];
+static char *outbuf = whitespace_and_outbuf+SPACE_AMOUNT;
+static char *whitespace = whitespace_and_outbuf;
+static unsigned max_rightward = 0x12345678; /* space for RIGHT stuff */
+static unsigned max_leftward = 0x12345678; /* space for LEFT stuff */
+
+/* Justification control for flags field. */
+#define JUST_MASK 0x0f
+     /* AIXHACK   0 */
+#define USER      1  /* left if text, right if numeric */
+#define LEFT      2
+#define RIGHT     3
+#define UNLIMITED 4
+#define WCHAN     5  /* left if text, right if numeric */
+#define SIGNAL    6  /* right in 9, or 16 if screen_cols>107 */
+
+#define CUMUL     16  /* mark cumulative (Summed) headers with 'C' */
+
+static int wide_signals;  /* true if we have room */
+
+static proc_t *pp;     /* the process being printed */
+
+static unsigned long seconds_since_1970;
+static unsigned long time_of_boot;
+static unsigned long page_shift;
+
+
+/*************************************************************************/
+/************ Lots of sort functions, starting with the NOP **************/
+
+static int sr_nop(const proc_t* a, const proc_t* b){
+  (void)a;(void)b; /* shut up gcc */
+  return 0;
+}
+
+#define CMP_STR(NAME) \
+static int sr_ ## NAME(const proc_t* P, const proc_t* Q) { \
+    return strcmp(P->NAME, Q->NAME); \
+}
+
+#define CMP_INT(NAME) \
+static int sr_ ## NAME (const proc_t* P, const proc_t* Q) { \
+    if (P->NAME < Q->NAME) return -1; \
+    if (P->NAME > Q->NAME) return  1; \
+    return 0; \
+}
+
+CMP_INT(cutime)
+CMP_INT(cstime)
+CMP_INT(priority)                                             /* nice */
+CMP_INT(timeout)
+CMP_INT(nice)                                                 /* priority */
+CMP_INT(rss)      /* resident set size from stat file */ /* vm_rss, resident */
+CMP_INT(it_real_value)
+CMP_INT(size)      /* total pages */                     /* vm_size, vsize */
+CMP_INT(resident)  /* resident pages */                     /* vm_rss, rss */
+CMP_INT(share)     /* shared pages */
+CMP_INT(trs)       /* executable pages */
+CMP_INT(lrs)       /* obsolete "library" pages above 0x60000000 */
+CMP_INT(drs)       /* other pages (assumed data?) */
+CMP_INT(dt)        /* dirty pages */
+
+CMP_INT(vm_size)    /* kB VM */                             /* size, vsize */
+CMP_INT(vm_lock)    /* kB locked */
+CMP_INT(vm_rss)     /* kB rss */                          /* rss, resident */
+CMP_INT(vm_data)    /* kB "data" == data-stack */
+CMP_INT(vm_stack)   /* kB stack */
+CMP_INT(vm_exe)     /* kB "exec" == exec-lib */
+CMP_INT(vm_lib)     /* kB "libraries" */
+CMP_INT(vsize)      /* pages VM */                        /* size, vm_size */
+CMP_INT(rss_rlim)
+CMP_INT(flags)
+CMP_INT(min_flt)
+CMP_INT(maj_flt)
+CMP_INT(cmin_flt)
+CMP_INT(cmaj_flt)
+CMP_INT(nswap)
+CMP_INT(cnswap)
+CMP_INT(utime)
+CMP_INT(stime)    /* Old: sort by systime. New: show start time. Uh oh. */
+CMP_INT(start_code)
+CMP_INT(end_code)
+CMP_INT(start_stack)
+CMP_INT(kstk_esp)
+CMP_INT(kstk_eip)
+CMP_INT(start_time)
+CMP_INT(wchan)
+
+/* CMP_STR(*environ) */
+/* CMP_STR(*cmdline) */
+
+CMP_STR(ruser)
+CMP_STR(euser)
+CMP_STR(suser)
+CMP_STR(fuser)
+CMP_STR(rgroup)
+CMP_STR(egroup)
+CMP_STR(sgroup)
+CMP_STR(fgroup)
+CMP_STR(cmd)
+/* CMP_STR(ttyc) */    /* FIXME -- use strncmp with 8 max */
+
+CMP_INT(ruid)
+CMP_INT(rgid)
+CMP_INT(euid)
+CMP_INT(egid)
+CMP_INT(suid)
+CMP_INT(sgid)
+CMP_INT(fuid)
+CMP_INT(fgid)
+CMP_INT(pid)
+CMP_INT(ppid)
+CMP_INT(pgrp)
+CMP_INT(session)
+CMP_INT(tty)
+CMP_INT(tpgid)
+
+CMP_INT(pcpu)
+
+CMP_INT(state)
+
+/***************************************************************************/
+/************ Lots of format functions, starting with the NOP **************/
+
+static int pr_nop(void){
+  return snprintf(outbuf, COLWID, "%c", '-');
+}
+
+
+/********* Unix 98 ************/
+
+/***
+
+Only comm and args are allowed to contain blank characters; all others are
+not. Any implementation-dependent variables will be specified in the system
+documentation along with the default header and indicating if the field
+may contain blank characters.
+
+Some headers do not have a standardized specifier!
+
+%CPU   pcpu    The % of cpu time used recently, with unspecified "recently".
+ADDR           The address of the process.
+C              Processor utilisation for scheduling.
+CMD            The command name, or everything with -f.
+COMMAND        args    Command + args. May chop as desired. May use either version.
+COMMAND        comm    argv[0]
+ELAPSED        etime   Elapsed time since the process was started. [[dd-]hh:]mm:ss
+F              Flags (octal and additive)
+GROUP  group   Effective group ID, prefer text over decimal.
+NI     nice    Decimal system scheduling priority, see nice(1).
+PGID   pgid    The decimal value of the process group ID.
+PID    pid     Decimal PID.
+PPID   ppid    Decimal PID.
+PRI            Priority. Higher numbers mean lower priority.
+RGROUP rgroup  Real group ID, prefer text over decimal.
+RUSER  ruser   Real user ID, prefer text over decimal.
+S              The state of the process.
+STIME          Starting time of the process.
+SZ             The size in blocks of the core image of the process.
+TIME   time    Cumulative CPU time. [dd-]hh:mm:ss
+TT     tty     Name of tty in format used by who(1).
+TTY            The controlling terminal for the process.
+UID            UID, or name when -f
+USER   user    Effective user ID, prefer text over decimal.
+VSZ    vsz     Virtual memory size in decimal kB.
+WCHAN          Where waiting/sleeping or blank if running.
+
+The nice value is used to compute the priority.
+
+For some undefined ones, Digital does:
+
+F       flag    Process flags -- but in hex!
+PRI     pri     Process priority
+S       state   Symbolic process status
+TTY     tt,tty,tname,longtname  -- all do "ttyp1", "console", "??"
+UID     uid     Process user ID (effective UID)
+WCHAN   wchan   Address of event on which a
+
+For some undefined ones, Sun does:
+
+ADDR   addr    memory address of the process
+C      c       Processor utilization  for  scheduling  (obsolete).
+CMD
+F      f
+S      s       state: OSRZT
+STIME          start time, printed w/o blanks. If 24h old, months & days
+SZ             size (in pages) of the swappable process's image in main memory
+TTY
+UID    uid
+WCHAN  wchan
+
+For some undefined ones, SCO does:
+ADDR   addr    Virtual address of the process' entry in the process table.
+SZ             swappable size in kB of the virtual data and stack
+STIME  stime   hms or md time format
+***/
+
+/* Source & destination are known. Return bytes or screen characters? */
+static int forest_helper(void){
+  char *p = forest_prefix;
+  char *q = outbuf;
+  if(!*p) return 0;
+  /* Arrrgh! somebody defined unix as 1 */
+  if(forest_type == 'u') goto unixy;
+  while(*p){
+    switch(*p){
+    case ' ': strcpy(q, "    ");  break;
+    case 'L': strcpy(q, " \\_ "); break;
+    case '+': strcpy(q, " \\_ "); break;
+    case '|': strcpy(q, " |  ");  break;
+    case '\0': return q-outbuf;    /* redundant & not used */
+    }
+    q += 4;
+    p++;
+  }
+  return q-outbuf;   /* gcc likes this here */
+unixy:
+  while(*p){
+    switch(*p){
+    case ' ': strcpy(q, "  "); break;
+    case 'L': strcpy(q, "  "); break;
+    case '+': strcpy(q, "  "); break;
+    case '|': strcpy(q, "  "); break;
+    case '\0': return q-outbuf;    /* redundant & not used */
+    }
+    q += 2;
+    p++;
+  }
+  return q-outbuf;   /* gcc likes this here */
+}
+
+
+/* XPG4-UNIX, according to Digital:
+The "args" and "command" specifiers show what was passed to the command.
+Modifications to the arguments are not shown.
+*/
+
+/*
+ * pp->cmd       short accounting name (comm & ucomm)
+ * pp->cmdline   long name with args (args & command)
+ * pp->environ   environment
+ */
+
+/* "command" is the same thing: long unless c */
+static int pr_args(void){
+  char *endp;
+  endp = outbuf + forest_helper();
+  if(bsd_c_option){
+    endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */
+  }else{
+    const char **lc = (const char**)pp->cmdline; /* long version */
+    if(lc && *lc) {
+      endp += escape_strlist(endp, lc, OUTBUF_SIZE);
+    } else {
+      char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */
+      escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE);
+      endp += snprintf(endp, COLWID, "[%s]", buf);
+    }
+  }
+  if(bsd_e_option){
+    const char **env = (const char**)pp->environ;
+    if(env && *env){
+      *endp++ = ' ';
+      endp += escape_strlist(endp, env, OUTBUF_SIZE);
+    }
+  }
+  return endp - outbuf;
+}
+
+/* "ucomm" is the same thing: short unless -f */
+static int pr_comm(void){
+  char *endp;
+  endp = outbuf + forest_helper();
+  if(!unix_f_option){ /* does -f matter? */
+    endp += escape_str(endp, pp->cmd, PAGE_SIZE); /* short version */
+  }else{
+    const char **lc = (const char**)pp->cmdline; /* long version */
+    if(lc && *lc) {
+      endp += escape_strlist(endp, lc, OUTBUF_SIZE);
+    } else {
+      char buf[ESC_STRETCH*PAGE_SIZE]; /* TODO: avoid copy */
+      escape_str(buf, pp->cmd, ESC_STRETCH*PAGE_SIZE);
+      endp += snprintf(endp, COLWID, "[%s]", buf);
+    }
+  }
+  if(bsd_e_option){
+    const char **env = (const char**)pp->environ;
+    if(env && *env){
+      *endp++ = ' ';
+      endp += escape_strlist(endp, env, OUTBUF_SIZE);
+    }
+  }
+  return endp - outbuf;
+}
+/* Non-standard, from SunOS 5 */
+static int pr_fname(void){
+  char *endp;
+  endp = outbuf + forest_helper();
+  endp += escape_str(endp, pp->cmd, 8);
+  return endp - outbuf;
+}
+
+/* elapsed wall clock time, [[dd-]hh:]mm:ss format (not same as "time") */
+static int pr_etime(void){
+  unsigned t, dd,hh,mm,ss;
+  char *cp = outbuf;
+  t = (
+          ((unsigned long)seconds_since_boot)
+        - ((unsigned long)pp->start_time)
+        / Hertz
+      );
+  ss = t%60;
+  t /= 60;
+  mm = t%60;
+  t /= 60;
+  hh = t%24;
+  t /= 24;
+  dd = t;
+  cp +=(     dd      ?  snprintf(cp, COLWID, "%u-", dd)           :  0 );
+  cp +=( (dd || hh)  ?  snprintf(cp, COLWID, "%02u:", hh)         :  0 );
+  cp +=                 snprintf(cp, COLWID, "%02u:%02u", mm, ss)       ;
+  return (int)(cp-outbuf);
+}
+static int pr_nice(void){
+  return snprintf(outbuf, COLWID, "%ld", pp->nice);
+}
+
+/* "Processor utilisation for scheduling."  --- we use %cpu w/o fraction */
+static int pr_c(void){
+  unsigned long total_time;   /* jiffies used by this process */
+  unsigned long pcpu = 0;     /* scaled %cpu, 999 means 99.9% */
+  unsigned long seconds;      /* seconds of process life */
+  total_time = pp->utime + pp->stime;
+  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+  seconds =
+    seconds_since_boot  -  ((unsigned long)pp->start_time)  /  Hertz
+  ;
+  /* Use 100ULL (not 100) to avoid 32-bit overflow. */
+  if(seconds) pcpu = (total_time * 100ULL / Hertz) / seconds;
+  if (pcpu > 99) pcpu = 99;
+  return snprintf(outbuf, COLWID, "%2u", (unsigned)pcpu);
+}
+/* normal %CPU in ##.# format. */
+static int pr_pcpu(void){
+  unsigned long total_time;   /* jiffies used by this process */
+  unsigned long pcpu = 0;     /* scaled %cpu, 999 means 99.9% */
+  unsigned long seconds;      /* seconds of process life */
+  total_time = pp->utime + pp->stime;
+  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+  seconds =
+    seconds_since_boot  -  ((unsigned long)pp->start_time)  /  Hertz
+  ;
+  /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */
+  if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+  if (pcpu > 999) pcpu = 999;
+  return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pcpu/10), (unsigned)(pcpu%10));
+}
+/* this is a "per-mill" format, like %cpu with no decimal point */
+static int pr_cp(void){
+  unsigned long total_time;   /* jiffies used by this process */
+  unsigned long pcpu = 0;     /* scaled %cpu, 999 means 99.9% */
+  unsigned long seconds;      /* seconds of process life */
+  total_time = pp->utime + pp->stime;
+  if(include_dead_children) total_time += (pp->cutime + pp->cstime);
+  seconds =
+    seconds_since_boot  -  ((unsigned long)pp->start_time)  /  Hertz
+  ;
+  /* Use 1000ULL (not 1000) to avoid 32-bit overflow. */
+  if(seconds) pcpu = (total_time * 1000ULL / Hertz) / seconds;
+  if (pcpu > 999) pcpu = 999;
+  return snprintf(outbuf, COLWID, "%3u", (unsigned)pcpu);
+}
+
+static int pr_pgid(void){
+  return snprintf(outbuf, COLWID, "%u", pp->pgrp);
+}
+static int pr_pid(void){
+  return snprintf(outbuf, COLWID, "%u", pp->pid);
+}
+static int pr_ppid(void){
+  return snprintf(outbuf, COLWID, "%u", pp->ppid);
+}
+
+
+/* cumulative CPU time, [dd-]hh:mm:ss format (not same as "etime") */
+static int pr_time(void){
+  unsigned t, dd,hh,mm,ss;
+  int c;
+  t = (unsigned)(
+      (
+        (unsigned long)(pp->utime)
+        +
+        (unsigned long)(pp->stime)
+      )
+      /
+      (unsigned long)Hertz
+  );
+  ss = t%60;
+  t /= 60;
+  mm = t%60;
+  t /= 60;
+  hh = t%24;
+  t /= 24;
+  dd = t;
+  c  =( dd ? snprintf(outbuf, COLWID, "%u-", dd) : 0              );
+  c +=( snprintf(outbuf+c, COLWID, "%02u:%02u:%02u", hh, mm, ss)    );
+  return c;
+}
+
+/* HP-UX puts this (I forget, vsz or vsize?) in kB and uses "sz" for pages.
+ * Unix98 requires "vsz" to be kB.
+ * Tru64 does both vsize and vsz like "1.23M"
+ *
+ * Our pp->vm_size is kB and our pp->vsize is pages.
+ *
+ * TODO: add flag for "1.23M" behavior, on this and other columns.
+ */
+static int pr_vsz(void){
+  return snprintf(outbuf, COLWID, "%lu", pp->vm_size);
+}
+
+/*
+ * internal terms:  ruid  euid  suid  fuid
+ * kernel vars:      uid  euid  suid fsuid
+ * command args:    ruid   uid svuid   n/a
+ */
+
+static int pr_ruser(void){
+  if(user_is_number || (strlen(pp->ruser)>max_rightward))
+    return snprintf(outbuf, COLWID, "%d", pp->ruid);
+  return snprintf(outbuf, COLWID, "%s", pp->ruser);
+}
+static int pr_egroup(void){
+  if(strlen(pp->egroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->egid);
+  return snprintf(outbuf, COLWID, "%s", pp->egroup);
+}
+static int pr_rgroup(void){
+  if(strlen(pp->rgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->rgid);
+  return snprintf(outbuf, COLWID, "%s", pp->rgroup);
+}
+static int pr_euser(void){
+  if(user_is_number || (strlen(pp->euser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->euid);
+  return snprintf(outbuf, COLWID, "%s", pp->euser);
+}
+
+/********* maybe standard (Unix98 only defines the header) **********/
+
+
+/*
+ * "PRI" is created by "opri", or by "pri" when -c is used.
+ *
+ * Unix98 only specifies that a high "PRI" is low priority.
+ * Sun and SCO add the -c behavior. Sun defines "pri" and "opri".
+ * Linux may use "priority" for historical purposes.
+ */
+static int pr_priority(void){    /* -20..20 */
+    return snprintf(outbuf, COLWID, "%ld", pp->priority);
+}
+static int pr_pri(void){         /* 20..60 */
+    return snprintf(outbuf, COLWID, "%ld", 39 - pp->priority);
+}
+static int pr_opri(void){        /* 39..79 */
+    return snprintf(outbuf, COLWID, "%ld", 60 + pp->priority);
+}
+
+static int pr_wchan(void){
+/*
+ * Unix98 says "blank if running" and also "no blanks"! :-(
+ * Unix98 also says to use '-' if something is meaningless.
+ * Digital uses both '*' and '-', with undocumented differences.
+ * (the '*' for -1 (rare) and the '-' for 0)
+ * Sun claims to use a blank AND use '-', in the same man page.
+ * Perhaps "blank" should mean '-'.
+ *
+ * AIX uses '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ */
+    if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-");
+    if(wchan_is_number) return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff);
+    return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan));
+}
+
+/* Terrible trunctuation, like BSD crap uses: I999 J999 K999 */
+/* FIXME: disambiguate /dev/tty69 and /dev/pts/69. */
+static int pr_tty4(void){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+  return dev_to_tty(outbuf, 4, pp->tty, pp->pid, ABBREV_DEV|ABBREV_TTY|ABBREV_PTS);
+}
+
+/* Unix98: format is unspecified, but must match that used by who(1). */
+static int pr_tty8(void){
+/* snprintf(outbuf, COLWID, "%02x:%02x", pp->tty>>8, pp->tty&0xff); */
+  return dev_to_tty(outbuf, PAGE_SIZE-1, pp->tty, pp->pid, ABBREV_DEV);
+}
+
+#if 0
+/* This BSD state display may contain spaces, which is illegal. */
+static int pr_oldstate(void){
+    return snprintf(outbuf, COLWID, "%s", status(pp));
+}
+#endif
+
+/* This state display is Unix98 compliant and has lots of info like BSD. */
+static int pr_stat(void){
+    int end = 0;
+    outbuf[end++] = pp->state;
+    if(pp->rss == 0 && pp->state != 'Z')    outbuf[end++] = 'W';
+    if(pp->nice < 0)                        outbuf[end++] = '<';
+    if(pp->nice > 0)                        outbuf[end++] = 'N';
+    if(pp->vm_lock)                         outbuf[end++] = 'L';
+    outbuf[end] = '\0';
+    return end;
+}
+
+/* This minimal state display is Unix98 compliant, like SCO and SunOS 5 */
+static int pr_s(void){
+    outbuf[0] = pp->state;
+    outbuf[1] = '\0';
+    return 1;
+}
+
+static int pr_flag(void){
+    /* Unix98 requires octal -- good thing Linux hex looks octal! */
+    return snprintf(outbuf, COLWID, "%03lx", (pp->flags)&0x777);
+}
+
+static int pr_euid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->euid);
+}
+
+/*********** non-standard ***********/
+
+/*** BSD
+sess   session pointer
+(SCO has:Process session leader ID as a decimal value. (SESSION))
+jobc   job control count
+cpu    short-term cpu usage factor (for scheduling)
+sl     sleep time (in seconds; 127 = infinity)
+re     core residency time (in seconds; 127 = infinity)
+pagein pageins (same as majflt)
+lim    soft memory limit
+tsiz   text size (in Kbytes)
+***/
+
+static int pr_stackp(void){
+    return snprintf(outbuf, COLWID, "%08lx", pp->start_stack);
+}
+
+static int pr_esp(void){
+    return snprintf(outbuf, COLWID, "%08lx", pp->kstk_esp);
+}
+
+static int pr_eip(void){
+    return snprintf(outbuf, COLWID, "%08lx", pp->kstk_eip);
+}
+
+/* This function helps print old-style time formats */
+static int old_time_helper(char *dst, unsigned long t, unsigned long rel) {
+  if(!t)            return snprintf(dst, COLWID, "    -");
+  if((long)t == -1) return snprintf(dst, COLWID, "   xx");
+  if((long)(t-=rel) < 0)  t=0;
+  if(t>9999)        return snprintf(dst, COLWID, "%5lu", t/100);
+  else              return snprintf(dst, COLWID, "%2lu.%02lu", t/100, t%100);
+}
+
+static int pr_bsdtime(void){
+    unsigned long t;
+    t = pp->utime + pp->stime;
+    if(include_dead_children) t += (pp->cutime + pp->cstime);
+    t /= Hertz;
+    return snprintf(outbuf, COLWID, "%3ld:%02d", t/60, (int)(t%60));
+}
+
+static int pr_bsdstart(void){
+  time_t start;
+  time_t seconds_ago;
+  start = time_of_boot + pp->start_time/Hertz;
+  seconds_ago = seconds_since_1970 - start;
+  if(seconds_ago < 0) seconds_ago=0;
+  if(seconds_ago > 3600*24)  strcpy(outbuf, ctime(&start)+4);
+  else                       strcpy(outbuf, ctime(&start)+10);
+  outbuf[6] = '\0';
+  return 6;
+}
+
+static int pr_timeout(void){
+    return old_time_helper(outbuf, pp->timeout, seconds_since_boot*Hertz);
+}
+
+static int pr_alarm(void){
+    return old_time_helper(outbuf, pp->it_real_value, 0);
+}
+
+/* HP-UX puts this in pages and uses "vsz" for kB */
+static int pr_sz(void){
+  return snprintf(outbuf, COLWID, "%lu", (pp->vm_size)/(PAGE_SIZE/1024));
+}
+
+
+/*
+ * FIXME: trs,drs,tsiz,dsiz,m_trs,m_drs,vm_exe,vm_data,trss
+ * I suspect some/all of those are broken. They seem to have been
+ * inherited by Linux and AIX from early BSD systems. FreeBSD only
+ * retains tsiz. The prefixed versions come from Debian.
+ * Sun and Digital have none of this crap. The code here comes
+ * from an old Linux ps, and might not be correct for ELF executables.
+ *
+ * AIX            TRS    size of resident-set (real memory) of text
+ * AIX            TSIZ   size of text (shared-program) image
+ * FreeBSD        tsiz   text size (in Kbytes)
+ * 4.3BSD NET/2   trss   text resident set size (in Kbytes)
+ * 4.3BSD NET/2   tsiz   text size (in Kbytes)
+ */
+
+/* kB data size. See drs, tsiz & trs. */
+static int pr_dsiz(void){
+    long dsiz = 0;
+    if(pp->vsize) dsiz += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+    return snprintf(outbuf, COLWID, "%ld", dsiz);
+}
+
+/* kB text (code) size. See trs, dsiz & drs. */
+static int pr_tsiz(void){
+    long tsiz = 0;
+    if(pp->vsize) tsiz += (pp->end_code - pp->start_code) >> 10;
+    return snprintf(outbuf, COLWID, "%ld", tsiz);
+}
+
+/* kB _resident_ data size. See dsiz, tsiz & trs. */
+static int pr_drs(void){
+    long drs = 0;
+    if(pp->vsize) drs += (pp->vsize - pp->end_code + pp->start_code) >> 10;
+    return snprintf(outbuf, COLWID, "%ld", drs);
+}
+
+/* kB text _resident_ (code) size. See tsiz, dsiz & drs. */
+static int pr_trs(void){
+    long trs = 0;
+    if(pp->vsize) trs += (pp->end_code - pp->start_code) >> 10;
+    return snprintf(outbuf, COLWID, "%ld", trs);
+}
+
+
+
+static int pr_minflt(void){
+    long flt = pp->min_flt;
+    if(include_dead_children) flt += pp->cmin_flt;
+    return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_majflt(void){
+    long flt = pp->maj_flt;
+    if(include_dead_children) flt += pp->cmaj_flt;
+    return snprintf(outbuf, COLWID, "%ld", flt);
+}
+
+static int pr_lim(void){
+    if(pp->rss_rlim == RLIM_INFINITY) return snprintf(outbuf, COLWID, "%s", "xx");
+    return snprintf(outbuf, COLWID, "%5ld", pp->rss_rlim >> 10);
+}
+
+/* should print leading tilde ('~') if process is bound to the CPU */
+static int pr_psr(void){
+  return snprintf(outbuf, COLWID, "%d", pp->processor);
+}
+
+static int pr_wname(void){
+/* SGI's IRIX always uses a number for "wchan", so "wname" is provided too.
+ *
+ * We use '-' for running processes, the location when there is
+ * only one thread waiting in the kernel, and '*' when there is
+ * more than one thread waiting in the kernel.
+ */
+    if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "%s", "-");
+    return snprintf(outbuf, COLWID, "%s", wchan(pp->wchan));
+}
+
+static int pr_nwchan(void){
+    if(!(pp->wchan & 0xffffff)) return snprintf(outbuf, COLWID, "-");
+    return snprintf(outbuf, COLWID, "%lx", pp->wchan & 0xffffff);
+}
+
+static int pr_rss(void){
+  return snprintf(outbuf, COLWID, "%lu", pp->vm_rss);
+}
+
+/* pp->vm_rss * 1000 would overflow on 32-bit systems with 64 GB memory */
+static int pr_pmem(void){
+  unsigned long pmem = 0;
+  pmem = pp->vm_rss * 1000ULL / kb_main_total;
+  if (pmem > 999) pmem = 999;
+  return snprintf(outbuf, COLWID, "%2u.%u", (unsigned)(pmem/10), (unsigned)(pmem%10));
+}
+
+static int pr_lstart(void){
+  time_t t;
+  t = (
+           ((unsigned long)time_of_boot)
+         + ((unsigned long)pp->start_time)
+         / Hertz
+  );
+  return snprintf(outbuf, COLWID, "%24.24s", ctime(&t));
+}
+
+/* Unix98 specifies a STIME header for a column that shows the start
+ * time of the process, but does not specify a format or format specifier.
+ * From the general Unix98 rules, we know there must not be any spaces.
+ * Most systems violate that rule, though the Solaris documentation
+ * claims to print the column without spaces. (NOT!)
+ *
+ * So this isn't broken, but could be renamed to u98_std_stime,
+ * as long as it still shows as STIME when using the -f option.
+ */
+static int pr_stime(void){
+  struct tm *proc_time;
+  struct tm *our_time;
+  time_t t;
+  char *fmt;
+  int tm_year;
+  int tm_yday;
+  our_time = localtime(&seconds_since_1970);   /* not reentrant */
+  tm_year = our_time->tm_year;
+  tm_yday = our_time->tm_yday;
+  t = (time_t)(
+            ((unsigned long)time_of_boot)
+          + ((unsigned long)pp->start_time)
+          / Hertz
+  );
+  proc_time = localtime(&t); /* not reentrant, this corrupts our_time */
+  fmt = "%H:%M";                                   /* 03:02 23:59 */
+  if(tm_yday != proc_time->tm_yday) fmt = "%b%d";  /* Jun06 Aug27 */
+  if(tm_year != proc_time->tm_year) fmt = "%Y";    /* 1991 2001 */
+  return strftime(outbuf, 42, fmt, proc_time);
+}
+
+static int pr_start(void){
+  time_t t;
+  char *str;
+  t = (
+           ((unsigned long)time_of_boot)
+         + ((unsigned long)pp->start_time)
+         / Hertz
+  );
+  str = ctime(&t);
+  if(str[8]==' ')  str[8]='0';
+  if(str[11]==' ') str[11]='0';
+  if((unsigned long)t+60*60*24 > seconds_since_1970)
+    return snprintf(outbuf, COLWID, "%8.8s", str+11);
+  return snprintf(outbuf, COLWID, "  %6.6s", str+4);
+}
+
+
+#ifdef SIGNAL_STRING
+static int help_pr_sig(const char *sig){
+  long len = 0;
+  len = strlen(sig);
+  if(wide_signals){
+    if(len>8) return snprintf(outbuf, COLWID, "%s", sig);
+    return snprintf(outbuf, COLWID, "00000000%s", sig);
+  }
+  if(len-strspn(sig,"0") > 8)
+    return snprintf(outbuf, COLWID, "<%s", sig+len-8);
+  return snprintf(outbuf, COLWID,  "%s", sig+len-8);
+}
+#else
+static int help_pr_sig(unsigned long long sig){
+  if(wide_signals) return snprintf(outbuf, COLWID, "%016Lx", sig);
+  if(sig>>32)      return snprintf(outbuf, COLWID, "<%08Lx", sig&0xffffffffLL);
+  return                  snprintf(outbuf, COLWID,  "%08Lx", sig&0xffffffffLL);
+}
+#endif
+
+static int pr_sig(void){
+  return help_pr_sig(pp->signal);
+}
+static int pr_sigmask(void){
+  return help_pr_sig(pp->blocked);
+}
+static int pr_sigignore(void){
+  return help_pr_sig(pp->sigignore);
+}
+static int pr_sigcatch(void){
+  return help_pr_sig(pp->sigcatch);
+}
+
+
+static int pr_egid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->egid);
+}
+static int pr_rgid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->rgid);
+}
+static int pr_sgid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->sgid);
+}
+static int pr_fgid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->fgid);
+}
+static int pr_ruid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->ruid);
+}
+static int pr_suid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->suid);
+}
+static int pr_fuid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->fuid);
+}
+
+
+static int pr_fgroup(void){
+  if(strlen(pp->fgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->fgid);
+  return snprintf(outbuf, COLWID, "%s", pp->fgroup);
+}
+static int pr_sgroup(void){
+  if(strlen(pp->sgroup)>max_rightward) return snprintf(outbuf, COLWID, "%d", pp->sgid);
+  return snprintf(outbuf, COLWID, "%s", pp->sgroup);
+}
+static int pr_fuser(void){
+  if(user_is_number || (strlen(pp->fuser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->fuid);
+  return snprintf(outbuf, COLWID, "%s", pp->fuser);
+}
+static int pr_suser(void){
+  if(user_is_number || (strlen(pp->suser)>max_rightward)) return snprintf(outbuf, COLWID, "%d", pp->suid);
+  return snprintf(outbuf, COLWID, "%s", pp->suser);
+}
+
+
+static int pr_thread(void){  /* TID tid LWP lwp SPID spid */
+  return snprintf(outbuf, COLWID, "%u", pp->pid);  /* for now... FIXME */
+}
+static int pr_nlwp(void){  /* THCNT thcount NLWP nlwp */
+  return snprintf(outbuf, COLWID, "-");  /* for now... FIXME */
+}
+
+static int pr_sess(void){
+  return snprintf(outbuf, COLWID, "%u", pp->session);
+}
+static int pr_tpgid(void){
+  return snprintf(outbuf, COLWID, "%d", pp->tpgid);
+}
+
+
+/* SGI uses "cpu" to print the processor ID with header "P" */
+static int pr_sgi_p(void){          /* FIXME */
+  if(pp->state == 'R') return snprintf(outbuf, COLWID, "%d", pp->processor);
+  return snprintf(outbuf, COLWID, "*");
+}
+
+
+
+/***************************************************************************/
+/*************************** other stuff ***********************************/
+
+/*
+ * Old header specifications.
+ *
+ * short   Up  "  PID TTY STAT  TIME COMMAND"
+ * long  l Pp  " FLAGS   UID   PID  PPID PRI  NI   SIZE   RSS WCHAN       STA TTY TIME COMMAND
+ * user  u up  "USER       PID %CPU %MEM  SIZE   RSS TTY STAT START   TIME COMMAND
+ * jobs  j gPp " PPID   PID  PGID   SID TTY TPGID  STAT   UID   TIME COMMAND
+ * sig   s p   "  UID   PID SIGNAL   BLOCKED  IGNORED  CATCHED  STAT TTY   TIME COMMAND
+ * vm    v r   "  PID TTY STAT  TIME  PAGEIN TSIZ DSIZ  RSS   LIM %MEM COMMAND
+ * m     m r   "  PID TTY MAJFLT MINFLT   TRS   DRS  SIZE  SWAP   RSS  SHRD   LIB  DT COMMAND
+ * regs  X p   "NR   PID    STACK      ESP      EIP TMOUT ALARM STAT TTY   TIME COMMAND
+ */
+
+/*
+ * Unix98 requires that the heading for tty is TT, though XPG4, Digital,
+ * and BSD use TTY. The Unix98 headers are:
+ *              args,comm,etime,group,nice,pcpu,pgid
+ *              pid,ppid,rgroup,ruser,time,tty,user,vsz
+ *
+ * BSD c:   "command" becomes accounting name ("comm" or "ucomm")
+ * BSD n:   "user" becomes "uid" and "wchan" becomes "nwchan" (number)
+ */
+
+/* short names to save space */
+#define MEM PROC_FILLMEM     /* read statm  */
+#define CMD PROC_FILLCMD     /* read cmdline */
+#define ENV PROC_FILLENV     /* read environ */
+#define USR PROC_FILLUSR     /* uid_t and gid_t -> user and group names */
+#define BUG PROC_FILLBUG     /* what does this need? */
+
+/* TODO
+ *      pull out annoying BSD aliases into another table (to macro table?)
+ *      add sorting functions here (to unify names)
+ */
+
+/* temporary hack -- mark new stuff grabbed from Debian ps */
+#define LNx LNX
+
+/* there are about 195 listed */
+
+/* Many of these are placeholders for unsupported options. */
+static const format_struct format_array[] = {
+/* code       header     print()      sort()    width  ?   vendor flags  */
+{"%cpu",      "%CPU",    pr_pcpu,     sr_pcpu,    4,   0,    BSD, RIGHT}, /*pcpu*/
+{"%mem",      "%MEM",    pr_pmem,     sr_nop,     4,   0,    BSD, RIGHT}, /*pmem*/
+{"acflag",    "ACFLG",   pr_nop,      sr_nop,     5,   0,    XXX, RIGHT}, /*acflg*/
+{"acflg",     "ACFLG",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT}, /*acflag*/
+{"addr",      "ADDR",    pr_nop,      sr_nop,     4,   0,    XXX, RIGHT},
+{"addr_1",    "ADDR",    pr_nop,      sr_nop,     1,   0,    LNX, LEFT},
+{"alarm",     "ALARM",   pr_alarm,    sr_it_real_value, 5, 0, LNX, RIGHT},
+{"argc",      "ARGC",    pr_nop,      sr_nop,     4,   0,    LNX, RIGHT},
+{"args",      "COMMAND", pr_args,     sr_nop,    16,   0,    U98, UNLIMITED}, /*command*/
+{"atime",     "TIME",    pr_time,     sr_nop,     8,   0,    SOE, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */
+{"blocked",   "BLOCKED", pr_sigmask,  sr_nop,     9,   0,    BSD, SIGNAL}, /*sigmask*/
+{"bnd",       "BND",     pr_nop,      sr_nop,     1,   0,    AIX, RIGHT},
+{"bsdstart",  "START",   pr_bsdstart, sr_nop,     6,   0,    LNX, RIGHT},
+{"bsdtime",   "TIME",    pr_bsdtime,  sr_nop,     6,   0,    LNX, RIGHT},
+{"c",         "C",       pr_c,        sr_pcpu,    2,   0,    SUN, RIGHT},
+{"caught",    "CAUGHT",  pr_sigcatch, sr_nop,     9,   0,    BSD, SIGNAL}, /*sigcatch*/
+{"class",     "CLS",     pr_nop,      sr_nop,     5,   0,    XXX, RIGHT},
+{"cls",       "-",       pr_nop,      sr_nop,     1,   0,    HPU, RIGHT},
+{"cmaj_flt",  "-",       pr_nop,      sr_cmaj_flt, 1,  0,    LNX, RIGHT},
+{"cmd",       "CMD",     pr_args,     sr_cmd,    16,   0,    DEC, UNLIMITED}, /*ucomm*/
+{"cmin_flt",  "-",       pr_nop,      sr_cmin_flt, 1,  0,    LNX, RIGHT},
+{"cnswap",    "-",       pr_nop,      sr_cnswap,  1,   0,    LNX, RIGHT},
+{"comm",      "COMMAND", pr_comm,     sr_nop,    16,   0,    U98, UNLIMITED}, /*ucomm*/
+{"command",   "COMMAND", pr_args,     sr_nop,    16,   0,    XXX, UNLIMITED}, /*args*/
+{"cp",        "CP",      pr_cp,       sr_pcpu,    3,   0,    DEC, RIGHT}, /*cpu*/
+{"cpu",       "CPU",     pr_nop,      sr_nop,     3,   0,    BSD, RIGHT}, /* FIXME ... HP-UX wants this as the CPU number for SMP? */
+{"cputime",   "TIME",    pr_time,     sr_nop,     8,   0,    DEC, RIGHT}, /*time*/
+{"cstime",    "-",       pr_nop,      sr_cstime,  1,   0,    LNX, RIGHT},
+{"cursig",    "CURSIG",  pr_nop,      sr_nop,     6,   0,    DEC, RIGHT},
+{"cutime",    "-",       pr_nop,      sr_cutime,  1,   0,    LNX, RIGHT},
+{"cwd",       "CWD",     pr_nop,      sr_nop,     3,   0,    LNX, LEFT},
+{"drs",       "DRS",     pr_drs,      sr_drs,     4,   0,    LNX, RIGHT},
+{"dsiz",      "DSIZ",    pr_dsiz,     sr_nop,     4,   0,    LNX, RIGHT},
+{"egid",      "EGID",    pr_egid,     sr_egid,    5,   0,    LNX, RIGHT},
+{"egroup",    "EGROUP",  pr_egroup,   sr_egroup,  8,   0,    LNX, USER},
+{"eip",       "EIP",     pr_eip,      sr_kstk_eip, 8,  0,    LNX, RIGHT},
+{"end_code",  "E_CODE",  pr_nop,      sr_end_code, 8,  0,    LNx, RIGHT},
+{"environ","ENVIRONMENT",pr_nop,      sr_nop,    11,   0,    LNx, UNLIMITED},
+{"esp",       "ESP",     pr_esp,      sr_kstk_esp, 8,  0,    LNX, RIGHT},
+{"etime",     "ELAPSED", pr_etime,    sr_nop,    11,   0,    U98, RIGHT}, /* was 7 wide */
+{"euid",      "EUID",    pr_euid,     sr_euid,    5,   0,    LNX, RIGHT},
+{"euser",     "EUSER",   pr_euser,    sr_euser,   8,   0,    LNX, USER},
+{"f",         "F",       pr_flag,     sr_nop,     3,   0,    XXX, RIGHT}, /*flags*/
+{"fgid",      "FGID",    pr_fgid,     sr_fgid,    5,   0,    LNX, RIGHT},
+{"fgroup",    "FGROUP",  pr_fgroup,   sr_fgroup,  8,   0,    LNX, USER},
+{"flag",      "F",       pr_flag,     sr_flags,   3,   0,    DEC, RIGHT},
+{"flags",     "F",       pr_flag,     sr_flags,   3,   0,    BSD, RIGHT}, /*f*/ /* was FLAGS, 8 wide */
+{"fname",     "COMMAND", pr_fname,    sr_nop,     8,   0,    SUN, LEFT},
+{"fsgid",     "FSGID",   pr_fgid,     sr_fgid,    5,   0,    LNX, RIGHT},
+{"fsgroup",   "FSGROUP", pr_fgroup,   sr_fgroup,  8,   0,    LNX, USER},
+{"fsuid",     "FSUID",   pr_fuid,     sr_fuid,    5,   0,    LNX, RIGHT},
+{"fsuser",    "FSUSER",  pr_fuser,    sr_fuser,   8,   0,    LNX, USER},
+{"fuid",      "FUID",    pr_fuid,     sr_fuid,    5,   0,    LNX, RIGHT},
+{"fuser",     "FUSER",   pr_fuser,    sr_fuser,   8,   0,    LNX, USER},
+{"gid",       "GID",     pr_egid,     sr_egid,    5,   0,    SUN, RIGHT},
+{"group",     "GROUP",   pr_egroup,   sr_egroup,  5,   0,    U98, USER}, /* was 8 wide */
+{"ignored",   "IGNORED", pr_sigignore,sr_nop,     9,   0,    BSD, SIGNAL}, /*sigignore*/
+{"inblk",     "INBLK",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT}, /*inblock*/
+{"inblock",   "INBLK",   pr_nop,      sr_nop,     5,   0,    DEC, RIGHT}, /*inblk*/
+{"intpri",    "PRI",     pr_opri,     sr_priority, 3,  0,    HPU, RIGHT},
+{"jobc",      "JOBC",    pr_nop,      sr_nop,     4,   0,    XXX, RIGHT},
+{"ktrace",    "KTRACE",  pr_nop,      sr_nop,     8,   0,    BSD, RIGHT},
+{"ktracep",   "KTRACEP", pr_nop,      sr_nop,     8,   0,    BSD, RIGHT},
+{"label",     "LABEL",   pr_nop,      sr_nop,    25,  0,     SGI, LEFT},
+{"lim",       "LIM",     pr_lim,      sr_rss_rlim, 5,  0,    BSD, RIGHT},
+{"login",     "LOGNAME", pr_nop,      sr_nop,     8,   0,    BSD, LEFT}, /*logname*/   /* double check */
+{"logname",   "LOGNAME", pr_nop,      sr_nop,     8,   0,    XXX, LEFT}, /*login*/
+{"longtname", "TTY",     pr_tty8,     sr_tty,     8,   0,    DEC, LEFT},
+{"lstart",    "STARTED", pr_lstart,   sr_nop,    24,   0,    XXX, RIGHT},
+{"luid",      "LUID",    pr_nop,      sr_nop,     5,   0,    LNX, RIGHT}, /* login ID */
+{"luser",     "LUSER",   pr_nop,      sr_nop,     8,   0,    LNX, USER}, /* login USER */
+{"lwp",       "LWP",     pr_thread,   sr_nop,     5,   0,    SUN, RIGHT},
+{"m_drs",     "DRS",     pr_drs,      sr_drs,     5,   0,    LNx, RIGHT},
+{"m_dt",      "DT",      pr_nop,      sr_dt,      4,   0,    LNx, RIGHT},
+{"m_lrs",     "LRS",     pr_nop,      sr_lrs,     5,   0,    LNx, RIGHT},
+{"m_resident", "RES",    pr_nop,      sr_resident, 5,  0,    LNx, RIGHT},
+{"m_share",   "SHRD",    pr_nop,      sr_share,   5,   0,    LNx, RIGHT},
+{"m_size",    "SIZE",    pr_nop,      sr_size,    5,   0,    LNx, RIGHT},
+{"m_swap",    "SWAP",    pr_nop,      sr_nop,     5,   0,    LNx, RIGHT},
+{"m_trs",     "TRS",     pr_trs,      sr_trs,     5,   0,    LNx, RIGHT},
+{"maj_flt",   "MAJFL",   pr_majflt,   sr_maj_flt, 6,   0,    LNX, CUMUL|RIGHT},
+{"majflt",    "MAJFLT",  pr_majflt,   sr_maj_flt, 6,   0,    XXX, RIGHT},
+{"min_flt",   "MINFL",   pr_minflt,   sr_min_flt, 6,   0,    LNX, CUMUL|RIGHT},
+{"minflt",    "MINFLT",  pr_minflt,   sr_min_flt, 6,   0,    XXX, RIGHT},
+{"msgrcv",    "MSGRCV",  pr_nop,      sr_nop,     6,   0,    XXX, RIGHT},
+{"msgsnd",    "MSGSND",  pr_nop,      sr_nop,     6,   0,    XXX, RIGHT},
+{"ni",        "NI",      pr_nice,     sr_nice,    3,   0,    BSD, RIGHT}, /*nice*/
+{"nice",      "NI",      pr_nice,     sr_nice,    3,   0,    U98, RIGHT}, /*ni*/
+{"nivcsw",    "IVCSW",   pr_nop,      sr_nop,     5,   0,    XXX, RIGHT},
+{"nlwp",      "NLWP",    pr_nlwp,     sr_nop,     4,   0,    SUN, RIGHT},
+{"nsignals",  "NSIGS",   pr_nop,      sr_nop,     5,   0,    DEC, RIGHT}, /*nsigs*/
+{"nsigs",     "NSIGS",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT}, /*nsignals*/
+{"nswap",     "NSWAP",   pr_nop,      sr_nswap,   5,   0,    XXX, RIGHT},
+{"nvcsw",     "VCSW",    pr_nop,      sr_nop,     5,   0,    XXX, RIGHT},
+{"nwchan",    "WCHAN",   pr_nwchan,   sr_nop,     6,   0,    XXX, RIGHT},
+{"opri",      "PRI",     pr_opri,     sr_priority, 3,  0,    SUN, RIGHT},
+{"osz",       "SZ",      pr_nop,      sr_nop,     2,   0,    SUN, RIGHT},
+{"oublk",     "OUBLK",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT}, /*oublock*/
+{"oublock",   "OUBLK",   pr_nop,      sr_nop,     5,   0,    DEC, RIGHT}, /*oublk*/
+{"p_ru",      "P_RU",    pr_nop,      sr_nop,     6,   0,    BSD, RIGHT},
+{"paddr",     "PADDR",   pr_nop,      sr_nop,     6,   0,    BSD, RIGHT},
+{"pagein",    "PAGEIN",  pr_majflt,   sr_nop,     6,   0,    XXX, RIGHT},
+{"pcpu",      "%CPU",    pr_pcpu,     sr_pcpu,    4,   0,    U98, RIGHT}, /*%cpu*/
+{"pending",   "PENDING", pr_sig,      sr_nop,     9,   0,    BSD, SIGNAL}, /*sig*/
+{"pgid",      "PGID",    pr_pgid,     sr_pgrp,    5,   0,    U98, RIGHT},
+{"pgrp",      "PGRP",    pr_pgid,     sr_pgrp,    5,   0,    LNX, RIGHT},
+{"pid",       "PID",     pr_pid,      sr_pid,     5,   0,    U98, RIGHT},
+{"pmem",      "%MEM",    pr_pmem,     sr_nop,     4,   0,    XXX, RIGHT}, /*%mem*/
+{"poip",      "-",       pr_nop,      sr_nop,     1,   0,    BSD, RIGHT},
+{"policy",    "POL",     pr_nop,      sr_nop,     3,   0,    DEC, RIGHT},
+{"ppid",      "PPID",    pr_ppid,     sr_ppid,    5,   0,    U98, RIGHT},
+{"pri",       "PRI",     pr_pri,      sr_nop,     3,   0,    XXX, RIGHT},
+{"priority",  "PRI",     pr_priority, sr_priority, 3,  0,    LNX, RIGHT}, /*ni,nice*/ /* from Linux sorting names */
+{"prmgrp",    "-",       pr_nop,      sr_nop,     1,   0,    HPU, RIGHT},
+{"prmid",     "-",       pr_nop,      sr_nop,     1,   0,    HPU, RIGHT},
+{"pset",      "PSET",    pr_nop,      sr_nop,     4,   0,    DEC, RIGHT},
+{"psr",       "PSR",     pr_psr,      sr_nop,     3,   0,    DEC, RIGHT},
+{"psxpri",    "PPR",     pr_nop,      sr_nop,     3,   0,    DEC, RIGHT},
+{"re",        "RE",      pr_nop,      sr_nop,     3,   0,    BSD, RIGHT},
+{"resident",  "RES",     pr_nop,      sr_resident, 5,  0,    LNX, RIGHT},
+{"rgid",      "RGID",    pr_rgid,     sr_rgid,    5,   0,    XXX, RIGHT},
+{"rgroup",    "RGROUP",  pr_rgroup,   sr_rgroup,  6,   0,    U98, USER}, /* was 8 wide */
+{"rlink",     "RLINK",   pr_nop,      sr_nop,     8,   0,    BSD, RIGHT},
+{"rss",       "RSS",     pr_rss,      sr_rss,     4,   0,    XXX, RIGHT}, /* was 5 wide */
+{"rssize",    "RSS",     pr_rss,      sr_vm_rss,  4,   0,    DEC, RIGHT}, /*rsz*/
+{"rsz",       "RSZ",     pr_rss,      sr_vm_rss,  4,   0,    BSD, RIGHT}, /*rssize*/
+{"rtprio",    "RTPRIO",  pr_nop,      sr_nop,     7,   0,    BSD, RIGHT},
+{"ruid",      "RUID",    pr_ruid,     sr_ruid,    5,   0,    XXX, RIGHT},
+{"ruser",     "RUSER",   pr_ruser,    sr_ruser,   8,   0,    U98, USER},
+{"s",         "S",       pr_s,        sr_state,   1,   0,    SUN, LEFT}, /*stat,state*/
+{"sched",     "SCH",     pr_nop,      sr_nop,     1,   0,    AIX, RIGHT},
+{"scnt",      "SCNT",    pr_nop,      sr_nop,     4,   0,    DEC, RIGHT},  /* man page misspelling of scount? */
+{"scount",    "SC",      pr_nop,      sr_nop,     4,   0,    AIX, RIGHT},  /* scnt==scount, DEC claims both */
+{"sess",      "SESS",    pr_sess,     sr_session, 5,   0,    XXX, RIGHT},
+{"session",   "SESS",    pr_sess,     sr_session, 5,   0,    LNX, RIGHT},
+{"sgi_p",     "P",       pr_sgi_p,    sr_nop,     1,   0,    LNX, RIGHT}, /* "cpu" number */
+{"sgi_rss",   "RSS",     pr_rss,      sr_nop,     4,   0,    LNX, LEFT}, /* SZ:RSS */
+{"sgid",      "SGID",    pr_sgid,     sr_sgid,    5,   0,    LNX, RIGHT},
+{"sgroup",    "SGROUP",  pr_sgroup,   sr_sgroup,  8,   0,    LNX, USER},
+{"share",     "-",       pr_nop,      sr_share,   1,   0,    LNX, RIGHT},
+{"sid",       "SID",     pr_sess,     sr_session, 5,   0,    XXX, RIGHT}, /* Sun & HP */
+{"sig",       "PENDING", pr_sig,      sr_nop,     9,   0,    XXX, SIGNAL}, /*pending*/
+{"sig_block", "BLOCKED",  pr_sigmask, sr_nop,     9,   0,    LNX, SIGNAL},
+{"sig_catch", "CATCHED", pr_sigcatch, sr_nop,     9,   0,    LNX, SIGNAL},
+{"sig_ignore", "IGNORED",pr_sigignore, sr_nop,    9,   0,    LNX, SIGNAL},
+{"sig_pend",  "SIGNAL",   pr_sig,     sr_nop,     9,   0,    LNX, SIGNAL},
+{"sigcatch",  "CAUGHT",  pr_sigcatch, sr_nop,     9,   0,    XXX, SIGNAL}, /*caught*/
+{"sigignore", "IGNORED", pr_sigignore,sr_nop,     9,   0,    XXX, SIGNAL}, /*ignored*/
+{"sigmask",   "BLOCKED", pr_sigmask,  sr_nop,     9,   0,    XXX, SIGNAL}, /*blocked*/
+{"size",      "-",       pr_nop,      sr_size,    1,   0,    SCO, RIGHT},
+{"sl",        "SL",      pr_nop,      sr_nop,     3,   0,    XXX, RIGHT},
+{"spid",      "SPID",    pr_thread,   sr_nop,     5,   0,    SGI, RIGHT},
+{"stackp",    "STACKP",  pr_stackp,   sr_nop,     8,   0,    LNX, RIGHT}, /*start_stack*/
+{"start",     "STARTED", pr_start,    sr_nop,     8,   0,    XXX, RIGHT},
+{"start_code", "S_CODE",  pr_nop,     sr_start_code, 8, 0,   LNx, RIGHT},
+{"start_stack", "STACKP", pr_stackp,  sr_start_stack, 8, 0,  LNX, RIGHT}, /*stackp*/
+{"start_time", "START",  pr_stime,    sr_start_time, 5, 0,   LNx, RIGHT},
+{"stat",      "STAT",    pr_stat,     sr_state,   4,   0,    BSD, LEFT}, /*state,s*/
+{"state",     "S",       pr_s,        sr_state,   1,   0,    XXX, LEFT}, /*stat,s*/ /* was STAT */
+{"status",    "STATUS",  pr_nop,      sr_nop,     6,   0,    DEC, RIGHT},
+{"stime",     "STIME",   pr_stime,    sr_stime,   5,   0,    XXX, /* CUMUL| */RIGHT}, /* was 6 wide */
+{"suid",      "SUID",    pr_suid,     sr_suid,    5,   0,    LNx, RIGHT},
+{"suser",     "SUSER",   pr_suser,    sr_suser,   8,   0,    LNx, USER},
+{"svgid",     "SVGID",   pr_sgid,     sr_sgid,    5,   0,    XXX, RIGHT},
+{"svgroup",   "SVGROUP", pr_sgroup,   sr_sgroup,  8,   0,    LNX, USER},
+{"svuid",     "SVUID",   pr_suid,     sr_suid,    5,   0,    XXX, RIGHT},
+{"svuser",    "SVUSER",  pr_suser,    sr_suser,   8,   0,    LNX, USER},
+{"systime",   "SYSTEM",  pr_nop,      sr_nop,     6,   0,    DEC, RIGHT},
+{"sz",        "SZ",      pr_sz,       sr_nop,     5,   0,    HPU, RIGHT},
+{"tdev",      "TDEV",    pr_nop,      sr_nop,     4,   0,    XXX, RIGHT},
+{"thcount",   "THCNT",   pr_nlwp,     sr_nop,     5,   0,    AIX, RIGHT},
+{"tid",       "TID",     pr_thread,   sr_nop,     5,   0,    AIX, RIGHT},
+{"time",      "TIME",    pr_time,     sr_nop,     8,   0,    U98, CUMUL|RIGHT}, /*cputime*/ /* was 6 wide */
+{"timeout",   "TMOUT",   pr_timeout,  sr_timeout, 5,   0,    LNX, RIGHT},
+{"tmout",     "TMOUT",   pr_timeout,  sr_timeout, 5,   0,    LNX, RIGHT},
+{"tname",     "TTY",     pr_tty8,     sr_tty,     8,   0,    DEC, LEFT},
+{"tpgid",     "TPGID",   pr_tpgid,    sr_tpgid,   5,   0,    XXX, RIGHT},
+{"trs",       "TRS",     pr_trs,      sr_trs,     4,   0,    AIX, RIGHT},
+{"trss",      "TRSS",    pr_trs,      sr_trs,     4,   0,    BSD, RIGHT}, /* 4.3BSD NET/2 */
+{"tsess",     "TSESS",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT},
+{"tsession",  "TSESS",   pr_nop,      sr_nop,     5,   0,    DEC, RIGHT},
+{"tsiz",      "TSIZ",    pr_tsiz,     sr_nop,     4,   0,    BSD, RIGHT},
+{"tt",        "TT",      pr_tty8,     sr_tty,     8,   0,    BSD, LEFT},
+{"tty",       "TT",      pr_tty8,     sr_tty,     8,   0,    U98, LEFT}, /* Unix98 requires "TT" but has "TTY" too. :-( */  /* was 3 wide */
+{"tty4",      "TTY",     pr_tty4,     sr_tty,     4,   0,    LNX, LEFT},
+{"tty8",      "TTY",     pr_tty8,     sr_tty,     8,   0,    LNX, LEFT},
+{"u_procp",   "UPROCP",  pr_nop,      sr_nop,     6,   0,    DEC, RIGHT},
+{"ucmd",      "CMD",     pr_comm,     sr_cmd,    16,   0,    DEC, UNLIMITED}, /*ucomm*/
+{"ucomm",     "COMMAND", pr_comm,     sr_nop,    16,   0,    XXX, UNLIMITED}, /*comm*/
+{"uid",       "UID",     pr_euid,     sr_euid,    5,   0,    XXX, RIGHT},
+{"uid_hack",  "UID",     pr_euser,    sr_nop,     8,   0,    XXX, USER},
+{"umask",     "UMASK",   pr_nop,      sr_nop,     5,   0,    DEC, RIGHT},
+{"uname",     "USER",    pr_euser,    sr_euser,   8,   0,    DEC, USER}, /* man page misspelling of user? */
+{"upr",       "UPR",     pr_nop,      sr_nop,     3,   0,    BSD, RIGHT}, /*usrpri*/
+{"uprocp",    "-",       pr_nop,      sr_nop,     1,   0,    BSD, RIGHT},
+{"user",      "USER",    pr_euser,    sr_euser,   8,   0,    U98, USER}, /* BSD n forces this to UID */
+{"usertime",  "USER",    pr_nop,      sr_nop,     4,   0,    DEC, RIGHT},
+{"usrpri",    "UPR",     pr_nop,      sr_nop,     3,   0,    DEC, RIGHT}, /*upr*/
+{"utime",     "UTIME",   pr_nop,      sr_utime,   6,   0,    LNx, CUMUL|RIGHT},
+{"vm_data",   "DATA",    pr_nop,      sr_vm_data, 5,   0,    LNx, RIGHT},
+{"vm_exe",    "EXE",     pr_nop,      sr_vm_exe,  5,   0,    LNx, RIGHT},
+{"vm_lib",    "LIB",     pr_nop,      sr_vm_lib,  5,   0,    LNx, RIGHT},
+{"vm_lock",   "LCK",     pr_nop,      sr_vm_lock, 3,   0,    LNx, RIGHT},
+{"vm_stack",  "STACK",   pr_nop,      sr_vm_stack, 5,  0,    LNx, RIGHT},
+{"vsize",     "VSZ",     pr_vsz,      sr_vsize,   5,   0,    DEC, RIGHT}, /*vsz*/
+{"vsz",       "VSZ",     pr_vsz,      sr_vm_size, 5,   0,    U98, RIGHT}, /*vsize*/
+{"wchan",     "WCHAN",   pr_wchan,    sr_wchan,   6,   0,    XXX, WCHAN}, /* BSD n forces this to nwchan */ /* was 10 wide */
+{"wname",     "WCHAN",   pr_wname,    sr_nop,     6,   0,    SGI, WCHAN}, /* opposite of nwchan */
+{"xstat",     "XSTAT",   pr_nop,      sr_nop,     5,   0,    BSD, RIGHT},
+{"~",         "-",       pr_nop,      sr_nop,     1,   0,    LNX, RIGHT}  /* NULL would ruin alphabetical order */
+};
+
+static const int format_array_count = sizeof(format_array)/sizeof(format_struct);
+
+
+/****************************** Macro formats *******************************/
+/* First X field may be NR, which is p->start_code>>26 printed with %2ld */
+/* That seems useless though, and Debian already killed it. */
+/* The ones marked "Digital" have the name defined, not just the data. */
+static const macro_struct macro_array[] = {
+{"DFMT",     "pid,tname,state,cputime,cmd"},         /* Digital's default */
+{"DefBSD",   "pid,tname,stat,bsdtime,args"},               /* Our BSD default */
+{"DefSysV",  "pid,tname,time,cmd"},                     /* Our SysV default */
+{"END_BSD",  "state,tname,cputime,comm"},                 /* trailer for O */
+{"END_SYS5", "state,tname,time,command"},                 /* trailer for -O */
+{"F5FMT",    "uname,pid,ppid,c,start,tname,time,cmd"},       /* Digital -f */
+
+{"FB_",      "pid,tt,stat,time,command"},                          /* FreeBSD default */
+{"FB_j",     "user,pid,ppid,pgid,sess,jobc,stat,tt,time,command"},     /* FreeBSD j */
+{"FB_l",     "uid,pid,ppid,cpu,pri,nice,vsz,rss,wchan,stat,tt,time,command"},   /* FreeBSD l */
+{"FB_u",     "user,pid,pcpu,pmem,vsz,rss,tt,stat,start,time,command"},     /* FreeBSD u */
+{"FB_v",     "pid,stat,time,sl,re,pagein,vsz,rss,lim,tsiz,pcpu,pmem,command"},   /* FreeBSD v */
+
+{"FD_",      "pid,tty,time,comm"},                                 /* Fictional Debian SysV default */
+{"FD_f",     "user,pid,ppid,start_time,tty,time,comm"},                /* Fictional Debian -f */
+{"FD_fj",    "user,pid,ppid,start_time,tty,time,pgid,sid,comm"},        /* Fictional Debian -jf */
+{"FD_j",     "pid,tty,time,pgid,sid,comm"},                                  /* Fictional Debian -j */
+{"FD_l",     "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,comm"},    /* Fictional Debian -l */
+{"FD_lj",    "flags,state,uid,pid,ppid,priority,nice,vsz,wchan,tty,time,pgid,sid,comm"}, /* Fictional Debian -jl */
+
+{"FL5FMT",   "f,state,uid,pid,ppid,pcpu,pri,nice,rss,wchan,start,time,command"},  /* Digital -fl */
+
+{"HP_",      "pid,tty,time,comm"},  /* HP default */
+{"HP_f",     "user,pid,ppid,cpu,stime,tty,time,args"},  /* HP -f */
+{"HP_fl",    "flags,state,user,pid,ppid,cpu,intpri,nice,addr,sz,wchan,stime,tty,time,args"},  /* HP -fl */
+{"HP_l",     "flags,state,uid,pid,ppid,cpu,intpri,nice,addr,sz,wchan,tty,time,comm"},  /* HP -l */
+
+{"J390",     "pid,sid,pgrp,tname,atime,args"},   /* OS/390 -j */
+{"JFMT",     "user,pid,ppid,pgid,sess,jobc,state,tname,cputime,command"},   /* Digital j and -j */
+{"L5FMT",    "f,state,uid,pid,ppid,c,pri,nice,addr,sz,wchan,tt,time,ucmd"},   /* Digital -l */
+{"LFMT",     "uid,pid,ppid,cp,pri,nice,vsz,rss,wchan,state,tname,cputime,command"},   /* Digital l */
+
+{"OL_X",     "pid,start_stack,esp,eip,timeout,alarm,stat,tname,bsdtime,args"},      /* Old i386 Linux X */
+{"OL_j",     "ppid,pid,pgid,sid,tname,tpgid,stat,uid,bsdtime,args"},                   /* Old Linux j */
+{"OL_l",     "flags,uid,pid,ppid,priority,nice,vsz,rss,wchan,stat,tname,bsdtime,args"},     /* Old Linux l */
+{"OL_m",     "pid,tname,majflt,minflt,m_trs,m_drs,m_size,m_swap,rss,m_share,vm_lib,m_dt,args"}, /* Old Linux m */
+{"OL_s",     "uid,pid,pending,sig_block,sig_ignore,caught,stat,tname,bsdtime,args"},  /* Old Linux s */
+{"OL_u",     "user,pid,pcpu,pmem,vsz,rss,tname,stat,start_time,bsdtime,args"},       /* Old Linux u */
+{"OL_v",     "pid,tname,stat,bsdtime,maj_flt,m_trs,m_drs,rss,pmem,args"},            /* Old Linux v */
+
+{"RD_",      "pid,tname,state,bsdtime,comm"},                                       /* Real Debian default */
+{"RD_f",     "uid,pid,ppid,start_time,tname,bsdtime,args"},                         /* Real Debian -f */
+{"RD_fj",    "uid,pid,ppid,start_time,tname,bsdtime,pgid,sid,args"},                /* Real Debian -jf */
+{"RD_j",     "pid,tname,state,bsdtime,pgid,sid,comm"},                               /* Real Debian -j */
+{"RD_l",     "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,comm"},           /* Real Debian -l */
+{"RD_lj",    "flags,state,uid,pid,ppid,priority,nice,wchan,tname,bsdtime,pgid,sid,comm"},  /* Real Debian -jl */
+
+{"RUSAGE",   "minflt,majflt,nswap,inblock,oublock,msgsnd,msgrcv,nsigs,nvcsw,nivcsw"}, /* Digital -o "RUSAGE" */
+{"SCHED",    "user,pcpu,pri,usrpri,nice,psxpri,psr,policy,pset"},                /* Digital -o "SCHED" */
+{"SFMT",     "uid,pid,cursig,sig,sigmask,sigignore,sigcatch,stat,tname,command"},  /* Digital s */
+
+{"Std_f",    "uid_hack,pid,ppid,c,stime,tname,time,cmd"},                     /* new -f */
+{"Std_fl",   "f,s,uid_hack,pid,ppid,c,opri,ni,addr,sz,wchan,stime,tname,time,cmd"}, /* -fl */
+{"Std_l",    "f,s,uid,pid,ppid,c,opri,ni,addr,sz,wchan,tname,time,ucmd"},  /* new -l */
+
+{"THREAD",   "user,pcpu,pri,scnt,wchan,usertime,systime"},                /* Digital -o "THREAD" */
+{"UFMT",     "uname,pid,pcpu,pmem,vsz,rss,tt,state,start,time,command"},   /* Digital u */
+{"VFMT",     "pid,tt,state,time,sl,pagein,vsz,rss,pcpu,pmem,command"},   /* Digital v */
+{"~", "~"} /* NULL would ruin alphabetical order */
+};
+
+static const int macro_array_count = sizeof(macro_array)/sizeof(macro_struct);
+
+
+/*************************** AIX formats ********************/
+/* Convert AIX format codes to normal format specifiers. */
+static const aix_struct aix_array[] = {
+{'C', "pcpu",   "%CPU"},
+{'G', "group",  "GROUP"},
+{'P', "ppid",   "PPID"},
+{'U', "user",   "USER"},
+{'a', "args",   "COMMAND"},
+{'c', "comm",   "COMMAND"},
+{'g', "rgroup", "RGROUP"},
+{'n', "nice",   "NI"},
+{'p', "pid",    "PID"},
+{'r', "pgid",   "PGID"},
+{'t', "etime",  "ELAPSED"},
+{'u', "ruser",  "RUSER"},
+{'x', "time",   "TIME"},
+{'y', "tty",    "TTY"},
+{'z', "vsz",    "VSZ"},
+{'~', "~",      "~"} /* NULL would ruin alphabetical order */
+};
+static const int aix_array_count = sizeof(aix_array)/sizeof(aix_struct);
+
+
+/********************* sorting ***************************/
+/* Convert short sorting codes to normal format specifiers. */
+static const shortsort_struct shortsort_array[] = {
+{'C', "pcpu"       },
+{'G', "tpgid"      },
+{'J', "cstime"     },
+/* {'K', "stime"      }, */  /* conflict, system vs. start time */
+{'M', "maj_flt"    },
+{'N', "cmaj_flt"   },
+{'P', "ppid"       },
+{'R', "resident"   },
+{'S', "share"      },
+{'T', "start_time" },
+{'U', "uid"        }, /* euid */
+{'c', "cmd"        },
+{'f', "flags"      },
+{'g', "pgrp"       },
+{'j', "cutime"     },
+{'k', "utime"      },
+{'m', "min_flt"    },
+{'n', "cmin_flt"   },
+{'o', "session"    },
+{'p', "pid"        },
+{'r', "rss"        },
+{'s', "size"       },
+{'t', "tty"        },
+{'u', "user"       },
+{'v', "vsize"      },
+{'y', "priority"   }, /* nice */
+{'~', "~"          } /* NULL would ruin alphabetical order */
+};
+static const int shortsort_array_count = sizeof(shortsort_array)/sizeof(shortsort_struct);
+
+
+/*********** print format_array **********/
+/* called by the parser in another file */
+void print_format_specifiers(void){
+  const format_struct *walk = format_array;
+  while(*(walk->spec) != '~'){
+    if(walk->pr != pr_nop) printf("%-12.12s %-8.8s\n", walk->spec, walk->head);
+    walk++;
+  }
+}
+
+/************ comparison functions for bsearch *************/
+
+static int compare_format_structs(const void *a, const void *b){
+  return strcmp(((format_struct*)a)->spec,((format_struct*)b)->spec);
+}
+
+static int compare_macro_structs(const void *a, const void *b){
+  return strcmp(((macro_struct*)a)->spec,((macro_struct*)b)->spec);
+}
+
+/******** look up structs as needed by the sort & format parsers ******/
+
+const shortsort_struct *search_shortsort_array(const int findme){
+  const shortsort_struct *walk = shortsort_array;
+  while(walk->desc != '~'){
+    if(walk->desc == findme) return walk;
+    walk++;
+  }
+  return NULL;
+}
+
+const aix_struct *search_aix_array(const int findme){
+  const aix_struct *walk = aix_array;
+  while(walk->desc != '~'){
+    if(walk->desc == findme) return walk;
+    walk++;
+  }
+  return NULL;
+}
+
+const format_struct *search_format_array(const char *findme){
+  format_struct key;
+  key.spec = findme;
+  return bsearch(&key, format_array, format_array_count,
+    sizeof(format_struct), compare_format_structs
+  );
+}
+
+const macro_struct *search_macro_array(const char *findme){
+  macro_struct key;
+  key.spec = findme;
+  return bsearch(&key, macro_array, macro_array_count,
+    sizeof(macro_struct), compare_macro_structs
+  );
+}
+
+static unsigned int active_cols;  /* some multiple of screen_cols */
+
+/***** Last chance, avoid needless trunctuation. */
+static void check_header_width(void){
+  format_node *walk = format_list;
+  unsigned int total = 0;
+  int was_normal = 0;
+  unsigned int i = 0;
+  unsigned int sigs = 0;
+  while(walk){
+    switch((walk->flags) & JUST_MASK){
+    default:
+      total += walk->width;
+      total += was_normal;
+      was_normal = 1;
+      break;
+    case SIGNAL:
+      sigs++;
+      total += walk->width;
+      total += was_normal;
+      was_normal = 1;
+      break;
+    case UNLIMITED:  /* could chop this a bit */
+      if(walk->next) total += walk->width;
+      else total += 3; /* not strlen(walk->name) */
+      total += was_normal;
+      was_normal = 1;
+      break;
+    case 0:  /* AIX */
+      total += walk->width;
+      was_normal = 0;
+      break;
+    }
+    walk = walk->next;
+  }
+  for(;;){
+    i++;
+    active_cols = screen_cols * i;
+    if(active_cols>=total) break;
+    if(screen_cols*i >= OUTBUF_SIZE/2) break; /* can't go over */
+  }
+  wide_signals = (total+sigs*7 <= active_cols);
+  
+#if 0
+  printf("123456789-123456789-123456789-123456789-"
+         "123456789-123456789-123456789-123456789\n");
+  printf("need %d, using %d\n", total, active_cols);
+#endif
+}
+
+
+/********** show one process (NULL proc prints header) **********/
+void show_one_proc(proc_t* p){
+  /* unknown: maybe set correct & actual to 1, remove +/- 1 below */
+  int correct  = 0;  /* screen position we should be at */
+  int actual   = 0;  /* screen position we are at */
+  int amount   = 0;  /* amount of text that this data is */
+  int leftpad  = 0;  /* amount of space this column _could_ need */
+  int space    = 0;  /* amount of space we actually need to print */
+  int dospace  = 0;  /* previous column determined that we need a space */
+  int legit    = 0;  /* legitimately stolen extra space */
+  format_node *fmt = format_list;
+  static int did_stuff = 0;  /* have we ever printed anything? */
+
+  if(-1==(long)p){    /* true only once, at the end */
+    check_header_width();  /* temporary test code */
+    if(did_stuff) return;
+    /* have _never_ printed anything, but might need a header */
+    if(!--lines_to_next_header){
+      lines_to_next_header = header_gap;
+      show_one_proc(NULL);
+    }
+    /* fprintf(stderr, "No processes available.\n"); */  /* legal? */
+    exit(1);
+  }
+  if(p){   /* not header, maybe we should call ourselves for it */
+    if(!--lines_to_next_header){
+      lines_to_next_header = header_gap;
+      show_one_proc(NULL);
+    }
+  }
+  did_stuff = 1;
+  pp = p;                 /* global, the proc_t struct */
+  if(active_cols>(int)OUTBUF_SIZE) fprintf(stderr,"Fix bigness error.\n");
+
+  /* print row start sequence */
+  for(;;){
+    legit = 0;
+    /* set width suggestion which might be ignored */
+    if(fmt->next) max_rightward = fmt->width;
+    else max_rightward = active_cols-((correct>actual) ? correct : actual);
+    max_leftward  = fmt->width + actual - correct; /* TODO check this */
+    /* prepare data and calculate leftpad */
+    if(p && fmt->pr) amount = (*fmt->pr)();
+    else amount = strlen(strcpy(outbuf, fmt->name)); /* AIX or headers */
+    switch((fmt->flags) & JUST_MASK){
+    case 0:  /* for AIX, assigned outside this file */
+      leftpad = 0;
+      break;
+    case LEFT:          /* bad */
+      leftpad = 0;
+      break;
+    case RIGHT:     /* OK */
+      leftpad = fmt->width - amount;
+      if(leftpad < 0) leftpad = 0;
+      break;
+    case SIGNAL:
+      /* if the screen is wide enough, use full 16-character output */
+      if(wide_signals){
+        leftpad = 16 - amount;
+        legit = 7;
+      }else{
+        leftpad =  9 - amount;
+      }
+      if(leftpad < 0) leftpad = 0;
+      break;
+    case USER:       /* bad */
+      leftpad = fmt->width - amount;
+      if(leftpad < 0) leftpad = 0;
+      if(!user_is_number) leftpad = 0;
+      break;
+    case WCHAN:       /* bad */
+      if(wchan_is_number){
+        leftpad = fmt->width - amount;
+        if(leftpad < 0) leftpad = 0;
+        break;
+      }else{
+        if(fmt->next){
+          outbuf[fmt->width] = '\0';  /* Must chop, more columns! */
+        }else{
+          int chopspot;  /* place to chop */
+          int tmpspace;  /* need "space" before it is calculated below */
+          tmpspace = correct - actual;
+          if(tmpspace<1) tmpspace = dospace;
+          chopspot = active_cols-actual-tmpspace;
+          if(chopspot<1) chopspot=1;  /* oops, we (mostly) lose this column... */
+          outbuf[chopspot] = '\0';    /* chop at screen/buffer limit */
+        }
+        leftpad = 0;
+        break;
+      }
+    case UNLIMITED:
+      if(fmt->next){
+        outbuf[fmt->width] = '\0';  /* Must chop, more columns! */
+      }else{
+        int chopspot;  /* place to chop */
+        int tmpspace;  /* need "space" before it is calculated below */
+        tmpspace = correct - actual;
+        if(tmpspace<1) tmpspace = dospace;
+        chopspot = active_cols-actual-tmpspace;
+        if(chopspot<1) chopspot=1;  /* oops, we (mostly) lose this column... */
+        outbuf[chopspot] = '\0';    /* chop at screen/buffer limit */
+      }
+      leftpad = 0;
+      break;
+    default:
+      fputs("bad alignment code\n", stderr);
+      break;
+    }
+    /* At this point:
+     *
+     * correct   from previous column
+     * actual    from previous column
+     * amount    not needed (garbage due to chopping)
+     * leftpad   left padding for this column alone (not make-up or gap)
+     * space     not needed (will recalculate now)
+     * dospace   if we require space between this and the prior column
+     * legit     space we were allowed to steal, and thus did steal
+     */
+    space = correct - actual + leftpad;
+    if(space<1) space=dospace;
+    if(space>SPACE_AMOUNT) space=SPACE_AMOUNT;
+
+    /* print data, set x position stuff */
+    amount = strlen(outbuf);  /* post-chop data width */
+    if(!fmt->next){
+      /* Last column. Write padding + data + newline all together. */
+      outbuf[amount] = '\n';
+      fwrite(outbuf-space, space+amount+1, 1, stdout);
+      break;
+    }
+    /* Not the last column. Write padding + data together. */
+    fwrite(outbuf-space, space+amount, 1, stdout);
+    actual  += space+amount;
+    correct += fmt->width;
+    correct += legit;        /* adjust for SIGNAL expansion */
+    if(fmt->pr && fmt->next->pr){ /* neither is AIX filler */
+      correct++;
+      dospace = 1;
+    }else{
+      dospace = 0;
+    }
+    fmt = fmt->next;
+    /* At this point:
+     *
+     * correct   screen position we should be at
+     * actual    screen position we are at
+     * amount    not needed
+     * leftpad   not needed
+     * space     not needed
+     * dospace   if have determined that we need a space next time
+     * legit     not needed
+     */
+  }
+}
+
+#ifdef TESTING
+static void sanity_check(void){
+  format_struct *fs = format_array;
+  while((fs->spec)[0] != '~'){
+    if(strlen(fs->head) > fs->width) printf("%d %s\n",strlen(fs->head),fs->spec);
+    fs++;
+  }
+}
+#endif
+
+void init_output(void){
+  memset(whitespace, ' ', PAGE_SIZE);
+#if 0
+  mprotect(whitespace, PAGE_SIZE, PROT_READ); /* FIXME may fail if unaligned */
+  mprotect(
+    (void *)((unsigned long)(whitespace_and_outbuf-PAGE_SIZE) &~ (PAGE_SIZE-1)),
+    PAGE_SIZE, PROT_NONE
+  );
+#endif
+  seconds_since_1970 = time(NULL);
+  time_of_boot = seconds_since_1970 - seconds_since_boot;
+  meminfo();
+  switch(getpagesize()){
+  case 65536: page_shift = 16; break;
+  case 32768: page_shift = 15; break;
+  case 16384: page_shift = 14; break;
+  case  8192: page_shift = 13; break;
+  default: fprintf(stderr, "Unknown page size! (assume 4096)\n");
+  case  4096: page_shift = 12; break;
+  case  2048: page_shift = 11; break;
+  case  1024: page_shift = 10; break;
+  }
+  check_header_width();
+}
diff --git a/ps/p b/ps/p
new file mode 100755 (executable)
index 0000000..880df4a
--- /dev/null
+++ b/ps/p
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Wow, using $* causes great pain with:   ps "pid,user pcpu,pmem"
+# The "$@" won't break that into 2 arguments.
+#
+LD_PRELOAD=../proc/libproc.so exec ./ps "$@"
diff --git a/ps/parser.c b/ps/parser.c
new file mode 100644 (file)
index 0000000..4ecf0e8
--- /dev/null
@@ -0,0 +1,1123 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights reserved.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+
+/* Ought to have debug print stuff like this:
+ * #define Print(fmt, args...) printf("Debug: " fmt, ## args)
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "../proc/version.h"
+
+#define ARG_GNU  0
+#define ARG_END  1
+#define ARG_PGRP 2
+#define ARG_SYSV 3
+#define ARG_PID  4
+#define ARG_BSD  5
+#define ARG_FAIL 6
+#define ARG_SESS 7
+
+static int w_count = 0;
+
+static int ps_argc;    /* global argc */
+static char **ps_argv; /* global argv */
+static int thisarg;    /* index into ps_argv */
+static char *flagptr;  /* current location in ps_argv[thisarg] */
+static int not_pure_unix = 0;  /* set by BSD and GNU options */
+static int force_bsd = 0;  /* set when normal parsing fails */
+
+#define exclusive(x) if((ps_argc != 2) || strcmp(ps_argv[1],x))\
+  return "The " x " option is exclusive."
+
+
+/********** utility functions **********/
+
+/*
+ * Both "-Oppid" and "-O ppid" should be legal, though Unix98
+ * does not require it. BSD and Digital Unix allow both.
+ * Return the argument or NULL;
+ */
+static const char *get_opt_arg(void){
+  if(*(flagptr+1)){     /* argument is part of ps_argv[thisarg] */
+    not_pure_unix = 1;
+    return flagptr+1;
+  }
+  if(thisarg+2 > ps_argc) return NULL;   /* there is nothing left */
+  /* argument follows ps_argv[thisarg] */
+  if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+  return ps_argv[++thisarg];
+}
+
+/********** parse lists (of UID, tty, GID, PID...) **********/
+
+static const char *parse_pid(char *str, sel_union *ret){
+  char *endp;
+  int num;
+  static const char *pidrange  = "Process ID out of range.";
+  static const char *pidsyntax = "Process ID list syntax error.";
+  num = strtol(str, &endp, 0);
+  if(*endp != '\0')   return pidsyntax;
+  if(num>0x7fff)      return pidrange;  /* Linux PID limit */
+  if(num<1)           return pidrange;
+  ret->pid = num;
+  return 0;
+}
+
+static const char *parse_uid(char *str, sel_union *ret){
+  struct passwd *passwd_data;
+  char *endp;
+  unsigned long num;
+  static const char *uidrange = "User ID out of range.";
+  static const char *uidexist = "User name does not exist.";
+  num = strtoul(str, &endp, 0);
+  if(*endp != '\0'){  /* hmmm, try as login name */
+    passwd_data = getpwnam(str);
+    if(!passwd_data)    return uidexist;
+    num = passwd_data->pw_uid;
+  }
+  if(num > 0xfffffffeUL) return uidrange;
+  ret->uid = num;
+  return 0;
+}
+
+static const char *parse_gid(char *str, sel_union *ret){
+  struct group *group_data;
+  char *endp;
+  unsigned long num;
+  static const char *gidrange = "Group ID out of range.";
+  static const char *gidexist = "Group name does not exist.";
+  num = strtoul(str, &endp, 0);
+  if(*endp != '\0'){  /* hmmm, try as login name */
+    group_data = getgrnam(str);
+    if(!group_data)    return gidexist;
+    num = group_data->gr_gid;
+  }
+  if(num > 0xfffffffeUL) return gidrange;
+  ret->gid = num;
+  return 0;
+}
+
+static const char *parse_cmd(char *str, sel_union *ret){
+  strncpy(ret->cmd, str, 8);  /* strncpy pads to end */
+  return 0;
+}
+
+static const char *parse_tty(char *str, sel_union *ret){
+  struct stat sbuf;
+  static const char *missing = "TTY could not be found.";
+  static const char *not_tty = "List member was not a TTY.";
+  char path[4096];
+  if(str[0]=='/'){
+    if(stat(str, &sbuf) >= 0) goto found_it;
+    return missing;
+  }
+#define lookup(p) \
+  snprintf(path,4096,p,str); \
+  if(stat(path, &sbuf) >= 0) goto found_it
+
+  lookup("/dev/pts/%s");  /* New Unix98 ptys go first */
+  lookup("/dev/%s");
+  lookup("/dev/tty%s");
+  lookup("/dev/pty%s");
+  lookup("/dev/%snsole"); /* "co" means "console", maybe do all VCs too? */
+  if(!strcmp(str,"-")){   /* "-" means no tty (from AIX) */
+    ret->tty = -1;  /* processes w/o tty */
+    return 0;
+  }
+  if(!strcmp(str,"?")){   /* "?" means no tty, which bash eats (Reno BSD?) */
+    ret->tty = -1;  /* processes w/o tty */
+    return 0;
+  }
+  if(!*(str+1) && (stat(str,&sbuf)>=0)){  /* Kludge! Assume bash ate '?'. */
+    ret->tty = -1;  /* processes w/o tty */
+    return 0;
+  }
+#undef lookup
+  return missing;
+found_it:
+  if(!S_ISCHR(sbuf.st_mode)) return not_tty;
+  ret->tty = sbuf.st_rdev;
+  return 0;
+}
+
+/*
+ * Used to parse lists in a generic way. (function pointers)
+ */
+static const char *parse_list(const char *arg, const char *(*parse_fn)(char *, sel_union *) ){
+  selection_node *node;
+  char *buf;                      /* temp copy of arg to hack on */
+  char *sep_loc;                  /* separator location: " \t," */
+  char *walk;
+  int items;
+  int need_item;
+  const char *err;       /* error code that could or did happen */
+  /*** prepare to operate ***/
+  node = malloc(sizeof(selection_node));
+  node->u = malloc(strlen(arg)*sizeof(sel_union)); /* waste is insignificant */
+  node->n = 0;
+  buf = malloc(strlen(arg)+1);
+  strcpy(buf, arg);
+  /*** sanity check and count items ***/
+  need_item = 1; /* true */
+  items = 0;
+  walk = buf;
+  err = "Improper list.";
+  do{
+    switch(*walk){
+    case ' ': case ',': case '\t': case '\0':
+      if(need_item) goto parse_error;
+      need_item=1;
+      break;
+    default:
+      if(need_item) items++;
+      need_item=0;
+    }
+  } while (*++walk);
+  if(need_item) goto parse_error;
+  node->n = items;
+  /*** actually parse the list ***/
+  walk = buf;
+  while(items--){
+    sep_loc = strpbrk(walk," ,\t");
+    if(sep_loc) *sep_loc = '\0';
+    if(( err=(parse_fn)(walk, node->u+items) )) goto parse_error;
+    walk = sep_loc + 1; /* point to next item, if any */
+  }
+  free(buf);
+  node->next = selection_list;
+  selection_list = node;
+  return NULL;
+parse_error:
+  free(buf);
+  free(node->u);
+  free(node);
+  return err;
+}
+
+/***************** parse SysV options, including Unix98  *****************/
+static const char *parse_sysv_option(void){
+  const char *arg;
+  const char *err;
+  flagptr = ps_argv[thisarg];
+  while(*++flagptr){
+    /* Find any excuse to ignore stupid Unix98 misfeatures. */
+    if(!strchr("aAdefgGlnoptuU", *flagptr)) not_pure_unix = 1;
+    switch(*flagptr){
+    case 'A':
+      trace("-A selects all processes.\n");
+      all_processes = 1;
+      break;
+    case 'C': /* end */
+      trace("-C select by process name.\n");  /* Why only HP/UX and us? */
+      arg=get_opt_arg();
+      if(!arg) return "List of command names must follow -C.";
+      err=parse_list(arg, parse_cmd);
+      if(err) return err;
+      selection_list->typecode = SEL_COMM;
+      return NULL; /* can't have any more options */
+    case 'F':  /* DYNIX/ptx -f plus sz,rss,psr=ENG between c and stime */
+      trace("-F does fuller listing\n");
+      format_modifiers |= FM_F;
+      format_flags |= FF_Uf;
+      unix_f_option = 1; /* does this matter? */
+      break;
+    case 'G': /* end */
+      trace("-G select by RGID (supports names)\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of real groups must follow -G.";
+      err=parse_list(arg, parse_gid);
+      if(err) return err;
+      selection_list->typecode = SEL_RGID;
+      return NULL; /* can't have any more options */
+    case 'H':     /* another nice HP/UX feature */
+      trace("-H Process heirarchy (like ASCII art forest option)\n");
+      forest_type = 'u';
+      break;
+    case 'L':  /*  */
+      /* In spite of the insane 2-level thread system, Sun appears to
+       * have made this option Linux-compatible. If a process has N
+       * threads, ps will produce N lines of output. (not N+1 lines)
+       * Zombies are the only exception, with NLWP==0 and 1 output line.
+       * SCO UnixWare uses -L too.
+       */
+      trace("-L Print LWP (thread) info.\n");
+      format_modifiers |= FM_L;
+      break;
+    case 'M':  /* someday, maybe, we will have MAC like SGI's Irix */
+      trace("-M Print security label for Mandatory Access Control.\n");
+      format_modifiers |= FM_M;
+      return "Sorry, no Mandatory Access Control support.";
+      break;
+    case 'N':
+      trace("-N negates.\n");
+      negate_selection = 1;
+      break;
+    case 'O': /* end */
+      trace("-O is preloaded -o.\n");
+      arg=get_opt_arg();
+      if(!arg) return "Format or sort specification must follow -O.";
+      defer_sf_option(arg, SF_U_O);
+      return NULL; /* can't have any more options */
+    case 'P':     /* SunOS 5 "psr" or unknown HP/UX feature */
+      trace("-P adds columns of PRM info (HP) or PSR column (Sun)\n");
+      format_modifiers |= FM_P;
+      break;
+#ifdef WE_UNDERSTAND_THIS
+    case 'R':     /* unknown HP/UX feature */
+      trace("-R selects PRM groups\n");
+      return "Don't understand PRM on Linux.";
+      break;
+#endif
+    case 'T':
+      /* IRIX 6.5 docs suggest POSIX threads get shown individually.
+       * This would make -T be like -L, -m, and m. (but an extra column)
+       * Testing (w/ normal processes) shows 1 line/process, not 2.
+       * Also, testing shows PID==SPID for all normal processes.
+       */
+      trace("-T adds strange SPID column (old sproc() threads?)\n");
+      format_modifiers |= FM_T;
+      break;
+    case 'U': /* end */
+      trace("-U select by RUID (supports names).\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of real groups must follow -U.";
+      err=parse_list(arg, parse_uid);
+      if(err) return err;
+      selection_list->typecode = SEL_RUID;
+      return NULL; /* can't have any more options */
+    case 'V': /* single */
+      trace("-V prints version.\n");
+      exclusive("-V");
+      display_version();
+      exit(0);
+    case 'Z':     /* full Mandatory Access Control level info */
+      trace("-Z shows full MAC info\n");
+      return "Don't understand MAC on Linux.";
+      break;
+    case 'a':
+      trace("-a select all with a tty, but omit session leaders.\n");
+      simple_select |= SS_U_a;
+      break;
+    case 'c':
+      /* HP-UX and SunOS 5 scheduling info modifier */
+      trace("-c changes scheduling info.\n");
+      format_modifiers |= FM_c;
+      break;
+    case 'd':
+      trace("-d select all, but omit session leaders.\n");
+      simple_select |= SS_U_d;
+      break;
+    case 'e':
+      trace("-e selects all processes.\n");
+      all_processes = 1;
+      break;
+    case 'f':
+      trace("-f does full listing\n");
+      format_flags |= FF_Uf;
+      unix_f_option = 1; /* does this matter? */
+      break;
+    case 'g': /* end */
+      trace("-g selects by session leader OR by group name\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of session leaders OR effective group names must follow -g.";
+      err=parse_list(arg, parse_pid);
+      if(!err){
+        selection_list->typecode = SEL_SESS;
+        return NULL; /* can't have any more options */
+      }
+      err=parse_list(arg, parse_gid);
+      if(!err){
+        selection_list->typecode = SEL_EGID;
+        return NULL; /* can't have any more options */
+      }
+      return "List of session leaders OR effective group IDs was invalid.";
+    case 'j':
+      trace("-j jobs format.\n");
+      /* Debian uses RD_j and Digital uses JFMT */
+      if(sysv_j_format) format_flags |= FF_Uj;
+      else format_modifiers |= FM_j;
+      break;
+    case 'l':
+      trace("-l long format.\n");
+      format_flags |= FF_Ul;
+      break;
+    case 'm':
+      trace("-m shows threads.\n");
+      /* note that AIX shows 2 lines for a normal process */
+      /* not implemented -- silently ignore the option */
+      break;
+    case 'n': /* end */
+      trace("-n sets namelist file.\n");
+      arg=get_opt_arg();
+      if(!arg) return "Alternate System.map file must follow -n.";
+      namelist_file = arg;
+      return NULL; /* can't have any more options */
+    case 'o': /* end */
+      /* Unix98 has gross behavior regarding this. From the following: */
+      /*            ps -o pid,nice=NICE,tty=TERMINAL,comm              */
+      /* The result must be 2 columns: "PID NICE,tty=TERMINAL,comm"    */
+      /* Yes, the second column has the name "NICE,tty=TERMINAL,comm"  */
+      /* This parser looks for any excuse to ignore that braindamage.  */
+      trace("-o user-defined format.\n");
+      arg=get_opt_arg();
+      if(!arg) return "Format specification must follow -o.";
+      not_pure_unix |= defer_sf_option(arg, SF_U_o);
+      return NULL; /* can't have any more options */
+    case 'p': /* end */
+      trace("-p select by PID.\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of process IDs must follow -p.";
+      err=parse_list(arg, parse_pid);
+      if(err) return err;
+      selection_list->typecode = SEL_PID;
+      return NULL; /* can't have any more options */
+#ifdef KNOW_WHAT_TO_DO_WITH_THIS
+    case 'r':
+      trace("-r some Digital Unix thing about warnings...\n");
+      trace("   or SCO's option to chroot() for new /proc and /dev.\n");
+      return "The -r option is reserved.";
+      break;
+#endif
+    case 's': /* end */
+      trace("-s Select processes belonging to the sessions given.\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of session IDs must follow -s.";
+      err=parse_list(arg, parse_pid);
+      if(err) return err;
+      selection_list->typecode = SEL_SESS;
+      return NULL; /* can't have any more options */
+    case 't': /* end */
+      trace("-t select by tty.\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of terminals (pty, tty...) must follow -t.";
+      err=parse_list(arg, parse_tty);
+      if(err) return err;
+      selection_list->typecode = SEL_TTY;
+      return NULL; /* can't have any more options */
+    case 'u': /* end */
+      trace("-u select by user ID (the EUID?) (supports names).\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of users must follow -u.";
+      err=parse_list(arg, parse_uid);
+      if(err) return err;
+      selection_list->typecode = SEL_EUID;
+      return NULL; /* can't have any more options */
+    case 'w':
+      trace("-w wide output.\n");
+      w_count++;
+      break;
+#ifdef NOBODY_HAS_BSD_HABITS_ANYMORE
+    case 'x':     /* Same as -y, but for System V Release 4 MP */
+      trace("-x works like Sun Solaris & SCO Unixware -y option\n");
+      format_modifiers |= FM_x;
+      break;
+#endif
+    case 'y':  /* Sun's -l hack (also: Irix "lnode" resource control info) */
+      trace("-y Print lnone info in UID/USER column or do Sun -l hack.\n");
+      format_modifiers |= FM_y;
+      break;
+    case 'z':     /* alias of Mandatory Access Control level info */
+      trace("-z shows aliased MAC info\n");
+      return "Don't understand MAC on Linux.";
+      break;
+    case '-':
+      return "Embedded '-' among SysV options makes no sense.";
+      break;
+    case '\0':
+      return "Please report the \"SysV \\0 can't happen\" bug.";
+      break;
+    default:
+      return "Unsupported SysV option.";
+    } /* switch */
+  } /* while */
+  return NULL;
+}
+
+/************************* parse BSD options **********************/
+static const char *parse_bsd_option(void){
+  const char *arg;
+  const char *err;
+
+  flagptr = ps_argv[thisarg];  /* assume we _have_ a '-' */
+  if(flagptr[0]=='-'){
+    if(!force_bsd) return "Can't happen!  Problem #1.";
+  }else{
+    flagptr--; /* off beginning, will increment before use */
+    if(personality & PER_FORCE_BSD){
+      if(!force_bsd) return "Can't happen!  Problem #2.";
+    }else{
+      if(force_bsd) return "2nd chance parse failed, not BSD or SysV.";
+    }
+  }
+
+  while(*++flagptr){
+    switch(*flagptr){
+    case '0' ... '9': /* end */
+      trace("0..9  Old BSD-style select by process ID\n");
+      arg=flagptr;
+      err=parse_list(arg, parse_pid);
+      if(err) return err;
+      selection_list->typecode = SEL_PID;
+      return NULL; /* can't have any more options */
+#if 0
+    case 'A':
+      /* maybe this just does a larger malloc() ? */
+      trace("A Increases the argument space (Digital Unix)\n");
+      return "Option A is reserved.";
+      break;
+    case 'C':
+      /* should divide result by 1-(e**(foo*log(bar))) */
+      trace("C Use raw CPU time for %%CPU instead of decaying ave\n");
+      return "Option C is reserved.";
+      break;
+#endif
+    case 'L': /* single */
+      trace("L List all format specifiers\n");
+      exclusive("L");
+      print_format_specifiers();
+      exit(0);
+#if 0
+    case 'M':
+      trace("M junk (use alternate core)\n");
+      return "Option M is unsupported, try N or -n instead.";
+      break;
+#endif
+    case 'N': /* end */
+      trace("N Specify namelist file\n");
+      arg=get_opt_arg();
+      if(!arg) return "Alternate System.map file must follow N.";
+      namelist_file = arg;
+      return NULL; /* can't have any more options */
+    case 'O': /* end */
+      trace("O Like o + defaults, add new columns after PID. Also sort.\n");
+      arg=get_opt_arg();
+      if(!arg) return "Format or sort specification must follow O.";
+      defer_sf_option(arg, SF_B_O);
+      return NULL; /* can't have any more options */
+      break;
+    case 'S':
+      trace("S include dead kids in sum\n");
+      include_dead_children = 1;
+      break;
+    case 'T':
+      trace("T Select all processes on this terminal\n");
+      /* put our tty on a tiny list */
+      {
+        selection_node *node;
+        node = malloc(sizeof(selection_node));
+        node->u = malloc(sizeof(sel_union));
+        node->u[0].tty = cached_tty;
+        node->typecode = SEL_TTY;
+        node->n = 1;
+        node->next = selection_list;
+        selection_list = node;
+      }
+      break;
+    case 'U': /* end */
+      trace("U Select processes for specified users.\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of users must follow U.";
+      err=parse_list(arg, parse_uid);
+      if(err) return err;
+      selection_list->typecode = SEL_EUID;
+      return NULL; /* can't have any more options */
+    case 'V': /* single */
+      trace("V show version info\n");
+      exclusive("V");
+      display_version();
+      exit(0);
+    case 'W':
+      trace("W N/A get swap info from ... not /dev/drum.\n");
+      return "Obsolete W option not supported. (You have a /dev/drum?)";
+      break;
+    case 'X':
+      trace("X Old Linux i386 register format\n");
+      format_flags |= FF_LX;
+      break;
+    case 'a':
+      trace("a Select all w/tty, including other users\n");
+      simple_select |= SS_B_a;
+      break;
+    case 'c':
+      trace("c true command name\n");
+      bsd_c_option = 1;
+      break;
+    case 'e':
+      trace("e environment\n");
+      bsd_e_option = 1;
+      break;
+    case 'f':
+      trace("f ASCII art forest\n");
+      forest_type = 'b';
+      break;
+    case 'g':
+      trace("g _all_, even group leaders!.\n");
+      simple_select |= SS_B_g;
+      break;
+    case 'h':
+      trace("h Repeat header... yow.\n");
+      if(header_type) return "Only one heading option may be specified.";
+      if(personality & PER_BSD_h) header_type = HEAD_MULTI;
+      else                        header_type = HEAD_NONE;
+      break;
+    case 'j':
+      trace("j job control format\n");
+      format_flags |= FF_Bj;
+      break;
+#if 0
+    case 'k':
+      trace("k N/A Use /vmcore as c-dumpfile\n");
+      return "Obsolete k option not supported.";
+      break;
+#endif
+    case 'l':
+      trace("l Display long format\n");
+      format_flags |= FF_Bl;
+      break;
+    case 'm':
+      trace("m all threads, sort on mem use, show mem info\n");
+      if(personality & PER_OLD_m){
+        format_flags |= FF_Lm;
+        break;
+      }
+      if(personality & PER_BSD_m){
+        defer_sf_option("pmem", SF_B_m);
+        break;
+      }
+      /* not implemented -- silently ignore the option */
+      break;
+    case 'n':
+      trace("n Numeric output for WCHAN, and USER replaced by UID\n");
+      wchan_is_number = 1;
+      user_is_number = 1;
+      /* TODO add tty_is_number too? */
+      break;
+    case 'o': /* end */
+      trace("o Specify user-defined format\n");
+      arg=get_opt_arg();
+      if(!arg) return "Format specification must follow o.";
+      defer_sf_option(arg, SF_B_o);
+      return NULL; /* can't have any more options */
+    case 'p': /* end */
+      trace("p Select by process ID\n");
+      arg=get_opt_arg();
+      if(!arg) return "List of process IDs must follow p.";
+      err=parse_list(arg, parse_pid);
+      if(err) return err;
+      selection_list->typecode = SEL_PID;
+      return NULL; /* can't have any more options */
+    case 'r':
+      trace("r Select running processes\n");
+      running_only = 1;
+      break;
+    case 's':
+      trace("s Display signal format\n");
+      format_flags |= FF_Bs;
+      break;
+    case 't': /* end */
+      trace("t Select by tty.\n");
+      /* List of terminals (tty, pty...) _should_ follow t. */
+      arg=get_opt_arg();
+      if(!arg){
+        /* Wow, obsolete BSD syntax. Put our tty on a tiny list. */
+        selection_node *node;
+        node = malloc(sizeof(selection_node));
+        node->u = malloc(sizeof(sel_union));
+        node->u[0].tty = cached_tty;
+        node->typecode = SEL_TTY;
+        node->n = 1;
+        node->next = selection_list;
+        selection_list = node;
+        return NULL;
+      }
+      err=parse_list(arg, parse_tty);
+      if(err) return err;
+      selection_list->typecode = SEL_TTY;
+      return NULL; /* can't have any more options */
+    case 'u':
+      trace("u Display user-oriented\n");
+      format_flags |= FF_Bu;
+      break;
+    case 'v':
+      trace("v Display virtual memory\n");
+      format_flags |= FF_Bv;
+      break;
+    case 'w':
+      trace("w wide output\n");
+      w_count++;
+      break;
+    case 'x':
+      trace("x Select processes without controlling ttys\n");
+      simple_select |= SS_B_x;
+      break;
+    case '-':
+      return "Embedded '-' among BSD options makes no sense.";
+      break;
+    case '\0':
+      return "Please report the \"BSD \\0 can't happen\" bug.";
+      break;
+    default:
+      return "Unsupported option (BSD syntax)";
+    } /* switch */
+  } /* while */
+  return NULL;
+}
+
+/*************** gnu long options **********************/
+
+/*
+ * Return the argument or NULL
+ */
+static const char *grab_gnu_arg(void){
+  switch(*flagptr){     /* argument is part of ps_argv[thisarg] */
+  default:
+    return NULL;                     /* something bad */
+  case '=': case ':':
+    if(*++flagptr) return flagptr;   /* found it */
+    return NULL;                     /* empty '=' or ':' */
+  case '\0': /* try next argv[] */
+  }
+  if(thisarg+2 > ps_argc) return NULL;   /* there is nothing left */
+  /* argument follows ps_argv[thisarg] */
+  if(*(ps_argv[thisarg+1]) == '\0') return NULL;
+  return ps_argv[++thisarg];
+}
+
+typedef struct gnu_table_struct {
+  const char *name; /* long option name */
+  const void *jump; /* See gcc extension info.   :-)   */
+} gnu_table_struct;
+
+static int compare_gnu_table_structs(const void *a, const void *b){
+  return strcmp(((gnu_table_struct*)a)->name,((gnu_table_struct*)b)->name);
+}
+
+/* Option arguments are after ':', after '=', or in argv[n+1] */
+static const char *parse_gnu_option(void){
+  const char *arg;
+  const char *err;
+  char *s;
+  size_t sl;
+  char buf[16];
+  gnu_table_struct findme = { buf, NULL};
+  gnu_table_struct *found;
+  static const gnu_table_struct gnu_table[] = {
+  {"Group",         &&case_Group},       /* rgid */
+  {"User",          &&case_User},        /* ruid */
+  {"cols",          &&case_cols},
+  {"columns",       &&case_columns},
+  {"cumulative",    &&case_cumulative},
+  {"deselect",      &&case_deselect},    /* -N */
+  {"forest",        &&case_forest},      /* f -H */
+  {"format",        &&case_format},
+  {"group",         &&case_group},       /* egid */
+  {"header",        &&case_header},
+  {"headers",       &&case_headers},
+  {"heading",       &&case_heading},
+  {"headings",      &&case_headings},
+  {"help",          &&case_help},
+  {"info",          &&case_info},
+  {"lines",         &&case_lines},
+  {"no-header",     &&case_no_header},
+  {"no-headers",    &&case_no_headers},
+  {"no-heading",    &&case_no_heading},
+  {"no-headings",   &&case_no_headings},
+  {"noheader",      &&case_noheader},
+  {"noheaders",     &&case_noheaders},
+  {"noheading",     &&case_noheading},
+  {"noheadings",    &&case_noheadings},
+  {"pid",           &&case_pid},
+  {"rows",          &&case_rows},
+  {"sid",           &&case_sid},
+  {"sort",          &&case_sort},
+  {"tty",           &&case_tty},
+  {"user",          &&case_user},        /* euid */
+  {"version",       &&case_version},
+  {"width",         &&case_width},
+  };
+  const int gnu_table_count = sizeof(gnu_table)/sizeof(gnu_table_struct);
+
+  s = ps_argv[thisarg]+2;
+  sl = strcspn(s,":=");
+  if(sl > 15) return "Unknown gnu long option.";
+  strncpy(buf, s, sl);
+  buf[sl] = '\0';
+  flagptr = s+sl;
+
+  found = bsearch(&findme, gnu_table, gnu_table_count,
+      sizeof(gnu_table_struct), compare_gnu_table_structs
+  );
+
+  if(!found) return "Unknown gnu long option.";
+
+  goto *(found->jump);    /* See gcc extension info.  :-)   */
+
+  case_Group:
+    trace("--Group\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of real groups must follow --Group.";
+    err=parse_list(arg, parse_gid);
+    if(err) return err;
+    selection_list->typecode = SEL_RGID;
+    return NULL;
+  case_User:
+    trace("--User\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of real users must follow --User.";
+    err=parse_list(arg, parse_uid);
+    if(err) return err;
+    selection_list->typecode = SEL_RUID;
+    return NULL;
+  case_cols:
+  case_width:
+  case_columns:
+    trace("--cols\n");
+    arg = grab_gnu_arg();
+    if(arg && *arg){
+      long t;
+      char *endptr;
+      t = strtol(arg, &endptr, 0);
+      if(!*endptr && (t>0) && (t<2000000000)){
+        screen_cols = (int)t;
+        return NULL;
+      }
+    }
+    return "Number of columns must follow --cols, --width, or --columns.";
+  case_cumulative:
+    trace("--cumulative\n");
+    if(s[sl]) return "Option --cumulative does not take an argument.";
+    include_dead_children = 1;
+    return NULL;
+  case_deselect:
+    trace("--deselect\n");
+    if(s[sl]) return "Option --deselect does not take an argument.";
+    negate_selection = 1;
+    return NULL;
+  case_no_header:
+  case_no_headers:
+  case_no_heading:
+  case_no_headings:
+  case_noheader:
+  case_noheaders:
+  case_noheading:
+  case_noheadings:
+    trace("--noheaders\n");
+    if(s[sl]) return "Option --no-heading does not take an argument.";
+    if(header_type) return "Only one heading option may be specified.";
+    header_type = HEAD_NONE;
+    return NULL;
+  case_header:
+  case_headers:
+  case_heading:
+  case_headings:
+    trace("--headers\n");
+    if(s[sl]) return "Option --heading does not take an argument.";
+    if(header_type) return "Only one heading option may be specified.";
+    header_type = HEAD_MULTI;
+    return NULL;
+  case_forest:
+    trace("--forest\n");
+    if(s[sl]) return "Option --forest does not take an argument.";
+    forest_type = 'g';
+    return NULL;
+  case_format:
+    trace("--format\n");
+    arg=grab_gnu_arg();
+    if(!arg) return "Format specification must follow --format.";
+    defer_sf_option(arg, SF_G_format);
+    return NULL;
+  case_group:
+    trace("--group\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of effective groups must follow --group.";
+    err=parse_list(arg, parse_gid);
+    if(err) return err;
+    selection_list->typecode = SEL_EGID;
+    return NULL;
+  case_help:
+    trace("--help\n");
+    exclusive("--help");
+    fputs(help_message, stderr);
+    exit(0);
+    return NULL;
+  case_info:
+    trace("--info\n");
+    exclusive("--info");
+    self_info();
+    exit(0);
+    return NULL;
+  case_pid:
+    trace("--pid\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of process IDs must follow --pid.";
+    err=parse_list(arg, parse_pid);
+    if(err) return err;
+    selection_list->typecode = SEL_PID;
+    return NULL;
+  case_rows:
+  case_lines:
+    trace("--rows\n");
+    arg = grab_gnu_arg();
+    if(arg && *arg){
+      long t;
+      char *endptr;
+      t = strtol(arg, &endptr, 0);
+      if(!*endptr && (t>0) && (t<2000000000)){
+        screen_rows = (int)t;
+        return NULL;
+      }
+    }
+    return "Number of rows must follow --rows or --lines.";
+  case_sid:
+    trace("--sid\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "Some sid thing(s) must follow --sid.";
+    err=parse_list(arg, parse_pid);
+    if(err) return err;
+    selection_list->typecode = SEL_SESS;
+    return NULL;
+  case_sort:
+    trace("--sort\n");
+    arg=grab_gnu_arg();
+    if(!arg) return "Long sort specification must follow --sort.";
+    defer_sf_option(arg, SF_G_sort);
+    return NULL;
+  case_tty:
+    trace("--tty\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of ttys must follow --tty.";
+    err=parse_list(arg, parse_tty);
+    if(err) return err;
+    selection_list->typecode = SEL_TTY;
+    return NULL;
+  case_user:
+    trace("--user\n");
+    arg = grab_gnu_arg();
+    if(!arg) return "List of effective users must follow --user.";
+    err=parse_list(arg, parse_uid);
+    if(err) return err;
+    selection_list->typecode = SEL_EUID;
+    return NULL;
+  case_version:
+    trace("--version\n");
+    exclusive("--version");
+    display_version();
+    exit(0);
+    return NULL;
+}
+
+/*************** process trailing PIDs  **********************/
+static const char *parse_trailing_pids(void){
+  selection_node *pidnode;  /* pid */
+  selection_node *grpnode;  /* process group */
+  selection_node *sidnode;  /* session */
+  char **argp;     /* pointer to pointer to text of PID */
+  const char *err;       /* error code that could or did happen */
+  int i;
+
+  i = ps_argc - thisarg;  /* how many trailing PIDs, SIDs, PGRPs?? */
+  argp = ps_argv + thisarg;
+  thisarg = ps_argc - 1;   /* we must be at the end now */
+
+  pidnode = malloc(sizeof(selection_node));
+  pidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+  pidnode->n = 0;
+
+  grpnode = malloc(sizeof(selection_node));
+  grpnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+  grpnode->n = 0;
+
+  sidnode = malloc(sizeof(selection_node));
+  sidnode->u = malloc(i*sizeof(sel_union)); /* waste is insignificant */
+  sidnode->n = 0;
+
+  while(i--){
+    char *data;
+    data = *(argp++);
+    switch(*data){
+    default:   err = parse_pid(  data, pidnode->u + pidnode->n++); break;
+    case '-':  err = parse_pid(++data, grpnode->u + grpnode->n++); break;
+    case '+':  err = parse_pid(++data, sidnode->u + sidnode->n++); break;
+    }
+    if(err) return err;     /* the node gets freed with the list */
+  }
+
+  if(pidnode->n){
+    pidnode->next = selection_list;
+    selection_list = pidnode;
+    selection_list->typecode = SEL_PID;
+  }  /* else free both parts */
+
+  if(grpnode->n){
+    grpnode->next = selection_list;
+    selection_list = grpnode;
+    selection_list->typecode = SEL_PGRP;
+  }  /* else free both parts */
+
+  if(sidnode->n){
+    sidnode->next = selection_list;
+    selection_list = sidnode;
+    selection_list->typecode = SEL_SESS;
+  }  /* else free both parts */
+
+  return NULL;
+}
+
+/************** misc stuff ***********/
+
+static void reset_parser(void){
+  w_count = 0;
+}
+
+static int arg_type(const char *str){
+  int tmp = str[0];
+  if((tmp>='a') && (tmp<='z'))   return ARG_BSD;
+  if((tmp>='A') && (tmp<='Z'))   return ARG_BSD;
+  if((tmp>='0') && (tmp<='9'))   return ARG_PID;
+  if(tmp=='+')                   return ARG_SESS;
+  if(tmp!='-')                   return ARG_FAIL;
+  tmp = str[1];
+  if((tmp>='a') && (tmp<='z'))   return ARG_SYSV;
+  if((tmp>='A') && (tmp<='Z'))   return ARG_SYSV;
+  if((tmp>='0') && (tmp<='9'))   return ARG_PGRP;
+  if(tmp!='-')                   return ARG_FAIL;
+  tmp = str[2];
+  if((tmp>='a') && (tmp<='z'))   return ARG_GNU;
+  if((tmp>='A') && (tmp<='Z'))   return ARG_GNU;
+  if(tmp=='\0')                  return ARG_END;
+                                 return ARG_FAIL;
+}
+
+/* First assume sysv, because that is the POSIX and Unix98 standard. */
+static const char *parse_all_options(void){
+  const char *err = NULL;
+  int at;
+  while(++thisarg < ps_argc){
+  trace("parse_all_options calling arg_type for \"%s\"\n", ps_argv[thisarg]);
+    at = arg_type(ps_argv[thisarg]);
+    trace("ps_argv[thisarg] is %s\n", ps_argv[thisarg]);
+    if(at != ARG_SYSV) not_pure_unix = 1;
+    switch(at){
+    case ARG_GNU:
+      err = parse_gnu_option();
+      break;
+    case ARG_SYSV:
+      if(!force_bsd){   /* else go past case ARG_BSD */
+        err = parse_sysv_option();
+        break;
+    case ARG_BSD:
+        if(force_bsd && !(personality & PER_FORCE_BSD)) return "way bad";
+      }
+      prefer_bsd_defaults = 1;
+      err = parse_bsd_option();
+      break;
+    case ARG_PGRP:
+    case ARG_SESS:
+    case ARG_PID:
+      prefer_bsd_defaults = 1;
+      err = parse_trailing_pids();
+      break;
+    case ARG_END:
+    case ARG_FAIL:
+      trace("              FAIL/END on [%s]\n",ps_argv[thisarg]);
+      return "Garbage option.";
+      break;
+    default:
+      printf("                  ?    %s\n",ps_argv[thisarg]);
+      return "Something broke.";
+    } /* switch */
+    if(err) return err;
+  } /* while */
+  return NULL;
+}
+
+static void choose_dimensions(void){
+  if(w_count && (screen_cols<132)) screen_cols=132;
+  if(w_count>1) screen_cols=OUTBUF_SIZE;
+  /* perhaps --html and --null should set unlimited width */
+}
+
+int arg_parse(int argc, char *argv[]){
+  const char *err = NULL;
+  const char *err2 = NULL;
+  ps_argc = argc;
+  ps_argv = argv;
+  thisarg = 0;
+
+#if 0
+  {int debugloop = 0; while(debugloop<argc){
+  trace("argv[%d]=%s\n", debugloop, argv[debugloop]); debugloop++;}}
+#endif
+
+  if(personality & PER_FORCE_BSD) goto try_bsd;
+
+  err = parse_all_options();
+  if(err) goto try_bsd;
+  err = process_sf_options(!not_pure_unix);
+  if(err) goto try_bsd;
+  err = select_bits_setup();
+  if(err) goto try_bsd;
+
+  choose_dimensions();
+  return 0;
+
+try_bsd:
+  trace("--------- now try BSD ------\n");
+
+  reset_global();
+  reset_parser();
+  reset_sortformat();
+  format_flags = 0;
+  ps_argc = argc;
+  ps_argv = argv;
+  thisarg = 0;
+  /* no need to reset flagptr */
+  not_pure_unix=1;
+  force_bsd=1;
+  prefer_bsd_defaults=1;
+  if(!( (PER_OLD_m|PER_BSD_m) & personality )) /* if default m setting... */
+    personality |= PER_OLD_m; /* Prefer old Linux over true BSD. */
+  /* Do not set PER_FORCE_BSD! It is tested below. */
+
+  err2 = parse_all_options();
+  if(err2) goto total_failure;
+  err2 = process_sf_options(!not_pure_unix);
+  if(err2) goto total_failure;
+  err2 = select_bits_setup();
+  if(err2) goto total_failure;
+
+  if(!(personality & PER_FORCE_BSD))
+    fprintf(stderr, "Bad syntax, perhaps a bogus '-'?\n");
+
+  choose_dimensions();
+  return 0;
+
+total_failure:
+  reset_parser();
+  if(personality & PER_FORCE_BSD) fprintf(stderr, "ERROR: %s\n", err2);
+  else fprintf(stderr, "ERROR: %s\n", err);
+  fputs(help_message, stderr);
+  exit(1);
+  /* return 1; */ /* useless */
+}
diff --git a/ps/ps.1 b/ps/ps.1
new file mode 100644 (file)
index 0000000..2206872
--- /dev/null
+++ b/ps/ps.1
@@ -0,0 +1,521 @@
+.\" Man page for ps.
+.\" Quick hack conversion by Albert Cahalan, 1998.
+.\" Licensed under version 2 of the Gnu General Public License.
+.\"
+.\" This man page is a horrid hack because *roff sucks.
+.\" The whole system is way obsolete. The internal header
+.\" stuff must die, and will when I figure out how to kill it.
+.\" I've already killed the wasteful left margin and screwy
+.\" old perfect justification. Gross! You'd think someone
+.\" invented this crap in 1973. Oh yeah, they did. Sorry.
+.\"
+.TH PS 1 "July 5, 1998" "Linux" "Linux User's Manual"
+.SH \fRNAME\fR
+ps \- report process status
+.ad r
+.na
+.ss 12 0
+.in 0
+.nh
+.nf
+
+SYNOPSIS
+ps [options]
+
+
+DESCRIPTION
+ps gives a snapshot of the current processes. If you want
+a repetitive update of this status, use top. This man
+page documents the /proc-based version of ps, or tries to.
+
+
+COMMAND-LINE OPTIONS
+
+This version of ps accepts several kinds of options.
+
+Unix options may be grouped and must be preceeded by a dash.
+BSD options may be grouped and must not be used with a dash.
+Gnu long options are preceeded by two dashes.
+
+Options of different types may be freely mixed.
+
+Set the I_WANT_A_BROKEN_PS environment variable to force BSD syntax even
+when options are preceeded by a dash. The PS_PERSONALITY environment
+variable (described below) provides more detailed control of ps behavior.
+
+SIMPLE PROCESS SELECTION
+-A           select all processes
+-N           negate selection
+-a           select all with a tty except session leaders
+-d           select all, but omit session leaders
+-e           select all processes
+T            select all processes on this terminal
+a            select all processes on a terminal, including those of other users
+g            really all, even group leaders (does nothing w/o SunOS settings)
+r            restrict output to running processes
+x            select processes without controlling ttys
+--deselect   negate selection
+
+PROCESS SELECTION BY LIST
+-C           select by command name
+-G           select by RGID (supports names)
+-U           select by RUID (supports names)
+-g           select by session leader OR by group name
+-p           select by PID
+-s           select processes belonging to the sessions given
+-t           select by tty
+-u           select by effective user ID (supports names)
+U            select processes for specified users
+p            select by process ID
+t            select by tty
+--Group      select by real group name or ID
+--User       select by real user name or ID
+--group      select by effective group name or ID
+--pid        select by process ID
+--sid        select by session ID
+--tty        select by terminal
+--user       select by effective user name or ID
+-123         implied --sid
+123          implied --pid
+
+OUTPUT FORMAT CONTROL
+-O           is preloaded "-o"
+-c           different scheduler info for -l option
+-f           does full listing
+-j           jobs format
+-l           long format
+-o           user-defined format
+-y           do not show flags; show rss in place of addr
+O            is preloaded "o" (overloaded)
+X            old Linux i386 register format
+j            job control format
+l            display long format
+o            specify user-defined format
+s            display signal format
+u            display user-oriented format
+v            display virtual memory format
+--format     user-defined format
+
+OUTPUT MODIFIERS
+-H           show process hierarchy (forest)
+-m           show threads
+-n           set namelist file
+-w           wide output
+C            use raw CPU time for %CPU instead of decaying average
+N            specify namelist file
+O            sorting order (overloaded)
+S            include some dead child process data (as a sum with the parent)
+c            true command name
+e            show environment after the command
+f            ASCII-art process hierarchy (forest)
+h            no header (or, one header per screen in the BSD personality)
+m            all threads
+n            numeric output for WCHAN and USER
+w            wide output
+--cols       set screen width
+--columns    set screen width
+--cumulative include some dead child process data (as a sum with the parent)
+--forest     ASCII art process tree
+--headers    repeat header lines, one per page of output
+--no-headers print no header line at all
+--lines      set screen height
+--rows       set screen height
+--sort       specify sorting order
+--width      set screen width
+
+INFORMATION
+-V          print version
+L           list all format specifiers
+V           show version info
+--help      print help message
+--info      print debugging info
+--version   print version
+
+OBSOLETE
+A           increases the argument space (DecUnix)
+M           use alternate core (try -n or N instead)
+W           get swap info from ... not /dev/drum (try -n or N instead)
+k           use /vmcore as c-dumpfile (try -n or N instead)
+
+
+
+NOTES
+
+The "-g" option can select by session leader OR by group name.
+Selection by session leader is specified by many standards,
+but selection by group is the logical behavior that several other
+operating systems use. This ps will select by session leader when
+the list is completely numeric (as sessions are). Group ID numbers
+will work only when some group names are also specified.
+
+The "m" option should not be used. Use "-m" or "-o" with a list.
+("m" displays memory info, shows threads, or sorts by memory use)
+
+The "h" option is problematic.  Standard BSD ps uses the option to
+print a header on each page of output, but older Linux ps uses the option
+to totally disable the header.  This version of ps follows the Linux
+usage of not printing the header unless the BSD personality has been
+selected, in which case it prints a header on each page of output.
+Regardless of the current personality, you can use the long options
+--headers and --no-headers to enable printing headers each page and
+disable headers entirely, respectively.
+
+Terminals (ttys, or screens for text output) can be specified in several
+forms: /dev/ttyS1, ttyS1, S1. Obsolete "ps t" (your own terminal) and
+"ps t?" (processes without a terminal) syntax is supported, but modern
+options ("T", "-t" with list, "x", "t" with list) should be used instead.
+
+The BSD "O" option can act like "-O" (user-defined output format with
+some common fields predefined) or can be used to specify sort order.
+Heuristics are used to determine the behavior of this option. To ensure
+that the desired behavior is obtained, specify the other option (sorting
+or formatting) in some other way.
+
+For sorting, BSD "O" option syntax is O[+|-]k1[,[+|-]k2[,...]]
+Order the process listing according to the multilevel sort specified by
+the sequence of short keys from SORT KEYS, k1, k2, ... The `+' is quite
+optional, merely re-iterating the default direction on a key. `-' reverses
+direction only on the key it precedes. The O option must be the last option
+in a single command argument, but specifications in successive arguments are
+catenated.
+
+Gnu sorting syntax is --sortX[+|-]key[,[+|-]key[,...]]
+Choose a multi-letter key from the SORT KEYS section. X may be any
+convenient separator character. To be GNU-ish use `='. The `+' is really
+optional since default direction is increasing numerical or lexicographic
+order. For example, ps jax --sort=uid,-ppid,+pid
+
+This ps works by reading the virtual files in /proc. This ps does not
+need to be suid kmem or have any privileges to run. Do not give this ps
+any special permissions.
+
+This ps needs access to a namelist file for proper WCHAN display.
+The namelist file must match the current Linux kernel exactly for
+correct output.
+
+To produce the WCHAN field, ps needs to read the System.map file created
+when the kernel is compiled. The search path is:
+
+$PS_SYSTEM_MAP
+/boot/System.map-`uname -r`
+/boot/System.map
+/lib/modules/`uname -r`/System.map
+/usr/src/linux/System.map
+
+The member used_math of task_struct is not shown, since crt0.s checks
+to see if math is present. This causes the math flag to be set for all
+processes, and so it is worthless. (Somebody fix libc or the kernel please)
+
+Programs swapped out to disk will be shown without command line arguments,
+and unless the c option is given, in brackets.
+
+%CPU shows the cputime/realtime percentage. It will not add up to 100%
+unless you are lucky. It is time used divided by the time the process has
+been running.
+
+The SIZE and RSS fields don't count the page tables and the task_struct of a
+proc; this is at least 12k of memory that is always resident. SIZE is the
+virtual size of the proc (code+data+stack).
+
+Processes marked <defunct> are dead processes (so-called "zombies") that
+remain because their parent has not destroyed them properly. These processes
+will be destroyed by init(8) if the parent process exits.
+
+
+PROCESS FLAGS
+
+ALIGNWARN    001    print alignment warning msgs
+STARTING     002    being created
+EXITING      004    getting shut down
+PTRACED      010    set if ptrace (0) has been called
+TRACESYS     020    tracing system calls
+FORKNOEXEC   040    forked but didn't exec
+SUPERPRIV    100    used super-user privileges
+DUMPCORE     200    dumped core
+SIGNALED     400    killed by a signal
+
+
+PROCESS STATE CODES
+
+D uninterruptible sleep (usually IO)
+R runnable (on run queue)
+S sleeping
+T traced or stopped
+Z a defunct ("zombie") process
+
+For BSD formats and when the "stat" keyword is used, additional
+letters may be displayed:
+
+W has no resident pages
+< high-priority process
+N low-priority task
+L has pages locked into memory (for real-time and custom IO)
+
+
+SORT KEYS
+
+Note that the values used in sorting are the internal values ps uses and not
+the `cooked' values used in some of the output format fields. Pipe ps
+output into the sort(1) command if you want to sort the cooked values.
+
+KEY LONG       DESCRIPTION
+c   cmd        simple name of executable
+C   cmdline    full command line
+f   flags      flags as in long format F field
+g   pgrp       process group ID
+G   tpgid      controlling tty process group ID
+j   cutime     cumulative user time
+J   cstime     cumulative system time
+k   utime      user time
+K   stime      system time
+m   min_flt    number of minor page faults
+M   maj_flt    number of major page faults
+n   cmin_flt   cumulative minor page faults
+N   cmaj_flt   cumulative major page faults
+o   session    session ID
+p   pid        process ID
+P   ppid       parent process ID
+r   rss        resident set size
+R   resident   resident pages
+s   size       memory size in kilobytes
+S   share      amount of shared pages
+t   tty        the minor device number of tty
+T   start_time time process was started
+U   uid        user ID number
+u   user       user name
+v   vsize      total VM size in kB
+y   priority   kernel scheduling priority
+
+
+AIX FORMAT DESCRIPTORS
+
+This ps supports AIX format descriptors, which work somewhat like the
+formatting codes of printf(1) and printf(3). For example, the normal
+default output can be produced with this:   ps -eo "%p %y %x %c"
+
+CODE  NORMAL    HEADER
+%C    pcpu      %CPU
+%G    group     GROUP
+%P    ppid      PPID
+%U    user      USER
+%a    args      COMMAND
+%c    comm      COMMAND
+%g    rgroup    RGROUP
+%n    nice      NI
+%p    pid       PID
+%r    pgid      PGID
+%t    etime     ELAPSED
+%u    ruser     RUSER
+%x    time      TIME
+%y    tty       TTY
+%z    vsz       VSZ
+
+
+STANDARD FORMAT SPECIFIERS
+
+These may be used to control both output format and sorting.
+For example:  ps -eo pid,user,args --sort user
+
+CODE         HEADER
+%cpu         %CPU    
+%mem         %MEM    
+alarm        ALARM   
+args         COMMAND 
+blocked      BLOCKED 
+bsdstart     START   
+bsdtime      TIME    
+c            C       
+caught       CAUGHT  
+cmd          CMD     
+comm         COMMAND 
+command      COMMAND 
+cputime      TIME    
+drs          DRS     
+dsiz         DSIZ    
+egid         EGID    
+egroup       EGROUP  
+eip          EIP     
+esp          ESP     
+etime        ELAPSED 
+euid         EUID    
+euser        EUSER   
+f            F       
+fgid         FGID    
+fgroup       FGROUP  
+flag         F       
+flags        F       
+fname        COMMAND 
+fsgid        FSGID   
+fsgroup      FSGROUP 
+fsuid        FSUID   
+fsuser       FSUSER  
+fuid         FUID    
+fuser        FUSER   
+gid          GID     
+group        GROUP   
+ignored      IGNORED 
+intpri       PRI     
+lim          LIM     
+longtname    TTY     
+lstart       STARTED 
+m_drs        DRS     
+m_trs        TRS     
+maj_flt      MAJFL   
+majflt       MAJFLT  
+min_flt      MINFL   
+minflt       MINFLT  
+ni           NI      
+nice         NI      
+nwchan       WCHAN   
+opri         PRI     
+pagein       PAGEIN  
+pcpu         %CPU    
+pending      PENDING 
+pgid         PGID    
+pgrp         PGRP    
+pid          PID     
+pmem         %MEM    
+ppid         PPID    
+pri          PRI     
+priority     PRI     
+rgid         RGID    
+rgroup       RGROUP  
+rss          RSS     
+rssize       RSS     
+rsz          RSZ     
+ruid         RUID    
+ruser        RUSER   
+s            S       
+sess         SESS    
+session      SESS    
+sgi_p        P       
+sgi_rss      RSS     
+sgid         SGID    
+sgroup       SGROUP  
+sid          SID     
+sig          PENDING 
+sig_block    BLOCKED 
+sig_catch    CATCHED 
+sig_ignore   IGNORED 
+sig_pend     SIGNAL  
+sigcatch     CAUGHT  
+sigignore    IGNORED 
+sigmask      BLOCKED 
+stackp       STACKP  
+start        STARTED 
+start_stack  STACKP  
+start_time   START   
+stat         STAT    
+state        S       
+stime        STIME   
+suid         SUID    
+suser        SUSER   
+svgid        SVGID   
+svgroup      SVGROUP 
+svuid        SVUID   
+svuser       SVUSER  
+sz           SZ      
+time         TIME    
+timeout      TMOUT   
+tmout        TMOUT   
+tname        TTY     
+tpgid        TPGID   
+trs          TRS     
+trss         TRSS    
+tsiz         TSIZ    
+tt           TT      
+tty          TT      
+tty4         TTY     
+tty8         TTY     
+ucmd         CMD     
+ucomm        COMMAND 
+uid          UID     
+uid_hack     UID     
+uname        USER    
+user         USER    
+vsize        VSZ     
+vsz          VSZ     
+wchan        WCHAN   
+
+
+
+
+ENVIRONMENT VARIABLES
+The following environment variables could affect ps:
+    COLUMNS             Override default display width.
+    LINES               Override default display height.
+    PS_PERSONALITY      Set to one of posix,old,linux,bsd,sun,digital...
+    CMD_ENV             Set to one of posix,old,linux,bsd,sun,digital...
+    I_WANT_A_BROKEN_PS  Force obsolete command line interpretation.
+    LC_TIME             Date format.
+    PS_COLORS           Not currently supported.
+    PS_FORMAT           Default output format override.
+    PS_SYSMAP           Default namelist (System.map) location.
+    PS_SYSTEM_MAP       Default namelist (System.map) location.
+    POSIXLY_CORRECT     Don't find excuses to ignore bad "features".
+    UNIX95              Don't find excuses to ignore bad "features".
+    _XPG                Cancel CMD_ENV=irix non-standard behavior.
+
+In general, it is a bad idea to set these variables. The one exception
+is CMD_ENV or PS_PERSONALITY, which could be set to Linux for normal
+systems. Without that setting, ps follows the useless and bad parts
+of the Unix98 standard.
+
+
+PERSONALITY
+    390      like the S/390 OpenEdition ps
+    aix      like AIX ps
+    bsd      like FreeBSD ps (totally non-standard)
+    compaq   like Digital Unix ps
+    debian   like the old Debian ps
+    digital  like Digital Unix ps
+    gnu      like the old Debian ps
+    hp       like HP-UX ps
+    hpux     like HP-UX ps
+    irix     like Irix ps
+    linux    ***** RECOMMENDED *****
+    old      like the original Linux ps (totally non-standard)
+    posix    standard
+    sco      like SCO ps
+    sgi      like Irix ps
+    sun      like SunOS 4 ps (totally non-standard)
+    sunos    like SunOS 4 ps (totally non-standard)
+    sysv     standard
+    unix     standard
+    unix95   standard
+    unix98   standard
+
+
+EXAMPLES
+To see every process on the system using standard syntax:
+    ps -e
+To see every process on the system using BSD syntax:
+    ps ax
+To see every process except those running as root (real & effective ID)
+    ps -U root -u root -N
+To see every process with a user-defined format:
+    ps -eo pid,tt,user,fname,tmout,f,wchan
+Odd display with AIX field descriptors:
+    ps -o "%u : %U : %p : %a"
+Print only the process IDs of syslogd:
+    ps -C syslogd -o pid=
+
+SEE ALSO
+top(1) pstree(1) proc(5)
+
+STANDARDS
+This ps conforms to version 2 of the Single Unix Specification.
+
+AUTHOR
+ps was originally written by Branko Lankester <lankeste@fwi.uva.nl>. Michael
+K. Johnson <johnsonm@redhat.com> re-wrote it significantly to use the proc
+filesystem, changing a few things in the process. Michael Shields
+<mjshield@nyx.cs.du.edu> added the pid-list feature. Charles Blake
+<cblake@bbn.com> added multi-level sorting, the dirent-style library, the
+device name-to-number mmaped database, the approximate binary search
+directly on System.map, and many code and documentation cleanups. David
+Mossberger-Tang wrote the generic BFD support for psupdate. Albert Cahalan
+<acahalan@cs.uml.edu> rewrote ps for full Unix98 and BSD support, along with
+some ugly hacks for obsolete and foreign syntax.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/ps/regression b/ps/regression
new file mode 100644 (file)
index 0000000..c71c826
--- /dev/null
@@ -0,0 +1,26 @@
+-u 500 -o pid,ppid,fname,comm,args           # right margin trouble
+-u 500 -o pid,ppid,fname,comm,args,wchan,wchan,wchan,wchan,wchan,nice,wchan
+-u 500 -o pid,pid,pid,pid,user,user,user,args   # had trouble
+-u 500 -o user,user,user,pid,pid,pid,pid,args   # no trouble!
+
+Test with each type of field (RIGHT,LEFT,UNLIMITED...) hanging off the
+edge of the screen and each type of field to the left of the one that
+hangs off the edge.
+
+Test "ps ef" as _both_ normal user and root. Especially after su!
+
+On a 108-col screen, try "ps alx" and "ps alx | cat"
+
+These ought to be the same:
+CMD_ENV=old ps -m
+CMD_ENV=old ps m
+
+These ought to be the same:
+CMD_ENV=old ps -X
+CMD_ENV=old ps X
+ps X
+ps -X        # needs to be a non-SysV option
+
+This should fail:
+ps x -x
+
diff --git a/ps/select.c b/ps/select.c
new file mode 100644 (file)
index 0000000..e6bf8c7
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                 
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "../proc/readproc.h"
+#include "../proc/procps.h"
+
+#define session_leader(p)       ((p)->session == (p)->pid)
+#define process_group_leader(p) ((p)->pgid    == (p)->pid)
+#define without_a_tty(p)        ((unsigned short)((p)->tty) == (unsigned short)-1)
+#define some_other_user(p)      ((p)->euid    != cached_euid)
+#define running(p)              (((p)->state=='R')||((p)->state=='D'))
+#define has_our_euid(p)         ((unsigned short)((p)->euid) == (unsigned short)cached_euid)
+#define on_our_tty(p)           ((unsigned short)((p)->tty)  == (unsigned short)cached_tty)
+
+static unsigned long select_bits = 0;
+
+/***** prepare select_bits for use */
+const char *select_bits_setup(void){
+  int switch_val = 0;
+  /* don't want a 'g' screwing up simple_select */
+  if(!simple_select && !prefer_bsd_defaults){
+    select_bits = 0xaa00; /* the STANDARD selection */
+    return NULL;
+  }
+  /* For every BSD but SunOS, the 'g' option is a NOP. (enabled by default) */
+  if( !(personality & PER_NO_DEFAULT_g) && !(simple_select&(SS_U_a|SS_U_d)) )
+    switch_val = simple_select|SS_B_g;
+  else
+    switch_val = simple_select;
+  switch(switch_val){
+  /* UNIX options */
+  case SS_U_a | SS_U_d:           select_bits = 0x3f3f; break; /* 3333 or 3f3f */
+  case SS_U_a:                    select_bits = 0x0303; break; /* 0303 or 0f0f */
+  case SS_U_d:                    select_bits = 0x3333; break;
+  /* SunOS 4 only (others have 'g' enabled all the time) */
+  case 0:                         select_bits = 0x0202; break;
+  case                   SS_B_a:  select_bits = 0x0303; break;
+  case          SS_B_x         :  select_bits = 0x2222; break;
+  case          SS_B_x | SS_B_a:  select_bits = 0x3333; break;
+  /* General BSD options */
+  case SS_B_g                  :  select_bits = 0x0a0a; break;
+  case SS_B_g |          SS_B_a:  select_bits = 0x0f0f; break;
+  case SS_B_g | SS_B_x         :  select_bits = 0xaaaa; break;
+  case SS_B_g | SS_B_x | SS_B_a:  /* convert to -e instead of using 0xffff */
+    all_processes = 1;
+    simple_select = 0;
+    break;
+  default:
+    return "Process selection options conflict.";
+    break;
+  }
+  return NULL;
+}
+
+/***** selected by simple option? */
+static int table_accept(proc_t *buf){
+  unsigned proc_index;
+  proc_index = (has_our_euid(buf)    <<0)
+             | (session_leader(buf)  <<1)
+             | (without_a_tty(buf)   <<2)
+             | (on_our_tty(buf)      <<3);
+  return (select_bits & (1<<proc_index));
+}
+
+/***** selected by some kind of list? */
+static int proc_was_listed(proc_t *buf){
+  selection_node *sn = selection_list;
+  int i;
+  if(!sn) return 0;
+  while(sn){
+    switch(sn->typecode){
+    default:
+      printf("Internal error in ps! Please report this bug.\n");
+
+#define return_if_match(foo,bar) \
+        i=sn->n; while(i--) \
+        if((unsigned short )(buf->foo) == (unsigned short)(*(sn->u+i)).bar) \
+        return 1
+
+    break; case SEL_RUID: return_if_match(ruid,uid);
+    break; case SEL_EUID: return_if_match(euid,uid);
+    break; case SEL_SUID: return_if_match(suid,uid);
+    break; case SEL_FUID: return_if_match(fuid,uid);
+
+    break; case SEL_RGID: return_if_match(rgid,gid);
+    break; case SEL_EGID: return_if_match(egid,gid);
+    break; case SEL_SGID: return_if_match(sgid,gid);
+    break; case SEL_FGID: return_if_match(fgid,gid);
+
+    break; case SEL_PGRP: return_if_match(pgrp,pid);
+    break; case SEL_PID : return_if_match(pid,pid);
+    break; case SEL_TTY : return_if_match(tty,tty);
+    break; case SEL_SESS: return_if_match(session,pid);
+
+    /* TODO Should use a long long cast for performance */
+    break; case SEL_COMM: i=sn->n; while(i--)
+    if(!strncmp( buf->cmd, (*(sn->u+i)).cmd, 8 )) return 1;
+
+
+
+#undef return_if_match
+
+    }
+    sn = sn->next;
+  }
+  return 0;
+}
+
+
+/***** This must satisfy Unix98 and as much BSD as possible */
+int want_this_proc(proc_t *buf){
+  int accepted_proc = 1; /* assume success */
+  /* elsewhere, convert T to list, U sets x implicitly */
+
+  /* handle -e -A */
+  if(all_processes) goto finish;
+
+  /* use table for -a a d g x */
+  if((simple_select || !selection_list))
+    if(table_accept(buf)) goto finish;
+
+  /* search lists */
+  if(proc_was_listed(buf)) goto finish;
+
+  /* fail, fall through to loose ends */
+  accepted_proc = 0;
+
+  /* do r N */
+finish:
+  if(running_only && !running(buf)) accepted_proc = 0;
+  if(negate_selection) return !accepted_proc;
+  return accepted_proc;
+}
diff --git a/ps/sortformat.c b/ps/sortformat.c
new file mode 100644 (file)
index 0000000..9c08d9a
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.         
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version  
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */                                 
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+/* username lookups */
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include "../proc/readproc.h"
+#include "common.h"
+
+static sf_node *sf_list = NULL;         /* deferred sorting and formatting */
+static int broken;                      /* use gross Unix98 parsing? */
+static int have_gnu_sort = 0;           /* if true, "O" must be format */
+static int already_parsed_sort = 0;     /* redundantly set in & out of fn */
+static int already_parsed_format = 0;
+
+
+#define parse_sort_opt <-- arrgh! do not use this -->
+#define gnusort_parse  <-- arrgh! do not use this -->
+
+
+/****************  Parse single format specifier *******************/
+static format_node *do_one_spec(const char *spec, const char *override){
+  const format_struct *fs;
+  const macro_struct *ms;
+
+  fs = search_format_array(spec);
+  if(fs){
+    int w1, w2;
+    format_node *thisnode;
+    thisnode = malloc(sizeof(format_node));
+    w1 = fs->width;
+    if(override){
+      w2 = strlen(override);
+      thisnode->width = (w1>w2)?w1:w2;
+      thisnode->name = malloc(strlen(override)+1);
+      strcpy(thisnode->name, override);
+    }else{
+      thisnode->width = w1;
+      thisnode->name = malloc(strlen(fs->head)+1);
+      strcpy(thisnode->name, fs->head);
+    }
+    thisnode->pr = fs->pr;
+    thisnode->pad = fs->pad;
+    thisnode->vendor = fs->vendor;
+    thisnode->flags = fs->flags;
+    thisnode->next = NULL;
+    return thisnode;
+  }
+
+  /* That failed, so try it as a macro. */
+  ms = search_macro_array(spec);
+  if(ms){
+    format_node *list = NULL;
+    format_node *newnode;
+    const char *walk;
+    int dist;
+    char buf[16]; /* trust strings will be short (from above, not user) */
+    walk = ms->head;
+    while(*walk){
+      dist = strcspn(walk, ", ");
+      strncpy(buf,walk,dist);
+      buf[dist] = '\0';
+      newnode = do_one_spec(buf,override); /* call self, assume success */
+      newnode->next = list;
+      list = newnode;
+      walk += dist;
+      if(*walk) walk++;
+    }
+    return list;
+  }
+  return NULL;   /* bad, spec not found */
+}
+
+
+/************ must wrap user format in default *************/
+static void O_wrap(sf_node *sfn, int otype){
+  format_node *fnode;
+  format_node *endp;
+  char *trailer;
+
+  trailer = (otype=='b') ? "END_BSD" : "END_SYS5" ;
+
+  fnode =  do_one_spec("pid",NULL);
+  if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+  endp = sfn->f_cooked; while(endp->next) endp = endp->next;  /* find end */
+  endp->next = fnode;
+  
+  fnode =  do_one_spec(trailer,NULL);
+  if(!fnode)fprintf(stderr,"Seriously crashing. Goodbye cruel world.\n");
+  endp = fnode; while(endp->next) endp = endp->next;  /* find end */
+  endp->next = sfn->f_cooked;
+  sfn->f_cooked = fnode;
+}
+
+/******************************************************************
+ * Used to parse option AIX field descriptors.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *aix_format_parse(sf_node *sfn){
+  char *buf;                   /* temp copy of arg to hack on */
+  char *walk;
+  int items;
+
+  /*** sanity check and count items ***/
+  items = 0;
+  walk = sfn->sf;
+  /* state machine */ {
+  int c;
+  initial:
+    c = *walk++;
+    if(c=='%')    goto get_desc;
+    if(!c)        goto looks_ok;
+  /* get_text: */
+    items++;
+  get_more_text:
+    c = *walk++;
+    if(c=='%')    goto get_desc;
+    if(c)         goto get_more_text;
+    goto looks_ok;
+  get_desc:
+    items++;
+    c = *walk++;
+    if(c)         goto initial;
+    return "Improper AIX field descriptor.";
+  looks_ok:
+  }
+
+  /*** sanity check passed ***/
+  buf = malloc(strlen(sfn->sf)+1);
+  strcpy(buf, sfn->sf);
+  walk = sfn->sf;
+  
+  while(items--){
+    format_node *fnode;  /* newly allocated */
+    format_node *endp;   /* for list manipulation */
+
+    if(*walk == '%'){
+      const aix_struct *aix;
+      walk++;
+      if(*walk == '%') goto double_percent;
+      aix = search_aix_array(*walk);
+      walk++;
+      if(!aix){
+        free(buf);
+        return "Unknown AIX field descriptor.";
+      }
+      fnode =  do_one_spec(aix->spec, aix->head);
+      if(!fnode){
+        free(buf);
+        return "AIX field descriptor processing bug.";
+      }
+    } else {
+      int len;
+      len = strcspn(walk, "%");
+      memcpy(buf,walk,len);
+      if(0){
+double_percent:
+        len = 1;
+        buf[0] = '%';
+      }
+      buf[len] = '\0';
+      walk += len;
+      fnode = malloc(sizeof(format_node));
+      fnode->width = len;
+      fnode->name = malloc(len+1);
+      strcpy(fnode->name, buf);
+      fnode->pr = NULL;     /* checked for */
+      fnode->pad = 0;
+      fnode->vendor = AIX;
+      fnode->flags = 0;
+      fnode->next = NULL;
+    }
+    
+    endp = fnode; while(endp->next) endp = endp->next;  /* find end */
+    endp->next = sfn->f_cooked;
+    sfn->f_cooked = fnode;
+  }
+  free(buf);
+  already_parsed_format = 1;
+  return NULL;
+}
+
+/***************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Put each completed format_node onto the list starting at ->f_cooked
+ */
+static const char *format_parse(sf_node *sfn){
+  char *buf;                   /* temp copy of arg to hack on */
+  char *sep_loc;               /* separator location: " \t,\n" */
+  char *walk;
+  const char *err;       /* error code that could or did happen */
+  format_node *fnode;
+  int items;
+  int need_item;
+  static char errbuf[80]; /* for variable-text error message */
+
+  /*** prepare to operate ***/
+  buf = malloc(strlen(sfn->sf)+1);
+  strcpy(buf, sfn->sf);
+  
+  /*** sanity check and count items ***/
+  need_item = 1; /* true */
+  items = 0;
+  walk = buf;
+  do{
+    switch(*walk){
+    case ' ': case ',': case '\t': case '\n': case '\0':
+    /* Linux extension: allow \t and \n as delimiters */
+      if(need_item){
+        free(buf);
+        goto improper;
+      }
+      need_item=1;
+      break;
+    case '=':
+      if(broken) goto out;
+      /* fall through */
+    default:
+      if(need_item) items++;
+      need_item=0;
+    }
+  } while (*++walk);
+out:
+  if(!items){
+    free(buf);
+    goto empty;
+  }
+#ifdef STRICT_LIST
+  if(need_item){    /* can't have trailing deliminator */
+    free(buf);
+    goto improper;
+  }
+#else
+  if(need_item){    /* allow 1 trailing deliminator */
+    *--walk='\0';  /* remove the trailing deliminator */
+  }
+#endif
+  /*** actually parse the list ***/
+  walk = buf;
+  while(items--){
+    format_node *endp;
+    char *equal_loc;
+    sep_loc = strpbrk(walk," ,\t\n");
+    /* if items left, then sep_loc is not in header override */
+    if(items && sep_loc) *sep_loc = '\0';
+    equal_loc = strpbrk(walk,"=");
+    if(equal_loc){   /* if header override */
+      *equal_loc = '\0';
+      equal_loc++;
+    }
+    fnode =  do_one_spec(walk,equal_loc);
+    if(!fnode){
+      if(!*errbuf){  /* if didn't already create an error string */
+        snprintf(
+          errbuf,
+          sizeof(errbuf),
+          "Unknown user-defined format specifier \"%s\".",
+          walk
+        );
+      }
+      free(buf);
+      goto unknown;
+    }
+    endp = fnode; while(endp->next) endp = endp->next;  /* find end */
+    endp->next = sfn->f_cooked;
+    sfn->f_cooked = fnode;
+    walk = sep_loc + 1; /* point to next item, if any */
+  }
+  free(buf);
+  already_parsed_format = 1;
+  return NULL;
+
+  /* errors may cause a retry looking for AIX format codes */
+  if(0) unknown:  err=errbuf;
+  if(0) empty:    err="Empty format list.";
+  if(0) improper: err="Improper format list.";
+  if(strchr(sfn->sf,'%')) err = aix_format_parse(sfn);
+  return err;
+}
+
+/****************  Parse single sort specifier *******************/
+static sort_node *do_one_sort_spec(const char *spec){
+  const format_struct *fs;
+  int reverse = 0;
+  if(*spec == '-'){
+    reverse = 1;
+    spec++;
+  }
+  fs = search_format_array(spec);
+  if(fs){
+    sort_node *thisnode;
+    thisnode = malloc(sizeof(format_node));
+    thisnode->sr = fs->sr;
+    thisnode->reverse = reverse;
+    thisnode->next = NULL;
+    return thisnode;
+  }
+  return NULL;   /* bad, spec not found */
+}
+
+
+/**************************************************************
+ * Used to parse long sorting options.
+ * Put each completed sort_node onto the list starting at ->s_cooked
+ */
+static const char *long_sort_parse(sf_node *sfn){
+  char *buf;                   /* temp copy of arg to hack on */
+  char *sep_loc;               /* separator location: " \t,\n" */
+  char *walk;
+  const char *err;       /* error code that could or did happen */
+  sort_node *snode;
+  int items;
+  int need_item;
+
+  /*** prepare to operate ***/
+  buf = malloc(strlen(sfn->sf)+1);
+  strcpy(buf, sfn->sf);
+  
+  /*** sanity check and count items ***/
+  need_item = 1; /* true */
+  items = 0;
+  walk = buf;
+  err = "Improper sort specifier list.";
+  do{
+    switch(*walk){
+    case ' ': case ',': case '\t': case '\n': case '\0':
+      if(need_item){
+        free(buf);
+        return "Improper sort list";
+      }
+      need_item=1;
+      break;
+    default:
+      if(need_item) items++;
+      need_item=0;
+    }
+  } while (*++walk);
+  if(!items){
+    free(buf);
+    return "Empty sort list.";
+  }
+#ifdef STRICT_LIST
+  if(need_item){    /* can't have trailing deliminator */
+    free(buf);
+    return "Improper sort list.";
+  }
+#else
+  if(need_item){    /* allow 1 trailing deliminator */
+    *--walk='\0';  /* remove the trailing deliminator */
+  }
+#endif
+  /*** actually parse the list ***/
+  walk = buf;
+  while(items--){
+    sort_node *endp;
+    sep_loc = strpbrk(walk," ,\t\n");
+    if(sep_loc) *sep_loc = '\0';
+    snode = do_one_sort_spec(walk);
+    if(!snode){
+      free(buf);
+      return "Unknown sort specifier.";
+    }
+    endp = snode; while(endp->next) endp = endp->next;  /* find end */
+    endp->next = sfn->s_cooked;
+    sfn->s_cooked = snode;
+    walk = sep_loc + 1; /* point to next item, if any */
+  }
+  free(buf);
+  already_parsed_sort = 1;
+  return NULL;
+}
+
+
+
+
+
+
+/************ pre-parse short sorting option *************/
+/* Errors _must_ be detected so that the "O" option can try to
+ * reparse as formatting codes.
+ */
+static const char *verify_short_sort(const char *arg){
+  const char *all = "CGJKMNPRSTUcfgjkmnoprstuvy+-";
+  char checkoff[256];
+  int i;
+  const char *walk;
+  int tmp;
+  if(strspn(arg,all) != strlen(arg)) return "Bad sorting code.";
+  for(i=256; i--;) checkoff[i] = 0;
+  walk = arg;
+  for(;;){
+    tmp = *walk;
+    switch(tmp){
+    case '\0':
+      return NULL;   /* looks good */
+    case '+':
+    case '-':
+      tmp = *(walk+1);
+      if(!tmp || tmp=='+' || tmp=='-') return "Bad sorting code.";
+      break;
+    case 'P':
+      if(forest_type) return "PPID sort and forest output conflict.";
+      /* fall through */
+    default:
+      if(checkoff[tmp]) return "Bad sorting code.";   /* repeated */
+      /* ought to check against already accepted sort options */
+      checkoff[tmp] = 1;
+      break;
+    }
+    walk++;
+  }
+}
+
+
+
+/************ parse short sorting option *************/
+static const char *short_sort_parse(sf_node *sfn){
+  int direction = 0;
+  const char *walk;
+  int tmp;
+  sort_node *snode;
+  sort_node *endp;
+  const struct shortsort_struct *ss;
+  walk = sfn->sf;
+  for(;;){
+    tmp = *walk;
+    switch(tmp){
+    case '\0':
+      already_parsed_sort = 1;
+      return NULL;
+    case '+':
+      direction = 0;
+      break;
+    case '-':
+      direction = 1;
+      break;
+    default:
+      ss = search_shortsort_array(tmp);
+      if(!ss) return "Unknown sort specifier.";
+      snode = do_one_sort_spec(ss->spec);
+      if(!snode) return "Unknown sort specifier.";
+      snode->reverse = direction;
+      endp = snode; while(endp->next) endp = endp->next;  /* find end */
+      endp->next = sfn->s_cooked;
+      sfn->s_cooked = snode;
+      direction = 0;
+      break;
+    }
+    walk++;
+  }
+}
+
+/******************* high-level below here *********************/
+
+
+/*
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ * Recursion is to preserve original order.
+ */
+static const char *parse_O_option(sf_node *sfn){
+  const char *err;     /* error code that could or did happen */
+
+  if(sfn->next){
+    err = parse_O_option(sfn->next);
+    if(err) return err;
+  }
+
+  switch(sfn->sf_code){
+    case SF_B_o: case SF_G_format: case SF_U_o: /*** format ***/
+      err = format_parse(sfn);
+      if(!err) already_parsed_format = 1;
+      break;
+    case SF_U_O:                                /*** format ***/
+      /* Can have -l -f f u... set already_parsed_format like DEC does */
+      if(already_parsed_format) return "option -O can not follow other format options.";
+      err = format_parse(sfn);
+      if(err) return err;
+      already_parsed_format = 1;
+      O_wrap(sfn,'u'); /* must wrap user format in default */
+      break;
+    case SF_B_O:                                /***  both  ***/
+      if(have_gnu_sort || already_parsed_sort) err = "Multiple sort options.";
+      else err = verify_short_sort(sfn->sf);
+      if(!err){ /* success as sorting code */
+        short_sort_parse(sfn);
+        already_parsed_sort = 1;
+        return NULL;
+      }
+      if(already_parsed_format){
+        err = "option O is neither first format nor sort order.";
+        break;
+      }
+      if(!format_parse(sfn)){ /* if success as format code */
+        already_parsed_format = 1;
+        O_wrap(sfn,'b'); /* must wrap user format in default */
+        return NULL;
+      }
+      break;
+    case SF_G_sort: case SF_B_m:                 /***  sort  ***/
+      if(already_parsed_sort) err = "Multiple sort options.";
+      else err = long_sort_parse(sfn);
+      already_parsed_sort = 1;
+      break;
+    default:                                    /***  junk  ***/
+      return "Bug: parse_O_option got weirdness!";
+  }
+  return err; /* could be NULL */
+}
+
+
+/************ Main parser calls this to save lists for later **********/
+/* store data for later and return 1 if arg looks non-standard */
+int defer_sf_option(const char *arg, int source){
+  sf_node *sfn;
+  char buf[16];
+  int dist;
+  const format_struct *fs;
+  int need_item = 1;
+
+  sfn = malloc(sizeof(sf_node));
+  sfn->sf = malloc(strlen(arg)+1);
+  strcpy(sfn->sf, arg);
+  sfn->sf_code = source;
+  sfn->s_cooked = NULL;
+  sfn->f_cooked = NULL;
+  sfn->next = sf_list;
+  sf_list = sfn;
+
+  if(source == SF_G_sort) have_gnu_sort = 1;
+
+  /* Now try to find an excuse to ignore broken Unix98 parsing. */
+  if(source != SF_U_o) return 1;    /* Wonderful! Already non-Unix98. */
+  do{
+    switch(*arg){
+    case ' ': case ',': case '\0':  /* no \t\n\r support in Unix98 */
+      if(need_item) return 1;       /* something wrong */
+      need_item=1;
+      break;
+    case '=':
+      if(need_item) return 1;       /* something wrong */
+      return 0;                     /* broken Unix98 parsing is required */
+    default:
+      if(!need_item) break;
+      need_item=0;
+      dist = strcspn(arg,", =");
+      if(dist>15) return 1;         /* something wrong, sort maybe? */
+      strncpy(buf,arg,dist);   /* no '\0' on end */
+      buf[dist] = '\0';        /* fix that problem */
+      fs = search_format_array(buf);
+      if(!fs) return 1;             /* invalid spec, macro or sort maybe? */
+      if(fs->vendor) return 1;      /* Wonderful! Legal non-Unix98 spec. */
+    }
+  } while (*++arg);
+
+  return 0;                         /* boring, Unix98 is no change */
+}
+
+/***** Since ps is not long-lived, the memory leak can be ignored. ******/
+void reset_sortformat(void){
+  sf_list = NULL;          /* deferred sorting and formatting */
+  format_list = NULL;      /* digested formatting options */
+  sort_list = NULL;        /* digested sorting options (redundant?) */
+  have_gnu_sort = 0;
+  already_parsed_sort = 0;
+  already_parsed_format = 0;
+}
+
+
+/***** Search format_list for findme, then insert putme after findme. ****/
+static int fmt_add_after(const char *findme, format_node *putme){
+  format_node *walk;
+  if(!strcmp(format_list->name, findme)){
+    putme->next = format_list->next;
+    format_list->next = putme;
+    return 1; /* success */
+  }
+  walk = format_list;
+  while(walk->next){
+    if(!strcmp(walk->next->name, findme)){
+      putme->next = walk->next->next;
+      walk->next->next = putme;
+      return 1; /* success */
+    }
+    walk = walk->next;
+  }
+  return 0; /* fail */
+}
+
+/******* Search format_list for findme, then delete it. ********/
+static int fmt_delete(const char *findme){
+  format_node *walk;
+  format_node *old;
+  if(!strcmp(format_list->name, findme)){
+    old = format_list;
+    format_list = format_list->next;
+    free(old);
+    return 1; /* success */
+  }
+  walk = format_list;
+  while(walk->next){
+    if(!strcmp(walk->next->name, findme)){
+      old = walk->next;
+      walk->next = walk->next->next;
+      free(old);
+      return 1; /* success */
+    }
+    walk = walk->next;
+  }
+  return 0; /* fail */
+}
+
+
+/************ Build a SysV format backwards. ***********/
+#define PUSH(foo) (fn=do_one_spec(foo, NULL), fn->next=format_list, format_list=fn)
+static const char *generate_sysv_list(void){
+  format_node *fn;
+  if((format_modifiers & FM_y) && !(format_flags & FF_Ul))
+    return "Modifier -y without format -l makes no sense.";
+  if(prefer_bsd_defaults){
+    if(format_flags) PUSH("cmd");
+    else PUSH("args");
+    PUSH("bsdtime");
+    if(!(format_flags & FF_Ul)) PUSH("stat");
+  }else{
+    if(format_flags & FF_Uf) PUSH("cmd");
+    else PUSH("ucmd");
+    PUSH("time");
+  }
+  PUSH("tname");  /* Unix98 says "TTY" here, yet "tty" produces "TT". */
+  if(format_flags & FF_Uf) PUSH("stime");
+  /* avoid duplicate columns from -FP and -Fly */
+  if(format_modifiers & FM_F){
+    /* if -FP take the Sun-style column instead (sorry about "sgi_p") */
+    if(!(format_modifiers & FM_P)) PUSH("psr");  /* should be ENG */
+    /* if -Fly take the ADDR-replacement RSS instead */
+    if(!( (format_flags & FF_Ul) && (format_modifiers & FM_y) )) PUSH("rss");
+  }
+  if(format_flags & FF_Ul){
+    PUSH("wchan");
+  }
+  /* since FM_y adds RSS anyway, don't do this hack when that is true */
+  if( (format_flags & FF_Ul) && !(format_modifiers & FM_y) ){
+    if(personality & PER_IRIX_l){ /* add "rss" then ':' here */
+      PUSH("sgi_rss");
+      fn = malloc(sizeof(format_node));
+      fn->width = 1;
+      fn->name = malloc(2);
+      strcpy(fn->name, ":");
+      fn->pr = NULL;     /* checked for */
+      fn->pad = 0;
+      fn->vendor = AIX;   /* yes, for SGI weirdness */
+      fn->flags = 0;
+      fn->next = format_list;
+      format_list=fn;
+    }
+  }
+  if((format_modifiers & FM_F) || (format_flags & FF_Ul)){
+    PUSH("sz");
+  }
+  if(format_flags & FF_Ul){
+    if(format_modifiers & FM_y) PUSH("rss");
+    else if(personality & (PER_ZAP_ADDR|PER_IRIX_l)) PUSH("sgi_p");
+    else PUSH("addr_1");
+  }
+  if(format_modifiers & FM_c){
+    PUSH("pri"); PUSH("class");
+  }else if(format_flags & FF_Ul){
+    PUSH("ni"); PUSH("opri");
+  }
+  if((format_modifiers & FM_L) && (format_flags & FF_Uf)) PUSH("nlwp");
+  if( (format_flags & (FF_Uf|FF_Ul)) && !(format_modifiers & FM_c) ) PUSH("c");
+  if(format_modifiers & FM_P) PUSH("psr");
+  if(format_modifiers & FM_L) PUSH("lwp");
+  if(format_modifiers & FM_j){
+    PUSH("sid");
+    PUSH("pgid");
+  }
+  if(format_flags & (FF_Uf|FF_Ul)) PUSH("ppid");
+  if(format_modifiers & FM_T) PUSH("spid");
+  PUSH("pid");
+  if(format_flags & FF_Uf){
+    if(personality & PER_SANE_USER) PUSH("user");
+    else PUSH("uid_hack");
+  }else if(format_flags & FF_Ul){
+    PUSH("uid");
+  }
+  if(format_flags & FF_Ul){
+    PUSH("s");
+    if(!(format_modifiers & FM_y)) PUSH("f");
+  }
+  if(format_modifiers & FM_M){
+    PUSH("label");  /* Mandatory Access Control */
+  }
+  format_modifiers = 0;
+  return NULL;
+}
+
+
+/**************************************************************************
+ * Used to parse option O lists. Option O is shared between
+ * sorting and formatting. Users may expect one or the other.
+ * The "broken" flag enables a really bad Unix98 misfeature.
+ */
+const char *process_sf_options(int localbroken){
+  const char *err;
+  sf_node *sf_walk;
+  int option_source;  /* true if user-defined */
+  if(personality & PER_BROKEN_o) localbroken = 1;
+  if(personality & PER_GOOD_o)   localbroken = 0;
+  broken = localbroken;
+  if(sf_list){
+    err = parse_O_option(sf_list);
+    if(err) return err;
+  }
+
+  if(format_list) printf("Bug: must reset the list first!\n");
+
+  /* merge formatting info of sf_list into format_list here */
+  sf_walk = sf_list;
+  while(sf_walk){
+    format_node *fmt_walk;
+    fmt_walk = sf_walk->f_cooked;
+    sf_walk->f_cooked = NULL;
+    while(fmt_walk){   /* put any nodes onto format_list in opposite way */
+      format_node *travler;
+      travler = fmt_walk;
+      fmt_walk = fmt_walk->next;
+      travler->next = format_list;
+      format_list = travler;
+    }
+    sf_walk = sf_walk->next;
+  }
+
+  /* merge sorting info of sf_list into sort_list here */
+  sf_walk = sf_list;
+  while(sf_walk){
+    sort_node *srt_walk;
+    srt_walk = sf_walk->s_cooked;
+    sf_walk->s_cooked = NULL;
+    while(srt_walk){   /* put any nodes onto sort_list in opposite way */
+      sort_node *travler;
+      travler = srt_walk;
+      srt_walk = srt_walk->next;
+      travler->next = sort_list;
+      sort_list = travler;
+    }
+    sf_walk = sf_walk->next;
+  }
+
+  if(format_list){
+    if(format_flags) return "Conflicting format options.";
+    option_source = 1;
+  }else{
+    format_node *fmt_walk;
+    format_node *fn;
+    const char *spec;
+    switch(format_flags){
+
+    default:             return "Conflicting format options.";
+
+    /* These can be NULL, which enables SysV list generation code. */
+    case 0:              spec=NULL;           break;
+    case FF_Uf | FF_Ul:  spec=sysv_fl_format; break;
+    case FF_Uf:          spec=sysv_f_format;  break;
+    case FF_Ul:          spec=sysv_l_format;  break;
+
+    /* These are NOT REACHED for normal -j processing. */
+    case FF_Uj:          spec=sysv_j_format;  break; /* Debian & Digital */
+    case FF_Uj | FF_Ul:  spec="RD_lj";        break; /* Debian */
+    case FF_Uj | FF_Uf:  spec="RD_fj";        break; /* Debian */
+
+    /* These are true BSD options. */
+    case FF_Bj:          spec=bsd_j_format;   break;
+    case FF_Bl:          spec=bsd_l_format;   break;
+    case FF_Bs:          spec=bsd_s_format;   break;
+    case FF_Bu:          spec=bsd_u_format;   break;
+    case FF_Bv:          spec=bsd_v_format;   break;
+
+    /* These are old Linux options. Option m is overloaded. */
+    case FF_LX:          spec="OL_X";         break;
+    case FF_Lm:          spec="OL_m";         break;
+
+    }  /* end switch(format_flags) */
+
+    option_source = 0;
+    if(!format_flags && !format_modifiers){   /* was default */
+      char *tmp;
+      tmp = getenv("PS_FORMAT");  /* user override kills default */
+      if(tmp && *tmp){
+        spec = tmp;
+        option_source = 2;
+      }
+    }
+
+    if(spec){
+      fn = do_one_spec(spec, NULL); /* use override "" for no headers */
+      fmt_walk = fn;
+      while(fmt_walk){   /* put any nodes onto format_list in opposite way */
+        format_node *travler;
+        travler = fmt_walk;
+        fmt_walk = fmt_walk->next;
+        travler->next = format_list;
+        format_list = travler;
+      }
+    }else{
+      err = generate_sysv_list();
+      if(err) return err;
+      option_source = 3;
+    }
+  }
+  if(format_modifiers){  /* generate_sysv_list() may have cleared some bits */
+    format_node *fn;
+    if(option_source) return "Can't use output modifiers with user-defined output";
+    if(format_modifiers & FM_j){
+      fn = do_one_spec("pgid", NULL);
+      if(!fmt_add_after("PPID", fn)) if(!fmt_add_after("PID", fn))
+        return "Internal error, no PID or PPID for -j option.";
+      fn = do_one_spec("sid", NULL);
+      if(!fmt_add_after("PGID", fn)) return "Lost my PGID!";
+    }
+    if(format_modifiers & FM_y){
+      /* TODO: check for failure to do something, and complain if so */
+      fmt_delete("F");
+      fn = do_one_spec("rss", NULL);
+      if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+    }
+    if(format_modifiers & FM_c){
+      fmt_delete("%CPU"); fmt_delete("CPU"); fmt_delete("CP"); fmt_delete("C");
+      fmt_delete("NI");
+      fn = do_one_spec("class", NULL);
+      if(!fmt_add_after("PRI", fn))
+        return "Internal error, no PRI for -c option.";
+      fmt_delete("PRI"); /* we want a different one */
+      fn = do_one_spec("pri", NULL);
+      if(!fmt_add_after("CLS", fn)) return "Lost my CLS!";
+    }
+  }
+  if(!option_source){  /* OK to really muck with stuff */
+    format_node *fn;
+    /* Do personality-specific translations not covered by format_flags.
+     * Generally, these only get hit when personality overrides unix output.
+     * That (mostly?) means the Digital and Debian personalities.
+     */
+    if((personality & PER_ZAP_ADDR) && (format_flags & FF_Ul)){
+      fn = do_one_spec("sgi_p", NULL);
+      if(fmt_add_after("ADDR", fn)) fmt_delete("ADDR");
+    }
+    if((personality & PER_SANE_USER) && (format_flags & FF_Uf)){
+      fn = do_one_spec("user", NULL);
+      if(fmt_add_after("UID", fn)) fmt_delete("UID");
+    }
+  }
+
+  /* Could scan for duplicates (format and sort) here. Digital does. */
+  return NULL;
+}
+
diff --git a/ps/stacktrace.c b/ps/stacktrace.c
new file mode 100644 (file)
index 0000000..79814bb
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Gnu debugger stack trace code provided by Peter Mattis
+ * <petm@CSUA.Berkeley.EDU> on Thu, 2 Nov 1995
+ *
+ * Modified for easy use by Albert Cahalan.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#define INTERACTIVE 0
+#define STACK_TRACE 1
+
+char *stored_prog_name = "You forgot to set \"program\"";
+static int stack_trace_done;
+
+/***********/
+static void debug_stop(char **args){
+  execvp (args[0], args);
+  perror ("exec failed");
+  _exit (0);
+}
+
+/***********/
+static void stack_trace_sigchld(int signum){
+  stack_trace_done = 1;
+}
+
+/************/
+static void stack_trace(char **args){
+  pid_t pid;
+  int in_fd[2];
+  int out_fd[2];
+  fd_set fdset;
+  fd_set readset;
+  struct timeval tv;
+  int sel, index, state;
+  char buffer[256];
+  char c;
+
+  stack_trace_done = 0;
+  signal(SIGCHLD, stack_trace_sigchld);
+  
+  if((pipe (in_fd) == -1) || (pipe (out_fd) == -1)){
+    perror ("could open pipe");
+    _exit (0);
+  }
+
+  pid = fork ();
+  if (pid == 0){
+    close (0); dup (in_fd[0]);   /* set the stdin to the in pipe */
+    close (1); dup (out_fd[1]);  /* set the stdout to the out pipe */
+    close (2); dup (out_fd[1]);  /* set the stderr to the out pipe */
+    execvp (args[0], args);      /* exec gdb */
+    perror ("exec failed");
+    _exit (0);
+  } else {
+    if(pid == (pid_t) -1){
+      perror ("could not fork");
+      _exit (0);
+    }
+  }
+
+  FD_ZERO (&fdset);
+  FD_SET (out_fd[0], &fdset);
+
+  write (in_fd[1], "backtrace\n", 10);
+  write (in_fd[1], "p x = 0\n", 8);
+  write (in_fd[1], "quit\n", 5);
+  
+  index = 0;
+  state = 0;
+  
+  for(;;){
+    readset = fdset;
+    tv.tv_sec = 1;
+    tv.tv_usec = 0;
+
+    sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
+    if (sel == -1) break;
+    
+    if((sel > 0) && (FD_ISSET (out_fd[0], &readset))){
+      if(read (out_fd[0], &c, 1)){
+        switch(state){
+        case 0:
+          if(c == '#'){
+            state = 1;
+            index = 0;
+            buffer[index++] = c;
+          }
+          break;
+        case 1:
+          buffer[index++] = c;
+          if((c == '\n') || (c == '\r')){
+            buffer[index] = 0;
+            fprintf (stderr, "%s", buffer);
+            state = 0;
+            index = 0;
+          }
+          break;
+        default:
+          break;
+        }
+      }
+    }
+    else if(stack_trace_done) break;
+  }
+  
+  close (in_fd[0]);
+  close (in_fd[1]);
+  close (out_fd[0]);
+  close (out_fd[1]);
+  _exit (0);
+}
+
+/************/
+void debug(int method, char *prog_name){
+  pid_t pid;
+  char buf[16];
+  char *args[4] = { "gdb", NULL, NULL, NULL };
+  int x;
+  
+  snprintf (buf, 99, "%d", getpid ());
+
+  args[1] = prog_name;
+  args[2] = buf;
+
+  pid = fork ();
+  if(pid == 0){
+    switch (method){
+    case INTERACTIVE:
+      fprintf (stderr, "debug_stop\n");
+      debug_stop(args);
+      break;
+    case STACK_TRACE:
+      fprintf (stderr, "stack_trace\n");
+      stack_trace(args);
+      break;
+    }
+    _exit(0);
+  } else if(pid == (pid_t) -1){
+    perror ("could not fork");
+    return;
+  }
+
+  x = 1;
+  while(x);  /* wait for debugger? */
+}
+
+/************/
+static void stack_trace_sigsegv(int signum){
+  debug(STACK_TRACE, stored_prog_name);
+}
+
+/************/
+void init_stack_trace(char *prog_name){
+  stored_prog_name = prog_name;
+  signal(SIGSEGV, stack_trace_sigsegv);
+}
diff --git a/skill.1 b/skill.1
new file mode 100644 (file)
index 0000000..643cffe
--- /dev/null
+++ b/skill.1
@@ -0,0 +1,118 @@
+,\" t
+.\" (The preceding line is a note to broken versions of man to tell
+.\" them to pre-process this man page with tbl)
+.\" Man page for skill and snice.
+.\" Licensed under version 2 of the GNU General Public License.
+.\" Written by Albert Cahalan, converted to a man page by
+.\" Michael K. Johnson
+.\"
+.TH SKILL 1 "March 12, 1999" "Linux" "Linux User's Manual"
+.SH NAME
+skill, snice \- report process status
+
+.SH SYNOPSIS
+.nf
+skill [signal to send] [options] process selection criteria
+snice [new priority] [options] process selection criteria
+.fi
+
+.SH DESCRIPTION
+The default signal for skill is TERM. Use -l or -L to list available signals.
+Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.
+Alternate signals may be specified in three ways: -9 -SIGKILL -KILL.
+
+The default priority for snice is +4. (snice +4 ...)
+Priority numbers range from +20 (slowest) to -20 (fastest).
+Negative priority numbers are restricted to administrative users.
+
+.SH "GENERAL OPTIONS"
+.TS
+l l l.
+-f     fast mode       This is not currently useful.
+-i     interactive use T{
+You will be asked to approve each action.
+T}
+-v     verbose output  T{
+Display information about selected processes.
+T}
+-w     warnings enabled        This is not currently useful.
+-n     no action       This only displays the process ID.
+.TE
+
+.SH "PROCESS SELECTION OPTIONS"
+Selection criteria can be: terminal, user, pid, command.
+The options below may be used to ensure correct interpretation.
+.TS
+l l.
+-t     The next argument is a terminal (tty or pty).
+-u     The next argument is a username.
+-p     The next argument is a process ID number.
+-c     The next argument is a command name.
+.TE
+
+.SH SIGNALS
+The signals listed below may be available for use with skill.
+When known, numbers and default behavior are shown.
+.TS
+lB rB lB lB
+lfCW r l l.
+Name   Num     Action  Description
+.TH
+ALRM   14      exit
+HUP    1       exit
+INT    2       exit
+KILL   9       exit    this signal may not be blocked
+PIPE   13      exit
+POLL           exit
+PROF           exit
+TERM   15      exit
+USR1           exit
+USR2           exit
+VTALRM         exit
+STKFLT         exit    may not be implemented
+PWR            ignore  may exit on some systems
+WINCH          ignore
+CHLD           ignore
+URG            ignore
+TSTP           stop    may interact with the shell
+TTIN           stop    may interact with the shell
+TTOU           stop    may interact with the shell
+STOP           stop    this signal may not be blocked
+CONT           restart continue if stopped, otherwise ignore
+ABRT   6       core
+FPE    8       core
+ILL    4       core
+QUIT   3       core
+SEGV   11      core
+TRAP   5       core
+SYS            core    may not be implemented
+EMT            core    may not be implemented
+BUS            core    core dump may fail
+XCPU           core    core dump may fail
+XFSZ           core    core dump may fail
+.TE
+
+.SH EXAMPLES
+.TS
+lB lB
+lfCW l.
+Command        Description
+.TC
+snice netscape crack +7        Slow down netscape and crack
+skill -KILL -v /dev/pts/*      Kill users on new-style PTY devices
+skill -STOP torvalds davem tytso       Stop 3 users
+snice -17 root bash    Give priority to root's shell
+.TE
+
+.SH "SEE ALSO"
+top(1) kill(1) renice(1) nice(1)
+
+.SH STANDARDS
+No standards apply.
+
+.SH AUTHOR
+Albert Cahalan <acahalan@cs.uml.edu> wrote skill and snice in 1999 as a
+replacement for a non-free version. Michael K. Johnson <johnsonm@redhat.com>
+is the current maintainer of the procps collection.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/skill.c b/skill.c
new file mode 100644 (file)
index 0000000..7d78469
--- /dev/null
+++ b/skill.c
@@ -0,0 +1,557 @@
+/*
+ * Copyright 1998 by Albert Cahalan; all rights resered.
+ * This file may be used subject to the terms and conditions of the
+ * GNU Library General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Library General Public License for more details.
+ */
+#include <fcntl.h>
+#include <pwd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <proc/sig.h>
+#include <proc/devname.h>
+#include <proc/procps.h>  /* char *user_from_uid(uid_t uid) */
+
+static int f_flag, i_flag, v_flag, w_flag, n_flag;
+
+static int tty_count, uid_count, cmd_count, pid_count;
+static int *ttys;
+static uid_t *uids;
+static char **cmds;
+static int *pids;
+
+#define ENLIST(thing,addme) do{ \
+if(!thing##s) thing##s = malloc(sizeof(*thing##s)*saved_argc); \
+if(!thing##s) fprintf(stderr,"No memory.\n"),exit(2); \
+thing##s[thing##_count++] = addme; \
+}while(0)
+
+static int my_pid;
+static int saved_argc;
+static char **saved_argv;
+
+static int sig_or_pri;
+
+static int program;
+#define PROG_GARBAGE 0  /* keep this 0 */
+#define PROG_KILL  1
+#define PROG_SKILL 2
+/* #define PROG_NICE  3 */ /* easy, but the old one isn't broken */
+#define PROG_SNICE 4
+
+
+/********************************************************************/
+
+
+/***** kill or nice a process */
+static void hurt_proc(int tty, int uid, int pid, char *cmd){
+  int failed;
+  int saved_errno;
+  char dn_buf[1000];
+  dev_to_tty(dn_buf, 999, tty, pid, ABBREV_DEV);
+  if(i_flag){
+    char buf[8];
+    fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s   ? ",
+      (char*)dn_buf,user_from_uid(uid),pid,cmd
+    );
+    if(!fgets(buf,7,stdin)){
+      printf("\n");
+      exit(0);
+    }
+    if(*buf!='y' && *buf!='Y') return;
+  }
+  /* do the actual work */
+  if(program==PROG_SKILL) failed=kill(pid,sig_or_pri);
+  else                    failed=setpriority(PRIO_PROCESS,pid,sig_or_pri);
+  saved_errno = errno;
+  if(w_flag && failed){
+    fprintf(stderr, "%-8.8s %-8.8s %5d %-16.16s   ",
+      (char*)dn_buf,user_from_uid(uid),pid,cmd
+    );
+    errno = saved_errno;
+    perror("");
+    return;
+  }
+  if(i_flag) return;
+  if(v_flag){
+    printf("%-8.8s %-8.8s %5d %-16.16s\n",
+      (char*)dn_buf,user_from_uid(uid),pid,cmd
+    );
+    return;
+  }
+  if(n_flag){
+   printf("%d\n",pid);
+   return;
+  }
+}
+
+
+/***** check one process */
+static void check_proc(int pid){
+  char buf[128];
+  struct stat statbuf;
+  char *tmp;
+  int tty;
+  int fd;
+  int i;
+  if(pid==my_pid) return;
+  sprintf(buf, "/proc/%d/stat", pid); /* pid (cmd) state ppid pgrp session tty */
+  fd = open(buf,O_RDONLY);
+  if(fd==-1){  /* process exited maybe */
+    if(pids && w_flag) printf("WARNING: process %d could not be found.",pid);
+    return;
+  }
+  fstat(fd, &statbuf);
+  if(uids){  /* check the EUID */
+    i=uid_count;
+    while(i--) if(uids[i]==statbuf.st_uid) break;
+    if(i==-1) goto closure;
+  }
+  read(fd,buf,128);
+  buf[127] = '\0';
+  tmp = strrchr(buf, ')');
+  *tmp++ = '\0';
+  i = 5; while(i--) while(*tmp++!=' '); /* scan to find tty */
+  tty = atoi(tmp);
+  if(ttys){
+    i=tty_count;
+    while(i--) if(ttys[i]==tty) break;
+    if(i==-1) goto closure;
+  }
+  tmp = strchr(buf, '(') + 1;
+  if(cmds){
+    i=cmd_count;
+    /* fast comparison trick -- useful? */
+    while(i--) if(cmds[i][0]==*tmp && !strcmp(cmds[i],tmp)) break;
+    if(i==-1) goto closure;
+  }
+  /* This is where we kill/nice something. */
+/*  fprintf(stderr, "PID %d, UID %d, TTY %d,%d, COMM %s\n",
+    pid, statbuf.st_uid, tty>>8, tty&0xf, tmp
+  );
+*/
+  hurt_proc(tty, statbuf.st_uid, pid, tmp);
+closure:
+  close(fd); /* kill/nice _first_ to avoid PID reuse */
+}
+
+
+/***** debug function */
+#if 0
+static void show_lists(void){
+  int i;
+
+  fprintf(stderr, "%d TTY: ", tty_count);
+  if(ttys){
+    i=tty_count;
+    while(i--){
+      fprintf(stderr, "%d,%d%c", (ttys[i]>>8)&0xff, ttys[i]&0xff, i?' ':'\n');
+    }
+  }else fprintf(stderr, "\n");
+  
+  fprintf(stderr, "%d UID: ", uid_count);
+  if(uids){
+    i=uid_count;
+    while(i--) fprintf(stderr, "%d%c", uids[i], i?' ':'\n');
+  }else fprintf(stderr, "\n");
+  
+  fprintf(stderr, "%d PID: ", pid_count);
+  if(pids){
+    i=pid_count;
+    while(i--) fprintf(stderr, "%d%c", pids[i], i?' ':'\n');
+  }else fprintf(stderr, "\n");
+  
+  fprintf(stderr, "%d CMD: ", cmd_count);
+  if(cmds){
+    i=cmd_count;
+    while(i--) fprintf(stderr, "%s%c", cmds[i], i?' ':'\n');
+  }else fprintf(stderr, "\n");
+}
+#endif
+
+
+/***** iterate over all PIDs */
+static void iterate(void){
+  int pid;
+  DIR *d;
+  struct dirent *de;
+  if(pids){
+    pid = pid_count;
+    while(pid--) check_proc(pids[pid]);
+    return;
+  }
+#if 0
+  /* could setuid() and kill -1 to have the kernel wipe out a user */
+  if(!ttys && !cmds && !pids && !i_flag){
+  }
+#endif
+  d = opendir("/proc");
+  if(!d){
+    perror("/proc");
+    exit(1);
+  }
+  while(( de = readdir(d) )){
+    if(de->d_name[0] > '9') continue;
+    if(de->d_name[0] < '1') continue;
+    pid = atoi(de->d_name);
+    if(pid) check_proc(pid);
+  }
+  closedir (d);
+}
+
+/***** kill help */
+static void kill_usage(void){
+  fprintf(stderr,
+    "Usage:\n"
+    "  kill pid ...              Send SIGTERM to every process listed.\n"
+    "  kill signal pid ...       Send a signal to every process listed.\n"
+    "  kill -s signal pid ...    Send a signal to every process listed.\n"
+    "  kill -l                   List all signal names.\n"
+    "  kill -L                   List all signal names in a nice table.\n"
+    "  kill -l signal            Convert between signal numbers and names.\n"
+  );
+  exit(1);
+}
+
+/***** kill */
+static void kill_main(int argc, char *argv[]){
+  char *sigptr;
+  int signo = SIGTERM;
+  int exitvalue = 0;
+  if(argc<2) kill_usage();
+  if(argv[1][0]!='-'){
+    argv++;
+    argc--;
+    goto no_more_args;
+  }
+
+  /* The -l option prints out signal names. */
+  if(argv[1][1]=='l' && argv[1][2]=='\0'){
+    if(argc==2){
+      unix_print_signals();
+      exit(0);
+    }
+    /* at this point, argc must be 3 or more */
+    if(argc>128 || argv[2][0] == '-') kill_usage();
+    exit(print_given_signals(argc-2, argv+2, 80));
+  }
+
+  /* The -L option prints out signal names in a nice table. */
+  if(argv[1][1]=='L' && argv[1][2]=='\0'){
+    if(argc==2){
+      pretty_print_signals();
+      exit(0);
+    }
+    kill_usage();
+  }
+  if(argv[1][1]=='-' && argv[1][2]=='\0'){
+    argv+=2;
+    argc-=2;
+    goto no_more_args;
+  }
+  if(argv[1][1]=='-') kill_usage(); /* likely --help */
+  if(argv[1][1]=='s' && argv[1][2]=='\0'){
+    sigptr = argv[2];
+    argv+=3;
+    argc-=3;
+  }else{
+    sigptr = argv[1]+1;
+    argv+=2;
+    argc-=2;
+  }
+  signo = signal_name_to_number(sigptr);
+  if(signo<0){
+    fprintf(stderr, "ERROR: unknown signal name \"%s\".\n", sigptr);
+    kill_usage();
+  }
+no_more_args:
+  if(!argc) kill_usage();  /* nothing to kill? */
+  while(argc--){
+    long pid;
+    char *endp;
+    pid = strtol(argv[argc],&endp,10);
+    if(!*endp){
+      if(!kill((pid_t)pid,signo)) continue;
+      exitvalue = 1;
+      continue;
+    }
+    fprintf(stderr, "ERROR: garbage process ID \"%s\".\n", argv[argc]);
+    kill_usage();
+  }
+  exit(exitvalue);
+}
+
+/***** skill/snice help */
+static void skillsnice_usage(void){
+  if(program==PROG_SKILL){
+    fprintf(stderr,
+      "Usage:   skill [signal to send] [options] process selection criteria\n"
+      "Example: skill -KILL -v pts/*\n"
+      "\n"
+      "The default signal is TERM. Use -l or -L to list available signals.\n"
+      "Particularly useful signals include HUP, INT, KILL, STOP, CONT, and 0.\n"
+      "Alternate signals may be specified in three ways: -SIGKILL -KILL -9\n"
+    );
+  }else{
+    fprintf(stderr,
+      "Usage:   snice [new priority] [options] process selection criteria\n"
+      "Example: snice netscape crack +7\n"
+      "\n"
+      "The default priority is +4. (snice +4 ...)\n"
+      "Priority numbers range from +20 (slowest) to -20 (fastest).\n"
+      "Negative priority numbers are restricted to administrative users.\n"
+    );
+  }
+  fprintf(stderr,
+    "\n"
+    "General options:\n"
+    "-f  fast mode            This is not currently useful.\n"
+    "-i  interactive use      You will be asked to approve each action.\n"
+    "-v  verbose output       Display information about selected processes.\n"
+    "-w  warnings enabled     This is not currently useful.\n"
+    "-n  no action            This only displays the process ID.\n"
+    "\n"
+    "Selection criteria can be: terminal, user, pid, command.\n"
+    "The options below may be used to ensure correct interpretation.\n"
+    "-t  The next argument is a terminal (tty or pty).\n"
+    "-u  The next argument is a username.\n"
+    "-p  The next argument is a process ID number.\n"
+    "-c  The next argument is a command name.\n"
+  );
+  exit(1);
+}
+
+#if 0
+static void _skillsnice_usage(int line){
+  fprintf(stderr,"Something at line %d.\n", line);
+  skillsnice_usage();
+}
+#define skillsnice_usage() _skillsnice_usage(__LINE__)
+#endif
+
+#define NEXTARG (argc?( argc--, ((argptr=*++argv)) ):NULL)
+
+/***** common skill/snice argument parsing code */
+#define NO_PRI_VAL ((int)0xdeafbeef)
+static void skillsnice_parse(int argc, char *argv[]){
+  int signo = -1;
+  int prino = NO_PRI_VAL;
+  int force = 0;
+  int num_found = 0;
+  char *argptr;
+  if(argc<2) skillsnice_usage();
+  if(argc==2 && argv[1][0]=='-'){
+    if(!strcmp(argv[1],"-L")){
+      pretty_print_signals();
+      exit(0);
+    }
+    if(!strcmp(argv[1],"-l")){
+      unix_print_signals();
+      exit(0);
+    }
+    skillsnice_usage();
+  }
+  NEXTARG;
+  /* Time for serious parsing. What does "skill -int 123 456" mean? */
+  while(argc){
+    if(force && !num_found){  /* if forced, _must_ find something */
+      fprintf(stderr,"ERROR: -%c used with bad data.\n", force);
+      skillsnice_usage();
+    }
+    force = 0;
+    if(program==PROG_SKILL && signo<0 && *argptr=='-'){
+      signo = signal_name_to_number(argptr+1);
+      if(signo>=0){      /* found a signal */
+        if(!NEXTARG) break;
+        continue;
+      }
+    }
+    if(program==PROG_SNICE && prino==NO_PRI_VAL
+    && (*argptr=='+' || *argptr=='-') && argptr[1]){
+      long val;
+      char *endp;
+      val = strtol(argptr,&endp,10);
+      if(!*endp && val<=999 && val>=-999){
+        prino=val;
+        if(!NEXTARG) break;
+        continue;
+      }
+    }
+    /* If '-' found, collect any flags. (but lone "-" is a tty) */
+    if(*argptr=='-' && argptr[1]){
+      argptr++;
+      do{
+        switch(( force = *argptr++ )){
+        default:  skillsnice_usage();
+        case 't':
+        case 'u':
+        case 'p':
+        case 'c':
+          if(!*argptr){ /* nothing left here, *argptr is '\0' */
+            if(!NEXTARG){
+              fprintf(stderr,"ERROR: -%c with nothing after it.\n", force);
+              skillsnice_usage();
+            }
+          }
+          goto selection_collection;
+        case 'f': f_flag++; break;
+        case 'i': i_flag++; break;
+        case 'v': v_flag++; break;
+        case 'w': w_flag++; break;
+        case 'n': n_flag++; break;
+        case 0:
+          NEXTARG;
+          /*
+           * If no more arguments, all the "if(argc)..." tests will fail
+           * and the big loop will exit.
+           */
+        } /* END OF SWITCH */
+      }while(force);
+    } /* END OF IF */
+selection_collection:
+    num_found = 0; /* we should find at least one thing */
+    switch(force){ /* fall through each data type */
+    default: skillsnice_usage();
+    case 0: /* not forced */
+    case 't':
+      if(argc){
+        struct stat sbuf;
+        char path[32];
+        if(!argptr) skillsnice_usage(); /* Huh? Maybe "skill -t ''". */
+        snprintf(path,32,"/dev/%s",argptr);
+        if(stat(path, &sbuf)>=0 && S_ISCHR(sbuf.st_mode)){
+          num_found++;
+          ENLIST(tty,sbuf.st_rdev);
+          if(!NEXTARG) break;
+        }else if(!(argptr[1])){  /* if only 1 character */
+          switch(*argptr){
+          default:
+            if(stat(argptr,&sbuf)<0) break; /* the shell eats '?' */
+          case '-':
+          case '?':
+            num_found++;
+            ENLIST(tty,-1);
+            if(!NEXTARG) break;
+          }
+        }
+      }
+      if(force) continue;
+    case 'u':
+      if(argc){
+        struct passwd *passwd_data;
+        passwd_data = getpwnam(argptr);
+        if(passwd_data){
+          num_found++;
+          ENLIST(uid,passwd_data->pw_uid);
+          if(!NEXTARG) break;
+        }
+      }
+      if(force) continue;
+    case 'p':
+      if(argc && *argptr>='0' && *argptr<='9'){
+        char *endp;
+        int num;
+        num = strtol(argptr, &endp, 0);
+        if(*endp == '\0'){
+          num_found++;
+          ENLIST(pid,num);
+          if(!NEXTARG) break;
+        }
+      }
+      if(force) continue;
+      if(num_found) continue; /* could still be an option */
+    case 'c':
+      if(argc){
+        num_found++;
+        ENLIST(cmd,argptr);
+        if(!NEXTARG) break;
+      }
+    } /* END OF SWITCH */
+  } /* END OF WHILE */
+  /* No more arguments to process. Must sanity check. */
+  if(!tty_count && !uid_count && !cmd_count && !pid_count){
+    fprintf(stderr,"ERROR: no process selection criteria.\n");
+    skillsnice_usage();
+  }
+  if((f_flag|i_flag|v_flag|w_flag|n_flag) & ~1){
+    fprintf(stderr,"ERROR: general flags may not be repeated.\n");
+    skillsnice_usage();
+  }
+  if(i_flag && (v_flag|f_flag|n_flag)){
+    fprintf(stderr,"ERROR: -i makes no sense with -v, -f, and -n.\n");
+    skillsnice_usage();
+  }
+  if(v_flag && (i_flag|f_flag)){
+    fprintf(stderr,"ERROR: -v makes no sense with -i and -f.\n");
+    skillsnice_usage();
+  }
+  if(n_flag && signo>=0){
+    fprintf(stderr,"ERROR: -n makes no sense with a signal.\n");
+    skillsnice_usage();
+  }
+  if(n_flag && prino!=NO_PRI_VAL){
+    fprintf(stderr,"ERROR: -n makes no sense with a priority value.\n");
+    skillsnice_usage();
+  }
+  /* OK, set up defaults */
+  if(prino==NO_PRI_VAL) prino=4;
+  if(signo<0) signo=SIGTERM;
+  if(n_flag){
+    program=PROG_SKILL;
+    signo=0; /* harmless */
+  }
+  if(program==PROG_SKILL) sig_or_pri = signo;
+  else sig_or_pri = prino;
+}
+
+/***** main body */
+int main(int argc, char *argv[]){
+  char *tmpstr;
+  my_pid = getpid();
+  saved_argv = argv;
+  saved_argc = argc;
+  if(!argc){
+    fprintf(stderr,"ERROR: could not determine own name.\n");
+    exit(1);
+  }
+  tmpstr=strrchr(*argv,'/');
+  if(tmpstr) tmpstr++;
+  if(!tmpstr) tmpstr=*argv;
+  program = PROG_GARBAGE;
+  if(*tmpstr=='s'){
+    setpriority(PRIO_PROCESS,my_pid,-20);
+    if(!strcmp(tmpstr,"snice")) program = PROG_SNICE;
+    if(!strcmp(tmpstr,"skill")) program = PROG_SKILL;
+  }else{
+    if(!strcmp(tmpstr,"kill")) program = PROG_KILL;
+  }
+  switch(program){
+  case PROG_SNICE:
+  case PROG_SKILL:
+    skillsnice_parse(argc, argv);
+/*    show_lists(); */
+    iterate(); /* this is it, go get them */
+    break;
+  case PROG_KILL:
+    kill_main(argc, argv);
+    break;
+  default:
+    fprintf(stderr,"ERROR: no \"%s\" support.\n",tmpstr);
+  }
+  return 0;
+}
+
+
diff --git a/snice.1 b/snice.1
new file mode 100644 (file)
index 0000000..1595a80
--- /dev/null
+++ b/snice.1
@@ -0,0 +1 @@
+.so man1/skill.1
diff --git a/sysctl.8 b/sysctl.8
new file mode 100644 (file)
index 0000000..1491252
--- /dev/null
+++ b/sysctl.8
@@ -0,0 +1,74 @@
+.\" Copyright 1999, George Staikos (staikos@0wned.org)
+.\" This file may be used subject to the terms and conditions of the
+.\" GNU General Public License Version 2, or any later version
+.\" at your option, as published by the Free Software Foundation.
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details."
+.TH SYSCTL 8 "21 Sep 1999" "" ""
+.SH NAME
+sysctl \- configure kernel parameters at runtime
+.SH SYNOPSIS
+.B "sysctl [-n] variable ..."
+.br
+.B "sysctl [-n] -w variable=value ..."
+.br
+.B "sysctl [-n] -p <filename>    (default /etc/sysctl.conf)"
+.br
+.B "sysctl [-n] -a"
+.br
+.B "sysctl [-n] -A"
+.SH DESCRIPTION
+.B sysctl
+is used to modify kernel parameters at runtime.  The parameters available
+are those listed under /proc/sys/.  Procfs is required for 
+.B sysctl(8)
+support in Linux.  You can use
+.B sysctl(8)
+to both read and write sysctl data.
+.SH PARAMETERS
+.TP
+.B "variable"
+The name of a key to read from.  An example is kernel.ostype.  The '/'
+separator is also accepted in place of a '.'.
+.TP
+.B "variable=value"
+To set a key, use the form variable=value, where variable is the key and
+value is the value to set it to.  If the value contains quotes or characters
+which are parsed by the shell, you may need to enclose the value in double
+quotes.  This requires the -w parameter to use.
+.TP
+.B "-n"
+Use this option to disable printing of the key name when printing values.
+.TP
+.B "-w"
+Use this option when you want to change a sysctl setting.
+.TP
+.B "-p"
+Load in sysctl settings from the file specified or /etc/sysctl.conf if none given.
+.TP
+.B "-a"
+Display all values currently available.
+.TP
+.B "-A"
+Display all values currently available in table form.
+.SH EXAMPLES
+.TP
+/sbin/sysctl -a
+.TP
+/sbin/sysctl -n kernel.hostname
+.TP
+/sbin/sysctl -w kernel.domainname="example.com"
+.TP
+/sbin/sysctl -p /etc/sysctl.conf 
+.SH FILES
+.I /proc/sys
+.I /etc/sysctl.conf
+.SH SEE ALSO
+.BR sysctl.conf (5)
+.SH BUGS
+The -A parameter behaves just as -a does.
+.SH AUTHOR
+George Staikos, <staikos@0wned.org>
+
diff --git a/sysctl.c b/sysctl.c
new file mode 100644 (file)
index 0000000..e1af4bd
--- /dev/null
+++ b/sysctl.c
@@ -0,0 +1,437 @@
+
+/*
+ * Sysctl 1.01 - A utility to read and manipulate the sysctl parameters
+ *
+ *
+ * "Copyright 1999 George Staikos
+ * This file may be used subject to the terms and conditions of the
+ * GNU General Public License Version 2, or any later version
+ * at your option, as published by the Free Software Foundation.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details."
+ *
+ * Changelog:
+ *            v1.01:
+ *                   - added -p <preload> to preload values from a file
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+
+/*
+ *    Additional types we might need.
+ */
+typedef int bool;
+
+static bool true  = 1;
+static bool false = 0;
+
+
+/*
+ *    Function Prototypes
+ */
+int Usage(const char *name);
+void Preload(const char *filename);
+int WriteSetting(const char *setting);
+int ReadSetting(const char *setting);
+int DisplayAll(const char *path, bool ShowTableUtil);
+
+
+/*
+ *    Globals...
+ */
+
+const char *PROC_PATH = "/proc/sys/";
+const char *DEFAULT_PRELOAD = "/etc/sysctl.conf";
+static bool PrintName;
+static bool PrintNewline;
+
+/* error messages */
+const char *ERR_UNKNOWN_PARAMETER = "error: Unknown parameter '%s'\n";
+const char *ERR_MALFORMED_SETTING = "error: Malformed setting '%s'\n";
+const char *ERR_NO_EQUALS = "error: '%s' must be of the form name=value\n";
+const char *ERR_INVALID_KEY = "error: '%s' is an unknown key\n";
+const char *ERR_UNKNOWN_WRITING = "error: unknown error %d setting key '%s'\n";
+const char *ERR_UNKNOWN_READING = "error: unknown error %d reading key '%s'\n";
+const char *ERR_PERMISSION_DENIED = "error: permission denied on key '%s'\n";
+const char *ERR_OPENING_DIR = "error: unable to open directory '%s'\n";
+const char *ERR_PRELOAD_FILE = "error: unable to open preload file '%s'\n";
+const char *WARN_BAD_LINE = "warning: %s(%d): invalid syntax, continuing...\n";
+
+
+#define DOTSLASH(x) do{ \
+  char *p_ = (x);           \
+  for(;;){                  \
+    p_ = strchr(p_, '.');   \
+    if(!p_) break;          \
+    *p_ = '/';              \
+  }                         \
+}while(0)
+
+#define SLASHDOT(x) do{ \
+  char *p_ = (x);           \
+  for(;;){                  \
+    p_ = strchr(p_, '.');   \
+    if(!p_) break;          \
+    *p_ = '/';              \
+  }                         \
+}while(0)
+
+
+/*
+ *    Main... 
+ *
+ */
+int main(int argc, char **argv) {
+const char *me = (const char *)basename(argv[0]);
+bool SwitchesAllowed = true;
+bool WriteMode = false;
+int ReturnCode = 0;
+const char *preloadfile = DEFAULT_PRELOAD;
+
+   PrintName = true;
+   PrintNewline = true;
+
+   if (argc < 2) {
+       return Usage(me);
+   } /* endif */
+
+   argv++;
+
+   for (; argv && *argv && **argv; argv++) {
+      if (SwitchesAllowed && **argv == '-') {        /* we have a switch */
+         switch((*argv)[1]) {
+         case 'b':
+              /* This is "binary" format, which means more for BSD. */
+              PrintNewline = false;
+           /* FALL THROUGH */
+         case 'n':
+              PrintName = false;
+           break;
+         case 'w':
+              SwitchesAllowed = false;
+              WriteMode = true;
+           break;
+         case 'p':
+              argv++;
+              if (argv && *argv && **argv) {
+                 preloadfile = *argv;
+              } /* endif */
+
+              Preload(preloadfile);
+              return(0);
+           break;
+         case 'a': /* string and integer values (for Linux, all of them) */
+         case 'A': /* the above, including "opaques" (would be unprintable) */
+         case 'X': /* the above, with opaques completly printed in hex */
+              SwitchesAllowed = false;
+              return DisplayAll(PROC_PATH, ((*argv)[1] == 'a') ? false : true);
+         case 'h':
+         case '?':
+              return Usage(me);
+         default:
+              fprintf(stderr, ERR_UNKNOWN_PARAMETER, *argv);
+              return Usage(me);
+         } /* end switch */
+      } else {
+         SwitchesAllowed = false;
+         if (WriteMode)
+            ReturnCode = WriteSetting(*argv);
+         else ReadSetting(*argv);
+      } /* end if */
+   } /* end for */      
+
+return ReturnCode;
+} /* end main */
+
+
+
+
+
+/*
+ *     Display the usage format
+ *
+ */
+int Usage(const char *name) {
+   printf("usage:  %s [-n] variable ... \n"
+          "        %s [-n] -w variable=value ... \n" 
+          "        %s [-n] -a \n" 
+          "        %s [-n] -p <file>   (default /etc/sysctl.conf) \n"
+          "        %s [-n] -A\n", name, name, name, name, name);
+return -1;
+}  /* end Usage() */
+
+
+/*
+ *     Strip the leading and trailing spaces from a string
+ *
+ */
+char *StripLeadingAndTrailingSpaces(char *oneline) {
+char *t;
+
+if (!oneline || !*oneline)
+   return oneline;
+
+t = oneline;
+t += strlen(oneline)-1;
+
+while ((*t == ' ' || *t == '\t' || *t == '\n' || *t == '\r') && t != oneline)
+   *t-- = 0;
+
+t = oneline;
+
+while ((*t == ' ' || *t == '\t') && *t != 0)
+   t++;
+
+return t;
+} /* end StripLeadingAndTrailingSpaces() */
+
+
+
+/*
+ *     Preload the sysctl's from the conf file
+ *           - we parse the file and then reform it (strip out whitespace)
+ *
+ */
+void Preload(const char *filename) {
+FILE *fp;
+char oneline[257];
+char buffer[257];
+char *t;
+int n = 0;
+char *name, *value;
+
+   if (!filename || ((fp = fopen(filename, "r")) == NULL)) {
+      fprintf(stderr, ERR_PRELOAD_FILE, filename);
+      return;
+   } /* endif */
+
+   while (fgets(oneline, 256, fp)) {
+      oneline[256] = 0;
+      n++;
+      t = StripLeadingAndTrailingSpaces(oneline);
+
+      if (strlen(t) < 2)
+         continue;
+
+      if (*t == '#' || *t == ';')
+         continue;
+
+      name = strtok(t, "=");
+      if (!name || !*name) {
+         fprintf(stderr, WARN_BAD_LINE, filename, n);
+         continue;
+      } /* endif */
+
+      StripLeadingAndTrailingSpaces(name);
+
+      value = strtok(NULL, "\n\r");
+      if (!value || !*value) {
+         fprintf(stderr, WARN_BAD_LINE, filename, n);
+         continue;
+      } /* endif */
+
+      while ((*value == ' ' || *value == '\t') && *value != 0)
+         value++;
+
+      sprintf(buffer, "%s=%s", name, value);
+      WriteSetting(buffer);
+   } /* endwhile */
+
+   fclose(fp);
+} /* end Preload() */
+
+
+
+/*
+ *     Write a sysctl setting 
+ *
+ */
+int WriteSetting(const char *setting) {
+int rc = 0;
+const char *name = setting;
+const char *value;
+const char *equals;
+char *tmpname;
+FILE *fp;
+char *outname;
+
+   if (!name) {                /* probably dont' want to display this  err */
+      return 0;
+   } /* end if */
+
+   equals = index(setting, '=');
+   if (!equals) {
+      fprintf(stderr, ERR_NO_EQUALS, setting);
+      return -1;
+   } /* end if */
+
+   value = equals + sizeof(char);      /* point to the value in name=value */   
+
+   if (!*name || !*value || name == equals) { 
+      fprintf(stderr, ERR_MALFORMED_SETTING, setting);
+      return -2;
+   } /* end if */
+
+   tmpname = (char *)malloc((equals-name+1+strlen(PROC_PATH))*sizeof(char));
+   outname = (char *)malloc((equals-name+1)*sizeof(char));
+
+   strcpy(tmpname, PROC_PATH);
+   strncat(tmpname, name, (int)(equals-name)); 
+   tmpname[equals-name+strlen(PROC_PATH)] = 0;
+   strncpy(outname, name, (int)(equals-name)); 
+   outname[equals-name] = 0;
+   DOTSLASH(tmpname); /* change . to / */
+   SLASHDOT(outname); /* change / to . */
+
+   fp = fopen(tmpname, "w");
+
+   if (!fp) {
+      switch(errno) {
+      case ENOENT:
+         fprintf(stderr, ERR_INVALID_KEY, outname);
+        break;
+      case EACCES:
+         fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+        break;
+      default:
+         fprintf(stderr, ERR_UNKNOWN_WRITING, errno, outname);
+        break;
+      } /* end switch */
+      rc = -1;
+   } else {
+      fprintf(fp, "%s\n", value);
+      fclose(fp);
+      if (PrintName) {
+         fprintf(stdout, "%s = %s\n", outname, value);
+      } else {
+         if (PrintNewline)
+            fprintf(stdout, "%s\n", value);
+         else
+            fprintf(stdout, "%s", value);
+      }
+   } /* endif */
+
+   free(tmpname);
+   free(outname);
+return rc;
+} /* end WriteSetting() */
+
+
+
+/*
+ *     Read a sysctl setting 
+ *
+ */
+int ReadSetting(const char *setting) {
+int rc = 0;
+char *tmpname, *outname;
+char inbuf[1025];
+const char *name = setting;
+FILE *fp;
+
+   if (!setting || !*setting) {
+      fprintf(stderr, ERR_INVALID_KEY, setting);
+   } /* endif */
+
+   tmpname = (char *)malloc((strlen(name)+strlen(PROC_PATH)+1)*sizeof(char));
+   outname = (char *)malloc((strlen(name)+1)*sizeof(char));
+
+   strcpy(tmpname, PROC_PATH);
+   strcat(tmpname, name); 
+   strcpy(outname, name); 
+
+   DOTSLASH(tmpname); /* change . to / */
+   SLASHDOT(outname); /* change / to . */
+
+   fp = fopen(tmpname, "r");
+
+   if (!fp) {
+      switch(errno) {
+      case ENOENT:
+         fprintf(stderr, ERR_INVALID_KEY, outname);
+        break;
+      case EACCES:
+         fprintf(stderr, ERR_PERMISSION_DENIED, outname);
+        break;
+      default:
+         fprintf(stderr, ERR_UNKNOWN_READING, errno, outname);
+        break;
+      } /* end switch */
+      rc = -1;
+   } else {
+      while(fgets(inbuf, 1024, fp)) {
+         /* already has the \n in it */
+         if (PrintName) {
+            fprintf(stdout, "%s = %s", outname, inbuf);
+         } else {
+            if (!PrintNewline) {
+              char *nlptr = strchr(inbuf,'\n');
+              if(nlptr) *nlptr='\0';
+            }
+            fprintf(stdout, "%s", inbuf);
+         }
+      } /* endwhile */
+      fclose(fp);
+   } /* endif */
+
+   free(tmpname);
+   free(outname);
+return rc;
+} /* end ReadSetting() */
+
+
+
+/*
+ *     Display all the sysctl settings 
+ *
+ */
+int DisplayAll(const char *path, bool ShowTableUtil) {
+int rc = 0;
+int rc2;
+DIR *dp;
+struct dirent *de;
+char *tmpdir;
+struct stat ts;
+
+   dp = opendir(path);
+
+   if (!dp) {
+      fprintf(stderr, ERR_OPENING_DIR, path);
+      rc = -1;
+   } else {
+      readdir(dp); readdir(dp);   /* skip . and .. */
+      while (( de = readdir(dp) )) {
+         tmpdir = (char *)malloc(strlen(path)+strlen(de->d_name)+2);
+         sprintf(tmpdir, "%s%s", path, de->d_name);
+         rc2 = stat(tmpdir, &ts);       /* should check this return code */
+         if (rc2 != 0) {
+            perror(tmpdir);
+         } else {
+            if (S_ISDIR(ts.st_mode)) {
+               strcat(tmpdir, "/");
+               DisplayAll(tmpdir, ShowTableUtil);
+            } else {
+               rc |= ReadSetting(tmpdir+strlen(PROC_PATH));
+            } /* endif */
+         } /* endif */
+         free(tmpdir);
+      } /* end while */
+      closedir(dp);
+   } /* endif */
+
+return rc;
+} /* end DisplayAll() */
+
diff --git a/sysctl.conf.5 b/sysctl.conf.5
new file mode 100644 (file)
index 0000000..0d8b073
--- /dev/null
@@ -0,0 +1,51 @@
+.\" Copyright 1999, George Staikos (staikos@0wned.org)
+.\" This file may be used subject to the terms and conditions of the
+.\" GNU General Public License Version 2, or any later version
+.\" at your option, as published by the Free Software Foundation.
+.\" This program is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+.\" GNU General Public License for more details."
+.TH SYSCTL.CONF 5 "21 Sep 1999" "" ""
+.SH NAME
+sysctl.conf \- sysctl(8) preload/configuration file 
+.SH DESCRIPTION
+.I sysctl.conf
+is a simple file containing sysctl values to be read in and set by sysctl(8).
+The syntax is simply as follows:
+.RS
+.sp
+.nf
+.ne 7
+# comment
+; comment
+
+  token  =   value
+.fi
+.sp
+.RE
+.PP
+Note that blank lines are ignored, and whitespace before and after a token or
+value is ignored, although a value can contain whitespace within.  Lines which
+begin with a # or ; are considered comments and ignored.
+.SH EXAMPLE
+.RS
+.sp
+.nf
+.ne 7
+# sysctl.conf sample
+#
+
+  kernel.domainname = example.com
+; this one has a space which will be written to the sysctl!
+  kernel.modprobe = /sbin/mod probe
+
+.fi
+.sp
+.RE
+.PP
+.SH SEE ALSO
+.BR sysctl(8)
+.SH AUTHOR
+George Staikos, <staikos@0wned.org>
+
diff --git a/t b/t
new file mode 100755 (executable)
index 0000000..c7b3c06
--- /dev/null
+++ b/t
@@ -0,0 +1,6 @@
+#!/bin/sh
+#
+# Wow, using $* causes great pain with embedded spaces in arguments.
+# The "$@" won't break that into 2 arguments.
+#
+LD_PRELOAD=proc/libproc.so exec ./top "$@"
diff --git a/tload.1 b/tload.1
new file mode 100644 (file)
index 0000000..aec34dd
--- /dev/null
+++ b/tload.1
@@ -0,0 +1,50 @@
+.\"             -*-Nroff-*-
+.\"  This page Copyright (C) 1993 Matt Welsh, mdw@tc.cornell.edu.
+.\"  Freely distributable under the terms of the GPL
+.TH TLOAD 1 "20 Mar 1993 " "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+tload \- graphic representation of system load average
+.SH SYNOPSIS
+.B tload
+.RB [ "\-V" "] [" "\-s"
+.IR scale "] ["
+.BI "\-d" " delay"
+.RI "] [" tty ]
+.SH DESCRIPTION
+\fBtload\fP prints a graph of the current system load average to the 
+specified \fItty\fP (or the tty of the tload process if none is specified).
+.SS Options
+The
+.BI "\-s" " scale"
+option allows a vertical scale to be specified for the
+display (in characters between graph ticks); thus, a smaller value
+represents a larger scale, and vice versa.
+
+The
+.BI "\-d" " delay"
+sets the delay between graph updates in seconds.
+.PP
+.SH FILES
+.I /proc/loadavg
+load average information
+
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR w (1)
+
+.SH BUGS
+The
+.BI "\-d" " delay"
+option sets the time argument for an
+.BR alarm (2);
+if -d 0 is specified, the alarm is set to 0, which will never send the
+.B SIGALRM
+and update the display.
+
+.SH AUTHORS
+Branko Lankester, David Engel <david@ods.com>, and 
+Michael K. Johnson <johnsonm@redhat.com>.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/tload.c b/tload.c
new file mode 100644 (file)
index 0000000..1d346bb
--- /dev/null
+++ b/tload.c
@@ -0,0 +1,156 @@
+/*
+ * tload.c     - terminal version of xload
+ *
+ * Options:
+ *     -s initial scaling exponent (default = 6)
+ *     -d delay
+ *
+ * Copyright (c) 1992 Branko Lankester
+ * /proc changes by David Engel (david@ods.com)
+ * Made a little more efficient by Michael K. Johnson (johnsonm@sunsite.unc.edu)
+ */
+#include "proc/version.h"
+#include "proc/sysinfo.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+static char *screen;
+
+static int nrows = 25;
+static int ncols = 80;
+static int scr_size;
+static int fd=1;
+static int dly=5;
+static jmp_buf jb;
+
+extern int optind;
+extern char *optarg;
+
+static void alrm(int signo)
+{
+    (void)signo;
+    signal(SIGALRM, alrm);
+    alarm(dly);
+}
+
+static void setsize(int i)
+{
+    struct winsize win;
+
+    signal(SIGWINCH, setsize);
+    if (ioctl(fd, TIOCGWINSZ, &win) != -1) {
+       if (win.ws_col > 0)
+           ncols = win.ws_col;
+       if (win.ws_row > 0)
+           nrows = win.ws_row;
+    }
+    scr_size = nrows * ncols;
+    if (screen == NULL)
+       screen = (char *) malloc(scr_size);
+    else
+       screen = (char *) realloc(screen, scr_size);
+
+    if (screen == NULL) {
+       perror("");
+       exit(1);
+    }
+    memset(screen, ' ', scr_size-1);
+    *(screen + scr_size - 2) = '\0';
+    if (i)
+       longjmp(jb, 0);
+}
+
+int main(int argc, char **argv)
+{
+    int lines, row, col=0;
+    int i, opt;
+    double av[3];
+    static double max_scale, scale_fact;
+    char *scale_arg = NULL;
+
+    while ((opt = getopt(argc, argv, "s:d:V")) != -1)
+       switch (opt) {
+           case 's': scale_arg = optarg; break;
+           case 'd': dly = atoi(optarg); break;
+           case 'V': display_version(); exit(0); break;
+           default:
+               printf("usage: tload [-V] [-d delay] [-s scale] [tty]\n");
+               exit(1);
+       }
+
+    if (argc > optind) {
+       if ((fd = open(argv[optind], 1)) == -1) {
+           perror(argv[optind]);
+           exit(1);
+       }
+    }
+
+    setsize(0);
+
+    if (scale_arg)
+      max_scale = atof(scale_arg);
+    else
+      max_scale = nrows;
+
+    scale_fact = max_scale;
+
+    setjmp(jb);
+    col = 0;
+    alrm(0);
+
+    while (1) {
+
+       if (scale_fact < max_scale)
+           scale_fact *= 2.0; /* help it drift back up. */
+
+       loadavg(&av[0], &av[1], &av[2]);
+
+    repeat:
+       lines = av[0] * scale_fact;
+       row = nrows-1;
+
+       while (--lines >= 0) {
+           *(screen + row * ncols + col) = '*';
+           if (--row < 0) {
+               scale_fact /= 2.0;
+               goto repeat;
+           }
+       }
+       while (row >= 0)
+           *(screen + row-- * ncols + col) = ' ';
+
+       for (i = 1; ; ++i) {
+           char *p;
+           row = nrows - (i * scale_fact);
+           if (row < 0)
+               break;
+           if (*(p = screen + row * ncols + col) == ' ')
+               *p = '-';
+           else
+               *p = '=';
+       }
+
+       if (++col == ncols) {
+           --col;
+           memmove(screen, screen + 1, scr_size-1);
+
+           for(row = nrows-2; row >= 0; --row)
+               *(screen + row * ncols + col) = ' ';
+       }
+       i = sprintf(screen, " %.2f, %.2f, %.2f",
+               av[0], av[1], av[2]);
+       if (i>0)
+           screen[i] = ' ';
+
+       write(fd, "\033[H", 3);
+       write(fd, screen, scr_size - 1);
+       pause();
+    }
+}
diff --git a/tmp-junk.c b/tmp-junk.c
new file mode 100644 (file)
index 0000000..db011b1
--- /dev/null
@@ -0,0 +1,730 @@
+/*
+ * w.c  v1.4
+ *
+ * An alternative "w" program for Linux.
+ * Shows users and their processes.
+ *
+ * Copyright (c) Dec 1993, Oct 1994 Steve "Mr. Bassman" Bryant
+ *             bassman@hpbbi30.bbn.hp.com (Old address)
+ *             bassman@muttley.soc.staffs.ac.uk
+ *
+ * Info:
+ *     I starting writing as an improvement of the w program included
+ * with linux. The idea was to add in some extra functionality to the
+ * program, and see if I could fix a couple of bugs which seemed to
+ * occur.
+ *                                             Mr. Bassman, 10/94
+ *
+ * Acknowledgments:
+ *
+ * The original version of w:
+ *     Copyright (c) 1993 Larry Greenfield  (greenfie@gauss.rutgers.edu)
+ *
+ * Uptime routine and w mods:
+ *     Michael K. Johnson  (johnsonm@stolaf.edu)
+ *
+ *
+ * Distribution:
+ *     This program is freely distributable under the terms of copyleft.
+ *     No warranty, no support, use at your own risk etc.
+ *
+ * Compilation:
+ *     gcc -O -o w sysinfo.c whattime.c w.c
+ *
+ * Usage:
+ *     w [-hfusd] [user]
+ *
+ *
+ * $Log: tmp-junk.c,v $
+ * Revision 1.1  2002/02/01 22:46:37  csmall
+ * Initial revision
+ *
+ * Revision 1.5  1994/10/26  17:57:35  bassman
+ * Loads of stuff - see comments.
+ *
+ * Revision 1.4  1994/01/01  12:57:21  johnsonm
+ * Added RCS, and some other fixes.
+ *
+ * Revision history:
+ * Jan 01, 1994 (mkj): Eliminated GCC warnings, took out unnecessary
+ *                     dead variables in fscanf, replacing them with
+ *                     *'d format qualifiers.  Also added RCS stuff.
+ * Oct 26, 1994 (bass):        Tidied up the code, fixed bug involving corrupt
+ *                     utmp records.  Added switch for From field;
+ *                     default is compile-time set.  Added -d option
+ *                     as a remnant from BSD 'w'.  Fixed bug so it now
+ *                     behaves if the first process on a tty isn't owned
+ *                     by the person first logged in on that tty, and
+ *                     also detects su'd users.  Changed the tty format
+ *                     to the short one.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <utmp.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include "proc/whattime.h"
+
+
+#define TRUE           1
+#define FALSE          0
+/*
+ * Default setting for whether to have a From field.  The -f switch
+ * toggles this - if the default is to have it, using -f will turn
+ * it off; if the default is not to have it, the -f switch will put
+ * it in.  Possible values are TRUE (to have the field by default),
+ * and FALSE.
+ */
+#define DEFAULT_FROM   TRUE
+#define ZOMBIE         "<zombie>"
+
+
+void put_syntax();
+char *idletime();
+char *logintime();
+
+static char rcsid[]="$Id: tmp-junk.c,v 1.1 2002/02/01 22:46:37 csmall Exp $";
+
+
+void main (argc, argv)
+
+int argc;
+char *argv[];
+
+{
+    int header=TRUE, long_format=TRUE, ignore_user=TRUE,
+       from_switch=DEFAULT_FROM, show_pid=FALSE, line_length;
+    int i, j;
+    struct utmp *utmp_rec;
+    struct stat stat_rec;
+    struct passwd *passwd_entry;
+    uid_t uid;
+    char username[9], tty[13], rhost[17], login_time[27];
+    char idle_time[7], what[1024], pid[10];
+    char out_line[1024], file_name[256];
+    char search_name[9];
+    int  jcpu, pcpu, tpgid, curr_pid, utime, stime, cutime, cstime;
+    char /*ch,*/ state, comm[1024], *columns_ptr;
+    FILE *fp;
+
+
+    search_name[0] = '\0';
+
+
+    /*
+     * Process the command line
+     */
+    if (argc > 1)
+    {
+       /*
+        * Args that start with '-'
+        */
+       for (i = 1; ((i < argc) && (argv[i][0] == '-')); i ++)
+       {
+           for (j = 1; argv[i][j] != '\0'; j++)
+           {
+               switch (argv[i][j])
+               {
+                   case 'h':
+                       header = FALSE;
+                       break;
+                   case 's':
+                       long_format = FALSE;
+                       break;
+                   case 'u':
+                       ignore_user = FALSE;
+                       break;
+                   case 'd':
+                       show_pid = TRUE;
+                       break;
+                   case 'f':
+                       if (DEFAULT_FROM == TRUE)
+                           from_switch = FALSE;
+                       else
+                           from_switch = TRUE;
+                       break;
+                   default:
+                       fprintf (stderr, "w: unknown option: '%c'\n",
+                           argv[i][j]);
+                       put_syntax ();
+                       break;
+               }
+           }
+       }
+
+
+       /*
+        * Check for arg not starting with '-' (ie: username)
+        */
+       if (argc > i)
+       {
+           strncpy (search_name, argv[i], 8);
+           search_name[8] = '\0';
+           i ++;
+
+           if (argc > i)
+           {
+               fprintf (stderr, "w: syntax error\n");
+               put_syntax ();
+           }
+       }
+    }
+
+
+
+    /*
+     * Check that /proc is actually there, or else we can't
+     * get all the information.
+     */
+    if (chdir ("/proc"))
+    {
+       fprintf (stderr, "w: fatal error: cannot access /proc\n");
+       perror (strerror(errno));
+       exit (-1);
+    }
+
+
+
+    /*
+     * Find out our screen width from $COLUMNS
+     */
+    columns_ptr = getenv ("COLUMNS");
+    if (columns_ptr == NULL)
+    {
+       struct winsize window;
+
+       /*
+        * Try getting it directly
+        */
+       if ((ioctl (1, TIOCGWINSZ, &window) != 1) && (window.ws_col > 0))
+           line_length = window.ws_col;
+       else
+           line_length = 80;           /* Default length assumed */
+    }
+    else
+       line_length = atoi (columns_ptr);
+
+    /*
+     * Maybe we should check whether there is enough space on
+     * the lines for the options selected...
+     */
+    if (line_length < 60)
+       long_format = FALSE;
+
+    line_length --;
+
+
+    /*
+     * Print whatever headers
+     */
+    if (header == TRUE)
+    {
+       /*
+        * uptime: from MKJ's uptime routine,
+        * found in whattime.c
+        */
+       print_uptime();
+
+
+       /*
+        * Print relevant header bits
+        */
+       printf ("User     tty     ");
+
+       if (long_format == TRUE)
+       {
+           if (from_switch == TRUE)
+               printf ("From             ");
+
+           printf (" login@   idle  JCPU  PCPU  ");
+
+           if (show_pid == TRUE)
+               printf (" PID  ");
+
+           printf ("what\n");
+       }
+       else
+       {
+           printf (" idle  ");
+
+           if (show_pid == TRUE)
+               printf (" PID  ");
+
+           printf ("what\n");
+       }
+    }
+
+
+
+
+    /*
+     * Process user information.
+     */
+    while ((utmp_rec = getutent()))
+    {
+       /*
+        * Check we actually want to see this record.
+        * It must be a valid active user process,
+        * and match a specified search name.
+        */
+       if ( (utmp_rec->ut_type == USER_PROCESS)
+         && (strcmp(utmp_rec->ut_user, ""))
+         && ( (search_name[0] == '\0')
+           || ( (search_name[0] != '\0')
+           && !strncmp(search_name, utmp_rec->ut_user, 8) ) ) )
+       {
+           /*
+            * Get the username
+            */
+           strncpy (username, utmp_rec->ut_user, 8);
+           username[8] = '\0';         /* Set end terminator */
+
+
+           /*
+            * Find out the uid of that user (from their
+            * passwd entry)
+            */
+           uid = -1;
+           if ((passwd_entry = getpwnam (username)) != NULL)
+           {
+            uid = passwd_entry->pw_uid;
+           }
+
+           /*
+            * Get (and clean up) the tty line
+            */
+           for (i = 0; (utmp_rec->ut_line[i] > 32) && (i < 6); i ++)
+               tty[i] = utmp_rec->ut_line[i];
+
+           utmp_rec->ut_line[i] = '\0';
+           tty[i] = '\0';
+
+
+           /*
+            * Don't bother getting info if it's not asked for
+            */
+           if (long_format == TRUE)
+           {
+
+               /*
+                * Get the remote hostname; this can be up to 16 chars,
+                * but if any chars are invalid (ie: [^a-zA-Z0-9\.])
+                * then the char is changed to a string terminator.
+                */
+               if (from_switch == TRUE)
+               {
+                   strncpy (rhost, utmp_rec->ut_host, 16);
+                   rhost[16] = '\0';
+
+               }
+
+
+               /*
+                * Get the login time
+                * (Calculated by LG's routine, below)
+                */
+               strcpy (login_time, logintime(utmp_rec->ut_time));
+           }
+
+
+
+           /*
+            * Get the idle time.
+            * (Calculated by LG's routine, below)
+            */
+           strcpy (idle_time, idletime (tty));
+
+
+
+           /*
+            * That's all the info out of /etc/utmp.
+            * The rest is more difficult.  We use the pid from
+            * utmp_rec->ut_pid to look in /proc for the info.
+            * NOTE: This is not necessarily the active pid, so we chase
+            * down the path of parent -> child pids until we find it,
+            * according to the information given in /proc/<pid>/stat.
+            */
+
+           sprintf (pid, "%d", utmp_rec->ut_pid);
+
+           what[0] = '\0';
+           strcpy (file_name, pid);
+           strcat (file_name, "/stat");
+           jcpu = 0;
+           pcpu = 0;
+
+           if ((fp = fopen(file_name, "r")))
+           {
+               while (what[0] == '\0')
+               {
+                   /*
+                    * Check /proc/<pid>/stat to see if the process
+                    * controlling the tty is the current one
+                    */
+                   fscanf (fp, "%d %s %c %*d %*d %*d %*d %d "
+                       "%*u %*u %*u %*u %*u %d %d %d %d",
+                       &curr_pid, comm, &state, &tpgid,
+                       &utime, &stime, &cutime, &cstime);
+
+                   fclose (fp);
+
+                   if (comm[0] == '\0')
+                       strcpy (comm, "-");
+
+                   /*
+                    * Calculate jcpu and pcpu.
+                    * JCPU is the time used by all processes and their
+                    * children, attached to the tty.
+                    * PCPU is the time used by the current process
+                    * (calculated once after the loop, using last
+                    * obtained values).
+                    */
+                   if (!jcpu)
+                       jcpu = cutime + cstime;
+
+                   /*
+                    * Check for a zombie first...
+                    */
+                   if (state == 'Z')
+                       strcpy (what, ZOMBIE);
+                   else if (curr_pid == tpgid)
+                   {
+                       /*
+                        * If it is the current process, read cmdline
+                        * If that's empty, then the process is swapped out,
+                        * or is a zombie, so we use the command given in stat
+                        * which is in normal round brackets, ie: "()".
+                        */
+                       strcpy (file_name, pid);
+                       strcat (file_name, "/cmdline");
+                       if ((fp = fopen(file_name, "r")))
+                       {
+                           i = 0;
+                           j = fgetc (fp);
+                           while ((j != EOF) && (i < 256))
+                           {
+                               if (j == '\0')
+                                   j = ' ';
+
+                               what[i] = j;
+                               i++;
+                               j = fgetc (fp);
+                           }
+                           what[i] = '\0';
+                           fclose (fp);
+                       }
+
+                       if (what[0] == '\0')
+                           strcpy (what, comm);
+                   }
+                   else
+                   {
+                       /* 
+                        * Check out the next process
+                        * If we can't open it, use info from this process,
+                        * so we have to check out cmdline first.
+                        *
+                        * If we're not using "-u" then should we just
+                        * say "-" (or "-su") instead of a command line ?
+                        * If so, we should strpcy(what, "-"); when we
+                        * fclose() in the if after the stat() below.
+                        */
+                       strcpy (file_name, pid);
+                       strcat (file_name, "/cmdline");
+
+                       if ((fp = fopen (file_name, "r")))
+                       {
+                           i = 0;
+                           j = fgetc (fp);
+                           while ((j != EOF) && (i < 256))
+                           {
+                               if (j == '\0')
+                                   j = ' ';
+
+                               what[i] = j;
+                               i++;
+                               j = fgetc (fp);
+                           }
+                           what[i] = '\0';
+                           fclose (fp);
+                       }
+
+                       if (what[0] == '\0')
+                           strcpy (what, comm);
+
+                       /*
+                        * Now we have something in the what variable,
+                        * in case we can't open the next process.
+                        */
+                       sprintf (pid, "%d", tpgid);
+                       strcpy (file_name, pid);
+                       strcat (file_name, "/stat");
+
+                       fp = fopen (file_name, "r");
+
+                       if (fp && (ignore_user == FALSE))
+                       {
+                           /*
+                            * We don't necessarily go onto the next process,
+                            * unless we are either ignoring who the effective
+                            * user is, or it's the same uid
+                            */
+                           stat (file_name, &stat_rec);
+
+                           /*
+                            * If the next process is not owned by this
+                            * user finish the loop.
+                            */
+                           if (stat_rec.st_uid != uid)
+                           {
+                               fclose (fp);
+
+                               strcpy (what, "-su");
+                               /*
+                                * See comment above somewhere;  I've used
+                                * "-su" here, as the next process is owned
+                                * by someone else; this is generally
+                                * because the user has done an "su" which
+                                * then exec'd something else.
+                                */
+                           }
+                           else
+                               what[0] = '\0';
+                       }
+                       else if (fp)    /* else we are ignoring uid's */
+                           what[0] = '\0';
+                   }
+               }
+           }
+           else        /* Could not open first process for user */
+               strcpy (what, "?");
+
+
+           /*
+            * There is a bug somewhere in my version of linux
+            * which means that utmp records are not cleaned
+            * up properly when users log out. However, we
+            * can detect this, by the users first process
+            * not being there when we look in /proc.
+            */
+
+
+           /*
+            * Don't output a line for "dead" users.
+            * This gets round a bug which doesn't update utmp/wtmp
+            * when users log out.
+            */
+           if (what[0] != '?')
+           {
+#ifdef 0
+/* This makes unix98 pty's not line up, so has been disabled - JEH. */
+               /*
+                * Remove the letters 'tty' from the tty id
+                */
+               if (!strncmp (tty, "tty", 3))
+               {
+                   for (i = 3; tty[i - 1] != '\0'; i ++)
+                       tty[i - 3] = tty[i];
+               }
+#endif
+
+               /*
+                * Common fields
+                */
+               sprintf (out_line, "%-9.8s%-6.7s ", username, tty);
+
+
+               /*
+                * Format the line for output
+                */
+               if (long_format == TRUE)
+               {
+                   /*
+                    * Calculate CPU usage
+                    */
+                   pcpu = utime + stime;
+                   jcpu /= 100;
+                   pcpu /= 100;
+
+                   if (from_switch == TRUE)
+                       sprintf (out_line, "%s %-16.15s", out_line, rhost);
+
+                   sprintf (out_line, "%s%8.8s ", out_line, login_time);
+
+               }
+
+               sprintf (out_line, "%s%6s", out_line, idle_time);
+
+
+               if (long_format == TRUE)
+               {
+                   if (!jcpu)
+                       strcat (out_line, "      ");
+                   else if (jcpu/60)
+                       sprintf (out_line, "%s%3d:%02d", out_line,
+                               jcpu/60, jcpu%60);
+                   else
+                       sprintf (out_line, "%s    %2d", out_line, jcpu);
+
+                   if (!pcpu)
+                       strcat (out_line, "      ");
+                    else if (pcpu/60)
+                       sprintf (out_line, "%s%3d:%02d", out_line,
+                               pcpu/60, pcpu%60);
+                   else
+                       sprintf (out_line, "%s    %2d", out_line, pcpu);
+               }
+
+               if (show_pid == TRUE)
+                   sprintf (out_line, "%s %5.5s", out_line, pid);
+
+
+               strcat (out_line, "  ");
+               strcat (out_line, what);
+
+
+               /*
+                * Try not to exceed the line length
+                */
+               out_line[line_length] = '\0';
+
+               printf ("%s\n", out_line);
+           }
+       }
+    }
+}
+
+
+
+/*
+ * put_syntax()
+ *
+ * Routine to print the correct syntax to call this program,
+ * and then exit out appropriately
+ */
+void put_syntax ()
+{
+    fprintf (stderr, "usage: w [-hfsud] [user]\n");
+    exit (-1);
+}
+
+
+
+/*
+ * idletime()
+ *
+ * Routine which returns a string containing
+ * the idle time of a given user.
+ *
+ * This routine was lifted from the original w program
+ * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
+ * Copyright (c) 1993 Larry Greenfield
+ *
+ */
+char *idletime (tty)
+
+char *tty;
+
+{
+    struct stat terminfo;
+    unsigned long idle;
+    char ttytmp[40];
+    static char give[20];
+    time_t curtime;
+
+    curtime = time (NULL);
+
+    sprintf (ttytmp, "/dev/%s", tty);
+    stat (ttytmp, &terminfo);
+    idle = (unsigned long) curtime - (unsigned long) terminfo.st_atime;
+
+    if (idle >= (60 * 60))             /* more than an hour */
+    {
+       if (idle >= (60 * 60 * 48))     /* more than two days */
+           sprintf (give, "%2ludays", idle / (60 * 60 * 24));
+       else
+           sprintf (give, " %2lu:%02u", idle / (60 * 60), 
+             (unsigned) ((idle / 60) % 60));
+    }
+    else
+    {
+       if (idle / 60)
+           sprintf (give, "%6lu", idle / 60);
+       else
+           give[0]=0;
+    }
+
+    return give;
+}
+
+
+
+/*
+ * logintime()
+ *
+ * Returns the time given in a suitable format
+ *
+ * This routine was lifted from the original w program
+ * by Larry Greenfield  (greenfie@gauss.rutgers.edu)
+ * Copyright (c) 1993 Larry Greenfield
+ *
+ */
+
+#undef ut_time
+
+char *logintime(ut_time)
+
+time_t ut_time;
+
+{
+    time_t curtime;
+    struct tm *logintime, *curtm;
+    int hour, am, curday, logday;
+    static char give[20];
+    static char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
+                               "Sat" };
+    static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+                               "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+    curtime = time(NULL);
+    curtm = localtime(&curtime);
+    curday = curtm->tm_yday;
+    logintime = localtime(&ut_time);
+    hour = logintime->tm_hour;
+    logday = logintime->tm_yday;
+    am = (hour < 12);
+
+    if (!am)
+       hour -= 12;
+
+    if (hour == 0)
+       hour = 12;
+
+    /*
+     * This is a newer behavior: it waits 12 hours and the next day, and then
+     * goes to the 2nd time format. This should reduce confusion.
+     * It then waits only 6 days (not till the last moment) to go the last
+     * time format.
+     */
+    if ((curtime > (ut_time + (60 * 60 * 12))) && (logday != curday))
+    {
+       if (curtime > (ut_time + (60 * 60 * 24 * 6)))
+           sprintf(give, "%2d%3s%2d", logintime->tm_mday,
+               month[logintime->tm_mon], (logintime->tm_year % 100));
+       else
+           sprintf(give, "%*s%2d%s", 3, weekday[logintime->tm_wday],
+               hour, am ? "am" : "pm");
+    }
+    else
+       sprintf(give, "%2d:%02d%s", hour, logintime->tm_min, am ? "am" : "pm");
+
+    return give;
+}
+
diff --git a/top.1 b/top.1
new file mode 100644 (file)
index 0000000..7be1667
--- /dev/null
+++ b/top.1
@@ -0,0 +1,456 @@
+.\" This file Copyright 1992 Robert J. Nation 
+.\" (nation@rocket.sanders.lockheed.com)
+.\" It may be distributed under the GNU Public License, version 2, or
+.\" any higher version.  See section COPYING of the GNU Public license
+.\" for conditions under which this file may be redistributed.
+.\"
+.\" Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
+.\" Cleaned up, and my changes documented.  New `.It' macro.  Edited.
+.\" modified 1996/01/27 Helmut Geyer to match my changes.
+.
+.de It
+.TP 0.5i
+.B "\\$1 "
+..
+.TH TOP 1 "Feb 1 1993" "Linux" "Linux User's Manual"
+.SH NAME
+top \- display top CPU processes
+.SH SYNOPSIS
+.B top
+.RB [ \- ]
+.RB [ d
+.IR delay ]
+.RB [ p
+.IR pid ]
+.RB [ q ]
+.RB [ c ]
+.RB [ C ]
+.RB [ S ]
+.RB [ s ]
+.RB [ i ]
+.RB [ n
+.IR iter ]
+.RB [ b ]
+.SH DESCRIPTION
+.B top
+provides an ongoing look at processor activity in real time.  It
+displays a listing of the most CPU-intensive tasks on the system, and
+can provide an interactive interface for manipulating processes.
+It can sort the tasks by CPU usage, memory usage and runtime.
+.N top
+can be better configured than the standard top from the procps suite.
+Most features can either be selected by an interactive command or by 
+specifying the feature in the personal or system-wide configuration
+file. See below for more information.
+.PP
+.SH "COMMAND\-LINE OPTIONS"
+.It d
+Specifies the delay between screen updates.  You can change this with
+the
+.B s
+interactive command.
+.It p
+Monitor only processes with given process id.
+This flag can be given up to twenty times. This option is neither
+available interactively nor can it be put into the configuration file.
+.It q
+This causes
+.B top
+to refresh without any delay. If the caller has superuser priviledges,
+top runs with the highest possible priority.
+.It S
+Specifies cumulative mode, where each process is listed with the CPU
+time that it
+.I as well as its dead children
+has spent.  This is like the
+.B -S
+flag to
+.BR ps (1).
+See the discussion below of the
+.B S
+interactive command.
+.It s
+Tells
+.B top
+to run in secure mode.  This disables the potentially dangerous of the
+interactive commands (see below).  A secure
+.B top
+is a nifty thing to leave running on a spare terminal.
+.It i
+Start
+.B top
+ignoring any idle or zombie processes. See the interactive command
+.B i
+below.
+.It C
+display total CPU states in addition to individual CPUs. This option
+only affects SMP systems.
+.It c
+display command line instead of the command name only. The default
+behaviour has been changed as this seems to be more useful.
+.It n
+Number of iterations. Update the display this number of times and then exit.
+.It b
+Batch mode. Useful for sending output from top to other programs or to a file.
+In this mode, 
+.B top
+will not accept command line input. It runs until it produces the number of
+iterations requested with the
+.B n
+option or until killed. Output is plain text suitable for display on a dumb
+terminal.
+.
+.SH "FIELD DESCRIPTIONS"
+.B top
+displays a variety of information about the processor state.  The
+display is updated every 5 seconds by default, but you can change that
+with the
+.B d
+command-line option or the
+.B s
+interactive command.
+.It "uptime"
+This line displays the time the system has been up, and the three load
+averages for the system.  The load averages are the average number of
+process ready to run during the last 1, 5 and 15 minutes.  This line is
+just like the output of
+.BR uptime (1).
+The uptime display may be toggled by the interactive 
+.B l
+command.
+.It processes
+The total number of processes running at the time of the last update.
+This is also broken down into the number of tasks which are running,
+sleeping, stopped, or undead. The processes and states display may be
+toggled by the 
+.B t
+interactive command.
+.It "CPU states"
+Shows the percentage of CPU time in user mode, system mode, niced tasks,
+and idle.  (Niced tasks are only those whose nice value is negative.)
+Time spent in niced tasks will also be counted in system and user time,
+so the total will be more than 100%.  The processes and states display
+may be
+toggled by the
+.B t
+interactive command.
+.It Mem
+Statistics on memory usage, including total available memory, free
+memory, used memory, shared memory, and memory used for buffers. The
+display of memory information may be toggled by the
+.B m
+interactive command.
+.It Swap
+Statistics on swap space, including total swap space, available swap
+space, and used swap space.  This and
+.B Mem
+are just like the output of
+.BR free (1).
+.It PID
+The process ID of each task.
+.It PPID
+The parent process ID each task.
+.It UID
+The user ID of the task's owner.
+.It USER
+The user name of the task's owner.
+.It PRI
+The priority of the task.
+.It NI
+The nice value of the task.  Negative nice values are higher priority.
+.It SIZE
+The size of the task's code plus data plus stack space, in kilobytes,
+is shown here.
+.It TSIZE
+The code size of the task. This gives strange values for kernel
+processes and is broken for ELF processes.
+.It DSIZE
+Data + Stack size. This is broken for ELF processes.
+.It TRS
+Text resident size.
+.It SWAP
+Size of the swapped out part of the task.
+.It D
+Size of pages marked dirty.
+.It LC
+Last used processor.  (That this changes from time to time is not
+a bug; Linux intentionally uses weak affinity.  Also notice that
+the very act of running top may break weak affinity and cause more
+processes to change current CPU more often because of the extra
+demand for CPU time.)
+.It RSS
+The total amount of physical memory used by the task, in kilobytes, is
+shown here. For ELF processes used library pages are counted here, for
+a.out processes not.
+.It SHARE
+The amount of shared memory used by the task is shown in this column.
+.It STAT
+The state of the task is shown here. The state is either
+.B S
+for sleeping,
+.B D
+for uninterruptible sleep,
+.B R
+for running,
+.B Z
+for zombies, or
+.B T
+for stopped or traced. These states are modified by trailing
+.B <
+for a process with negative nice value,
+.B N
+for a process with positive nice value,
+.B W
+for a swapped out process (this does not work correctly for kernel
+processes).
+.It WCHAN
+depending on the availablity of either 
+.I /boot/psdatabase
+or the kernel link map
+.I /boot/System.map
+this shows the address or the name of the kernel function the task
+currently is sleeping in.
+.It TIME
+Total CPU time the task has used since it started.  If cumulative mode
+is on, this also includes the CPU time used by the process's children
+which have died.  You can set cumulative mode with the
+.B S
+command line option or toggle it with the interactive command
+.BR S . 
+The header line will then be changed to 
+.BR CTIME .
+.It %CPU
+The task's share of the CPU time since the last screen update, expressed
+as a percentage of total CPU time per processor.
+.It %MEM
+The task's share of the physical memory.
+.It COMMAND
+The task's command name, which will be truncated if it is too long to be
+displayed on one line.  Tasks in memory will have a full command line,
+but swapped-out tasks will only have the name of the program in
+parentheses (for example, "(getty)").
+.It "A , WP"
+these fields from the kmem top are not supported.
+.
+.SH "INTERACTIVE COMMANDS"
+Several single-key commands are recognized while
+.B top
+is running.  Some are disabled if the
+.B s
+option has been given on the command line.
+.It space
+Immediately updates the display.
+.It ^L
+Erases and redraws the screen.
+.It "h\fR or \fB?"
+Displays a help screen giving a brief summary of commands, and the
+status of secure and cumulative modes.
+.It k
+Kill a process.  You will be prompted for the PID of the task, and the
+signal to send to it.  For a normal kill, send signal 15.  For a sure,
+but rather abrupt, kill, send signal 9.  The default signal, as with
+.BR kill (1),
+is 15,
+.BR SIGTERM .
+This command is not available in secure mode.
+.It i
+Ignore idle and zombie processes.  This is a toggle switch.
+.It I
+Toggle between Solaris (CPU percentage divided by total number of CPUs)
+and Irix (CPU percentage calculated solely by amount of time) views.
+This is a toggle switch that affects only SMP systems.
+.It "n\fR or \fB#"
+Change the number of processes to show.  You will be prompted to enter
+the number.  This overrides automatic determination of the number of
+processes to show, which is based on window size measurement.  If 0 is
+specified, then top will show as many processes as will fit on the
+screen; this is the default.
+.It q
+Quit.
+.It r
+Re-nice a process.  You will be prompted for the PID of the task, and
+the value to nice it to.  Entering a positve value will cause a process
+to be niced to negative values, and lose priority.  If root is running
+.BR top ,
+a negative value can be entered, causing a process to get a higher than
+normal priority.  The default renice value is 10.  This command is not
+available in secure mode.
+.It S
+This toggles cumulative mode, the equivalent of
+.BR "ps -S" ,
+i.e., that CPU times will include a process's defunct children.  For
+some programs, such as compilers, which work by forking into many
+seperate tasks, normal mode will make them appear less demanding than
+they actually are.  For others, however, such as shells and
+.BR init ,
+this behavior is correct.  In any case, try cumulative mode for an
+alternative view of CPU use.
+.It s
+Change the delay between updates.  You will be prompted to enter the
+delay time, in seconds, between updates.  Fractional values are
+recognized down to microseconds.  Entering 0 causes continuous updates.
+The default value is 5 seconds.  Note that low values cause nearly
+unreadably fast displays, and greatly raise the load.  This command is
+not available in secure mode.
+.It "f\fR or \fBF"
+Add fields to display or remove fields from the display. See below for
+more information.
+.It "o\fR or \fBO"
+Change order of displayed fields. See below for more information.
+.It l
+toggle display of load average and uptime information.
+.It m
+toggle display of memory information.
+.It t
+toggle display of processes and CPU states information.
+.It c
+toggle display of command name or full command line.
+.It N
+sort tasks by pid (\fIn\fPumerically).
+.It A
+sort tasks by age (newest first).
+.It P
+sort tasks by CPU usage (default).
+.It M
+sort tasks by resident memory usage.
+.It T
+sort tasks by time / cumulative time.
+.It W
+Write current setup to 
+.IR ~/.toprc .
+This is the recommended way to write a top configuration file.
+.
+.SH The Field and Order Screens
+After pressing 
+.BR f ,
+.BR F ,
+.B o
+or
+.B O
+you will be shown a screen specifying the field order on the top line
+and short descriptions of the field contents. The field order string
+uses the following syntax: If the letter in the filed string
+corresponding to a  field is upper case, the field will be displayed.
+This is furthermore indicated by an asterisk in front of the field
+description.
+The order of the fields corresponds to the order of the letters in the
+string. 
+ From the field select screen you can toggle the display of a field by
+pressing the corresponding letter. 
+ From the order screen you may move a field to the left by pressing
+the corresponding upper case letter resp. to the right by pressing the
+lower case one.
+.
+.SH Configuration Files
+Top reads it's default configuration from two files,
+.I /etc/toprc
+and
+.IR ~/.toprc .
+The global configuration file may be used to restrict the usage of top
+to the secure mode for non-priviledged users. If this is desired, the
+file should contain a 's' to specify secure mode and a digit d (2<=d<=9)
+for the default delay (in seconds) on a single line.
+.
+The personal configuration file contains two lines. The first line
+contains lower and upper letters to specify which fields in what
+order are to be displayed. The letters correspond to the letters in the
+Fields or Order screens from top. As this is not very instructive, it is
+recommended to select fields and order in a running top process and to
+save this using the 
+.I W
+interactive command. 
+.
+The second line is more interesting (and important). It contains
+information on the other options. Most important, if you have saved a
+configuration in secure mode, you will not get an insecure top without
+removing the lower 's' from the second line of your 
+.IR ~/.toprc .
+A digit specifies the delay time between updates, a capital 'S'
+cumulative mode, a lower 'i' no-idle mode, a capital 'I' Irix view. As
+in interactive mode, a lower 'm', 'l', and 't' suppresses the display
+of memory, uptime resp. process and CPU state information.
+Currently changing the default sorting order (by CPU usage) is not 
+supported.
+.
+.SH NOTES
+This
+.BR proc -based
+.B top
+works by reading the files in the
+.B proc
+filesystem,
+mounted on
+.IR /proc .
+If
+.I /proc
+is not mounted,
+.B top
+will not work.
+.PP
+.B %CPU
+shows the cputime/realtime percentage in the period of time between 
+updates.  For the first update, a short delay is used, and
+.B top
+itself dominates the CPU usage.  After that,
+.B top
+will drop back, and a more reliable estimate of CPU usage is available.
+.PP
+The
+.B SIZE
+and
+.B RSS fields don't count the page tables and the
+.B task_struct
+of a process; this is at least 12K of memory that is always resident.
+.B SIZE
+is the virtual size of the process (code+data+stack).
+.PP
+Keep in mind that a process must die for its time to be recorded on its
+parent by cumulative mode.  Perhaps more useful behavior would be to
+follow each process upwards, adding time, but that would be more
+expensive, possibly prohibitively so.  In any case, that would make
+.BR top 's
+behavior incompatible with
+.BR ps .
+.
+.SH FILES
+.I /etc/toprc 
+The global configuration file. 
+.I ~/.toprc
+The personal configuration file.
+.
+.SH "SEE ALSO"
+.BR ps (1),
+.BR free (1),
+.BR uptime (1),
+.BR kill (1),
+.BR renice (1).
+.
+.SH
+BUGS
+If the window is less than about 70x7,
+.B top
+will not format information correctly.
+ Many fields still have problems with ELF processes.
+ the help screens are not yet optimized for windows with less than 
+25 lines
+.
+.SH AUTHOR
+.B top
+was originally written by Roger Binns, based on Branko Lankester's
+<lankeste@fwi.uva.nl> ps program.
+Robert Nation <nation@rocket.sanders.lockheed.com> re-wrote it
+significantly to use the proc filesystem, based on Michael K. Johnson's
+<johnsonm@redhat.com> proc-based ps program.
+Michael Shields <mjshield@nyx.cs.du.edu> made many changes, including
+secure and cumulative modes and a general cleanup.
+Tim Janik <timj@gtk.org> added age sorting and the ability to monitor
+specific processes through their ids.
+
+Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+Heavily changed it to include support for configurable fields and other
+new options, and did further cleanup and use of the new readproc interface.
+
+The "b" and "n" options contributed by George Bonser <george@captech.com> 
+for CapTech IT Services.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/top.c b/top.c
new file mode 100644 (file)
index 0000000..848bf9b
--- /dev/null
+++ b/top.c
@@ -0,0 +1,1746 @@
+/*
+ * top.c              - show top CPU processes
+ *
+ * Copyright (c) 1992 Branko Lankester
+ * Copyright (c) 1992 Roger Binns
+ * Copyright (c) 1997 Michael K. Johnson
+ *
+ * Snarfed and HEAVILY modified in december 1992 for procps
+ * by Michael K. Johnson, johnsonm@sunsite.unc.edu.
+ *
+ * Modified Michael K. Johnson's ps to make it a top program.
+ * Also borrowed elements of Roger Binns kmem based top program.
+ * Changes made by Robert J. Nation (nation@rocket.sanders.lockheed.com)
+ * 1/93
+ *
+ * Modified by Michael K. Johnson to be more efficient in cpu use
+ * 2/21/93
+ *
+ * Changed top line to use uptime for the load average.  Also
+ * added SIGTSTP handling.  J. Cowley, 19 Mar 1993.
+ *
+ * Modified quite a bit by Michael Shields (mjshield@nyx.cs.du.edu)
+ * 1994/04/02.  Secure mode added.  "d" option added.  Argument parsing
+ * improved.  Switched order of tick display to user, system, nice, idle,
+ * because it makes more sense that way.  Style regularized (to K&R,
+ * more or less).  Cleaned up much throughout.  Added cumulative mode.
+ * Help screen improved.
+ *
+ * Fixed kill buglet brought to my attention by Rob Hooft.
+ * Problem was mixing of stdio and read()/write().  Added
+ * getnum() to solve problem.
+ * 12/30/93 Michael K. Johnson
+ *
+ * Added toggling output of idle processes via 'i' key.
+ * 3/29/94 Gregory K. Nickonov
+ *
+ * Fixed buglet where rawmode wasn't getting restored.
+ * Added defaults for signal to send and nice value to use.
+ * 5/4/94 Jon Tombs.
+ *
+ * Modified 1994/04/25 Michael Shields <mjshield@nyx.cs.du.edu>
+ * Merged previous changes to 0.8 into 0.95.
+ * Allowed the use of symbolic names (e.g., "HUP") for signal input.
+ * Rewrote getnum() into getstr(), getint(), getsig(), etc.
+ * 
+ * Modified 1995  Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de> 
+ * added kmem top functionality (configurable fields)
+ * configurable order of process display
+ * Added options for dis/enabling uptime, statistics, and memory info.
+ * fixed minor bugs for ELF systems (e.g. SIZE, RSS fields)
+ *
+ * Modified 1996/05/18 Helmut Geyer <Helmut.Geyer@iwr.uni-heidelberg.de>
+ * Use of new interface and general cleanup. The code should be far more
+ * readable than before.
+ *
+ * Modified 1996/06/25 Zygo Blaxell <zblaxell@ultratech.net>
+ * Added field scaling code for programs that run more than two hours or
+ * take up more than 100 megs.  We have lots of both on our production line.
+ *
+ * Modified 1998/02/21 Kirk Bauer <kirk@kaybee.org>
+ * Added the 'u' option to display only a selected user... plus it will
+ * take into account that not all 20 top processes are actually shown,
+ * so it can fit more onto the screen.  I think this may help the
+ * 'don't show idle' mode, but I'm not sure.
+ *
+ * Modified 1997/07/27 & 1999/01/27 Tim Janik <timj@gtk.org>
+ * added `-p' option to display specific process ids.
+ * process sorting is by default disabled in this case.
+ * added `N' and `A' keys to sort the tasks Numerically by pid or
+ * sort them by Age (newest first).
+ *
+ * Modified 1999/10/22 Tim Janik <timj@gtk.org>
+ * miscellaneous minor fixes, including "usage: ..." output for
+ * unrecognized options.
+ *
+ * Modified 2000/02/07 Jakub Jelinek <jakub@redhat.com>
+ * Only load System.map when we are going to display WCHAN.
+ * Show possible error messages from that load using SHOWMESSAGE.
+ *
+ * Modified 2000/07/10 Michael K. Johnson <johnsonm@redhat.com>
+ * Integrated a patch to display SMP information.
+ */
+
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libintl.h>
+#include <time.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+#include <termcap.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <ctype.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <sys/param.h>
+
+#include "proc/sysinfo.h"
+#include "proc/procps.h"
+#include "proc/whattime.h"
+#include "proc/sig.h"
+#include "proc/version.h"
+#include "proc/readproc.h"
+#include "proc/status.h"
+#include "proc/devname.h"
+#include "proc/compare.h"
+
+#define PUTP(x) (tputs(x,1,putchar))
+#define BAD_INPUT -30
+
+#include "top.h"  /* new header for top specific things */
+
+static int *cpu_mapping;
+static int nr_cpu;
+
+/*#######################################################################
+ *####  Startup routines: parse_options, get_options,      ##############
+ *####                    setup_terminal and main          ##############
+ *#######################################################################
+ */
+
+      /*
+       * parse the options string as read from the config file(s).
+       * if top is in secure mode, disallow changing of the delay time between
+       * screen updates.
+       */
+static void parse_options(char *Options, int secure)
+{
+    int i;
+    for (i = 0; i < strlen(Options); i++) {
+       switch (Options[i]) {
+         case '2':
+         case '3':
+         case '4':
+         case '5':
+         case '6':
+         case '7':
+         case '8':
+         case '9':
+           if (!secure)
+               Sleeptime = (float) Options[i] - '0';
+           break;
+         case 'S':
+           Cumulative = 1;
+           headers[22][1] = 'C';
+           break;
+         case 's':
+           Secure = 1;
+           break;
+         case 'i':
+           Noidle = 1;
+           break;
+         case 'm':
+           show_memory = 0;
+           header_lines -= 2;
+           break;
+         case 'M':
+           sort_type = S_MEM;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)mem_sort);
+           break;
+         case 'l':
+           show_loadav = 0;
+           header_lines -= 1;
+           break;
+         case 'P':
+           sort_type = S_PCPU;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)pcpu_sort);
+           break;
+         case 'N':
+           sort_type = S_NONE;
+           reset_sort_options();
+           break;
+         case 'A':
+           sort_type = S_AGE;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)age_sort);
+           break;
+         case 't':
+           show_stats = 0;
+           header_lines -= 2;
+           break;
+         case 'T':
+           sort_type = S_TIME;
+           reset_sort_options();
+           register_sort_function( -1, (cmp_t)time_sort);
+           break;
+         case 'c':
+           show_cmd = 0;
+           break;
+         case '\n':
+           break;
+         case 'I': 
+           Irixmode = 0;
+           break;
+         default:
+           fprintf(stderr, "Wrong configuration option %c\n", i);
+           exit(1);
+           break;
+       }
+    }
+}
+
+/* 
+ * Read the configuration file(s). There are two files, once SYS_TOPRC 
+ * which should only contain the secure switch and a sleeptime
+ * value iff ordinary users are to use top in secure mode only.
+ * 
+ * The other file is $HOME/RCFILE. 
+ * The configuration file should contain two lines (any of which may be
+ *  empty). The first line specifies the fields that are to be displayed
+ * in the order you want them to. Uppercase letters specify fields 
+ * displayed by default, lowercase letters specify fields not shown by
+ * default. The order of the letters in this line corresponds to the 
+ * order of the displayed fileds.
+ *
+ * all Options but 'q' can be read from this config file
+ * The delay time option syntax differs from the commandline syntax:
+ *   only integer values between 2 and 9 seconds are recognized
+ *   (this is for standard configuration, so I think this should do).
+ *
+ * usually this file is not edited by hand, but written from top using
+ * the 'W' command. 
+ */
+
+static void get_options(void)
+{
+    FILE *fp;
+    char *pt;
+    char rcfile[MAXNAMELEN];
+    char Options[256] = "";
+    int i;
+
+    nr_cpu = sysconf (_SC_NPROCESSORS_ONLN);
+    cpu_mapping = (int *) xmalloc (sizeof (int) * nr_cpu);
+    /* read cpuname */
+    for (i=0; i< nr_cpu; i++) cpu_mapping[i]=i;
+    header_lines = 6 + nr_cpu;
+    strcpy(rcfile, SYS_TOPRC);
+    fp = fopen(rcfile, "r");
+    if (fp != NULL) {
+       fgets(Options, 254, fp);
+       fclose(fp);
+    }
+    parse_options(Options, 0);
+    strcpy(Options, "");
+    if (getenv("HOME")) {
+       strcpy(rcfile, getenv("HOME"));
+       strcat(rcfile, "/");
+    }
+    strcat(rcfile, RCFILE);
+    fp = fopen(rcfile, "r");
+    if (fp == NULL) {
+       strcpy(Fields, DEFAULT_SHOW);
+    } else {
+       if (fgets(Fields, 254, fp) != NULL) {
+           pt = strchr(Fields, '\n');
+           if (pt) *pt = 0;
+       }
+       fgets(Options, 254, fp);
+       fclose(fp);
+    }
+    parse_options(Options, getuid()? Secure : 0);
+}
+
+/*
+ * Set up the terminal attributes.
+ */
+static void setup_terminal(void)
+{
+    char *termtype;
+    struct termios newtty;
+    if (!Batch)
+       termtype = getenv("TERM");
+    else 
+       termtype = "dumb";
+    if (!termtype) {
+       /* In theory, $TERM should never not be set, but in practice,
+          some gettys don't.  Fortunately, vt100 is nearly always
+          correct (or pretty close). */
+       termtype = "VT100";
+       /* fprintf(stderr, PROGNAME ": $TERM not set\n"); */
+       /* exit(1); */
+    }
+
+    /*
+     * Get termcap entries and window size.
+     */
+    if(tgetent(NULL, termtype) != 1) {
+       fprintf(stderr, PROGNAME ": Unknown terminal \"%s\" in $TERM\n",
+               termtype);
+       exit(1);
+    }
+
+    cm = tgetstr("cm", 0);
+    top_clrtobot = tgetstr("cd", 0);
+    cl = tgetstr("cl", 0);
+    top_clrtoeol = tgetstr("ce", 0);
+    ho = tgetstr("ho", 0);
+    md = tgetstr("md", 0);
+    mr = tgetstr("mr", 0);
+    me = tgetstr("me", 0);
+
+
+    if (Batch) return; /* the rest doesn't apply to batch mode */
+    if (tcgetattr(0, &Savetty) == -1) {
+        perror(PROGNAME ": tcgetattr() failed");
+       error_end(errno);
+    }
+    newtty = Savetty;
+    newtty.c_lflag &= ~ICANON;
+    newtty.c_lflag &= ~ECHO;
+    newtty.c_cc[VMIN] = 1;
+    newtty.c_cc[VTIME] = 0;
+    if (tcsetattr(0, TCSAFLUSH, &newtty) == -1) {
+       printf("cannot put tty into raw mode\n");
+       error_end(1);
+    }
+    tcgetattr(0, &Rawtty);
+}
+
+static int parseint(const char *src, const char *err)
+{
+    char *endp;
+    int num;
+    int len;
+    num = strtol(src, &endp, 0);
+    if (*endp == '\0') return num;
+    /* also accept prefixes of: infinite, infinity, maximum, all */
+    len = strlen(src);
+    if(len<1) goto fail;
+    if(len<9 && !strncmp(src,"infinite",len)) return INT_MAX;
+    if(len<9 && !strncmp(src,"infinity",len)) return INT_MAX;
+    if(len<8 && !strncmp(src,"maximum" ,len)) return INT_MAX;
+    if(len<4 && !strncmp(src,"all"     ,len)) return INT_MAX;
+fail:
+    fprintf(stderr, err, src);
+    exit(1);
+}
+
+static double parseflt(const char *src, const char *err)
+{
+    char *endp;
+    double num;
+    int len;
+    num = strtod(src, &endp);
+    if (*endp == '\0') return num;
+    /* also accept prefixes of: infinite, infinity, maximum, all */
+    len = strlen(src);
+    if(len<1) goto fail;
+    if(len<9 && !strncmp(src,"infinite",len)) return (double)INT_MAX;
+    if(len<9 && !strncmp(src,"infinity",len)) return (double)INT_MAX;
+    if(len<8 && !strncmp(src,"maximum" ,len)) return (double)INT_MAX;
+    if(len<4 && !strncmp(src,"all"     ,len)) return (double)INT_MAX;
+fail:
+    fprintf(stderr, err, src);
+    exit(1);
+}
+
+int main(int argc, char **argv)
+{
+    /* For select(2). */
+    struct timeval tv;
+    fd_set in;
+    /* For parsing arguments. */
+    char *cp;
+    /* The key read in. */
+    char c;
+
+    struct sigaction sact;
+
+    setlocale(LC_ALL, "");
+    get_options();
+    
+    /* set to PCPU sorting */
+    register_sort_function( -1, (cmp_t)pcpu_sort);
+    
+    /*
+     * Parse arguments.
+     */
+    (void)argc;
+    argv++;
+    while (*argv) {
+       cp = *argv++;
+       while (*cp) {
+           switch (*cp) {
+             case 'd':
+               if (cp[1]) {
+                   Sleeptime = parseflt(++cp, PROGNAME ": Bad delay time %s'\n");
+                   goto breakargv;
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   Sleeptime = parseflt(cp = *argv++, PROGNAME ": Bad delay time %s'\n");
+                   goto breakargv;
+               } else {
+                   fprintf(stderr, "-d requires an argument\n");
+                   exit(1);
+               }
+               break;
+             case 'n':
+               if (cp[1]) {
+                   Loops = parseint(++cp, PROGNAME ": Bad value %s'\n");
+                   goto breakargv;
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   Loops = parseint(cp = *argv++, PROGNAME ": Bad value %s'\n");
+                   goto breakargv;
+               }
+               break;
+                                       
+             case 'q':
+               if (!getuid())
+                   /* set priority to -10 in order to stay above kswapd */
+                   if (setpriority(PRIO_PROCESS, getpid(), -10)) {
+                       /* We check this just for paranoia.  It's not
+                          fatal, and shouldn't happen. */
+                       perror(PROGNAME ": setpriority() failed");
+                   }
+               Sleeptime = 0;
+               break;
+             case 'p':
+               if (monpids_index >= monpids_max) {
+                   fprintf(stderr, PROGNAME ": More than %u process ids specified\n",
+                           monpids_max);
+                   exit(1);
+               }
+               if (cp[1]) {
+                   if (sscanf(++cp, "%d", &monpids[monpids_index]) != 1 ||
+                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+                       exit(1);
+                   }
+               } else if (*argv) { /* last char in an argv, use next as arg */
+                   if (sscanf(cp = *argv++, "%d", &monpids[monpids_index]) != 1 ||
+                       monpids[monpids_index] < 0 || monpids[monpids_index] > 65535) {
+                       fprintf(stderr, PROGNAME ": Bad process id `%s'\n", cp);
+                       exit(1);
+                   }
+               } else {
+                   fprintf(stderr, "-p requires an argument\n");
+                   exit(1);
+               }
+               if (!monpids[monpids_index])
+                   monpids[monpids_index] = getpid();
+               /* default to no sorting when monitoring process ids */
+               if (!monpids_index++) {
+                   sort_type = S_NONE;
+                   reset_sort_options();
+               }
+               cp = "_";
+               break;
+             case 'b':
+               Batch = 1;
+               break;
+             case 'c':
+               show_cmd = !show_cmd;
+               break;
+             case 'S':
+               Cumulative = 1;
+               break;
+             case 'i':
+               Noidle = 1;
+               break;
+             case 's':
+                 Secure = 1;
+                 break;
+             case 'C': 
+                 CPU_states = 1;
+                 break;
+             case '-':
+               break;          /* Just ignore it */
+             case 'v':
+             case 'V':
+               fprintf(stdout, "top (%s)\n", procps_version);
+               exit(0);
+             case 'h': 
+               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+               exit(0);
+             default:
+               fprintf(stderr, PROGNAME ": Unknown argument `%c'\n", *cp);
+               fprintf(stdout, "usage: " PROGNAME " -hvbcisqS -d delay -p pid -n iterations\n");
+               exit(1);
+           }
+           cp++;
+       }
+    breakargv:
+    }
+    
+    if (nr_cpu > 1 && CPU_states)
+      header_lines++;
+
+    meminfo();  /* need kb_main_total value filled in */
+
+    setup_terminal();
+    window_size(0);
+    /*
+     * Set up signal handlers.
+     */
+    sact.sa_handler = end;
+    sact.sa_flags = 0;
+    sigemptyset(&sact.sa_mask);
+    sigaction(SIGHUP, &sact, NULL);
+    sigaction(SIGINT, &sact, NULL);
+    sigaction(SIGQUIT, &sact, NULL);
+    sact.sa_handler = stop;
+    sact.sa_flags = SA_RESTART;
+    sigaction(SIGTSTP, &sact, NULL);
+    sact.sa_handler = window_size;
+    sigaction(SIGWINCH, &sact, NULL);
+    sigaction(SIGCONT, &sact, NULL);
+
+    /* loop, collecting process info and sleeping */
+    while (1) {
+       if (Loops > 0)
+               Loops--;
+       /* display the tasks */
+       show_procs();
+       /* sleep & wait for keyboard input */
+       if (Loops == 0)
+           end(0);
+        if (!Batch)
+        {
+               tv.tv_sec = Sleeptime;
+               tv.tv_usec = (Sleeptime - (int) Sleeptime) * 1000000;
+               FD_ZERO(&in);
+               FD_SET(0, &in);
+               if (select(1, &in, 0, 0, &tv) > 0 && read(0, &c, 1) == 1)
+                       do_key(c);
+        } else {
+          sleep(Sleeptime);
+       }
+    }
+}
+
+/*#######################################################################
+ *#### Signal handled routines: error_end, end, stop, window_size     ###
+ *#### Small utilities: make_header, getstr, getint, getfloat, getsig ###
+ *#######################################################################
+ */
+
+
+       /*
+        *  end when exiting with an error.
+        */
+static void error_end(int rno)
+{
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 1));
+    fputs("\r\n", stdout);
+    exit(rno);
+}
+/*
+        * Normal end of execution.
+        */
+static void end(int signo)
+{
+    (void)signo;
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 1));
+    fputs("\r\n", stdout);
+    exit(0);
+}
+
+/*
+        * SIGTSTP catcher.
+        */
+static void stop(int signo)
+{
+    (void)signo;
+    /* Reset terminal. */
+    if (!Batch)
+       tcsetattr(0, TCSAFLUSH, &Savetty);
+    PUTP(tgoto(cm, 0, Lines - 3));
+    fflush(stdout);
+    raise(SIGSTOP);
+    /* Later... */
+    if (!Batch)
+       tcsetattr (0, TCSAFLUSH, &Rawtty);
+}
+
+/*
+       * Reads the window size and clear the window.  This is called on setup,
+       * and also catches SIGWINCHs, and adjusts Maxlines.  Basically, this is
+       * the central place for window size stuff.
+       */
+static void window_size(int signo)
+{
+    struct winsize ws;
+    (void)signo;
+    if((ioctl(1, TIOCGWINSZ, &ws) != -1) && (ws.ws_col>73) && (ws.ws_row>7)){
+       Cols = ws.ws_col;
+       Lines = ws.ws_row;
+    }else{
+       Cols = tgetnum("co");
+       Lines = tgetnum("li");
+    }
+    if (!Batch)
+        clear_screen();
+    /*
+     * calculate header size, length of cmdline field ...
+     */
+     Numfields = make_header();
+}
+/*
+       * this prints a possible message from open_psdb_message
+       */
+static void top_message(const char *format, ...) {
+    va_list arg;
+    int n;
+    char buffer[512];
+
+    va_start (arg, format);
+    n = vsnprintf (buffer, 512, format, arg);
+    va_end (arg);
+    if (n > -1 && n < 512)
+       SHOWMESSAGE(("%s", buffer));
+}
+
+/*
+       * this adjusts the lines needed for the header to the current value
+       */
+static int make_header(void)
+{
+    int i, j;
+
+    j = 0;
+    for (i = 0; i < strlen(Fields); i++) {
+       if (Fields[i] < 'a') {   
+           pflags[j++] = Fields[i] - 'A';
+           if (Fields[i] == 'U' && CL_wchan_nout == -1) {
+               CL_wchan_nout = 0;
+               /* for correct handling of WCHAN fields, we have to do distingu
+                * between kernel versions */
+               /* get kernel symbol table, if needed */
+               if (open_psdb_message(NULL, top_message)) {
+                   CL_wchan_nout = 1;
+               } else {
+                   psdbsucc = 1;
+               }
+           }
+       }
+    }
+    strcpy(Header, "");
+    for (i = 0; i < j; i++)
+       strcat(Header, headers[pflags[i]]);
+    /* readjust window size ... */
+    Maxcmd = Cols - strlen(Header) + 7;
+    Maxlines = Display_procs ? Display_procs : Lines - header_lines;
+    if (Maxlines > Lines - header_lines)
+       Maxlines = Lines - header_lines;
+    return (j);
+}
+
+
+
+/*
+ * Get a string from the user; the base of getint(), et al.  This really
+ * ought to handle long input lines and errors better.  NB: The pointer
+ * returned is a statically allocated buffer, so don't expect it to
+ * persist between calls.
+ */
+static char *getstr(void)
+{
+    static char line[BUFSIZ];  /* BUFSIZ from <stdio.h>; arbitrary */
+    int i = 0;
+
+    /* Must make sure that buffered IO doesn't kill us. */
+    fflush(stdout);
+    fflush(stdin);             /* Not POSIX but ok */
+
+    do {
+       read(STDIN_FILENO, &line[i], 1);
+    } while (line[i++] != '\n' && i < sizeof(line));
+    line[--i] = 0;
+
+    return (line);
+}
+
+
+/*
+ * Get an integer from the user.  Display an error message and
+ * return BAD_INPUT if it's invalid; else return the number.
+ */
+static int getint(void)
+{
+    char *line;
+    int i;
+    int r;
+
+    line = getstr();
+
+    for (i = 0; line[i]; i++) {
+       if (!isdigit(line[i]) && line[i] != '-') {
+           SHOWMESSAGE(("That's not a number!"));
+           return (BAD_INPUT);
+       }
+    }
+
+    /* An empty line is a legal error (hah!). */
+    if (!line[0])
+       return (BAD_INPUT);
+
+    sscanf(line, "%d", &r);
+    return (r);
+}
+
+
+/*
+ * Get a float from the user.  Just like getint().
+ */
+static float getfloat(void)
+{
+    char *line;
+    int i;
+    float r;
+
+    line = getstr();
+
+    for (i = 0; line[i]; i++) {
+       if (!isdigit(line[i]) && line[i] != '.' && line[i] != '-') {
+           SHOWMESSAGE(("That's not a float!"));
+           return (BAD_INPUT);
+       }
+    }
+
+    /* An empty line is a legal error (hah!). */
+    if (!line[0])
+       return (BAD_INPUT);
+
+    sscanf(line, "%f", &r);
+    return (r);
+}
+
+
+/*
+        * Get a signal number or name from the user.  Return the number, or -1
+        * on error.
+        */
+static int getsig(void)
+{
+    char *line;
+
+    /* This is easy. */
+    line = getstr();
+    return signal_name_to_number(line);
+}
+
+/*#######################################################################
+ *####  Routine for sorting on used time, resident memory and %CPU  #####
+ *####  It would be easy to include full sorting capability as in   #####
+ *####  ps, but I think there is no real use for something that     #####
+ *####  complicated. Using register_sort_function or parse_sort_opt #####
+ *####  you just have to do the natural thing and it will work.     #####
+ *#######################################################################
+ */
+
+static int time_sort (proc_t **P, proc_t **Q)
+{
+    if (Cumulative) {
+       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) < 
+           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return -1;
+       if( ((*P)->cutime + (*P)->cstime + (*P)->utime + (*P)->stime) >
+           ((*Q)->cutime + (*Q)->cstime + (*Q)->utime + (*Q)->stime) )
+           return 1;
+    } else {
+       if( ((*P)->utime + (*P)->stime) < ((*Q)->utime + (*Q)->stime))
+           return -1;
+       if( ((*P)->utime + (*P)->stime) > ((*Q)->utime + (*Q)->stime))
+           return 1;
+    }
+    return 0;
+}
+
+static int pcpu_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->pcpu < (*Q)->pcpu )      return -1;
+    if( (*P)->pcpu > (*Q)->pcpu )      return 1;
+    return 0;
+}
+
+static int mem_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->vm_rss < (*Q)->vm_rss )      return -1;
+    if( (*P)->vm_rss > (*Q)->vm_rss )      return 1;  
+    return 0;
+}
+
+int age_sort (proc_t **P, proc_t **Q)
+{
+    if( (*P)->start_time < (*Q)->start_time )      return -1;
+    if( (*P)->start_time > (*Q)->start_time )      return 1;
+    return 0;
+}
+
+/*#######################################################################
+ *####  Routines handling the field selection/ordering screens:  ########
+ *####    show_fields, change_order, change_fields               ########
+ *#######################################################################
+ */
+
+        /*
+        * Display the specification line of all fields. Upper case indicates
+        * a displayed field, display order is according to the order of the 
+        * letters. A short description of each field is shown as well.
+        * The description of a displayed field is marked by a leading 
+        * asterisk (*).
+        */
+static void show_fields(void)
+{
+    int i, row, col;
+    char *p;
+
+    clear_screen();
+    PUTP(tgoto(cm, 3, 0));
+    printf("Current Field Order: %s\n", Fields);
+    for (i = 0; i < sizeof headers / sizeof headers[0]; ++i) {
+       row = i % (Lines - 3) + 3;
+       col = i / (Lines - 3) * 40;
+       PUTP(tgoto(cm, col, row));
+       for (p = headers[i]; *p == ' '; ++p);
+       printf("%c %c: %-10s = %s", (strchr(Fields, i + 'A') != NULL) ? '*' : ' ', i + 'A',
+              p, headers2[i]);
+    }
+}
+
+/*
+        * change order of displayed fields
+        */
+static void change_order(void)
+{
+    char c, ch, *p;
+    int i;
+
+    show_fields();
+    for (;;) {
+       PUTP(tgoto(cm, 0, 0));
+       PUTP(top_clrtoeol);
+       PUTP(tgoto(cm, 3, 0));
+       PUTP(mr);
+       printf("Current Field Order: %s", Fields);
+       PUTP(me);
+       putchar('\n');
+       PUTP(tgoto(cm, 0, 1));
+       printf("Upper case characters move a field to the left, lower case to the right");
+       fflush(stdout);
+       if (!Batch) { /* should always be true, but... */
+           tcsetattr(0, TCSAFLUSH, &Rawtty);
+           read(0, &c, 1);
+           tcsetattr(0, TCSAFLUSH, &Savetty);
+       }
+       i = toupper(c) - 'A';
+       if ((p = strchr(Fields, i + 'A')) != NULL) {
+           if (isupper(c))
+               p--;
+           if ((p[1] != '\0') && (p >= Fields)) {
+               ch = p[0];
+               p[0] = p[1];
+               p[1] = ch;
+           }
+       } else if ((p = strchr(Fields, i + 'a')) != NULL) {
+           if (isupper(c))
+               p--;
+           if ((p[1] != '\0') && (p >= Fields)) {
+               ch = p[0];
+               p[0] = p[1];
+               p[1] = ch;
+           }
+       } else {
+           break;
+       }
+    }
+    Numfields = make_header();
+}
+/*
+        * toggle displayed fields
+        */
+static void change_fields(void)
+{
+    int i, changed = 0;
+    int row, col;
+    char c, *p;
+    char tmp[2] = " ";
+
+    show_fields();
+    for (;;) {
+       PUTP(tgoto(cm, 0, 0));
+       PUTP(top_clrtoeol);
+       PUTP(tgoto(cm, 3, 0));
+       PUTP(mr);
+       printf("Current Field Order: %s", Fields);
+       PUTP(me);
+       putchar('\n');
+       PUTP(tgoto(cm, 0, 1));
+       if (!Batch) { /* should always be true, but... */
+           printf("Toggle fields with a-x, any other key to return: ");
+           fflush(stdout);
+           tcsetattr(0, TCSAFLUSH, &Rawtty);
+           read(0, &c, 1);
+           tcsetattr(0, TCSAFLUSH, &Savetty);
+       }
+       i = toupper(c) - 'A';
+       if (i >= 0 && i < sizeof headers / sizeof headers[0]) {
+           row = i % (Lines - 3) + 3;
+           col = i / (Lines - 3) * 40;
+           PUTP(tgoto(cm, col, row));
+           if ((p = strchr(Fields, i + 'A')) != NULL) {        /* deselect Field */
+               *p = i + 'a';
+               putchar(' ');
+           } else if ((p = strchr(Fields, i + 'a')) != NULL) {         /* select previously */
+               *p = i + 'A';   /* deselected field */
+               putchar('*');
+           } else {            /* select new field */
+               tmp[0] = i + 'A';
+               strcat(Fields, tmp);
+               putchar('*');
+           }
+           changed = 1;
+           fflush(stdout);
+       } else
+           break;
+    }
+    if (changed)
+       Numfields = make_header();
+}
+
+/* Do the scaling stuff: interprets time in seconds, formats it to
+ * fit width, and returns pointer to static char*.
+ */
+static char *scale_time(int t,int width) 
+{
+       static char buf[100];
+
+       /* Try successively higher units until it fits */
+
+       sprintf(buf,"%d:%02d",t/60,t%60);       /* minutes:seconds */
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=60;  /* minutes */
+       sprintf(buf,"%dm",t);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=60;  /* hours */
+       sprintf(buf,"%dh",t);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       t/=24;  /* days */
+       sprintf(buf,"%dd",t);
+       if (strlen(buf)<=width) 
+               return buf;
+       
+       t/=7;   /* weeks */
+       sprintf(buf,"%dw",t);
+       return buf;     /* this is our last try; 
+                               if it still doesn't fit, too bad. */
+
+       /* FIXME: if someone has a 16-way SMP running over a year... */
+}
+
+/*   scale_k(k,width,unit)  - interprets k as a count, formats to fit width.
+                            if unit is 0, k is a byte count; 1 is a kilobyte
+                           count; 2 for megabytes; 3 for gigabytes.
+*/
+
+static char *scale_k(int k,int width,int unit) 
+{
+               /* kilobytes, megabytes, gigabytes, too-big-for-int-bytes */
+       static double scale[]={1024,1024*1024,1024*1024*1024,0};
+               /* kilo, mega, giga, tera */
+       static char unitletters[]={'K','M','G','T',0};
+       static char buf[100];
+       char *up;
+       double *dp;
+
+       /* Try successively higher units until it fits */
+
+       sprintf(buf,"%d",k);
+       if (strlen(buf)<=width) 
+               return buf;
+
+       for (up=unitletters+unit,dp=scale ; *dp ; ++dp,++up) {
+               sprintf(buf,"%.1f%c",k / *dp,*up);
+               if (strlen(buf)<=width) 
+                       return buf;
+               sprintf(buf,"%d%c",(int)(k / *dp),*up);
+               if (strlen(buf)<=width) 
+                       return buf;
+       }
+
+       /* Give up; give them what we got on our shortest attempt */
+       return buf;
+}
+
+/*
+ *#######################################################################
+ *####  Routines handling the main top screen:                   ########
+ *####    show_task_info, show_procs, show_memory, do_stats      ########
+ *#######################################################################
+ */
+       /*
+        * Displays infos for a single task
+        */
+static void show_task_info(proc_t *task)
+{
+    int i,j;
+    unsigned int t;
+    char *cmdptr;
+    char tmp[2048], tmp2[2048] = "", tmp3[2048] = "", *p;
+
+    for (i = 0; i < Numfields; i++) {
+       tmp[0] = 0;
+       switch (pflags[i]) {
+         case P_PID:
+           sprintf(tmp, "%5d ", task->pid);
+           break;
+         case P_PPID:
+           sprintf(tmp, "%5d ", task->ppid);
+           break;
+         case P_EUID:
+           sprintf(tmp, "%4d ", task->euid);
+           break;
+         case P_EUSER:
+           sprintf(tmp, "%-8.8s ", task->euser);
+           break;
+         case P_PCPU:
+           sprintf(tmp, "%4.1f ", (float)task->pcpu / 10);
+           break;
+         case P_LCPU:
+           sprintf(tmp, "%2d ", task->processor);
+           break;
+         case P_PMEM: {
+              unsigned pmem;
+             pmem = task->vm_rss * 1000ULL / kb_main_total;
+             if (pmem > 999) pmem = 999;
+             sprintf(tmp, "%2u.%u ", pmem/10U, pmem%10U);
+           }
+           break;
+         case P_TTY: {
+             char outbuf[9];
+             dev_to_tty(outbuf, 8, task->tty, task->pid, ABBREV_DEV);
+             sprintf(tmp, "%-8.8s ", outbuf);
+           }
+           break;
+         case P_PRI:
+           sprintf(tmp, "%3.3s ", scale_k(task->priority, 3, 0));
+           break;
+         case P_NICE:
+           sprintf(tmp, "%3.3s ", scale_k(task->nice, 3, 0));
+           break;
+         case P_PAGEIN:
+           sprintf(tmp, "%6.6s ", scale_k(task->maj_flt, 6, 0));
+           break;
+         case P_TSIZ:
+           sprintf(tmp, "%5.5s ",
+               scale_k(((task->end_code - task->start_code) / 1024), 5, 1));
+           break;
+         case P_DSIZ:
+           sprintf(tmp, "%5.5s ",
+               scale_k(((task->vsize - task->end_code) / 1024), 5, 1));
+           break;
+         case P_SIZE:
+           sprintf(tmp, "%5.5s ", scale_k((task->size << CL_pg_shift), 5, 1));
+           break;
+         case P_TRS:
+           sprintf(tmp, "%4.4s ", scale_k((task->trs << CL_pg_shift), 4, 1));
+           break;
+         case P_SWAP:
+           sprintf(tmp, "%4.4s ",
+               scale_k(((task->size - task->resident) << CL_pg_shift), 4, 1));
+           break;
+         case P_SHARE:
+           sprintf(tmp, "%5.5s ", scale_k((task->share << CL_pg_shift), 5, 1));
+           break;
+         case P_A:
+           sprintf(tmp, "%3.3s ", "NYI");
+           break;
+         case P_WP:
+           sprintf(tmp, "%3.3s ", "NYI");
+           break;
+         case P_DT:
+           sprintf(tmp, "%3.3s ", scale_k(task->dt, 3, 0));
+           break;
+         case P_RSS:   /* rss, not resident (which includes IO memory) */
+           sprintf(tmp, "%4.4s ",
+               scale_k((task->rss << CL_pg_shift), 4, 1));
+           break;
+         case P_WCHAN:
+           if (!CL_wchan_nout)
+               sprintf(tmp, "%-9.9s ", wchan(task->wchan));
+           else
+               sprintf(tmp, "%-9lx", task->wchan);
+           break;
+         case P_STAT:
+           sprintf(tmp, "%-4.4s ", status(task));
+           break;
+         case P_TIME:
+           t = (task->utime + task->stime) / Hertz;
+           if (Cumulative)
+               t += (task->cutime + task->cstime) / Hertz;
+           sprintf(tmp, "%6.6s ", scale_time(t,6));
+           break;
+         case P_COMMAND:
+           if (!show_cmd && task->cmdline && *(task->cmdline)) {
+               j=0;
+               while(((task->cmdline)[j] != NULL) && (strlen(tmp3)<1020)){
+/* #if 0 */ /* This is useless? FIXME */
+                   if (j > 0)
+                       strcat(tmp3, " ");
+/* #endif */
+                   strncat(tmp3, (task->cmdline)[j], 1000);
+                   j++; 
+               }
+               cmdptr = tmp3;
+           } else {
+               cmdptr = task->cmd;
+           }
+           if (strlen(cmdptr) > Maxcmd)
+               cmdptr[Maxcmd - 1] = 0;
+           sprintf(tmp, "%s", cmdptr);
+           tmp3[0]=0;
+           break;
+         case P_FLAGS:
+           sprintf(tmp, "%8lx ", task->flags);
+           break;
+       }
+       strcat(tmp2, tmp);
+    }
+    if (strlen(tmp2) > Cols - 1)
+       tmp2[Cols - 1] = 0;
+
+    /* take care of cases like:
+       perl -e 'foo
+          bar foo bar
+          foo
+          # end of perl script'
+    */
+    for (p=tmp2;*p;++p)
+        if (!isgraph(*p))
+            *p=' ';
+
+    printf("\n%s", tmp2);
+    PUTP(top_clrtoeol);
+}
+
+/*
+ * This is the real program!  Read process info and display it.
+ * One could differentiate options of readproctable2, perhaps it
+ * would be useful to support the PROC_UID and PROC_TTY
+ * as command line options.
+ */
+static void show_procs(void)
+{
+    static proc_t **p_table=NULL;
+    static int proc_flags;
+    int count;
+    int ActualLines;
+    float elapsed_time;
+    static int first=0;
+
+    if (first==0) {
+       proc_flags=PROC_FILLMEM|PROC_FILLCMD|PROC_FILLUSR|PROC_FILLSTATUS|PROC_FILLSTAT;
+       if (monpids_index)
+           proc_flags |= PROC_PID;
+       p_table=readproctab2(proc_flags, p_table, monpids);
+       elapsed_time = get_elapsed_time();
+       do_stats(p_table, elapsed_time, 0);
+       sleep(1);
+       first=1;
+    }
+    if (first && Batch)
+           fputs("\n\n",stdout);
+    /* Display the load averages. */
+    PUTP(ho);
+    PUTP(md);
+    if (show_loadav) {
+       printf("%s", sprint_uptime());
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    p_table=readproctab2(proc_flags, p_table, monpids);
+    /* Immediately find out the elapsed time for the frame. */
+    elapsed_time = get_elapsed_time();
+    /* Display the system stats, calculate percent CPU time
+     * and sort the list. */
+    do_stats(p_table, elapsed_time,1);
+    /* Display the memory and swap space usage. */
+    show_meminfo();
+    if (strlen(Header) + 2 > Cols)
+       Header[Cols - 2] = 0;
+    PUTP(mr);
+    fputs(Header, stdout);
+    PUTP(top_clrtoeol);
+    PUTP(me);
+
+    /*
+     * Finally!  Loop through to find the top task, and display it.
+     * Lather, rinse, repeat.
+     */
+    count = 0;
+    ActualLines = 0;
+    while ((ActualLines < Maxlines) && (p_table[count]->pid!=-1)) {
+       char Stat;
+
+       Stat = p_table[count]->state;
+
+       if ( (!Noidle || (Stat != 'S' && Stat != 'Z')) &&
+            ( (CurrUser[0] == '\0') ||
+             (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
+
+           /*
+            * Show task info.
+            */
+           show_task_info(p_table[count]);
+           if (!Batch)
+               ActualLines++;
+       }
+       count++;
+    }
+    PUTP(top_clrtobot);
+    PUTP(tgoto(cm, 0, header_lines - 2));
+    fflush(stdout);
+}
+
+
+/*
+ * Finds the current time (in microseconds) and calculates the time
+ * elapsed since the last update. This is essential for computing
+ * percent CPU usage.
+ */
+static float get_elapsed_time(void)
+{
+    struct timeval t;
+    static struct timeval oldtime;
+    struct timezone timez;
+    float elapsed_time;
+
+    gettimeofday(&t, &timez);
+    elapsed_time = (t.tv_sec - oldtime.tv_sec)
+       + (float) (t.tv_usec - oldtime.tv_usec) / 1000000.0;
+    oldtime.tv_sec  = t.tv_sec;
+    oldtime.tv_usec = t.tv_usec;
+    return (elapsed_time);
+}
+
+/*
+ * Reads the memory info and displays it.  Returns the total memory
+ * available, for use in percent memory usage calculations.
+ */
+static void show_meminfo(void)
+{
+    meminfo(); /* read+parse /proc/meminfo */
+    if (show_memory) {
+       printf(
+           "Mem:  %8dK total, %8dK used, %8dK free, %8dK buffers",
+           kb_main_total,
+           kb_main_used,
+           kb_main_free,
+           kb_main_buffers
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+       printf(
+           "Swap: %8dK total, %8dK used, %8dK free, %8dK cached",
+           kb_swap_total,
+           kb_swap_used,
+           kb_swap_free,
+           kb_main_cached
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    PUTP(me);
+    PUTP(top_clrtoeol);
+    putchar('\n');
+}
+
+
+/*
+ * Calculates the number of tasks in each state (running, sleeping, etc.).
+ * Calculates the CPU time in each state (system, user, nice, etc).
+ * Calculates percent cpu usage for each task.
+ */
+static void do_stats(proc_t** p, float elapsed_time, int pass)
+{
+    proc_t *this;
+    int arrindex, total_time, cpumap, i, n = 0;
+    int sleeping = 0, stopped = 0, zombie = 0, running = 0;
+    double system_ticks, user_ticks, nice_ticks, idle_ticks;
+    static int prev_count = 0;
+    int systime, usrtime;
+     /* start with one page as a reasonable allocate size */
+     static int save_history_size =
+         sizeof(long)*1024 / sizeof(struct save_hist);
+     static struct save_hist *save_history;
+     struct save_hist *New_save_hist;
+     if (!save_history)
+       save_history = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+     New_save_hist  = xcalloc(NULL, sizeof(struct save_hist)*save_history_size);
+
+    /*
+     * Make a pass through the data to get stats.
+     */
+    arrindex = 0;
+    while (p[n]->pid != -1) {
+       this = p[n];
+       switch (this->state) {
+         case 'S':
+         case 'D':
+           sleeping++;
+           break;
+         case 'T':
+           stopped++;
+           break;
+         case 'Z':
+           zombie++;
+           break;
+         case 'R':
+           running++;
+           break;
+         default:
+           /* Don't know how to handle this one. */
+           break;
+        }
+
+       /*
+        * Calculate time in this process.  Time is sum of user time
+        * (usrtime) plus system time (systime).
+        */
+       total_time = this->utime + this->stime;
+        if (arrindex >= save_history_size) {
+            save_history_size *= 2;
+            save_history  = xrealloc(save_history, sizeof(struct save_hist)*save_history_size);
+            New_save_hist = xrealloc(New_save_hist, sizeof(struct save_hist)*save_history_size);
+        }
+       New_save_hist[arrindex].ticks = total_time;
+       New_save_hist[arrindex].pid = this->pid;
+       systime = this->stime;
+       usrtime = this->utime;
+       New_save_hist[arrindex].stime = systime;
+       New_save_hist[arrindex].utime = usrtime;
+
+       /* find matching entry from previous pass */
+       for (i = 0; i < prev_count; i++) {
+           if (save_history[i].pid == this->pid) {
+               total_time -= save_history[i].ticks;
+               systime -= save_history[i].stime;
+               usrtime -= save_history[i].utime;
+
+               i = prev_count;
+           }
+       }
+
+       /*
+        * Calculate percent cpu time for this task.
+        */
+       this->pcpu = (total_time * 10 * 100/Hertz) / elapsed_time;
+       if (this->pcpu > 999)
+           this->pcpu = 999;
+
+       arrindex++;
+       n++;
+    }
+
+    /*
+     * Display stats.
+     */
+    if (pass > 0 && show_stats) {
+       printf("%d processes: %d sleeping, %d running, %d zombie, "
+              "%d stopped",
+              n, sleeping, running, zombie, stopped);
+       PUTP(top_clrtoeol);
+       putchar('\n');
+       four_cpu_numbers(&user_ticks,&nice_ticks,&system_ticks,&idle_ticks);
+       printf("CPU states:"
+           " %# 5.1f%% user, %# 5.1f%% system,"
+           " %# 5.1f%% nice, %# 5.1f%% idle",
+            user_ticks,
+            system_ticks,
+            nice_ticks,
+            idle_ticks
+       );
+       PUTP(top_clrtoeol);
+       putchar('\n');
+    }
+    /*
+     * Save this frame's information.
+     */
+    for (i = 0; i < n; i++) {
+       /* copy the relevant info for the next pass */
+       save_history[i].pid = New_save_hist[i].pid;
+       save_history[i].ticks = New_save_hist[i].ticks;
+       save_history[i].stime = New_save_hist[i].stime;
+       save_history[i].utime = New_save_hist[i].utime;
+    }
+    free(New_save_hist);
+
+    prev_count = n;
+    qsort(p, n, sizeof(proc_t*), (void*)mult_lvl_cmp);
+}
+
+
+/*
+ * Process keyboard input during the main loop
+ */
+static void do_key(char c)
+{
+    int numinput, i;
+    char rcfile[MAXNAMELEN];
+    FILE *fp;
+
+    /*
+     * First the commands which don't require a terminal mode switch.
+     */
+    if (c == 'q')
+       end(0);
+    else if (c == ' ')
+        return;
+    else if (c == 12) {
+       clear_screen();
+       return;
+    } else if (c == 'I') {
+       Irixmode=(Irixmode) ? 0 : 1;
+       return;
+    }
+
+    /*
+     * Switch the terminal to normal mode.  (Will the original
+     * attributes always be normal?  Does it matter?  I suppose the
+     * shell will be set up the way the user wants it.)
+     */
+    if (!Batch) tcsetattr(0, TCSANOW, &Savetty);
+
+    /*
+     * Handle the rest of the commands.
+     */
+    switch (c) {
+      case '?':
+      case 'h':
+       PUTP(cl); PUTP(ho); putchar('\n'); PUTP(mr);
+       printf("Proc-Top Revision 1.2");
+       PUTP(me); putchar('\n');
+       printf("Secure mode ");
+       PUTP(md);
+       fputs(Secure ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("; cumulative mode ", stdout);
+       PUTP(md);
+       fputs(Cumulative ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("; noidle mode ", stdout);
+       PUTP(md);
+       fputs(Noidle ? "on" : "off", stdout);
+       PUTP(me);
+       fputs("\n\n", stdout);
+       printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
+       if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+       (void) getchar();
+       break;
+      case 'i':
+       Noidle = !Noidle;
+       SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
+       break;
+      case 'u':
+       SHOWMESSAGE(("Which User (Blank for All): "));
+       strcpy(CurrUser,getstr());
+       break;
+      case 'k':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't kill in secure mode"));
+       else {
+           int pid, signo;
+           PUTP(md);
+           SHOWMESSAGE(("PID to kill: "));
+           pid = getint();
+           if (pid == BAD_INPUT)
+               break;
+           PUTP(top_clrtoeol);
+           SHOWMESSAGE(("Kill process %d with what signal? [15] ", pid));
+           PUTP(me);
+           signo = getsig();
+           /* FIXME: -1 may mean an unknown signal */
+           if (signo == -1)
+               signo = SIGTERM;
+           if (kill(pid, signo))
+               SHOWMESSAGE(("\aKill of PID %d with %d failed: %s",
+                            pid, signo, strerror(errno)));
+       }
+       break;
+      case 'l':
+       SHOWMESSAGE(("Display load average %s", !show_loadav ? "on" : "off"));
+       if (show_loadav) {
+           show_loadav = 0;
+           header_lines--;
+       } else {
+           show_loadav = 1;
+           header_lines++;
+       }
+       Numfields = make_header();
+       break;
+      case 'm':
+       SHOWMESSAGE(("Display memory information %s", !show_memory ? "on" : "off"));
+       if (show_memory) {
+           show_memory = 0;
+           header_lines -= 2;
+       } else {
+           show_memory = 1;
+           header_lines += 2;
+       }
+       Numfields = make_header();
+       break;
+      case 'M':
+        SHOWMESSAGE(("Sort by memory usage"));
+       sort_type = S_MEM;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)mem_sort);
+       break;
+      case 'n':
+      case '#':
+       printf("Processes to display (0 for unlimited): ");
+       numinput = getint();
+       if (numinput != -1) {
+           Display_procs = numinput;
+           window_size(0);
+       }
+       break;
+      case 'r':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't renice in secure mode"));
+       else {
+           int pid, val;
+
+           printf("PID to renice: ");
+           pid = getint();
+           if (pid == BAD_INPUT)
+               break;
+           PUTP(tgoto(cm, 0, header_lines - 2));
+           PUTP(top_clrtoeol);
+           printf("Renice PID %d to value: ", pid);
+           val = getint();
+           if (val == BAD_INPUT)
+               val = 10;
+           if (setpriority(PRIO_PROCESS, pid, val))
+               SHOWMESSAGE(("\aRenice of PID %d to %d failed: %s",
+                            pid, val, strerror(errno)));
+       }
+       break;
+      case 'P':
+        SHOWMESSAGE(("Sort by CPU usage"));
+       sort_type = S_PCPU;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)pcpu_sort);
+       break;
+      case 'A':
+       SHOWMESSAGE(("Sort by age"));
+       sort_type = S_AGE;
+       reset_sort_options();
+       register_sort_function(-1, (cmp_t)age_sort);
+       break;
+      case 'N':
+       SHOWMESSAGE(("Sort numerically by pid"));
+       sort_type = S_NONE;
+       reset_sort_options();
+       break;
+    case 'c':
+        show_cmd = !show_cmd;
+       SHOWMESSAGE(("Show %s", show_cmd ? "command names" : "command line"));
+       break;
+      case 'S':
+       Cumulative = !Cumulative;
+       SHOWMESSAGE(("Cumulative mode %s", Cumulative ? "on" : "off"));
+       if (Cumulative)
+           headers[22][1] = 'C';
+       else
+           headers[22][1] = ' ';
+       Numfields = make_header();
+       break;
+      case 's':
+       if (Secure)
+           SHOWMESSAGE(("\aCan't change delay in secure mode"));
+       else {
+           double tmp;
+           printf("Delay between updates: ");
+           tmp = getfloat();
+           if (!(tmp < 0))
+               Sleeptime = tmp;
+       }
+       break;
+      case 't':
+       SHOWMESSAGE(("Display summary information %s", !show_stats ? "on" : "off"));
+       if (show_stats) {
+           show_stats = 0;
+           header_lines -= 2;
+       } else {
+           show_stats = 1;
+           header_lines += 2;
+       }
+       Numfields = make_header();
+       break;
+      case 'T':
+       SHOWMESSAGE(("Sort by %stime", Cumulative ? "cumulative " : ""));
+       sort_type = S_TIME;
+       reset_sort_options();
+       register_sort_function( -1, (cmp_t)time_sort);  
+       break;
+      case 'f':
+      case 'F':
+       change_fields();
+       break;
+      case 'o':
+      case 'O':
+       change_order();
+       break;
+      case 'W':
+       if (getenv("HOME")) {
+           strcpy(rcfile, getenv("HOME"));
+           strcat(rcfile, "/");
+           strcat(rcfile, RCFILE);
+           fp = fopen(rcfile, "w");
+           if (fp != NULL) {
+               fprintf(fp, "%s\n", Fields);
+               i = (int) Sleeptime;
+               if (i < 2)
+                   i = 2;
+               if (i > 9)
+                   i = 9;
+               fprintf(fp, "%d", i);
+               if (Secure)
+                   fprintf(fp, "%c", 's');
+               if (Cumulative)
+                   fprintf(fp, "%c", 'S');
+               if (!show_cmd)
+                   fprintf(fp, "%c", 'c');
+               if (Noidle)
+                   fprintf(fp, "%c", 'i');
+               if (!show_memory)
+                   fprintf(fp, "%c", 'm');
+               if (!show_loadav)
+                   fprintf(fp, "%c", 'l');
+               if (!show_stats)
+                   fprintf(fp, "%c", 't');
+               if (!Irixmode)
+                   fprintf(fp, "%c", 'I');
+               fprintf(fp, "\n");
+               fclose(fp);
+               SHOWMESSAGE(("Wrote configuration to %s", rcfile));
+           } else {
+               SHOWMESSAGE(("Couldn't open %s", rcfile));
+           }
+       } else {
+           SHOWMESSAGE(("Couldn't get $HOME -- not saving"));
+       }
+       break;
+      default:
+       SHOWMESSAGE(("\aUnknown command `%c' -- hit `h' for help", c));
+    }
+
+    /*
+     * Return to raw mode.
+     */
+    if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
+    return;
+}
+
+
+/*#####################################################################
+ *#######   A readproctable function that uses already allocated  #####
+ *#######   table entries.                                        #####
+ *#####################################################################
+ */
+#define Do(x) (flags & PROC_ ## x)
+
+static proc_t** readproctab2(int flags, proc_t** tab, ...) {
+    PROCTAB* PT = NULL;
+    static proc_t *buff;
+    int n = 0;
+    static int len = 0;
+    va_list ap;
+
+    va_start(ap, tab);         /* pass through args to openproc */
+    if (Do(UID)) {
+       /* temporary variables to ensure that va_arg() instances
+        * are called in the right order
+        */
+       uid_t* u;
+       int i;
+
+       u = va_arg(ap, uid_t*);
+       i = va_arg(ap, int);
+       PT = openproc(flags, u, i);
+    }
+    else if (Do(PID)) {
+       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+       /* work around a bug in openproc() */
+       PT->procfs = NULL;
+       /* share some process time, since we skipped opendir("/proc") */
+       usleep (50*1000);
+    }
+    else if (Do(TTY) || Do(STAT))
+       PT = openproc(flags, va_arg(ap, void*)); /* assume ptr sizes same */
+    else
+       PT = openproc(flags);
+    va_end(ap);
+    buff = (proc_t *) 1;
+    while (n<len && buff) {     /* read table: (i) already allocated chunks */
+       if (tab[n]->cmdline) {
+           free((void*)*tab[n]->cmdline);
+           tab[n]->cmdline = NULL;
+       }
+        buff = readproc(PT, tab[n]);
+       if (buff) n++;
+    }
+    if (buff) {
+       do {               /* (ii) not yet allocated chunks */
+           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));/* realloc as we go, using */
+           buff = readproc(PT, NULL);            /* final null to terminate */
+           if(buff) tab[n]=buff;
+           len++;
+           n++;
+       } while (buff);                   /* stop when NULL reached */
+       tab[n-1] = xcalloc(NULL, sizeof (proc_t));
+       tab[n-1]->pid=-1;                /* Mark end of Table */
+    } else {
+       if (n == len) {
+           tab = xrealloc(tab, (n+1)*sizeof(proc_t*));
+           tab[n] = xcalloc(NULL, sizeof (proc_t));
+           len++;
+       }
+       tab[n]->pid=-1;    /* Use this instead of NULL when not at the end of */
+    }                   /* the allocated space */
+    closeproc(PT);
+    return tab;
+}
diff --git a/top.desktop b/top.desktop
new file mode 100644 (file)
index 0000000..cf1c191
--- /dev/null
@@ -0,0 +1,6 @@
+[Desktop Entry]
+Name=Top
+Type=Application
+Comment=Repeatedly display processes by CPU time, memory usage, etc.
+Exec=top
+Terminal=true
diff --git a/top.h b/top.h
new file mode 100644 (file)
index 0000000..e7cc2e2
--- /dev/null
+++ b/top.h
@@ -0,0 +1,228 @@
+/*
+ * top.h header file 1996/05/18, 
+ *
+ * function prototypes, global data definitions and string constants.
+ */
+
+static proc_t** readproctab2(int flags, proc_t** tab, ...);
+static void parse_options(char *Options, int secure);
+static void get_options(void);
+static void error_end(int rno);
+static void end(int signo);
+static void stop(int signo);
+static void window_size(int signo);
+static int make_header(void);
+static char *getstr(void);
+static int getsig(void);
+static float getfloat(void);
+static int time_sort(proc_t **P, proc_t **Q);
+static int pcpu_sort(proc_t **P, proc_t **Q);
+static int mem_sort(proc_t **P, proc_t **Q);
+static int age_sort(proc_t **P, proc_t **Q);
+static void show_fields(void);
+static void change_order(void);
+static void change_fields(void);
+static void show_task_info(proc_t *task);
+static void show_procs(void);
+static float get_elapsed_time(void);
+static void show_meminfo(void);
+static void do_stats(proc_t** p, float elapsed_time, int pass);
+static void do_key(char c);
+
+
+/* configurable field display support */
+
+static int pflags[30];
+static int Numfields;
+
+
+       /* Name of the config file (in $HOME)  */
+#ifndef RCFILE
+#define RCFILE         ".toprc"
+#endif
+
+#ifndef SYS_TOPRC
+#define SYS_TOPRC      "/etc/toprc"
+#endif
+
+#define MAXLINES 2048
+#define MAXNAMELEN 1024
+
+/* this is what procps top does by default, so let's do this, if nothing is
+ * specified
+ */
+#ifndef DEFAULT_SHOW
+/*                       0         1         2         3 */
+/*                       0123456789012345678901234567890 */
+#define DEFAULT_SHOW    "AbcDgHIjklMnoTP|qrsuzyV{EFWX"
+#endif
+static char Fields[256] = "";
+
+
+/* This structure stores some critical information from one frame to
+   the next. mostly used for sorting. Added cumulative and resident fields. */
+struct save_hist {
+    int ticks;
+    int pid;
+    int pcpu;
+    int utime;
+    int stime;
+};
+
+       /* The original terminal attributes. */
+static struct termios Savetty;
+       /* The new terminal attributes. */
+static struct termios Rawtty;
+       /* Cached termcap entries. */
+static char *cm, *cl, *top_clrtobot, *top_clrtoeol, *ho, *md, *me, *mr;
+       /* Current window size.  Note that it is legal to set Display_procs
+          larger than can fit; if the window is later resized, all will be ok.
+          In other words: Display_procs is the specified max number of
+          processes to display (zero for infinite), and Maxlines is the actual
+          number. */
+static int Lines, Cols, Maxlines, Display_procs;
+       /* Maximum length to display of the command line of a process. */
+static unsigned Maxcmd;
+
+       /* Controls how long we sleep between screen updates.  Accurate to
+          microseconds. */
+static float Sleeptime = 5;
+       /* for opening/closing the system map */
+static int psdbsucc = 0;
+       /* Mode flags. */
+static int Irixmode = 1;
+static int Secure = 0;
+static int Cumulative = 0;
+static int Noidle = 0;
+
+static int CPU_states = 0;
+static char CurrUser[BUFSIZ];
+
+static int CL_pg_shift = (PAGE_SHIFT - 10);
+static int CL_wchan_nout = -1;
+
+static int show_stats = 1;    /* show status summary */
+static int show_memory = 1;   /* show memory summary */
+static int show_loadav = 1;   /* show load average and uptime */
+static int show_cmd = 1;      /* show command name instead of commandline */
+
+static pid_t monpids[520]; /* randomly chosen value */
+static const int monpids_max = sizeof(monpids)/sizeof(pid_t);
+static int monpids_index = 0;
+
+static int Loops = -1;        /* number of iterations. -1 loops forever */
+static int Batch = 0;         /* batch mode. Collect no input, dumb output */
+
+/* sorting order: cpu%, mem, time (cumulative, if in cumulative mode) */
+enum {
+    S_PCPU, S_MEM, S_TIME, S_AGE, S_NONE
+};
+/* default sorting by CPU% */ 
+static int sort_type = S_PCPU;
+
+/* flags for each possible field. At the moment up to 30 are supported */
+enum {
+    P_PID, P_PPID, P_EUID, P_EUSER,
+    P_PCPU, P_PMEM, P_TTY, P_PRI,
+    P_NICE, P_PAGEIN, P_TSIZ, P_DSIZ,
+    P_SIZE, P_TRS, P_SWAP, P_SHARE,
+    P_A, P_WP, P_DT, P_RSS,
+    P_WCHAN, P_STAT, P_TIME, P_COMMAND,
+    P_LCPU, P_FLAGS, P_END
+};
+/* corresponding headers */
+static char *headers[] =
+{
+    "  PID ", " PPID ", " UID ",
+    "USER     ", "%CPU ", "%MEM ",
+    "TTY      ", "PRI ", " NI ",
+    "PAGEIN ", "TSIZE ", "DSIZE ",
+    " SIZE ", " TRS ", "SWAP ",
+    "SHARE ", "  A ", " WP ",
+    "  D ", " RSS ", "WCHAN     ",
+    "STAT ", "  TIME ", "COMMAND",
+    "LC ",
+    "   FLAGS "
+};
+/* corresponding field desciptions */
+static char *headers2[] =
+{
+    "Process Id", "Parent Process Id", "User Id",
+    "User Name", "CPU Usage", "Memory Usage",
+    "Controlling tty", "Priority", "Nice Value",
+    "Page Fault Count", "Code Size (kb)", "Data+Stack Size (kb)",
+    "Virtual Image Size (kb)", "Resident Text Size (kb)", "Swapped kb",
+    "Shared Pages (kb)", "Accessed Page count", "Write Protected Pages",
+    "Dirty Pages", "Resident Set Size (kb)", "Sleeping in Function",
+    "Process Status", "CPU Time", "Command",
+    "Last used CPU (expect this to change regularly)",
+    "Task Flags (see linux/sched.h)"
+};
+
+       /* The header printed at the top of the process list.*/
+static char Header[MAXLINES];
+
+       /* The response to the interactive 'h' command. */
+#define HELP_SCREEN "\
+Interactive commands are:\n\
+\n\
+space\tUpdate display\n\
+^L\tRedraw the screen\n\
+fF\tadd and remove fields\n\
+oO\tChange order of displayed fields\n\
+h or ?\tPrint this list\n\
+S\tToggle cumulative mode\n\
+i\tToggle display of idle proceses\n\
+I\tToggle between Irix and Solaris views (SMP-only)\n\
+c\tToggle display of command name/line\n\
+l\tToggle display of load average\n\
+m\tToggle display of memory information\n\
+t\tToggle display of summary information\n\
+k\tKill a task (with any signal)\n\
+r\tRenice a task\n\
+N\tSort by pid (Numerically)\n\
+A\tSort by age\n\
+P\tSort by CPU usage\n\
+M\tSort by resident memory usage\n\
+T\tSort by time / cumulative time\n\
+u\tShow only a specific user\n\
+n or #\tSet the number of process to show\n\
+s\tSet the delay in seconds between updates\n\
+W\tWrite configuration file ~/.toprc\n\
+q\tQuit"
+#define SECURE_HELP_SCREEN "\
+Interactive commands available in secure mode are:\n\
+\n\
+space\tUpdate display\n\
+^L\tRedraw the screen\n\
+fF\tadd and remove fields\n\
+h or ?\tPrint this list\n\
+S\tToggle cumulative mode\n\
+i\tToggle display of idle proceses\n\
+c\tToggle display of command name/line\n\
+l\tToggle display of load average\n\
+m\tToggle display of memory information\n\
+t\tToggle display of summary information\n\
+n or #\tSet the number of process to show\n\
+u\tShow only a specific user\n\
+oO\tChange order of displayed fields\n\
+W\tWrite configuration file ~/.toprc\n\
+q\tQuit"
+
+       /* Number of lines needed to display the header information. */
+static int header_lines;
+
+/* ############## Some Macro definitions for screen handling ######### */
+       /* String to use in error messages. */
+#define PROGNAME "top"
+       /* Clear the screen. */
+#define clear_screen() \
+           printf("%s", cl)
+       /* Show an error in the context of the spiffy full-screen display. */
+#define SHOWMESSAGE(x) do {                    \
+           printf("%s%s%s%s", tgoto(cm, 0, header_lines-2), top_clrtoeol,md,mr);       \
+           printf x;                                   \
+           printf ("%s",me);                           \
+           fflush(stdout);                             \
+           sleep(2);                                   \
+       } while (0)
diff --git a/uptime.1 b/uptime.1
new file mode 100644 (file)
index 0000000..ecb4e70
--- /dev/null
+++ b/uptime.1
@@ -0,0 +1,34 @@
+.\"             -*-Nroff-*-
+.\"
+.TH UPTIME 1 "26 Jan 1993" "Cohesive Systems" "Linux User's Manual"
+.SH NAME
+uptime \- Tell how long the system has been running.
+.SH SYNOPSIS
+.B uptime
+.br
+.BR uptime " [" "\-V" ]
+.SH DESCRIPTION
+.B uptime
+gives a one line display of the following information.
+The current time,
+how long the system has been running,
+how many users are currently logged on,
+and the system load averages for the past 1, 5, and 15 minutes.
+.sp
+This is the same information contained in the header line displayed by 
+.BR w (1).
+.SH FILES
+.IR /var/run/utmp "    information about who is currently logged on"
+.br
+.IR /proc "    process information"
+.SH AUTHORS
+.B uptime
+was written by Larry Greenfield <greenfie@gauss.rutgers.edu> and
+Michael K. Johnson <johnsonm@sunsite.unc.edu>.
+
+Please send bug reports to <acahalan@cs.uml.edu>
+.SH "SEE ALSO"
+.BR ps (1),
+.BR top (1),
+.BR utmp (5),
+.BR w (1)
diff --git a/uptime.c b/uptime.c
new file mode 100644 (file)
index 0000000..e63621e
--- /dev/null
+++ b/uptime.c
@@ -0,0 +1,9 @@
+#include <string.h>
+#include "proc/whattime.h"
+#include "proc/version.h"
+
+int main(int argc, char *argv[]) {
+  if(argc == 1) print_uptime();
+  if((argc == 2) && (!strcmp(argv[1], "-V"))) display_version();
+  return 0;
+}
diff --git a/utmp.c b/utmp.c
new file mode 100644 (file)
index 0000000..936a7f6
--- /dev/null
+++ b/utmp.c
@@ -0,0 +1,178 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <utmp.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* examine and fix utmp entries.  Note that the code for fixing entries
+   is not complete, and indeed does nothing at all at this time.  No bug
+   reports, please, as I am still actively working on this.  It is not
+   here for general use, but only so that I can ferret out any bugs that
+   exist on other peoples systems without having to log in to their systems
+   myself ;-)  */
+
+
+int main (int argc, char **argv) {
+
+  FILE *ut; /* /var/run/utmp */
+  struct utmp uts; /* utmp record */
+  char user[UT_NAMESIZE + 1];
+  char host[17];
+  char ch;
+  int print_all = 0, list = 0, fix = 0;
+
+/* get options */
+  while ((ch = getopt(argc, argv, "laf")) != EOF)
+    switch (ch) {
+    case 'a':
+      print_all = 1;
+      break;
+    case 'l':
+      list = 1;
+      break;
+    case 'f':
+      fix = 1;
+      break;
+    }
+
+/* check argument options */
+  if ( (!list && !print_all && !fix)) {
+    fprintf(stderr, "You must specify a command line option:\n\tl = list\n\
+\tf = fix\n\ta = all (requires l or f)\n");
+    exit(1);
+  }
+
+  
+  if (list) {
+    ut = fopen(UTMP_FILE, "r");
+    while (fread(&uts, sizeof(uts), 1, ut))
+      if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000'))
+         || print_all) {
+       strncpy(user, uts.ut_user, UT_NAMESIZE);
+       user[UT_NAMESIZE]=0;
+       strncpy(host, uts.ut_host, 16);
+       host[16]=0;
+       printf("ut_type: %d\n", uts.ut_type);
+       printf("ut_pid:  %d\n", uts.ut_pid);
+       printf("ut_line: %s\n", uts.ut_line);
+       printf("ut_id:   %2s\n", uts.ut_id);
+       printf("ut_time: %d\n", uts.ut_time);
+       printf("ut_user: %s\n", user);
+       printf("ut_host: %s\n", host);
+       printf("ut_addr: %d\n\n", uts.ut_addr);
+      }
+    fclose(ut);
+  }
+
+
+  if (fix) {
+    ut = fopen(UTMP_FILE, "r");
+    while (fread(&uts, sizeof(uts), 1, ut)) 
+      if (((uts.ut_type == USER_PROCESS) && (uts.ut_name[0] != '\000'))
+         || print_all) {
+       /* Display entry in utmp */
+       strncpy(user, uts.ut_user, UT_NAMESIZE);
+       user[UT_NAMESIZE]=0;
+       strncpy(host, uts.ut_host, 16);
+       host[16]=0;
+       printf("ut_type: %d\n", uts.ut_type);
+       printf("ut_pid:  %d\n", uts.ut_pid);
+       printf("ut_line: %s\n", uts.ut_line);
+       printf("ut_id:   %s2\n", uts.ut_id);
+       printf("ut_time: %d\n", uts.ut_time);
+       printf("ut_user: %s\n", user);
+       printf("ut_host: %s\n", host);
+       printf("ut_addr: %d\n\n", uts.ut_addr);
+      
+       printf("Modify this record? (y/N): "); fflush(stdout);
+       /* Ask if to delete or no */
+       if ((ch = getchar()) == 'y' || ch == 'Y') {
+         while (getchar() != '\n');
+         printf("Change ut_type? "); fflush(stdout);
+         if ((ch = getchar()) == 'y' || ch == 'Y') {
+           while (getchar() != '\n');
+           printf("INIT, LOGIN, USER, or DEAD_PROCESS? (I/L/U/D): ");
+           fflush(stdout);
+           ch = getchar();
+           switch (ch) {
+           case 'i':
+           case 'I':
+             uts.ut_type = INIT_PROCESS;
+             break;
+           case 'l':
+           case 'L':
+             uts.ut_type = LOGIN_PROCESS;
+             break;
+           case 'u':
+           case 'U':
+             uts.ut_type = USER_PROCESS;
+             break;
+           case 'd':
+           case 'D':
+             uts.ut_type = DEAD_PROCESS;
+             break;
+           default:
+             printf("Invalid choice: %c\n", ch);
+           }
+           if (ch != '\n') while ((ch = getchar()) != '\n');
+         }
+         if (ch != '\n') while ((ch = getchar()) != '\n');
+         printf("Change ut_id field? (y/N): "); fflush(stdout);
+         if ((ch = getchar()) == 'y' || ch == 'Y') {
+           while (getchar() != '\n');
+           printf("Please enter the two characters for ut_id: ");
+           fflush(stdout);
+           uts.ut_id[0] = getchar();
+           uts.ut_id[1] = getchar();
+           while ((ch = getchar()) != '\n');
+         }
+         if (ch != '\n') while ((ch = getchar()) != '\n');
+         printf("Change the ut_user field? (y/N): "); fflush(stdout);
+         if ((ch = getchar()) == 'y' || ch == 'Y') {
+           int i;
+           while (getchar() != '\n');
+           printf("Please enter the new ut_name, up to %c characters: ",
+                  UT_NAMESIZE);
+           fflush(stdout);
+           for (i=0; i<UT_NAMESIZE; i++) {
+             ch = getchar();
+             uts.ut_user[i] = (ch != '\n') ? ch : i = UT_NAMESIZE, (char) 0;
+           }
+         }
+         if (ch != '\n') while ((ch = getchar()) != '\n');
+         printf("Change the ut_host field? (y/N): "); fflush(stdout);
+         if ((ch = getchar()) == 'y' || ch == 'Y') {
+           int i;
+           while (getchar() != '\n');
+           printf("Please enter the new ut_host, up to 16 characters: ");
+           fflush(stdout);
+           for (i=0; i<16; i++) {
+             ch = getchar();
+             uts.ut_user[i] = (ch != '\n') ? ch : i = 16, (char) 0;
+           }
+           if (ch != '\n') while ((ch = getchar()) != '\n');
+         }
+
+         /* Here go the changes...*/
+/*       utmpname(UTMP_FILE);
+         setutent();
+         pututline(&uts);
+         endutent(); */
+/* But they don't work... */
+
+       }
+       if (ch != '\n') while ((ch = getchar()) != '\n');
+       /* here we should write the utmp entry */
+      }
+    fclose(ut);
+  }
+
+
+  return 0;
+
+
+}
diff --git a/vmstat.8 b/vmstat.8
new file mode 100644 (file)
index 0000000..32a8129
--- /dev/null
+++ b/vmstat.8
@@ -0,0 +1,105 @@
+.\"  This page Copyright (C) 1994 Henry Ware <al172@yfn.ysu.edu>
+.\"  Distributed under the GPL, Copyleft 1994.
+.TH VMSTAT 8 "27 July 1994 " "Throatwobbler Ginkgo Labs" "Linux Administrator's Manual"
+.SH NAME
+vmstat \- Report virtual memory statistics
+.SH SYNOPSIS
+.ft B
+.B vmstat
+.RB [ "\-n" ]
+.RI [ delay " [ " count ]]
+.br
+.BR vmstat [ "\-V" ]
+.SH DESCRIPTION
+\fBvmstat\fP reports information about processes, memory, paging,
+block IO, traps, and cpu activity.
+
+The first report produced gives averages since the last reboot.  Additional
+reports give information on a sampling period of length \fIdelay\fP.
+The process and memory reports are instantaneous in either case.
+
+.SS Options
+The \fB-n\fP switch  causes the header to be displayed only once rather than periodically.
+.PP
+.I delay
+is the delay between updates in seconds.  If no delay is specified,
+only one report is printed with the average values since boot.
+.PP
+.I count
+is the number of updates.  If no count is specified and delay is
+defined, \fIcount\fP defaults to infinity.
+.PP
+The \fB-V\fP switch results in displaying version information.
+.PP
+.SH FIELD DESCRIPTIONS
+.SS
+.B "Procs"
+.nf
+r: The number of processes waiting for run time.  
+b: The number of processes in uninterruptable sleep.
+w: The number of processes swapped out but otherwise runnable.  This 
+   field is calculated, but Linux never desperation swaps.
+.fi
+.PP
+.SS
+.B "Memory"
+.nf
+swpd: the amount of virtual memory used (kB).
+free: the amount of idle memory (kB).
+buff: the amount of memory used as buffers (kB).
+.fi
+.PP
+.SS
+.B "Swap"
+.nf
+si: Amount of memory swapped in from disk (kB/s).
+so: Amount of memory swapped to disk (kB/s).
+.fi
+.PP
+.SS
+.B "IO"
+.nf
+bi: Blocks sent to a block device (blocks/s).
+bo: Blocks received from a block device (blocks/s).
+.fi
+.PP
+.SS
+.B "System"
+.nf
+in: The number of interrupts per second, including the clock.
+cs: The number of context switches per second.
+.if
+.PP
+.SS
+.B "CPU "
+These are percentages of total CPU time.
+.nf
+us: user time
+sy: system time
+id: idle time 
+.nf
+.SH NOTES
+.B "vmstat "
+does not require special permissions.
+.PP
+These reports are intended to help identify system bottlenecks.  Linux
+.B "vmstat "
+does not count itself as a running process.
+.PP
+All linux blocks are currently 1k, except for CD-ROM blocks which are 2k.
+.PP
+.SH FILES
+.ta
+.nf
+/proc/meminfo
+/proc/stat
+/proc/*/stat
+.fi
+
+.SH "SEE ALSO"
+ps(1), top(1), free(1)
+.PP
+.SH BUGS
+Does not tabulate the block io per device or count the number of system calls.
+.SH AUTHOR
+Written by Henry Ware <al172@yfn.ysu.edu>. 
diff --git a/vmstat.c b/vmstat.c
new file mode 100644 (file)
index 0000000..a3664e2
--- /dev/null
+++ b/vmstat.c
@@ -0,0 +1,278 @@
+#define VERSION Version: 0.99, last modified 15 January 94: ALPHA 
+#define PROGNAME "vmstat"
+/* Copyright 1994 by Henry Ware <al172@yfn.ysu.edu>. Copyleft same year. */
+/* This attempts to display virtual memory statistics.
+   vmstat does not need to be run as root.
+*/
+/* TODO etc.
+ * Count the system calls.  How?  I don't even know where to start.
+ * add more items- aim at Posix 1003.2 (1003.7?) compliance, if relevant (I.  
+   don't think it is, but let me know.)
+ * sometimes d(inter)<d(ticks)!!  This is not possible: inter is a sum of 
+   positive numbers including ticks.  But it happens.  This doesn't seem to
+   affect things much, however.
+ * It would be interesting to see when the buffers avert a block io:
+   Like SysV4's "sar -b"... it might fit better here, with Linux's variable 
+   buffer size.
+ * Ideally, blocks in & out would be counted in 1k increments, rather than
+   by block: this only makes a difference for CDs and is a problematic fix.
+*/
+/* PROCPS
+   This is part of the procps package maintained by Michael K. Johnson
+   <johnsonm@redhat.com>; report bugs to <acahalan@cs.uml.edu>.
+*/
+   
+#include "proc/sysinfo.h"
+#include "proc/version.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/dir.h>
+#include <dirent.h>
+
+#define NDEBUG !DEBUG
+
+#define BUFFSIZE 4096
+#define FALSE 0
+#define TRUE 1
+
+static char buff[BUFFSIZE]; /* used in the procedures */
+
+typedef unsigned long jiff;
+
+
+/****************************************************************/
+
+
+static void usage(void) {
+  fprintf(stderr,"usage: %s [-V] [-n] [delay [count]]\n",PROGNAME);
+  fprintf(stderr,"              -V prints version.\n");
+  fprintf(stderr,"              -n causes the headers not to be reprinted regularly.\n");
+  fprintf(stderr,"              delay is the delay between updates in seconds. \n");
+  fprintf(stderr,"              count is the number of updates.\n");
+  exit(EXIT_FAILURE);
+}
+
+static void crash(char *filename) {
+    perror(filename);
+    exit(EXIT_FAILURE);
+}
+
+static int winhi(void) {
+    struct winsize win;
+    int rows = 24;
+    if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_row > 0)
+      rows = win.ws_row;
+    return rows;
+}
+
+
+static void showheader(void){
+  printf("%8s%28s%8s%12s%11s%12s\n",
+        "procs","memory","swap","io","system","cpu");
+  printf("%2s %2s %2s %6s %6s %6s %6s %3s %3s %5s %5s %4s %5s %3s %3s %3s\n",
+        "r","b","w","swpd","free","buff","cache","si","so","bi","bo",
+        "in","cs","us","sy","id");
+}
+
+static void getstat(jiff *cuse, jiff *cice, jiff *csys, jiff long *cide,
+            unsigned *pin, unsigned *pout, unsigned *s_in, unsigned *sout,
+            unsigned *itot, unsigned *i1, unsigned *ct) {
+  static int Stat;
+
+  if ((Stat=open("/proc/stat", O_RDONLY, 0)) != -1) {
+    char* b;
+    buff[BUFFSIZE-1] = 0;  /* ensure null termination in buffer */
+    read(Stat,buff,BUFFSIZE-1);
+    close(Stat);
+    *itot = 0; 
+    *i1 = 1;   /* ensure assert below will fail if the sscanf bombs */
+    b = strstr(buff, "cpu ");
+    sscanf(b, "cpu  %lu %lu %lu %lu", cuse, cice, csys, cide);
+    b = strstr(buff, "page ");
+    sscanf(b, "page %u %u", pin, pout);
+    b = strstr(buff, "swap ");
+    sscanf(b, "swap %u %u", s_in, sout);
+    b = strstr(buff, "intr ");
+    sscanf(b, "intr %u %u", itot, i1);
+    b = strstr(buff, "ctxt ");
+    sscanf(b, "ctxt %u", ct);
+  }
+  else {
+    crash("/proc/stat");
+  }
+}
+
+static void getrunners(unsigned int *running, unsigned int *blocked, 
+               unsigned int *swapped) {
+  static struct direct *ent;
+  static char filename[80];
+  static int fd;
+  static unsigned size;
+  static char c;
+  DIR *proc;
+
+  *running=0;
+  *blocked=0;
+  *swapped=0;
+
+  if ((proc=opendir("/proc"))==NULL) crash("/proc");
+
+  while((ent=readdir(proc))) {
+    if (isdigit(ent->d_name[0])) {  /*just to weed out the obvious junk*/
+      sprintf(filename, "/proc/%s/stat", ent->d_name);
+      if ((fd = open(filename, O_RDONLY, 0)) != -1) { /*this weeds the rest*/
+       read(fd,buff,BUFFSIZE-1);
+       sscanf(buff, "%*d %*s %c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %*d %*d %*d %*d %*d %*d %*u %*u %*d %*u %u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u\n",&c,&size);
+       close(fd);
+
+       if (c=='R') {
+         if (size>0) (*running)++;
+         else (*swapped)++;
+        }
+       else if (c=='D') {
+         if (size>0) (*blocked)++;
+         else (*swapped)++;
+        }
+      }
+    }
+  }
+  closedir(proc);
+
+#if 1
+  /* is this next line a good idea?  It removes this thing which
+     uses (hopefully) little time, from the count of running processes */
+  (*running)--;
+#endif
+}
+
+
+
+
+int main(int argc, char *argv[]) {
+
+  const char format[]="%2u %2u %2u %6u %6u %6u %6u %3u %3u %5u %5u %4u %5u %3u %3u %3u\n";
+  unsigned int height=22; /* window height, reset later if needed. */
+#if 0
+  unsigned long int args[2]={0,0};
+#endif
+  unsigned int moreheaders=TRUE;
+  unsigned int tog=0; /* toggle switch for cleaner code */
+  unsigned int i,hz;
+  unsigned int running,blocked,swapped;
+  jiff cpu_use[2], cpu_nic[2], cpu_sys[2], cpu_idl[2];
+  jiff duse,dsys,didl,Div,divo2;
+  unsigned int pgpgin[2], pgpgout[2], pswpin[2], pswpout[2];
+  unsigned int inter[2],ticks[2],ctxt[2];
+  unsigned int per=0, pero2; 
+  unsigned long num=0;
+  unsigned int kb_per_page = sysconf(_SC_PAGESIZE) / 1024;
+
+  setlinebuf(stdout);
+  argc=0; /* redefined as number of integer arguments */
+  per=1;
+  num=0;
+  for (argv++;*argv;argv++) {
+    if ('-' ==(**argv)) {
+      switch (*(++(*argv))) {
+       case 'V':
+       display_version();
+       exit(0);
+      case 'n':
+       /* print only one header */
+       moreheaders=FALSE;
+      break;
+      default:
+       /* no other aguments defined yet. */
+       usage();
+      }
+    } else {
+      argc++;
+      switch (argc) {
+      case 1:
+        if ((per = atoi(*argv)) == 0)
+         usage();
+       num = ULONG_MAX;
+       break;
+      case 2:
+        num = atol(*argv);
+       break;
+      default:
+       usage();
+      } /* switch */
+    }
+  }
+
+  if (moreheaders) {
+      int tmp=winhi()-3;
+      height=((tmp>0)?tmp:22);
+  }    
+
+  pero2=(per/2);
+  showheader();
+  getrunners(&running,&blocked,&swapped);
+  meminfo();
+  getstat(cpu_use,cpu_nic,cpu_sys,cpu_idl,
+         pgpgin,pgpgout,pswpin,pswpout,
+         inter,ticks,ctxt);
+  duse= *cpu_use + *cpu_nic; 
+  dsys= *cpu_sys;
+  didl= *cpu_idl;
+  Div= duse+dsys+didl;
+  hz=sysconf(_SC_CLK_TCK); /* get ticks/s from system */
+  divo2= Div/2UL;
+  printf(format,
+        running,blocked,swapped,
+        kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached,
+        (*pswpin *kb_per_page*hz+divo2)/Div,
+        (*pswpout*kb_per_page*hz+divo2)/Div,
+        (*pgpgin             *hz+divo2)/Div,
+        (*pgpgout            *hz+divo2)/Div,
+        (*inter              *hz+divo2)/Div,
+        (*ctxt               *hz+divo2)/Div,
+        (100*duse+divo2)/Div,
+        (100*dsys+divo2)/Div,
+        (100*didl+divo2)/Div
+  );
+
+  for(i=1;i<num;i++) { /* \\\\\\\\\\\\\\\\\\\\ main loop ////////////////// */
+    sleep(per);
+    if (moreheaders && ((i%height)==0)) showheader();
+    tog= !tog;
+    getrunners(&running,&blocked,&swapped);
+    meminfo();
+    getstat(cpu_use+tog,cpu_nic+tog,cpu_sys+tog,cpu_idl+tog,
+         pgpgin+tog,pgpgout+tog,pswpin+tog,pswpout+tog,
+         inter+tog,ticks+tog,ctxt+tog);
+    duse= cpu_use[tog]-cpu_use[!tog] + cpu_nic[tog]-cpu_nic[!tog];
+    dsys= cpu_sys[tog]-cpu_sys[!tog];
+    didl= cpu_idl[tog]-cpu_idl[!tog];
+    /* idle can run backwards for a moment -- kernel "feature" */
+    if(cpu_idl[tog]<cpu_idl[!tog]) didl=0;
+    Div= duse+dsys+didl;
+    divo2= Div/2UL;
+    printf(format,
+          running,blocked,swapped,
+          kb_swap_used,kb_main_free,kb_main_buffers,kb_main_cached,
+          ( (pswpin [tog]-pswpin [!tog])*kb_per_page+pero2 )/per,
+          ( (pswpout[tog]-pswpout[!tog])*kb_per_page+pero2 )/per,
+          (  pgpgin [tog]-pgpgin [!tog]             +pero2 )/per,
+          (  pgpgout[tog]-pgpgout[!tog]             +pero2 )/per,
+          (inter[tog]-inter[!tog]+pero2)/per,
+          (ctxt[tog]-ctxt[!tog]+pero2)/per,
+          (100*duse+divo2)/Div,
+          (100*dsys+divo2)/Div,
+          (100*didl+divo2)/Div
+    );
+  }
+  exit(EXIT_SUCCESS);
+}
diff --git a/w.1 b/w.1
new file mode 100644 (file)
index 0000000..647d55e
--- /dev/null
+++ b/w.1
@@ -0,0 +1,84 @@
+.\"             -*-Nroff-*-
+.\"
+.TH W 1 "8 Dec 1993 " " " "Linux User's Manual"
+.SH NAME
+w \- Show who is logged on and what they are doing.
+.SH SYNOPSIS
+.B w \-
+.RB [ husfV ]
+.RI [ user ]
+.SH DESCRIPTION
+.B "w "
+displays information about the users currently on the machine,
+and their processes.
+The header shows, in this order,  the current time,
+how long the system has been running,
+how many users are currently logged on,
+and the system load averages for the past 1, 5, and 15 minutes.
+.sp
+The following entries are displayed for each user:
+login name, the tty name, the remote host, login time, idle time, JCPU, PCPU,
+and the command line of their current process.
+.sp
+The JCPU time is the time used by all processes attached to the tty.  It
+does not include past background jobs, but does include currently
+running background jobs.
+.sp
+The PCPU time is the time used by the current process, named in the "what"
+field.
+
+.PP
+.SH "COMMAND\-LINE OPTIONS"
+.TP 0.5i
+.B "\-h "
+Don't print the header.
+.TP 0.5i
+.B "\-u "
+Ignores the username while figuring out the current process and cpu
+times.  To demonstrate this, do a "su" and do a "w" and a "w -u".
+.TP 0.5i
+.B "\-s "
+Use the short format.
+Don't print the login time, JCPU or PCPU times.
+.TP 0.5i
+.B "\-f "
+Toggle printing the
+.B from
+(remote hostname) field.  The default as
+released is for the
+.B from
+field to not be printed, although your system administrator or
+distribution maintainer may have compiled a version in which the
+.B from
+field is shown by default.
+.TP 0.5i
+.B "\-V "
+Display version information.
+.TP 0.5i
+.B "user "
+Show information about the specified user only.
+
+.SH FILES
+.TP
+.I /etc/utmp
+information about who is currently logged on
+.TP
+.I /proc
+process information
+.PP
+
+.SH "SEE ALSO"
+.BR free (1),
+.BR ps (1),
+.BR top (1),
+.BR uptime (1),
+.BR utmp (5),
+.BR who (1)
+
+.SH AUTHORS
+.B w
+was re-written almost entirely by Charles Blake, based on the version by Larry
+Greenfield <greenfie@gauss.rutgers.edu> and Michael K. Johnson
+<johnsonm@redhat.com>.
+
+Please send bug reports to <acahalan@cs.uml.edu>
diff --git a/w.c b/w.c
new file mode 100644 (file)
index 0000000..e847d86
--- /dev/null
+++ b/w.c
@@ -0,0 +1,298 @@
+/* w - show what logged in users are doing.  Almost entirely rewritten from
+ * scratch by Charles Blake circa June 1996.  Some vestigal traces of the
+ * original may exist.  That was done in 1993 by Larry Greenfield with some
+ * fixes by Michael K. Johnson.
+ */
+#include "proc/version.h"
+#include "proc/whattime.h"
+#include "proc/readproc.h"
+#include "proc/devname.h"
+#include "proc/procps.h"
+#include "proc/sysinfo.h"
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+/* #include <sys/param.h>*/    /* for HZ */
+
+static int ignoreuser = 0;     /* for '-u' */
+static proc_t **procs;         /* our snapshot of the process table */
+
+typedef struct utmp utmp_t;
+
+#ifdef W_SHOWFROM
+#   define FROM_STRING "on"
+#else
+#   define FROM_STRING "off"
+#endif
+
+/* Uh... same thing as UT_NAMESIZE */
+#define USERSZ (sizeof u->ut_user)
+
+
+/* This routine is careful since some programs leave utmp strings
+ * unprintable.  Always outputs at least 16 chars padded with spaces
+ * on the right if necessary.
+ */
+static void print_host(char* host, int len) {
+    char *last;
+    int width = 0;
+
+    /* FIXME: there should really be a way to configure this... */
+    /* for now, we'll just limit it to the 16 that the libc5 version
+     * of utmp uses.
+     */
+    if (len > 16) len = 16;
+    last = host + len;
+    for ( ; host < last ; host++){
+        if (isprint(*host) && *host != ' ') {
+           fputc(*host, stdout);
+           ++width;
+       } else {
+           break;
+       }
+    }
+    if(!width){   /* blank fields screw up parsers */
+      fputc('-', stdout);
+      ++width;
+    }
+    /* if *any* unprintables(or blanks), replace rest of line with spaces */
+    while (width < 16) {
+       fputc(' ', stdout);
+       ++width;
+    }
+}
+
+/***** compact 7 char format for time intervals (belongs in libproc?) */
+static void print_time_ival7(time_t t, int centi_sec, FILE* fout) {
+    if((long)t < (long)0){  /* system clock changed? */
+      printf("   ?   ");
+      return;
+    }
+    if (t >= 48*60*60)                         /* > 2 days */
+       fprintf(fout, " %2ludays", t/(24*60*60));
+    else if (t >= 60*60)                       /* > 1 hour */
+       fprintf(fout, " %2lu:%02um", t/(60*60), (unsigned) ((t/60)%60));
+    else if (t > 60)                           /* > 1 minute */
+       fprintf(fout, " %2lu:%02u ", t/60, (unsigned) t%60);
+    else
+       fprintf(fout, " %2lu.%02us", t, centi_sec);
+}
+
+/**** stat the device file to get an idle time */
+static time_t idletime(char *tty) {
+    struct stat sbuf;
+    if (stat(tty, &sbuf) != 0)
+       return 0;
+    return time(NULL) - sbuf.st_atime;
+}
+
+/***** 7 character formatted login time */
+static void print_logintime(time_t logt, FILE* fout) {
+    char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" },
+        *month  [] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul",
+                       "Aug", "Sep", "Oct", "Nov", "Dec" };
+    time_t curt;
+    struct tm *logtm, *curtm;
+    int hour;
+    char *merid; /* meridian indicator */
+    int today;
+
+    curt = time(NULL);
+    curtm = localtime(&curt);
+    /* localtime returns a pointer to static memory */
+    today = curtm->tm_yday;
+    logtm = localtime(&logt);
+    hour = logtm->tm_hour;
+    merid = (hour < 12) ? "am" : "pm";
+    if (hour >= 12) hour -= 12;
+    if (hour == 0)  hour = 12;
+    if (curt - logt > 12*60*60 && logtm->tm_yday != today) {
+       if (curt - logt > 6*24*60*60)
+           fprintf(fout, " %02d%3s%02d", logtm->tm_mday, month[logtm->tm_mon],
+                   logtm->tm_year % 100);
+       else
+           fprintf(fout, " %3s%02d%s", weekday[logtm->tm_wday], hour, merid);
+    } else {
+       fprintf(fout, " %02d:%02d%s", hour, logtm->tm_min, merid);
+    }
+}
+
+
+/* This function scans the process table accumulating total cpu times for
+ * any processes "associated" with this login session.  It also searches
+ * for the "best" process to report as "(w)hat" the user for that login
+ * session is doing currently.  This the essential core of 'w'.
+ */
+static proc_t *getproc(utmp_t *u, char *tty, int *jcpu, int *found_utpid) {
+    int line;
+    proc_t **p, *best = NULL, *secondbest = NULL;
+    unsigned uid = ~0U;
+
+    if(!ignoreuser){
+      char buf[UT_NAMESIZE+1];
+      struct passwd *passwd_data;   /* pointer to static data */
+      strncpy(buf,u->ut_user,UT_NAMESIZE);
+      buf[UT_NAMESIZE] = '\0';
+      passwd_data = getpwnam(buf);
+      if(!passwd_data) return NULL;
+      uid = passwd_data->pw_uid;
+      /* OK to have passwd_data go out of scope here */
+    }
+    line = tty_to_dev(tty);
+    *jcpu = *found_utpid = 0;
+    for(p = procs; *p; p++) {
+       if((**p).pid == u->ut_pid)   *found_utpid = 1;
+       if((**p).tty != line) continue;
+        (*jcpu) += (**p).utime + (**p).stime;
+        secondbest = *p;
+        /* same time-logic here as for "best" below */
+        if(!  (secondbest && (**p).start_time <= secondbest->start_time)  ){
+          secondbest = *p;
+        }
+        if(!ignoreuser && uid != (**p).euid && uid != (**p).ruid) continue;
+        if((**p).pid != (**p).tpgid) continue;
+        if(best && (**p).start_time <= best->start_time) continue;
+       best = *p;
+    }
+    return best ? best : secondbest;
+}
+
+
+/***** showinfo */
+static void showinfo(utmp_t *u, int formtype, int maxcmd, int from) {
+    int jcpu, ut_pid_found;
+    unsigned i;
+    char uname[USERSZ + 1] = "",
+       tty[5 + sizeof u->ut_line + 1] = "/dev/";
+    proc_t *best;
+
+    for (i=0; i < sizeof(u->ut_line); i++)     /* clean up tty if garbled */
+       if (isalnum(u->ut_line[i]) || (u->ut_line[i]=='/'))
+           tty[i+5] = u->ut_line[i];
+       else
+           tty[i+5] = '\0';
+
+    best = getproc(u, tty + 5, &jcpu, &ut_pid_found);
+
+    /* just skip if stale utmp entry (i.e. login proc doesn't exist).  If there
+     * is a desire a cmdline flag could be added to optionally show it with a
+     * prefix of (stale) in front of cmd or something like that.
+     */
+    if (!ut_pid_found)
+       return;
+
+    strncpy(uname, u->ut_user, USERSZ);                /* force NUL term for printf */
+    if (formtype) {
+       printf("%-9.8s%-9.8s", uname, u->ut_line);
+       if (from)
+           print_host(u->ut_host, sizeof u->ut_host);
+       print_logintime(u->ut_time, stdout);
+       if (*u->ut_line == ':')                 /* idle unknown for xdm logins */
+           printf(" ?xdm? ");
+       else
+           print_time_ival7(idletime(tty), 0, stdout);
+       print_time_ival7(jcpu/Hertz, (jcpu%Hertz)*(100./Hertz), stdout);
+       if (best) {
+           int pcpu = best->utime + best->stime;
+           print_time_ival7(pcpu/Hertz, (pcpu%Hertz)*(100./Hertz), stdout);
+       } else
+           printf("   ?   ");
+    } else {
+       printf("%-9.8s%-9.8s", u->ut_user, u->ut_line);
+       if (from)
+           print_host(u->ut_host, sizeof u->ut_host);
+       if (*u->ut_line == ':')                 /* idle unknown for xdm logins */
+           printf(" ?xdm? ");
+       else
+           print_time_ival7(idletime(tty), 0, stdout);
+    }
+    fputs("  ", stdout);
+    if (best) {
+       if (best->cmdline)
+           print_strlist(stdout, best->cmdline, " ", maxcmd);
+       else
+           printf("%*.*s", -maxcmd, maxcmd, best->cmd);
+    } else {
+       printf("-");
+    }
+    fputc('\n', stdout);
+}
+
+/***** main */
+int main(int argc, char **argv) {
+    char *user = NULL;
+    utmp_t *u;
+    struct winsize win;
+    int header=1, longform=1, from=1, args, maxcmd=80, ch;
+
+#ifndef W_SHOWFROM
+    from = 0;
+#endif
+
+    for (args=0; (ch = getopt(argc, argv, "hlusfV")) != EOF; args++)
+       switch (ch) {
+         case 'h': header = 0;         break;
+         case 'l': longform = 1;       break;
+         case 's': longform = 0;       break;
+         case 'f': from = !from;       break;
+         case 'V': display_version();  exit(0);
+         case 'u': ignoreuser = 1;     break;
+         default:
+           printf("usage: w -hlsufV [user]\n"
+                  "    -h    skip header\n"
+                  "    -l    long listing (default)\n"
+                  "    -s    short listing\n"
+                  "    -u    ignore uid of processes\n"
+                  "    -f    toggle FROM field (default %s)\n"
+                  "    -V    display version\n", FROM_STRING);
+           exit(1);
+       }
+
+    if ((argv[optind]))
+       user = (argv[optind]);
+
+    if (ioctl(1, TIOCGWINSZ, &win) != -1 && win.ws_col > 0)
+       maxcmd = win.ws_col;
+    if (maxcmd < 71) {
+       fprintf(stderr, "%d column window is too narrow\n", maxcmd);
+       exit(1);
+    }
+    maxcmd -= 29 + (from ? 16 : 0) + (longform ? 20 : 0);
+    if (maxcmd < 3)
+       fprintf(stderr, "warning: screen width %d suboptimal.\n", win.ws_col);
+
+    procs = readproctab(PROC_FILLCMD | PROC_FILLUSR);
+
+    if (header) {                              /* print uptime and headers */
+       print_uptime();
+       printf("USER     TTY      ");
+       if (from)
+           printf("FROM            ");
+       if (longform)
+           printf("  LOGIN@   IDLE   JCPU   PCPU  WHAT\n");
+       else
+           printf("   IDLE  WHAT\n");
+    }
+
+    utmpname(UTMP_FILE);
+    setutent();
+    while ((u=getutent())) {
+       if (u->ut_type == USER_PROCESS &&
+           (user ? !strncmp(u->ut_user, user, USERSZ) : *u->ut_user))
+           showinfo(u, longform, maxcmd, from);
+    }
+    endutent();
+
+    return 0;
+}
diff --git a/watch.1 b/watch.1
new file mode 100644 (file)
index 0000000..f6021a6
--- /dev/null
+++ b/watch.1
@@ -0,0 +1,82 @@
+.TH WATCH 1 "1999 Apr 3" " " "Linux User's Manual"
+.SH NAME
+watch \- execute a program periodically, showing output fullscreen
+.SH SYNOPSIS
+.B watch
+.I [\-dhv] [\-n <seconds>] [\-\-differences[=cumulative]] [\-\-help] [\-\-interval=<seconds>] [\-\-version] <command>
+.SH DESCRIPTION
+.BR watch
+runs
+.I command
+repeatedly, displaying its output (the first screenfull).  This allows you to
+watch the program output change over time.  By default, the program is run
+every 2 seconds; use 
+.I -n
+or
+.I --interval
+to specify a different interval.
+.PP
+The
+.I -d
+or
+.I --differences
+flag will highlight the differences between successive updates.  The 
+.I --cumulative
+option makes highlighting "sticky", presenting a running display of all
+positions that have ever changed.
+.PP
+.BR watch
+will run until interrupted.
+.SH NOTE
+Note that
+.I command
+is given to "sh -c"
+which means that you may need to use extra quoting to get the desired effect.
+.PP
+Note that POSIX option processing is used (i.e., option processing stops at
+the first non-option argument).  This means that flags after
+.I command
+don't get interpreted by
+.BR watch
+itself.
+.SH EXAMPLES
+.PP
+To watch for mail, you might do
+.IP
+watch \-n 60 from
+.PP
+To watch the contents of a directory change, you could use
+.IP
+watch \-d ls \-l
+.PP
+If you're only interested in files owned by user joe, you might use 
+.IP
+watch \-d 'ls \-l | fgrep joe'
+.PP
+To see the effects of quoting, try these out
+.IP
+watch echo $$
+.IP
+watch echo '$$'
+.IP
+watch echo "'"'$$'"'"
+.PP
+You can watch for your administrator to install the latest kernel with
+.IP
+watch uname -r
+.PP
+(Just kidding.)
+.SH BUGS
+Upon terminal resize, the screen will not be correctly repainted until the
+next scheduled update.  All
+.I --differences
+highlighting is lost on that update as well.
+.PP
+Non-printing characters are stripped from program output.  Use "cat -v" as
+part of the command pipeline if you want to see them.
+.SH AUTHORS
+The original
+.B watch
+was written by Tony Rems <rembo@unisoft.com> in 1991, with mods and
+corrections by Francois Pinard.  It was reworked and new features added by
+Mike Coleman <mkc@acm.org> in 1999.
diff --git a/watch.c b/watch.c
new file mode 100644 (file)
index 0000000..cd8ac4e
--- /dev/null
+++ b/watch.c
@@ -0,0 +1,277 @@
+/* watch -- execute a program repeatedly, displaying output fullscreen
+ *
+ * Based on the original 1991 'watch' by Tony Rems <rembo@unisoft.com>
+ * (with mods and corrections by Francois Pinard).
+ *
+ * Substantially reworked, new features (differences option, SIGWINCH
+ * handling, unlimited command length, long line handling) added Apr 1999 by
+ * Mike Coleman <mkc@acm.org>.
+ */
+
+
+#define VERSION "0.2.0"
+
+#include <ctype.h>
+#include <getopt.h>
+#include <signal.h>
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <time.h>
+#include <unistd.h>
+
+
+static struct option longopts[] =
+  {
+    { "differences", optional_argument, 0, 'd' },
+    { "help", no_argument, 0, 'h' },
+    { "interval", required_argument, 0, 'n' },
+    { "version", no_argument, 0, 'v' },
+    { 0, 0, 0, 0 }
+  };
+
+static char usage[] = "Usage: %s [-dhnv] [--differences[=cumulative]] [--help] [--interval=<n>] [--version] <command>\n";
+
+
+static char *progname;
+
+static int curses_started = 0;
+static int height=24, width=80;
+static int screen_size_changed=0;
+static int first_screen=1;
+
+
+#define min(x,y) ((x) > (y) ? (y) : (x))
+#define max(x,y) ((x) > (y) ? (x) : (y))
+
+
+static void
+do_usage(void)
+{
+  fprintf(stderr, usage, progname);
+  exit(1);
+}
+
+
+static void
+do_exit(int status) {
+  if (curses_started)
+    endwin();
+  exit(status);
+}
+
+
+/* signal handler */
+static void
+die(int notused)
+{
+  (void)notused;
+  do_exit(0);
+}
+
+
+static void
+winch_handler(int notused)
+{
+  (void)notused;
+  screen_size_changed = 1;
+}
+
+
+static void
+get_terminal_size(void)
+{
+  struct winsize w;
+  if (ioctl(2, TIOCGWINSZ, &w) == 0)
+    {
+      if (w.ws_row > 0)
+       height = w.ws_row;
+      if (w.ws_col > 0)
+       width = w.ws_col;
+    }
+}
+
+
+int
+main(int argc, char *argv[])
+{
+  int optc;
+  int option_differences=0,
+    option_differences_cumulative=0,
+    option_help=0,
+    option_version=0;
+  int interval=2;
+  char *command;
+  int command_length=0;                /* not including final \0 */
+
+  progname = argv[0];
+
+  while ((optc = getopt_long(argc, argv, "+d::hn:v", longopts, (int *) 0))
+        != EOF)
+    {
+      switch (optc)
+       {
+       case 'd':
+         option_differences = 1;
+         if (optarg)
+           option_differences_cumulative = 1;
+         break;
+       case 'h':
+         option_help = 1;
+         break;
+       case 'n':
+         {
+           char *s;
+           interval = strtol(optarg, &s, 10);
+           if (!*optarg || *s)
+             do_usage();
+         }
+         break;
+       case 'v':
+         option_version = 1;
+         break;
+       default:
+         do_usage();
+         break;
+       }
+    }
+
+  if (option_version)
+    {
+      fprintf (stderr, "%s\n", VERSION);
+      if (!option_help)
+       exit(0);
+    }
+
+  if (option_help)
+    {
+      fprintf(stderr, usage, progname);
+      fputs("  -d, --differences[=cumulative]\thighlight changes between updates\n", stderr);
+      fputs("\t\t(cumulative means highlighting is cumulative)\n", stderr);
+      fputs("  -h, --help\t\t\t\tprint a summary of the options\n", stderr);
+      fputs("  -n, --interval=<seconds>\t\tseconds to wait between updates\n", stderr);
+      fputs("  -v, --version\t\t\t\tprint the version number\n", stderr);
+      exit(0);
+    }
+
+  if (optind >= argc)
+    do_usage();
+
+  command = strdup(argv[optind++]);
+  command_length = strlen(command);
+  for (;optind<argc;optind++)
+    {
+      int s = strlen(argv[optind]);
+      char *endp = &command[command_length];
+      *endp = ' ';
+      command_length += s + 1;
+      command = realloc(command, command_length+1);
+      strcpy(endp+1, argv[optind]);
+    }
+
+  get_terminal_size();
+
+  /* Catch keyboard interrupts so we can put tty back in a sane state.  */
+  signal(SIGINT, die);
+  signal(SIGTERM, die);
+  signal(SIGHUP, die);
+  signal(SIGWINCH, winch_handler);
+
+  /* Set up tty for curses use.  */
+  curses_started = 1;
+  initscr();
+  nonl();
+  noecho();
+  cbreak();
+
+  for(;;)
+    {
+      time_t t = time(NULL);
+      char *ts = ctime(&t);
+      int tsl = strlen(ts);
+      char *header;
+      FILE *p;
+      int x, y;
+
+      if (screen_size_changed)
+       {
+         get_terminal_size();
+         resizeterm(height, width);
+         clear();
+         /* redrawwin(stdscr); */
+         screen_size_changed = 0;
+         first_screen = 1;
+       }
+
+      /* left justify interval and command, right justify time, clipping all
+        to fit window width */
+      asprintf(&header, "Every %ds: %.*s",
+              interval, max(width-1, command_length), command);
+      mvaddstr(0, 0, header);
+      if (strlen(header) > width - tsl - 1)
+       mvaddstr(0, width - tsl - 4, "...  ");
+      mvaddstr(0, width - tsl + 1, ts);
+      free(header);
+
+      if (!(p = popen(command, "r")))
+       {
+         perror("popen");
+         do_exit(2);
+       }
+
+      for (y=2; y<height; y++)
+       {
+         int eolseen = 0, tabpending = 0;
+         for (x=0; x<width; x++)
+           {
+             int c = ' ';
+             int attr = 0;
+             
+             if (!eolseen)
+               {
+                 /* if there is a tab pending, just spit spaces until the
+                    next stop instead of reading characters */
+                 if (!tabpending)
+                   do
+                     c = getc(p);
+                   while (c != EOF && !isprint(c) && c != '\n' && c != '\t');
+                 if (c == '\n')
+                   eolseen = 1;
+                 else if (c == '\t')
+                   tabpending = 1;
+                 if (c == EOF || c == '\n' || c == '\t')
+                   c = ' ';
+                 if (tabpending && (((x + 1) % 8) == 0))
+                   tabpending = 0;
+               }
+             move(y, x);
+             if (option_differences)
+               {
+                 int oldch = inch();
+                 char oldc = oldch & A_CHARTEXT;
+                 attr = !first_screen
+                   && (c != oldc
+                       || (option_differences_cumulative
+                           && (oldch & A_ATTRIBUTES)));
+               }
+             if (attr)
+               standout();
+             addch(c);
+             if (attr)
+               standend();
+           }
+       }
+
+      pclose(p);
+
+      first_screen = 0;
+      refresh();
+      sleep(interval);
+    }
+
+  endwin();
+
+  return 0;
+}