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