[comp.sources.misc] v13i098: tps - show process tree using 'ps'.

arndt@zyx.ZYX.SE (Arndt Jonasson) (07/03/90)

Posting-number: Volume 13, Issue 98
Submitted-by: arndt@zyx.ZYX.SE (Arndt Jonasson)
Archive-name: tps/part01

# This is a shell archive.  Remove anything before this line,
# then unpack it by saving it in a file and typing "sh file".
#
# Wrapped by Arndt Jonasson <arndt@coca> on Fri Jun 29 11:53:32 1990
#
# This archive contains:
#	README		makefile	options.c	options.h	
#	tps.1		tps.c		
#

LANG=""; export LANG
PATH=/bin:/usr/bin:$PATH; export PATH

echo x - README
cat >README <<'@EOF'
29 June 1990 - Arndt Jonasson - arndt@zyx.se

This is version 1.0 of 'tps', a program that invokes the 'ps' command
and rearranges its output into a more legible format.


Files:

README				this file
makefile			commands for building the program
tps.c				'tps' source code
tps.1				manual page
options.h			header file for option parsing
options.c			option parser source code


To build the program, if your system is one of the following:

	Hewlett-Packard's HP-UX (series 9000/300 or 9000/800)
	DEC's Ultrix (DecStation 3100)
	Apollo's Domain/OS
	SUN's SunOS
	Sony's NeWS

just type the command 'make'.

If your system is not one of those, but has the 'ps' program, it may
work anyway, or you may have to do some changes to tps.c first. If you
port 'tps' to another system, please send your changes back to me.


The option parser is usable independently of 'tps', but, alas, there
is no manual page yet.
@EOF

chmod 664 README

echo x - makefile
cat >makefile <<'@EOF'

tps:		tps.o options.o
		cc -o tps tps.o options.o -ltermcap

tps.o:		tps.c options.h
		cc -c tps.c

options.o:	options.c options.h
		cc -c options.c


clean:		
		-rm tps options.o tps.o
@EOF

chmod 664 makefile

echo x - options.c
cat >options.c <<'@EOF'
/*
  options.c - created 28 Mar 88.

  Command line option parser, version 1.0 (29 June 1990)

  Author: Arndt Jonasson, Zyx Sweden
  arndt@zyx.SE	(...<backbone>!mcsun!sunic!zyx!arndt)

  No manual page yet.
*/

#include "options.h"

extern void exit ();		/* '#define void' if your compiler doesn't */
				/* have it.*/

char *O_programname = NULL;

static char *usage_string = NULL;
static int option_error;

static fatal (msg)
char *msg;
{
   fprintf (stderr, "option parsing failure: %s\n", msg);
   exit (1);
}

static char *basename (str)
char *str;
{
   char *p1, *p2;
   extern char *strchr ();

   p2 = str;
   while ((p1 = strchr (p2, '/')) != NULL)
   {
      if (p1[1] == '\0')
      {
	 p1[0] = '\0';
	 return p2;
      }
      p2 = p1 + 1;
   }
   return p2;
}

static char *match (prefix, str)
char *prefix, *str;
{
   int n = strlen (prefix);

   if (strncmp (prefix, str, n) == 0)
      return str + n;
   else
      return NULL;
}

O_usage ()
{
   if (usage_string != NULL)
   {
      if (O_programname == NULL)
	 fprintf (stderr, "valid options: %s\n", usage_string);
      else
	 fprintf (stderr, "usage: %s %s\n", O_programname, usage_string);
   }
   else
   {
      if (O_programname != NULL)
	 fprintf (stderr, "%s: ", O_programname);
      fprintf (stderr, "invalid usage\n");
   }

   exit (1);
}

