home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume13 / tps / part01 next >
Encoding:
Text File  |  1990-07-02  |  24.7 KB  |  1,355 lines

  1. Newsgroups: comp.sources.misc
  2. organization: ZYX Sweden AB, Stockholm, Sweden
  3. subject: v13i098: tps - show process tree using 'ps'.
  4. from: arndt@zyx.ZYX.SE (Arndt Jonasson)
  5. Sender: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc)
  6.  
  7. Posting-number: Volume 13, Issue 98
  8. Submitted-by: arndt@zyx.ZYX.SE (Arndt Jonasson)
  9. Archive-name: tps/part01
  10.  
  11. # This is a shell archive.  Remove anything before this line,
  12. # then unpack it by saving it in a file and typing "sh file".
  13. #
  14. # Wrapped by Arndt Jonasson <arndt@coca> on Fri Jun 29 11:53:32 1990
  15. #
  16. # This archive contains:
  17. #    README        makefile    options.c    options.h    
  18. #    tps.1        tps.c        
  19. #
  20.  
  21. LANG=""; export LANG
  22. PATH=/bin:/usr/bin:$PATH; export PATH
  23.  
  24. echo x - README
  25. cat >README <<'@EOF'
  26. 29 June 1990 - Arndt Jonasson - arndt@zyx.se
  27.  
  28. This is version 1.0 of 'tps', a program that invokes the 'ps' command
  29. and rearranges its output into a more legible format.
  30.  
  31.  
  32. Files:
  33.  
  34. README                this file
  35. makefile            commands for building the program
  36. tps.c                'tps' source code
  37. tps.1                manual page
  38. options.h            header file for option parsing
  39. options.c            option parser source code
  40.  
  41.  
  42. To build the program, if your system is one of the following:
  43.  
  44.     Hewlett-Packard's HP-UX (series 9000/300 or 9000/800)
  45.     DEC's Ultrix (DecStation 3100)
  46.     Apollo's Domain/OS
  47.     SUN's SunOS
  48.     Sony's NeWS
  49.  
  50. just type the command 'make'.
  51.  
  52. If your system is not one of those, but has the 'ps' program, it may
  53. work anyway, or you may have to do some changes to tps.c first. If you
  54. port 'tps' to another system, please send your changes back to me.
  55.  
  56.  
  57. The option parser is usable independently of 'tps', but, alas, there
  58. is no manual page yet.
  59. @EOF
  60.  
  61. chmod 664 README
  62.  
  63. echo x - makefile
  64. cat >makefile <<'@EOF'
  65.  
  66. tps:        tps.o options.o
  67.         cc -o tps tps.o options.o -ltermcap
  68.  
  69. tps.o:        tps.c options.h
  70.         cc -c tps.c
  71.  
  72. options.o:    options.c options.h
  73.         cc -c options.c
  74.  
  75.  
  76. clean:        
  77.         -rm tps options.o tps.o
  78. @EOF
  79.  
  80. chmod 664 makefile
  81.  
  82. echo x - options.c
  83. cat >options.c <<'@EOF'
  84. /*
  85.   options.c - created 28 Mar 88.
  86.  
  87.   Command line option parser, version 1.0 (29 June 1990)
  88.  
  89.   Author: Arndt Jonasson, Zyx Sweden
  90.   arndt@zyx.SE    (...<backbone>!mcsun!sunic!zyx!arndt)
  91.  
  92.   No manual page yet.
  93. */
  94.  
  95. #include "options.h"
  96.  
  97. extern void exit ();        /* '#define void' if your compiler doesn't */
  98.                 /* have it.*/
  99.  
  100. char *O_programname = NULL;
  101.  
  102. static char *usage_string = NULL;
  103. static int option_error;
  104.  
  105. static fatal (msg)
  106. char *msg;
  107. {
  108.    fprintf (stderr, "option parsing failure: %s\n", msg);
  109.    exit (1);
  110. }
  111.  
  112. static char *basename (str)
  113. char *str;
  114. {
  115.    char *p1, *p2;
  116.    extern char *strchr ();
  117.  
  118.    p2 = str;
  119.    while ((p1 = strchr (p2, '/')) != NULL)
  120.    {
  121.       if (p1[1] == '\0')
  122.       {
  123.      p1[0] = '\0';
  124.      return p2;
  125.       }
  126.       p2 = p1 + 1;
  127.    }
  128.    return p2;
  129. }
  130.  
  131. static char *match (prefix, str)
  132. char *prefix, *str;
  133. {
  134.    int n = strlen (prefix);
  135.  
  136.    if (strncmp (prefix, str, n) == 0)
  137.       return str + n;
  138.    else
  139.       return NULL;
  140. }
  141.  
  142. O_usage ()
  143. {
  144.    if (usage_string != NULL)
  145.    {
  146.       if (O_programname == NULL)
  147.      fprintf (stderr, "valid options: %s\n", usage_string);
  148.       else
  149.      fprintf (stderr, "usage: %s %s\n", O_programname, usage_string);
  150.    }
  151.    else
  152.    {
  153.       if (O_programname != NULL)
  154.      fprintf (stderr, "%s: ", O_programname);
  155.       fprintf (stderr, "invalid usage\n");
  156.    }
  157.  
  158.    exit (1);
  159. }
  160.  
  161. int O_atoi (str)
  162. char *str;
  163. {
  164.    char c;
  165.    int base = 10;
  166.    int res = 0;
  167.    int negative = 1;
  168.    
  169. /*
  170.   No check for overflow.
  171. */
  172.  
  173.    c = str[0];
  174.    
  175.    if (*str == '-')
  176.    {
  177.       negative = -1;
  178.       str++;
  179.    }
  180.  
  181.    if (*str == '0')
  182.    {
  183.       if (*++str == 'x')
  184.       {
  185.      str++;
  186.      base = 16;
  187.       }
  188.       else
  189.      base = 8;
  190.    }
  191.    
  192.    if (*str == '\0' && (negative != -1 || base != 8)) /* kludgie */
  193.    {
  194.       option_error = 1;
  195.       return 0;
  196.    }
  197.  
  198.    for (;;)
  199.    {
  200.       switch (c = *str++)
  201.       {
  202.        case '8':
  203.        case '9':
  204.      if (base == 8)
  205.      {
  206.         option_error = 1;
  207.         return 0;
  208.      }
  209.        case '0':
  210.        case '1':
  211.        case '2':
  212.        case '3':
  213.        case '4':
  214.        case '5':
  215.        case '6':
  216.        case '7':
  217.      res = base * res + c - '0';
  218.      break;
  219.        case 'a':
  220.        case 'b':
  221.        case 'c':
  222.        case 'd':
  223.        case 'e':
  224.        case 'f':
  225.      if (base == 16)
  226.         res = base * res + c - 'a' + 10;
  227.      else
  228.      {
  229.         option_error = 1;
  230.         return 0;
  231.      }
  232.      break;
  233.        case '\0':
  234.      return (negative * res);
  235.      /* NOTREACHED */
  236.      break;
  237.        default:
  238.      option_error = 1;
  239.      return 0;
  240.       }
  241.    }
  242. }
  243.  
  244. double O_atof (s)
  245. char *s;
  246. {
  247. /*
  248.   No error-checking currently.
  249. */
  250.    extern double atof ();
  251.  
  252.    return atof (s);
  253. }
  254.  
  255. char *O_strid (s)
  256. char *s;
  257. {
  258.    return s;
  259. }
  260.  
  261. char O_chrid (s)
  262. char *s;
  263. {
  264.    return s[0];
  265. }
  266.  
  267. static get_it (p, arg, c)
  268. Option *p;
  269. char *arg;
  270. char c;
  271. {
  272.    option_error = 0;
  273.  
  274.    switch ((p->flags & 037))
  275.    {
  276.     case O_INT:
  277.       *p->ip = (*p->ipf) (arg);
  278.       if (option_error)
  279.       {
  280.      fprintf (stderr,
  281.           "Invalid integer '%s' given to the -%c option\n", arg, c);
  282.      O_usage ();
  283.       }
  284.       break;
  285.     case O_STR:
  286.       *p->cp = (*p->cpf) (arg);
  287.       break;
  288.     case O_DBL:
  289.       *p->dp = (*p->dpf) (arg);
  290.       if (option_error)
  291.       {
  292.      fprintf (stderr,
  293.       "Invalid floating-point number '%s' given to the -%c option\n", arg, c);
  294.      O_usage ();
  295.       }
  296.       break;
  297.     case O_CHR:
  298.       *p->tp = (*p->tpf) (arg);
  299.       break;
  300.     case O_MUL:
  301.       (*p->table_p)[p->data++] = arg;
  302.       break;
  303.     default:
  304.       fatal ("invalid option type");
  305.    }
  306. }
  307.  
  308. int O_parse_options (desc, argc, argv)
  309. Option *desc;
  310. int argc;
  311. char **argv;
  312. {
  313.    static char *empty_table[] = {NULL};
  314.    int first_char, multiple_error, multiple_add, hyphen_is_arg;
  315.    int min, max, i, remaining;
  316.    Option *p, *default_p = NULL;
  317.    char *cp, *dir, *arg;
  318.    char c;
  319.  
  320.    multiple_error = 1;
  321.    multiple_add = 0;
  322.    hyphen_is_arg = 1;
  323.    min = 0;
  324.    max = -1;
  325.  
  326.    for (p = desc; p->flags != O_END; p++)
  327.    {
  328.       p->data = 0;
  329.  
  330.       if (p->flags == O_DIR)
  331.       {
  332.      dir = p->directive;
  333.      if (cp = match ("usage: ", dir))
  334.         usage_string = cp;
  335.      else if (cp = match ("multiple: ", dir))
  336.      {
  337.         multiple_error = (strcmp ("error", cp) == 0);
  338.         multiple_add = (strcmp ("add", cp) == 0);
  339.      }
  340.      else if (cp = match ("remaining: ", dir))
  341.      {
  342.         int n;
  343.         char dummy;
  344.  
  345.         n = sscanf (cp, "%d%c%d", &min, &dummy, &max);
  346.         if (n == 1)
  347.            max = min;
  348.         else if (n == 2)
  349.            max = -1;
  350.      }
  351.      else
  352.         fatal ("unknown option directive");
  353.       }
  354.       else if (p->flags == O_MUL)
  355.      *p->table_p = (char **) malloc (argc * sizeof (char *));
  356.       else if (p->flags == O_INT && p->c == '\0')
  357.       {
  358.      p->c = -1;
  359.      default_p = p;
  360.       }
  361.       else if (p->flags == O_FLG && p->c == '\0')
  362.      hyphen_is_arg = 0;
  363.  
  364.       if (p->flags == O_FLG)
  365.      *p->ip = 0;
  366.    }
  367.  
  368.    O_programname = basename (argv[0]);
  369.  
  370.    for (i = 1; i < argc; i++)
  371.    {
  372.       arg = argv[i];
  373.       if (arg[0] != '-' || (arg[1] == '\0' && hyphen_is_arg))
  374.      break;
  375.  
  376.       first_char = 1;
  377.  
  378.       while ((c = *++arg) != '\0' || first_char)
  379.       {
  380.      for (p = desc; p->flags != O_END; p++)
  381.         if (p->c == c)
  382.         {
  383.            if (p->flags & 040)
  384.            {
  385.           fprintf (stderr, "The -%c option was given twice\n", c);
  386.           O_usage ();
  387.            }
  388.  
  389.            if (multiple_error && p->flags != O_MUL)
  390.           p->flags |= 040;
  391.  
  392.            if ((p->flags & 037) == O_FLG)
  393.            {
  394.  
  395.           if (multiple_add)
  396.              *(p->ip) += 1;
  397.           else
  398.              *(p->ip) = 1;
  399.           if (c == '\0')
  400.              goto next_argument;
  401.           else
  402.              goto next_character;
  403.            }
  404.  
  405.            arg += 1;
  406.            if (arg[0] == '\0')
  407.            {
  408.           if (++i == argc)
  409.           {
  410.              fprintf (stderr,
  411.                   "The -%c option requires an argument\n", c);
  412.              O_usage ();
  413.           }
  414.           arg = argv[i];
  415.            }
  416.            get_it (p, arg, c);
  417.            goto next_argument;
  418.         }
  419.      if ((p = default_p) != NULL && first_char)
  420.      {
  421.         *p->ip = (*p->ipf) (arg);
  422.         if (!option_error)
  423.            goto next_argument;
  424.      }
  425.      fprintf (stderr, "There is no -%c option\n", c);
  426.      O_usage ();
  427.  
  428.      /* NOTREACHED*/
  429.  
  430.        next_character:
  431.      first_char = 0;
  432.       }
  433.     next_argument: ;
  434.    }
  435.  
  436.    for (p = desc; p->flags != O_END; p++)
  437.       if ((p->flags & 037) == O_MUL)
  438.       {
  439.      if (p->data == 0)
  440.         (*p->table_p = empty_table);
  441.      else
  442.      {
  443.         (*p->table_p)[p->data++] = NULL;
  444.         *p->table_p = (char **) realloc (*p->table_p,
  445.                          p->data * sizeof (char *));
  446.      }
  447.       }
  448.  
  449.    remaining = argc - i;
  450.    if (remaining < min || max != -1 && remaining > max)
  451.    {
  452.       if (max == -1)
  453.      fprintf (stderr, "At least %d non-option argument%s required\n",
  454.           min, min == 1 ? " is" : "s are");
  455.       else if (max == 0)
  456.      fprintf (stderr, "No non-option arguments are allowed\n");
  457.       else if (min == max)
  458.      fprintf (stderr, "Exactly %d non-option argument%s required\n",
  459.           min, min == 1 ? " is" : "s are");
  460.       else
  461.      fprintf (stderr, "%d to %d non-option arguments are required\n",
  462.           min, max);
  463.       O_usage ();
  464.    }
  465.    return i;
  466. }
  467. @EOF
  468.  
  469. chmod 664 options.c
  470.  
  471. echo x - options.h
  472. cat >options.h <<'@EOF'
  473. /*
  474.   options.h - created 28 Mar 88.
  475.  
  476.   Command line option parser, version 1.0 (29 June 1990)
  477.  
  478.   Author: Arndt Jonasson, Zyx Sweden
  479.   arndt@zyx.SE    (...<backbone>!mcsun!sunic!zyx!arndt)
  480.  
  481.   No manual page yet.
  482. */
  483.  
  484. #include <stdio.h>
  485.  
  486. typedef struct
  487. {
  488.    char c;
  489.    int flags;
  490.    char **cp;
  491.    char *(*cpf) ();
  492.    int *ip;
  493.    int (*ipf) ();
  494.    double *dp;
  495.    double (*dpf) ();
  496.    char *tp;
  497.    char (*tpf) ();
  498.    char ***table_p;
  499.    char *directive;
  500.    int data;
  501. } Option;
  502.  
  503. extern int O_atoi ();
  504. extern double O_atof ();
  505. extern char O_chrid ();
  506.  
  507. extern char *O_strid ();
  508. extern char *O_programname;
  509.  
  510. #define O_FLG    0
  511. #define O_INT    1
  512. #define O_STR    2
  513. #define O_DBL    3
  514. #define O_END    4
  515. #define O_DIR    5
  516. #define O_CHR    6
  517. #define O_MUL    7
  518.  
  519.  
  520. #define O_flg(c, ip)    {c, O_FLG, 0, 0, &ip, 0, 0, 0, 0, 0, 0, 0}
  521. #define O_int(c, ip)    {c, O_INT, 0, 0, &ip, O_atoi, 0, 0, 0, 0, 0, 0}
  522. #define O_str(c, cp)    {c, O_STR, &cp, O_strid, 0, 0, 0, 0, 0, 0, 0, 0}
  523. #define O_dbl(c, dp)    {c, O_DBL, 0, 0, 0, 0, &dp, O_atof, 0, 0, 0, 0}
  524. #define O_end        {-1, O_END, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
  525. #define O_directive(str) {-1, O_DIR, 0, 0, 0, 0, 0, 0, 0, 0, 0, str}
  526. #define O_chr(c, cp)    {c, O_CHR, 0, 0, 0, 0, 0, 0, &cp, O_chrid, 0, 0}
  527. #define O_mul(c, cpp)    {c, O_MUL, 0, 0, 0, 0, 0, 0, 0, 0, &cpp, 0}
  528.  
  529. #define O_Empty            '\0'
  530.  
  531. #define usage()            O_usage ()
  532. @EOF
  533.  
  534. chmod 664 options.h
  535.  
  536. echo x - tps.1
  537. cat >tps.1 <<'@EOF'
  538. .TH TPS 1 local ""
  539. .SH NAME
  540. tps - tree-structured process status
  541. .SH SYNOPSIS
  542. .B tps
  543. [-wat] [-u
  544. .I user]
  545. [-i
  546. .I n]
  547. [-c
  548. .I time]
  549. .SH DESCRIPTION
  550. .I tps
  551. runs
  552. .B ps
  553. in an inferior fork and filters its output to present a more legible
  554. view of the currently running processes. One output line consists of
  555. the process pid, the name of its owner, its controlling terminal (optional)
  556. and the command line arguments of the process. If a process has any
  557. children, these are displayed directly following their father, with
  558. their pids indented.
  559.  
  560. If the command line arguments for one process don't fit in one display line,
  561. they wrap to the next line, indicating this with a plus sign in front
  562. of the continuation line. The width of the screen is taken from the
  563. .B terminfo/termcap
  564. database. If the width cannot be found, 80 is assumed.
  565.  
  566. The criterion for displaying a process is the following: for each process,
  567. if itself, any child or any ancestor is owned by the
  568. .IR "current user" ,
  569. it is displayed. In other words, all processes belonging to the user will
  570. be displayed, and additionally all children to any of those, whether owned
  571. by the user or not (thus including invocations of
  572. .BR su(1) ,
  573. and all parent processes (which will always include the
  574. .B init
  575. process (pid 1) and the
  576. .B swapper
  577. process (pid 0).
  578.  
  579. By default, the
  580. .I current user
  581. is the user running the program.
  582. .B tps
  583. can be told to use any other user, or to display all running processes.
  584.  
  585. .PP 
  586. The options have the following meanings:
  587. .TP 11
  588. .BR -t
  589. Show the controlling tty of the process, if it has one.
  590. .TP
  591. .BR -w
  592. If the process is sleeping, show the symbolic name of the address that
  593. the process is waiting for. This options is currently only implemented
  594. in HP-UX.
  595. .TP
  596. .BR -a
  597. Show all running processes, not just the process tree of one user.
  598. .TP
  599. .BI -u " user"
  600. Show the process tree of processes belonging to
  601. .IR user .
  602. If this option is not given, the process tree of the user invoking
  603. the program will be given (as defined by the environment variable
  604. LOGNAME, or the
  605. .IR "real user id" ,
  606. in that order).
  607. .TP
  608. .BI -i " n"
  609. This changes the default indentation of child processes from 2 to
  610. .IR n .
  611. .TP
  612. .BI -c " time"
  613. When this option is specified,
  614. .B ps
  615. is invoked twice, with the interval
  616. .I time
  617. seconds. The processes common to both invocations are displayed (subject
  618. to the usual selection by user described above), and the difference in
  619. run time, if not zero, is printed in front of the command line arguments.
  620. .SH FILES
  621. /bin/ps
  622. .SH ENVIRONMENT
  623. .TP 11
  624. TERM
  625. The name of the terminfo/termcap database entry. Used for obtaining the
  626. screen width.
  627. .TP
  628. LOGNAME
  629. The
  630. .I current user
  631. to use when the
  632. .B \-u
  633. option hasn't been given.
  634. .SH NOTES
  635. .B tps
  636. has currently been ported to Hewlett-Packard's HP-UX, DEC's Ultrix,
  637. Apollo's Domain/OS, SUN's SunOS, and Sony's NeWS.
  638. .SH SEE ALSO
  639. ps(1), terminfo(4), terminfo(5), termcap(3).
  640. .SH BUGS
  641. When the -w is given, the display of the command line arguments may be
  642. messed up.
  643. .SH AUTHOR
  644. Arndt Jonasson, ZYX Sweden (email address: arndt@zyx.se)
  645. @EOF
  646.  
  647. chmod 664 tps.1
  648.  
  649. echo x - tps.c
  650. cat >tps.c <<'@EOF'
  651. /*
  652.   tps.c -- Tree-structured ps program. It works by reading in the output
  653.   of a 'ps' command and reordering it so that the parent-son relation-
  654.   ships of the processes are displayed.
  655.  
  656.   The maximum width of the terminal is honored. Link with -ltermcap
  657.   (or -ltermlib).
  658.  
  659.   Author: Arndt Jonasson, Zyx.
  660. */
  661.  
  662. /*
  663.   Do error checking in various placesf (sscanf, malloc..)
  664.   Fix vararg for pfield.
  665.  
  666.   Foo! Linking curses adds 60K to the code size.
  667.  
  668.   New ideas:
  669.    Make 'tps -u root' only show root-owned processes.
  670.    -p pid: show only the subtree with the pid p in it.
  671. */
  672.  
  673. #include <sys/types.h>
  674. #include <sys/stat.h>
  675. #include <fcntl.h>
  676. #include <stdio.h>
  677. #include <pwd.h>
  678.  
  679. #include "options.h"
  680.  
  681. extern char *getenv ();
  682. extern struct passwd *getpwuid ();
  683.  
  684. #ifdef hpux
  685. #define MAX_PROC    200
  686. #define PS_COM        "ps -efl"
  687. #else
  688. #ifdef apollo
  689. #define MAX_PROC    200
  690. #define PS_COM        "ps -ef"
  691. #else
  692. #ifdef pyr
  693. #define MAX_PROC    536
  694. #define PS_COM        "ps -agxlw"
  695. #else
  696. #define MAX_PROC    100
  697. #define PS_COM        "ps -agxlw"
  698. #endif
  699. #endif
  700. #endif
  701.  
  702. #define LEFT    1
  703. #define RIGHT    2
  704. #define CENTER    4
  705. #define LAST    8
  706.  
  707. #define LINMAX    400
  708.  
  709. #ifdef hpux
  710.  
  711. #if 0
  712. #define OWNER    0
  713. #define PID    9
  714. #define PPID    15
  715. #define JUNK1    21
  716. #define TTY    33
  717. #define JUNK2    (TTY+8)
  718. #define CPU    41
  719. #define INFO    47
  720. #else
  721. #define OWNER    6
  722. #define PID    14
  723. #define PPID    20
  724. #define JUNK1    26
  725. #define WAITCH    47
  726. #define TTY    65
  727. #define JUNK2    (TTY+8)
  728. #define CPU    73
  729. #define INFO    79
  730. #endif
  731.  
  732. #else
  733.  
  734. #ifdef apollo
  735. #define OWNER    0
  736. #define PID    9
  737. #define PPID    15
  738. #define JUNK1    21
  739. #define TTY    33
  740. #define JUNK2    (TTY+9)
  741. #define CPU    42
  742. #define INFO    48
  743. #else
  744.  
  745. #ifdef ultrix
  746. #define OWNER    0
  747. #define PID    4
  748. #define PPID    10
  749. #define JUNK1    16
  750. #define INFO    60
  751. #define TTY    50
  752. #define CPU    54
  753. #define JUNK2    54
  754. #else
  755.  
  756. #ifdef sun
  757. #define OWNER    7
  758. #define PID    12
  759. #define PPID    18
  760. #define JUNK1    24
  761. #define INFO    70
  762. #define TTY    61
  763. #define CPU    65
  764. #define JUNK2    65
  765. #else
  766.  
  767. #define OWNER    7
  768. #define PID    12
  769. #define PPID    18
  770. #define JUNK1    24
  771. #ifdef pyr
  772. #define INFO    73
  773. #define TTY    63
  774. #define JUNK2    (TTY+4)
  775. #else
  776. #ifdef sony
  777. #define INFO    75
  778. #define TTY    65
  779. #define JUNK2    (TTY+4)
  780. #define CPU    69
  781. #else
  782. #define INFO    69
  783. #define TTY    60
  784. #define JUNK2    (TTY+4)
  785. #endif
  786. #endif
  787. #endif
  788. #endif
  789. #endif
  790. #endif
  791.  
  792. struct process
  793. {
  794.    int pid, ppid;
  795.    char *owner, *info, *tty;
  796.    int printed;
  797.    int cputime;
  798.    long channel;
  799. };
  800.  
  801. struct process *proc[MAX_PROC];
  802. int maxproc = 0;
  803.  
  804. int hmax, hpos;
  805. int maxlevel = 0;
  806. int show_tty = 0;
  807. int indentation = 2;
  808. int show_all = 0;
  809. int cpu_diff = 0;
  810. char *my_name = 0;
  811.  
  812. #ifdef WAITCH
  813. typedef struct
  814. {
  815.    long adr;
  816.    char name[20];
  817. } Symbol;
  818.  
  819. int show_waitchan = 0;
  820. Symbol *table;
  821. int symbol_n;
  822. #endif
  823.  
  824. char *newstr (str)
  825. char *str;
  826. {
  827.    char *cp;
  828.    while (*str++ == ' ');
  829.    str--;
  830.    cp = (char *) malloc (strlen (str) + 1);
  831.    strcpy (cp, str);
  832.    return cp;
  833. }
  834.  
  835. newline ()
  836. {
  837.    hpos = 0;
  838.    printf ("\n");
  839. }
  840.  
  841. #ifdef WAITCH
  842. Symbol *lookup (adr)
  843. long adr;
  844. {
  845.    int i1 = 0, i2 = symbol_n;
  846.    int i;
  847.  
  848.    for (;;)
  849.    {
  850.       i = (i1 + i2) / 2;
  851.       if (table[i].adr == adr)
  852.      return &table[i];
  853.       else if (table[i].adr > adr)
  854.      i2 = i;
  855.       else
  856.      i1 = i;
  857.       if (i1 > i2 - 2)
  858.      break;
  859.    }
  860.    if (table[i].adr > adr)
  861.       i -= 1;
  862.    return &table[i];
  863. }
  864.  
  865. slurp_channel_data ()
  866. {
  867.    int d;
  868.    struct stat s_buf;
  869.  
  870.    maybe_rebuild_tps_data ();
  871.  
  872.    d = open ("/tmp/tps_data", O_RDONLY);
  873.    fstat (d, &s_buf);
  874.    table = (Symbol *) malloc (s_buf.st_size);
  875.    read (d, table, s_buf.st_size);
  876.    symbol_n = s_buf.st_size / sizeof (Symbol);
  877. }
  878.  
  879. char *addr_to_name (a)
  880. long a;
  881. {
  882.    static char buf[80];
  883.    long diff;
  884.    Symbol *sym;
  885.  
  886.    if (a == 0)
  887.       return "";
  888.  
  889.    sym = lookup (a);
  890.  
  891.    if (sym == &table[symbol_n - 1])
  892.       return "?";
  893.  
  894.    diff = a - sym->adr;
  895.    if (diff > 0x100000)
  896.       return "?";
  897.  
  898.    if (diff == 0)
  899.       sprintf (buf, "%s", sym->name);
  900.    else
  901.       sprintf (buf, "%s+%#x", sym->name, diff);
  902.  
  903.    if (buf[0] == '_')
  904.       return buf+1;
  905.    else
  906.       return buf;
  907. }
  908.  
  909. maybe_rebuild_tps_data ()
  910. {
  911.    int s;
  912.    FILE *fi, *fo;
  913.    char buf[80];
  914.    long adr;
  915.    char type[5], name[80];
  916.    Symbol block;
  917.    struct stat b1, b2;
  918.  
  919.    s = stat ("/hp-ux", &b1);
  920.    s = stat ("/tmp/tps_data", &b2);
  921.    if (s == 0 && b1.st_mtime <= b2.st_mtime)
  922.       return;
  923.  
  924.    fprintf (stderr, "Rebuilding kernel symbol table ...\n");
  925.  
  926. #ifdef hp9000s800
  927.    s = system ("nm -vp /hp-ux >/tmp/tps_data_x");
  928. #else
  929.    s = system ("nm -n /hp-ux >/tmp/tps_data_x");
  930. #endif
  931.    fi = fopen ("/tmp/tps_data_x", "r");
  932.    fo = fopen ("/tmp/tps_data", "w");
  933.  
  934.    for (;;)
  935.    {
  936.       fgets (buf, 80, fi);
  937.       if (feof (fi))
  938.      break;
  939. #ifdef hp9000s800
  940.       sscanf (buf, "%x %s %s\n", &adr, type, name);
  941. #else
  942.       sscanf (buf, "0x%x %s %s\n", &adr, type, name);
  943. #endif
  944.       block.adr = adr;
  945.       strcpy (block.name, name);
  946.       fwrite ((char *) &block, sizeof (block), 1, fo);
  947.    }
  948.    fclose (fi);
  949.    fclose (fo);
  950. }
  951. #endif /* WAITCH */
  952.  
  953. pfield (len, align, format, a1, a2, a3, a4, a5)
  954. int len, align;
  955. char *format;
  956. {
  957.    char *text;
  958.    char buf[200];
  959.    int l, i, pre, post, o_hpos;
  960.  
  961.    o_hpos = hpos;
  962.    hpos += len;
  963.    if (hpos > hmax)
  964.    {
  965.       len -= hpos - hmax;
  966.       hpos = hmax;
  967.    }
  968.  
  969.    sprintf (buf, format, a1, a2, a3, a4, a5);
  970.    l = strlen (buf);
  971.  
  972.    if (align & LEFT)
  973.    {
  974.       pre = 0;
  975.       post = len - l;
  976.    }
  977.    else if (align & RIGHT)
  978.    {
  979.       pre = len - l;
  980.       post = 0;
  981.    }
  982.    else if (align & CENTER)
  983.    {
  984.       pre = len - l;
  985.       post = pre >> 1;
  986.       pre = pre - post;
  987.    }
  988.  
  989.    text = buf;
  990.  
  991.    for (;;)
  992.    {
  993.       for (i = 0; i < pre; i++) putchar (' ');
  994.       if (len < l)
  995.       {
  996.      printf ("%.*s", len, text);
  997.      newline ();
  998.      for (i = 0; i < o_hpos - 2; i++) putchar (' ');
  999.      putchar ('+'); putchar (' ');
  1000.      text += len;
  1001.      l -= len;
  1002.       }
  1003.       else
  1004.       {
  1005.      printf ("%.*s", l, text);
  1006.      hpos = o_hpos + len;
  1007.      break;
  1008.       }
  1009.    }
  1010.  
  1011.    if (!(align & LAST))
  1012.       for (i = 0; i < post; i++) putchar (' ');
  1013. }
  1014.  
  1015. int maxlev (n, level, me_above)
  1016. int n, level, me_above;
  1017. {
  1018.    int i;
  1019.    int pid = proc[n]->pid;
  1020.    int me_below = 0;
  1021.  
  1022.    if (level > maxlevel) maxlevel = level;
  1023.  
  1024.    if (strcmp (proc[n]->owner, my_name) == 0)
  1025.       me_above = 1;
  1026.  
  1027.    for (i = 0; i < maxproc; i++)
  1028.       if (proc[i]->ppid == pid && proc[i]->pid != 0)
  1029.       {
  1030.      if (maxlev (i, level + 1, me_above))
  1031.         me_below = 1;
  1032.       }
  1033.  
  1034.    me_above |= me_below;
  1035.    if (!me_above && !show_all)
  1036.       proc[n]->printed = 1;
  1037.    return me_above;
  1038. }
  1039.  
  1040. print_pp (n, level)
  1041. int n, level;
  1042. {
  1043.    int i;
  1044.    int pid = proc[n]->pid;
  1045.  
  1046.    if (proc[n]->printed) return;
  1047.  
  1048.    print_p (n, level);
  1049.  
  1050.    for (i = 0; i<maxproc; i++)
  1051.       if (proc[i]->ppid == pid)
  1052.      print_pp (i, level+1);
  1053. }
  1054.  
  1055. #ifdef hpux
  1056. #define TTY_WIDTH    10
  1057. #else
  1058. #define TTY_WIDTH    5
  1059. #endif
  1060.  
  1061. print_p (n, level)
  1062. int n, level;
  1063. {
  1064.    proc[n]->printed = 1;
  1065.  
  1066.    pfield (indentation * maxlevel + 6,
  1067.        LEFT,
  1068.        "%*.*s%d",
  1069.        indentation * level,
  1070.        indentation * level,
  1071.        " ",
  1072.        proc[n]->pid);
  1073.    pfield (7, LEFT, "%s", proc[n]->owner);
  1074.    if (show_tty)
  1075.       pfield (TTY_WIDTH, LEFT, "%s", proc[n]->tty);
  1076. #ifdef WAITCH
  1077.    if (show_waitchan)
  1078.       pfield (25, LEFT, "%s ", addr_to_name (proc[n]->channel));
  1079. #endif
  1080.    if (cpu_diff > 0)
  1081.    {
  1082.       if (proc[n]->cputime < 0)
  1083.      pfield (3, LEFT, "%d", -proc[n]->cputime);
  1084.       else
  1085.      pfield (3, LEFT, "");
  1086.    }
  1087.    pfield (100, LEFT+LAST, "%s", proc[n]->info);
  1088.    newline ();
  1089. }
  1090.  
  1091. int to_seconds (str)
  1092. char *str;
  1093. {
  1094.    int min, sec, n;
  1095.  
  1096.    n = sscanf (str, "%d:%d", &min, &sec);
  1097.    if (n == 2)
  1098.       return 60*min+sec;
  1099.    else
  1100.       return atoi (str);
  1101. }
  1102.  
  1103. sort ()
  1104. {
  1105.    int i, j, k, pid;
  1106.    struct process *sav;
  1107.  
  1108.    for (i = 0; i<maxproc; i++)
  1109.    {
  1110.       pid = 100000;
  1111.  
  1112.       for (j = i; j<maxproc; j++)
  1113.       {
  1114.      if (proc[j]->pid < pid)
  1115.      {
  1116.         k = j;
  1117.         pid = proc[j]->pid;
  1118.      }
  1119.       }
  1120.       sav = proc[i];
  1121.       proc[i] = proc[k];
  1122.       proc[k] = sav;
  1123.    }
  1124. }
  1125.  
  1126. #define warning(x, arg)        sprintf (wbuf, x, arg), warn (wbuf)
  1127.  
  1128. warn (msg)
  1129. char *msg;
  1130. {
  1131.    fprintf (stderr, "tps: %s, assuming width 80\n", msg);
  1132. }
  1133.  
  1134. int find_width ()
  1135. {
  1136.    char wbuf[200];
  1137.    char buffer[1024];
  1138.    char *name;
  1139.    int s;
  1140.    int width = 80;
  1141.  
  1142.    name = getenv ("TERM");
  1143.    if (name == 0)
  1144.       warning ("terminal type not set", 0);
  1145.    else
  1146.    {
  1147.       s = tgetent (buffer, name);
  1148.       switch (s)
  1149.       {
  1150.        case 1:
  1151.      s = tgetnum ("co");
  1152.      if (s == -1)
  1153.         warning ("width not given for type \"%s\"", name);
  1154.      else
  1155.         width = s;
  1156.      break;
  1157.        case -1:
  1158.      warning ("can't access terminal database", 0);
  1159.      break;
  1160.        case 0:
  1161.      warning ("can't find type %s in the terminal database", name);
  1162.      break;
  1163.        default:
  1164.      warning ("weird return value from tgetent (%d)", s);
  1165.      break;
  1166.       }
  1167.    }
  1168.    return width;
  1169. }
  1170.  
  1171. #ifdef ultrix
  1172. zap_flags (buf)
  1173. char *buf;
  1174. {
  1175.    char c;
  1176.    char *p=buf, *q;
  1177.  
  1178.    while (*p++ != ' ');
  1179.    q = buf;
  1180.    while ((c = *p++) != '\0')
  1181.       *q++ = c;
  1182.    *q = '\0';
  1183. }
  1184. #endif
  1185.  
  1186. main (argc, argv)
  1187. int argc;
  1188. char *argv[];
  1189. {
  1190.    FILE *f;
  1191.    char buf[LINMAX];
  1192.    struct process *p;
  1193.    int i, pid;
  1194.  
  1195.    static Option desc[] = {
  1196.       O_flg ('t', show_tty),
  1197.       O_flg ('a', show_all),
  1198. #ifdef WAITCH
  1199.       O_flg ('w', show_waitchan),
  1200. #endif
  1201.       O_int ('i', indentation),
  1202.       O_int ('c', cpu_diff),
  1203.       O_str ('u', my_name),
  1204.       O_directive ("remaining: 0"),
  1205. #ifdef WAITCH
  1206.       O_directive
  1207.      ("usage: [-wat] [-u user] [-i indentation] [-c time]"),
  1208. #else
  1209.       O_directive
  1210.      ("usage: [-at] [-u user] [-i indentation] [-c time]"),
  1211. #endif
  1212.       O_end,
  1213.    };
  1214.  
  1215.    O_parse_options (desc, argc, argv);
  1216.  
  1217.    if (indentation < 1)
  1218.       indentation = 1;
  1219.  
  1220.    if (my_name == 0)
  1221.       my_name = getenv ("LOGNAME");
  1222.    if (my_name == 0)
  1223.       my_name = getpwuid (getuid ())->pw_name;
  1224.    if (my_name == 0)
  1225.    {
  1226.       fprintf (stderr, "%s: couldn't obtain user name\n", O_programname);
  1227.       my_name = "";
  1228.    }
  1229.    else
  1230.       my_name = newstr (my_name);
  1231.  
  1232.    hpos = 0;
  1233.    hmax = find_width () - 1;
  1234.  
  1235. #ifdef WAITCH
  1236.    if (show_waitchan)
  1237.       slurp_channel_data ();
  1238. #endif
  1239.  
  1240.    sprintf (buf, "exec %s", PS_COM);
  1241.    f = popen (buf, "r");
  1242.    if (f == 0)
  1243.    {
  1244.       fprintf (stderr, "%s: couldn't run ps\n", O_programname);
  1245.       exit (1);
  1246.    }
  1247.  
  1248.    fgets (buf, LINMAX, f);
  1249.  
  1250.    for (;;)
  1251.    {
  1252.       fgets (buf, LINMAX, f);
  1253.       if (feof (f)) break;
  1254.  
  1255. #ifdef ultrix
  1256.       zap_flags (buf);
  1257. #endif
  1258.  
  1259.       buf[strlen (buf) - 1] = 0;
  1260.  
  1261.       buf[PID-1] = 0;
  1262.       buf[PPID-1] = 0;
  1263.       buf[JUNK1-1] = 0;
  1264.       buf[JUNK2-1] = 0;
  1265. #ifdef WAITCH
  1266.       buf[WAITCH+9] = 0;
  1267. #endif
  1268.  
  1269.       p = (struct process *) malloc (sizeof (struct process));
  1270. #ifdef hpux
  1271.       p->owner = newstr (&buf[OWNER]);
  1272. #else
  1273. #ifdef apollo
  1274.       p->owner = newstr (&buf[OWNER]);
  1275. #else
  1276.       {
  1277.      int uid;
  1278.      uid = atoi (&buf[OWNER]);
  1279.      p->owner = newstr (getpwuid (uid)->pw_name);
  1280.       }
  1281. #endif
  1282. #endif
  1283.       p->pid = atoi (&buf[PID]);
  1284.       p->ppid = atoi (&buf[PPID]);
  1285.  
  1286.       p->tty = newstr(&buf[TTY]);
  1287.       if (*p->tty == '?')
  1288.      *p->tty = '\0';
  1289.  
  1290.       p->info = newstr (&buf[INFO]);
  1291.       p->printed = 0;
  1292.  
  1293. #ifdef CPU
  1294.       p->cputime = to_seconds (&buf[CPU]);
  1295. #endif
  1296. #ifdef WAITCH
  1297.       if (sscanf (&buf[WAITCH], "%x", &p->channel) != 1)
  1298.      p->channel = 0;
  1299. #endif
  1300.  
  1301. #if 0
  1302.       if (p->pid != 1 && p->ppid == 1 && strncmp(p->info, "/etc/init", 9) == 0)
  1303.        continue;
  1304.        else
  1305.        proc[maxproc++] = p;
  1306. #else
  1307.       proc[maxproc++] = p;
  1308. #endif       
  1309.    }
  1310.    pclose (f);
  1311.  
  1312. #ifdef CPU
  1313.    if (cpu_diff > 0)
  1314.    {
  1315.       sleep (cpu_diff);
  1316.  
  1317.       sprintf (buf, "exec %s", PS_COM);
  1318.       f = popen (buf, "r");
  1319.       if (f == 0)
  1320.       {
  1321.      fprintf (stderr, "%s: couldn't run ps\n", O_programname);
  1322.      exit (1);
  1323.       }
  1324.  
  1325.       fgets (buf, LINMAX, f);
  1326.       for (;;)
  1327.       {
  1328.      fgets (buf, LINMAX, f);
  1329.      if (feof (f)) break;
  1330. #ifdef ultrix
  1331.      zap_flags (buf);
  1332. #endif
  1333.      pid = atoi (&buf[PID]);
  1334.      for (i = 0; i < maxproc; i++)
  1335.         if (proc[i]->pid == pid)
  1336.            proc[i]->cputime -= to_seconds (&buf[CPU]);
  1337.       }
  1338.       pclose (f);
  1339.    }
  1340. #endif
  1341.  
  1342.    sort ();
  1343.    (void) maxlev (0, 0, 0);
  1344.  
  1345.    for (i = 0; i < maxproc; i++)
  1346.       print_pp (i, 0);
  1347.    return 0;
  1348. }
  1349. @EOF
  1350.  
  1351. chmod 664 tps.c
  1352.  
  1353. exit 0
  1354.  
  1355.