int O_atoi (str)
char *str;
{
   char c;
   int base = 10;
   int res = 0;
   int negative = 1;
   
/*
  No check for overflow.
*/

   c = str[0];
   
   if (*str == '-')
   {
      negative = -1;
      str++;
   }

   if (*str == '0')
   {
      if (*++str == 'x')
      {
	 str++;
	 base = 16;
      }
      else
	 base = 8;
   }
   
   if (*str == '\0' && (negative != -1 || base != 8)) /* kludgie */
   {
      option_error = 1;
      return 0;
   }

   for (;;)
   {
      switch (c = *str++)
      {
       case '8':
       case '9':
	 if (base == 8)
	 {
	    option_error = 1;
	    return 0;
	 }
       case '0':
       case '1':
       case '2':
       case '3':
       case '4':
       case '5':
       case '6':
       case '7':
	 res = base * res + c - '0';
	 break;
       case 'a':
       case 'b':
       case 'c':
       case 'd':
       case 'e':
       case 'f':
	 if (base == 16)
	    res = base * res + c - 'a' + 10;
	 else
	 {
	    option_error = 1;
	    return 0;
	 }
	 break;
       case '\0':
	 return (negative * res);
	 /* NOTREACHED */
	 break;
       default:
	 option_error = 1;
	 return 0;
      }
   }
}

double O_atof (s)
char *s;
{
/*
  No error-checking currently.
*/
   extern double atof ();

   return atof (s);
}

char *O_strid (s)
char *s;
{
   return s;
}

char O_chrid (s)
char *s;
{
   return s[0];
}

static get_it (p, arg, c)
Option *p;
char *arg;
char c;
{
   option_error = 0;

   switch ((p->flags & 037))
   {
    case O_INT:
      *p->ip = (*p->ipf) (arg);
      if (option_error)
      {
	 fprintf (stderr,
		  "Invalid integer '%s' given to the -%c option\n", arg, c);
	 O_usage ();
      }
      break;
    case O_STR:
      *p->cp = (*p->cpf) (arg);
      break;
    case O_DBL:
      *p->dp = (*p->dpf) (arg);
      if (option_error)
      {
	 fprintf (stderr,
      "Invalid floating-point number '%s' given to the -%c option\n", arg, c);
	 O_usage ();
      }
      break;
    case O_CHR:
      *p->tp = (*p->tpf) (arg);
      break;
    case O_MUL:
      (*p->table_p)[p->data++] = arg;
      break;
    default:
      fatal ("invalid option type");
   }
}

int O_parse_options (desc, argc, argv)
Option *desc;
int argc;
char **argv;
{
   static char *empty_table[] = {NULL};
   int first_char, multiple_error, multiple_add, hyphen_is_arg;
   int min, max, i, remaining;
   Option *p, *default_p = NULL;
   char *cp, *dir, *arg;
   char c;

   multiple_error = 1;
   multiple_add = 0;
   hyphen_is_arg = 1;
   min = 0;
   max = -1;

   for (p = desc; p->flags != O_END; p++)
   {
      p->data = 0;

      if (p->flags == O_DIR)
      {
	 dir = p->directive;
	 if (cp = match ("usage: ", dir))
	    usage_string = cp;
	 else if (cp = match ("multiple: ", dir))
	 {
	    multiple_error = (strcmp ("error", cp) == 0);
	    multiple_add = (strcmp ("add", cp) == 0);
	 }
	 else if (cp = match ("remaining: ", dir))
	 {
	    int n;
	    char dummy;

	    n = sscanf (cp, "%d%c%d", &min, &dummy, &max);
	    if (n == 1)
	       max = min;
	    else if (n == 2)
	       max = -1;
	 }
	 else
	    fatal ("unknown option directive");
      }
      else if (p->flags == O_MUL)
	 *p->table_p = (char **) malloc (argc * sizeof (char *));
      else if (p->flags == O_INT && p->c == '\0')
      {
	 p->c = -1;
	 default_p = p;
      }
      else if (p->flags == O_FLG && p->c == '\0')
	 hyphen_is_arg = 0;

      if (p->flags == O_FLG)
	 *p->ip = 0;
   }

   O_programname = basename (argv[0]);

   for (i = 1; i < argc; i++)
   {
      arg = argv[i];
      if (arg[0] != '-' || (arg[1] == '\0' && hyphen_is_arg))
	 break;

      first_char = 1;

      while ((c = *++arg) != '\0' || first_char)
      {
	 for (p = desc; p->flags != O_END; p++)
	    if (p->c == c)
	    {
	       if (p->flags & 040)
	       {
		  fprintf (stderr, "The -%c option was given twice\n", c);
		  O_usage ();
	       }

	       if (multiple_error && p->flags != O_MUL)
		  p->flags |= 040;

	       if ((p->flags & 037) == O_FLG)
	       {

		  if (multiple_add)
		     *(p->ip) += 1;
		  else
		     *(p->ip) = 1;
		  if (c == '\0')
		     goto next_argument;
		  else
		     goto next_character;
	       }

	       arg += 1;
	       if (arg[0] == '\0')
	       {
		  if (++i == argc)
		  {
		     fprintf (stderr,
			      "The -%c option requires an argument\n", c);
		     O_usage ();
		  }
		  arg = argv[i];
	       }
	       get_it (p, arg, c);
	       goto next_argument;
	    }
	 if ((p = default_p) != NULL && first_char)
	 {
	    *p->ip = (*p->ipf) (arg);
	    if (!option_error)
	       goto next_argument;
	 }
	 fprintf (stderr, "There is no -%c option\n", c);
	 O_usage ();

	 /* NOTREACHED*/

       next_character:
	 first_char = 0;
      }
    next_argument: ;
   }

   for (p = desc; p->flags != O_END; p++)
      if ((p->flags & 037) == O_MUL)
      {
	 if (p->data == 0)
	    (*p->table_p = empty_table);
	 else
	 {
	    (*p->table_p)[p->data++] = NULL;
	    *p->table_p = (char **) realloc (*p->table_p,
					     p->data * sizeof (char *));
	 }
      }

   remaining = argc - i;
   if (remaining < min || max != -1 && remaining > max)
   {
      if (max == -1)
	 fprintf (stderr, "At least %d non-option argument%s required\n",
		  min, min == 1 ? " is" : "s are");
      else if (max == 0)
	 fprintf (stderr, "No non-option arguments are allowed\n");
      else if (min == max)
	 fprintf (stderr, "Exactly %d non-option argument%s required\n",
		  min, min == 1 ? " is" : "s are");
      else
	 fprintf (stderr, "%d to %d non-option arguments are required\n",
		  min, max);
      O_usage ();
   }
   return i;
}
@EOF

chmod 664 options.c

echo x - options.h
cat >options.h <<'@EOF'
/*
  options.h - created 28 Mar 88.

  Command line option parser, version 1.0 (29 June 1990)

  Author: Arndt Jonasson, Zyx Sweden
  arndt@zyx.SE	(...<backbone>!mcsun!sunic!zyx!arndt)

  No manual page yet.
*/

#include <stdio.h>

typedef struct
{
   char c;
   int flags;
   char **cp;
   char *(*cpf) ();
   int *ip;
   int (*ipf) ();
   double *dp;
   double (*dpf) ();
   char *tp;
   char (*tpf) ();
   char ***table_p;
   char *directive;
   int data;
} Option;

extern int O_atoi ();
extern double O_atof ();
extern char O_chrid ();

extern char *O_strid ();
extern char *O_programname;

#define O_FLG	0
#define O_INT	1
#define O_STR	2
#define O_DBL	3
#define O_END	4
#define O_DIR	5
#define O_CHR	6
#define O_MUL	7


#define O_flg(c, ip)	{c, O_FLG, 0, 0, &ip, 0, 0, 0, 0, 0, 0, 0}
#define O_int(c, ip)	{c, O_INT, 0, 0, &ip, O_atoi, 0, 0, 0, 0, 0, 0}
#define O_str(c, cp)	{c, O_STR, &cp, O_strid, 0, 0, 0, 0, 0, 0, 0, 0}
#define O_dbl(c, dp)	{c, O_DBL, 0, 0, 0, 0, &dp, O_atof, 0, 0, 0, 0}
#define O_end		{-1, O_END, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define O_directive(str) {-1, O_DIR, 0, 0, 0, 0, 0, 0, 0, 0, 0, str}
#define O_chr(c, cp)	{c, O_CHR, 0, 0, 0, 0, 0, 0, &cp, O_chrid, 0, 0}
#define O_mul(c, cpp)	{c, O_MUL, 0, 0, 0, 0, 0, 0, 0, 0, &cpp, 0}

#define O_Empty			'\0'

#define usage()			O_usage ()
@EOF

chmod 664 options.h

echo x - tps.1
cat >tps.1 <<'@EOF'
.TH TPS 1 local ""
.SH NAME
tps - tree-structured process status
.SH SYNOPSIS
.B tps
[-wat] [-u
.I user]
[-i
.I n]
[-c
.I time]
.SH DESCRIPTION
.I tps
runs
.B ps
in an inferior fork and filters its output to present a more legible
view of the currently running processes. One output line consists of
the process pid, the name of its owner, its controlling terminal (optional)
and the command line arguments of the process. If a process has any
children, these are displayed directly following their father, with
their pids indented.

If the command line arguments for one process don't fit in one display line,
they wrap to the next line, indicating this with a plus sign in front
of the continuation line. The width of the screen is taken from the
.B terminfo/termcap
database. If the width cannot be found, 80 is assumed.

The criterion for displaying a process is the following: for each process,
if itself, any child or any ancestor is owned by the
.IR "current user" ,
it is displayed. In other words, all processes belonging to the user will
be displayed, and additionally all children to any of those, whether owned
by the user or not (thus including invocations of
.BR su(1) ,
and all parent processes (which will always include the
.B init
process (pid 1) and the
.B swapper
process (pid 0).

By default, the
.I current user
is the user running the program.
.B tps
can be told to use any other user, or to display all running processes.

.PP 
The options have the following meanings:
.TP 11
.BR -t
Show the controlling tty of the process, if it has one.
.TP
.BR -w
If the process is sleeping, show the symbolic name of the address that
the process is waiting for. This options is currently only implemented
in HP-UX.
.TP
.BR -a
Show all running processes, not just the process tree of one user.
.TP
.BI -u " user"
Show the process tree of processes belonging to
.IR user .
If this option is not given, the process tree of the user invoking
the program will be given (as defined by the environment variable
LOGNAME, or the
.IR "real user id" ,
in that order).
.TP
.BI -i " n"
This changes the default indentation of child processes from 2 to
.IR n .
.TP
.BI -c " time"
When this option is specified,
.B ps
is invoked twice, with the interval
.I time
seconds. The processes common to both invocations are displayed (subject
to the usual selection by user described above), and the difference in
run time, if not zero, is printed in front of the command line arguments.
.SH FILES
/bin/ps
.SH ENVIRONMENT
.TP 11
TERM
The name of the terminfo/termcap database entry. Used for obtaining the
screen width.
.TP
LOGNAME
The
.I current user
to use when the
.B \-u
option hasn't been given.
.SH NOTES
.B tps
has currently been ported to Hewlett-Packard's HP-UX, DEC's Ultrix,
Apollo's Domain/OS, SUN's SunOS, and Sony's NeWS.
.SH SEE ALSO
ps(1), terminfo(4), terminfo(5), termcap(3).
.SH BUGS
When the -w is given, the display of the command line arguments may be
messed up.
.SH AUTHOR
Arndt Jonasson, ZYX Sweden (email address: arndt@zyx.se)
@EOF

chmod 664 tps.1

echo x - tps.c
cat >tps.c <<'@EOF'
/*
  tps.c -- Tree-structured ps program. It works by reading in the output
  of a 'ps' command and reordering it so that the parent-son relation-
  ships of the processes are displayed.

  The maximum width of the terminal is honored. Link with -ltermcap
  (or -ltermlib).

  Author: Arndt Jonasson, Zyx.
*/

/*
  Do error checking in various placesf (sscanf, malloc..)
  Fix vararg for pfield.

  Foo! Linking curses adds 60K to the code size.

  New ideas:
   Make 'tps -u root' only show root-owned processes.
   -p pid: show only the subtree with the pid p in it.
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <pwd.h>

#include "options.h"

extern char *getenv ();
extern struct passwd *getpwuid ();

#ifdef hpux
#define MAX_PROC	200
#define PS_COM		"ps -efl"
#else
#ifdef apollo
#define MAX_PROC	200
#define PS_COM		"ps -ef"
#else
#ifdef pyr
#define MAX_PROC	536
#define PS_COM		"ps -agxlw"
#else
#define MAX_PROC	100
#define PS_COM		"ps -agxlw"
#endif
#endif
#endif

#define LEFT	1
#define RIGHT	2
#define CENTER	4
#define LAST	8

#define LINMAX	400

#ifdef hpux

#if 0
#define OWNER	0
#define PID	9
#define PPID	15
#define JUNK1	21
#define TTY	33
#define JUNK2	(TTY+8)
#define CPU	41
#define INFO	47
#else
#define OWNER	6
#define PID	14
#define PPID	20
#define JUNK1	26
#define WAITCH	47
#define TTY	65
#define JUNK2	(TTY+8)
#define CPU	73
#define INFO	79
#endif

#else

#ifdef apollo
#define OWNER	0
#define PID	9
#define PPID	15
#define JUNK1	21
#define TTY	33
#define JUNK2	(TTY+9)
#define CPU	42
#define INFO	48
#else

#ifdef ultrix
#define OWNER	0
#define PID	4
#define PPID	10
#define JUNK1	16
#define INFO	60
#define TTY	50
#define CPU	54
#define JUNK2	54
#else

#ifdef sun
#define OWNER	7
#define PID	12
#define PPID	18
#define JUNK1	24
#define INFO	70
#define TTY	61
#define CPU	65
#define JUNK2	65
#else

#define OWNER	7
#define PID	12
#define PPID	18
#define JUNK1	24
#ifdef pyr
#define INFO	73
#define TTY	63
#define JUNK2	(TTY+4)
#else
#ifdef sony
#define INFO	75
#define TTY	65
#define JUNK2	(TTY+4)
#define CPU	69
#else
#define INFO	69
#define TTY	60
#define JUNK2	(TTY+4)
#endif
#endif
#endif
#endif
#endif
#endif

struct process
{
   int pid, ppid;
   char *owner, *info, *tty;
   int printed;
   int cputime;
   long channel;
};

struct process *proc[MAX_PROC];
int maxproc = 0;

int hmax, hpos;
int maxlevel = 0;
int show_tty = 0;
int indentation = 2;
int show_all = 0;
int cpu_diff = 0;
char *my_name = 0;

#ifdef WAITCH
typedef struct
{
   long adr;
   char name[20];
} Symbol;

int show_waitchan = 0;
Symbol *table;
int symbol_n;
#endif

char *newstr (str)
char *str;
{
   char *cp;
   while (*str++ == ' ');
   str--;
   cp = (char *) malloc (strlen (str) + 1);
   strcpy (cp, str);
   return cp;
}

newline ()
{
   hpos = 0;
   printf ("\n");
}

#ifdef WAITCH
Symbol *lookup (adr)
long adr;
{
   int i1 = 0, i2 = symbol_n;
   int i;

   for (;;)
   {
      i = (i1 + i2) / 2;
      if (table[i].adr == adr)
	 return &table[i];
      else if (table[i].adr > adr)
	 i2 = i;
      else
	 i1 = i;
      if (i1 > i2 - 2)
	 break;
   }
   if (table[i].adr > adr)
      i -= 1;
   return &table[i];
}

slurp_channel_data ()
{
   int d;
   struct stat s_buf;

   maybe_rebuild_tps_data ();

   d = open ("/tmp/tps_data", O_RDONLY);
   fstat (d, &s_buf);
   table = (Symbol *) malloc (s_buf.st_size);
   read (d, table, s_buf.st_size);
   symbol_n = s_buf.st_size / sizeof (Symbol);
}

char *addr_to_name (a)
long a;
{
   static char buf[80];
   long diff;
   Symbol *sym;

   if (a == 0)
      return "";

   sym = lookup (a);

   if (sym == &table[symbol_n - 1])
      return "?";

   diff = a - sym->adr;
   if (diff > 0x100000)
      return "?";

   if (diff == 0)
      sprintf (buf, "%s", sym->name);
   else
      sprintf (buf, "%s+%#x", sym->name, diff);

   if (buf[0] == '_')
      return buf+1;
   else
      return buf;
}

maybe_rebuild_tps_data ()
{
   int s;
   FILE *fi, *fo;
   char buf[80];
   long adr;
   char type[5], name[80];
   Symbol block;
   struct stat b1, b2;

   s = stat ("/hp-ux", &b1);
   s = stat ("/tmp/tps_data", &b2);
   if (s == 0 && b1.st_mtime <= b2.st_mtime)
      return;

   fprintf (stderr, "Rebuilding kernel symbol table ...\n");

#ifdef hp9000s800
   s = system ("nm -vp /hp-ux >/tmp/tps_data_x");
#else
   s = system ("nm -n /hp-ux >/tmp/tps_data_x");
#endif
   fi = fopen ("/tmp/tps_data_x", "r");
   fo = fopen ("/tmp/tps_data", "w");

   for (;;)
   {
      fgets (buf, 80, fi);
      if (feof (fi))
	 break;
#ifdef hp9000s800
      sscanf (buf, "%x %s %s\n", &adr, type, name);
#else
      sscanf (buf, "0x%x %s %s\n", &adr, type, name);
#endif
      block.adr = adr;
      strcpy (block.name, name);
      fwrite ((char *) &block, sizeof (block), 1, fo);
   }
   fclose (fi);
   fclose (fo);
}
#endif /* WAITCH */

pfield (len, align, format, a1, a2, a3, a4, a5)
int len, align;
char *format;
{
   char *text;
   char buf[200];
   int l, i, pre, post, o_hpos;

   o_hpos = hpos;
   hpos += len;
   if (hpos > hmax)
   {
      len -= hpos - hmax;
      hpos = hmax;
   }

   sprintf (buf, format, a1, a2, a3, a4, a5);
   l = strlen (buf);

   if (align & LEFT)
   {
      pre = 0;
      post = len - l;
   }
   else if (align & RIGHT)
   {
      pre = len - l;
      post = 0;
   }
   else if (align & CENTER)
   {
      pre = len - l;
      post = pre >> 1;
      pre = pre - post;
   }

   text = buf;

   for (;;)
   {
      for (i = 0; i < pre; i++) putchar (' ');
      if (len < l)
      {
	 printf ("%.*s", len, text);
	 newline ();
	 for (i = 0; i < o_hpos - 2; i++) putchar (' ');
	 putchar ('+'); putchar (' ');
	 text += len;
	 l -= len;
      }
      else
      {
	 printf ("%.*s", l, text);
	 hpos = o_hpos + len;
	 break;
      }
   }

   if (!(align & LAST))
      for (i = 0; i < post; i++) putchar (' ');
}

int maxlev (n, level, me_above)
int n, level, me_above;
{
   int i;
   int pid = proc[n]->pid;
   int me_below = 0;

   if (level > maxlevel) maxlevel = level;

   if (strcmp (proc[n]->owner, my_name) == 0)
      me_above = 1;

   for (i = 0; i < maxproc; i++)
      if (proc[i]->ppid == pid && proc[i]->pid != 0)
      {
	 if (maxlev (i, level + 1, me_above))
	    me_below = 1;
      }

   me_above |= me_below;
   if (!me_above && !show_all)
      proc[n]->printed = 1;
   return me_above;
}

print_pp (n, level)
int n, level;
{
   int i;
   int pid = proc[n]->pid;

   if (proc[n]->printed) return;

   print_p (n, level);

   for (i = 0; i<maxproc; i++)
      if (proc[i]->ppid == pid)
	 print_pp (i, level+1);
}

#ifdef hpux
#define TTY_WIDTH	10
#else
#define TTY_WIDTH	5
#endif

print_p (n, level)
int n, level;
{
   proc[n]->printed = 1;

   pfield (indentation * maxlevel + 6,
	   LEFT,
	   "%*.*s%d",
	   indentation * level,
	   indentation * level,
	   " ",
	   proc[n]->pid);
   pfield (7, LEFT, "%s", proc[n]->owner);
   if (show_tty)
      pfield (TTY_WIDTH, LEFT, "%s", proc[n]->tty);
#ifdef WAITCH
   if (show_waitchan)
      pfield (25, LEFT, "%s ", addr_to_name (proc[n]->channel));
#endif
   if (cpu_diff > 0)
   {
      if (proc[n]->cputime < 0)
	 pfield (3, LEFT, "%d", -proc[n]->cputime);
      else
	 pfield (3, LEFT, "");
   }
   pfield (100, LEFT+LAST, "%s", proc[n]->info);
   newline ();
}

int to_seconds (str)
char *str;
{
   int min, sec, n;

   n = sscanf (str, "%d:%d", &min, &sec);
   if (n == 2)
      return 60*min+sec;
   else
      return atoi (str);
}

sort ()
{
   int i, j, k, pid;
   struct process *sav;

   for (i = 0; i<maxproc; i++)
   {
      pid = 100000;

      for (j = i; j<maxproc; j++)
      {
	 if (proc[j]->pid < pid)
	 {
	    k = j;
	    pid = proc[j]->pid;
	 }
      }
      sav = proc[i];
      proc[i] = proc[k];
      proc[k] = sav;
   }
}

#define warning(x, arg)		sprintf (wbuf, x, arg), warn (wbuf)

warn (msg)
char *msg;
{
   fprintf (stderr, "tps: %s, assuming width 80\n", msg);
}

int find_width ()
{
   char wbuf[200];
   char buffer[1024];
   char *name;
   int s;
   int width = 80;

   name = getenv ("TERM");
   if (name == 0)
      warning ("terminal type not set", 0);
   else
   {
      s = tgetent (buffer, name);
      switch (s)
      {
       case 1:
	 s = tgetnum ("co");
	 if (s == -1)
	    warning ("width not given for type \"%s\"", name);
	 else
	    width = s;
	 break;
       case -1:
	 warning ("can't access terminal database", 0);
	 break;
       case 0:
	 warning ("can't find type %s in the terminal database", name);
	 break;
       default:
	 warning ("weird return value from tgetent (%d)", s);
	 break;
      }
   }
   return width;
}

#ifdef ultrix
zap_flags (buf)
char *buf;
{
   char c;
   char *p=buf, *q;

   while (*p++ != ' ');
   q = buf;
   while ((c = *p++) != '\0')
      *q++ = c;
   *q = '\0';
}
#endif

main (argc, argv)
int argc;
char *argv[];
{
   FILE *f;
   char buf[LINMAX];
   struct process *p;
   int i, pid;

   static Option desc[] = {
      O_flg ('t', show_tty),
      O_flg ('a', show_all),
#ifdef WAITCH
      O_flg ('w', show_waitchan),
#endif
      O_int ('i', indentation),
      O_int ('c', cpu_diff),
      O_str ('u', my_name),
      O_directive ("remaining: 0"),
#ifdef WAITCH
      O_directive
	 ("usage: [-wat] [-u user] [-i indentation] [-c time]"),
#else
      O_directive
	 ("usage: [-at] [-u user] [-i indentation] [-c time]"),
#endif
      O_end,
   };

   O_parse_options (desc, argc, argv);

   if (indentation < 1)
      indentation = 1;

   if (my_name == 0)
      my_name = getenv ("LOGNAME");
   if (my_name == 0)
      my_name = getpwuid (getuid ())->pw_name;
   if (my_name == 0)
   {
      fprintf (stderr, "%s: couldn't obtain user name\n", O_programname);
      my_name = "";
   }
   else
      my_name = newstr (my_name);

   hpos = 0;
   hmax = find_width () - 1;

#ifdef WAITCH
   if (show_waitchan)
      slurp_channel_data ();
#endif

   sprintf (buf, "exec %s", PS_COM);
   f = popen (buf, "r");
   if (f == 0)
   {
      fprintf (stderr, "%s: couldn't run ps\n", O_programname);
      exit (1);
   }

   fgets (buf, LINMAX, f);

   for (;;)
   {
      fgets (buf, LINMAX, f);
      if (feof (f)) break;

#ifdef ultrix
      zap_flags (buf);
#endif

      buf[strlen (buf) - 1] = 0;

      buf[PID-1] = 0;
      buf[PPID-1] = 0;
      buf[JUNK1-1] = 0;
      buf[JUNK2-1] = 0;
#ifdef WAITCH
      buf[WAITCH+9] = 0;
#endif

      p = (struct process *) malloc (sizeof (struct process));
#ifdef hpux
      p->owner = newstr (&buf[OWNER]);
#else
#ifdef apollo
      p->owner = newstr (&buf[OWNER]);
#else
      {
	 int uid;
	 uid = atoi (&buf[OWNER]);
	 p->owner = newstr (getpwuid (uid)->pw_name);
      }
#endif
#endif
      p->pid = atoi (&buf[PID]);
      p->ppid = atoi (&buf[PPID]);

      p->tty = newstr(&buf[TTY]);
      if (*p->tty == '?')
	 *p->tty = '\0';

      p->info = newstr (&buf[INFO]);
      p->printed = 0;

#ifdef CPU
      p->cputime = to_seconds (&buf[CPU]);
#endif
#ifdef WAITCH
      if (sscanf (&buf[WAITCH], "%x", &p->channel) != 1)
	 p->channel = 0;
#endif

#if 0
      if (p->pid != 1 && p->ppid == 1 && strncmp(p->info, "/etc/init", 9) == 0)
 	  continue;
       else
 	  proc[maxproc++] = p;
#else
      proc[maxproc++] = p;
#endif       
   }
   pclose (f);

#ifdef CPU
   if (cpu_diff > 0)
   {
      sleep (cpu_diff);

      sprintf (buf, "exec %s", PS_COM);
      f = popen (buf, "r");
      if (f == 0)
      {
	 fprintf (stderr, "%s: couldn't run ps\n", O_programname);
	 exit (1);
      }

      fgets (buf, LINMAX, f);
      for (;;)
      {
	 fgets (buf, LINMAX, f);
	 if (feof (f)) break;
#ifdef ultrix
	 zap_flags (buf);
#endif
	 pid = atoi (&buf[PID]);
	 for (i = 0; i < maxproc; i++)
	    if (proc[i]->pid == pid)
	       proc[i]->cputime -= to_seconds (&buf[CPU]);
      }
      pclose (f);
   }
#endif

   sort ();
   (void) maxlev (0, 0, 0);

   for (i = 0; i < maxproc; i++)
      print_pp (i, 0);
   return 0;
}
@EOF

chmod 664 tps.c

exit 0