BSD 4_1_snap release
[unix-history] / usr / src / cmd / sa.c
index a55a0bb..3a44d77 100644 (file)
@@ -1,13 +1,31 @@
-static char *sccsid = "@(#)sa.c        4.1 (Berkeley) 10/1/80";
+static char *sccsid = "@(#)sa.c        4.2 (Berkeley) 81/02/28";
+
+/*
+ *     Extensive modifications to internal data structures
+ *     to allow arbitrary number of different commands and users added.
+ *     
+ *     Also allowed the digit option on the -v flag (interactive
+ *     threshold compress) to be a digit string, so one can
+ *     set the threshold > 9.
+ *
+ *     Also added the -f flag, to force no interactive threshold
+ *     compression with the -v flag.
+ *
+ *     Robert Henry
+ *     UC Berkeley
+ *     31jan81
+ */
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/acct.h>
 #include <signal.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/acct.h>
 #include <signal.h>
+#include <utmp.h>
+#include <pwd.h>
 
 /* interpret command time accounting */
 
 
 /* interpret command time accounting */
 
-#define        size    2500
 #define        NC      sizeof(acctbuf.ac_comm)
 #define        NC      sizeof(acctbuf.ac_comm)
+
 struct acct acctbuf;
 int    lflg;
 int    cflg;
 struct acct acctbuf;
 int    lflg;
 int    cflg;
@@ -23,20 +41,43 @@ int rflg;
 int    oflg;
 int    tflg;
 int    vflg;
 int    oflg;
 int    tflg;
 int    vflg;
+int    fflg;
 int    uflg;
 int    uflg;
-int    thres   = 1;
+int    thres;
 int    sflg;
 int    bflg;
 int    mflg;
 
 int    sflg;
 int    bflg;
 int    mflg;
 
+struct utmp    utmp;
+#define        NAMELG  (sizeof(utmp.ut_name)+1)
+
+struct         Olduser{
+       int     Us_cnt;
+       double  Us_ctime;
+       double  Us_io;
+       double  Us_imem;
+};
+       
 struct user {
 struct user {
-       int     us_cnt;
-       double  us_ctime;
-       double  us_io;
-       double  us_imem;
-} user[1000];
+       char    name[NC];               /* this is <\001><user id><\000> */
+       struct  Olduser oldu;
+       char    us_name[NAMELG];
+};
+#define        us_cnt          oldu.Us_cnt
+#define        us_ctime        oldu.Us_ctime
+#define        us_io           oldu.Us_io
+#define        us_imem         oldu.Us_imem
 
 
-struct tab {
+/*
+ *     We protect ourselves from preposterous user id's by looking
+ *     through the passwd file for the highest uid allocated, and
+ *     then adding 10 to that.
+ *     This prevents the user structure from growing too large.
+ */
+#define        USERSLOP        10
+int    maxuser;                /* highest uid from /etc/passwd, + 10 for slop*/
+
+struct process {
        char    name[NC];
        int     count;
        double  realt;
        char    name[NC];
        int     count;
        double  realt;
@@ -44,29 +85,206 @@ struct     tab {
        double  syst;
        double  imem;
        double  io;
        double  syst;
        double  imem;
        double  io;
-} tab[size];
+};
+
+union  Tab{
+       struct  process p;
+       struct  user    u;
+};
+
+typedef        union Tab cell;
+
+int    (*cmp)();       /* compares 2 cells; set to appropriate func */
+cell   *enter();
+struct user *finduser();
+struct user *wasuser();
+
+/*
+ *     Table elements are keyed by the name of the file exec'ed.
+ *     Because on large systems, many files can be exec'ed,
+ *     a static table size may grow to be too large.
+ *
+ *     Table elements are allocated in chunks dynamically, linked
+ *     together so that they may be retrieved sequentially.
+ *
+ *     An index into the table structure is provided by hashing through
+ *     a seperate hash table.
+ *     The hash table is segmented, and dynamically extendable.
+ *     Realize that the hash table and accounting information is kept
+ *     in different segments!
+ *
+ *     We have a linked list of hash table segments; within each
+ *     segment we use a quadratic rehash that touches no more than 1/2
+ *     of the buckets in the hash table when probing.
+ *     If the probe does not find the desired symbol, it moves to the
+ *     next segment, or allocates a new segment.
+ *
+ *     Hash table segments are kept on the linked list with the first
+ *     segment always first (that will probably contain the
+ *     most frequently executed commands) and
+ *     the last added segment immediately after the first segment,
+ *     to hopefully gain something by locality of reference.
+ *
+ *     We store the per user information in the same structure as
+ *     the per exec'ed file information.  This allows us to use the
+ *     same managers for both, as the number of user id's may be very
+ *     large.
+ *     User information is keyed by the first character in the name
+ *     being a '\001', followed by four bytes of (long extended)
+ *     user id number, followed by a null byte.
+ *     The actual user names are kept in a seperate field of the
+ *     user structure, and is filled in upon demand later.
+ *     Iteration through all users by low user id to high user id
+ *     is done by just probing the table, which is gross.
+ */
+#define        USERKEY '\001'
+#define        ISPROCESS(tp)   (tp->p.name[0] && (tp->p.name[0] != USERKEY))
+#define        ISUSER(tp)      (tp->p.name[0] && (tp->p.name[0] == USERKEY))
+
+#define        TABDALLOP       500
+struct         allocbox{
+       struct  allocbox        *nextalloc;
+       cell                    tabslots[TABDALLOP];
+};
+
+struct allocbox        *allochead;     /*head of chunk list*/
+struct allocbox        *alloctail;     /*tail*/
+struct allocbox        *newbox;        /*for creating a new chunk*/
+cell                   *nexttab;       /*next table element that is free*/
+int                    tabsleft;       /*slots left in current chunk*/
+int                    ntabs;
+/*
+ *     Iterate through all symbols in the symbol table in declaration
+ *     order.
+ *     struct  allocbox        *allocwalk;
+ *     cell                    *sp, *ub;
+ *
+ *     sp points to the desired item, allocwalk and ub are there
+ *     to make the iteration go.
+ */
+
+#define DECLITERATE(allocwalk, walkpointer, ubpointer) \
+       for(allocwalk = allochead; \
+           allocwalk != 0; \
+           allocwalk = allocwalk->nextalloc) \
+               for (walkpointer = &allocwalk->tabslots[0],\
+                       ubpointer = &allocwalk->tabslots[TABDALLOP], \
+                       ubpointer = ubpointer > ( (cell *)alloctail) \
+                                ? nexttab : ubpointer ;\
+                    walkpointer < ubpointer; \
+                    walkpointer++ )
+
+#define TABCHUNKS(allocwalk, tabptr, size) \
+       for (allocwalk = allochead; \
+           allocwalk != 0; \
+           allocwalk = allocwalk->nextalloc) \
+           if ( \
+               (tabptr = &allocwalk->tabslots[0]), \
+               (size = \
+                (   (&allocwalk->tabslots[TABDALLOP]) \
+                  > ((cell *)alloctail) \
+                ) \
+                  ? (nexttab - tabptr) : TABDALLOP \
+               ), \
+               1 \
+           )
+#define        PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
+       DECLITERATE(allocwalk, walkpointer, ubpointer) \
+       if (ISPROCESS(walkpointer))
+
+#define        USERITERATE(allocwalk, walkpointer, ubpointer) \
+       DECLITERATE(allocwalk, walkpointer, ubpointer) \
+       if (ISUSER(walkpointer))
+/*
+ *     When we have to sort the segmented accounting table, we
+ *     create a vector of sorted queues that is merged
+ *     to sort the entire accounting table.
+ */
+struct chunkdesc   {
+       cell    *chunk_tp;
+       int     chunk_n;
+};
+
+/*
+ *     Hash table segments and manager
+ */
+#define        NHASH   1103
+struct hashdallop {
+       int     h_nused;
+       struct  hashdallop      *h_next;
+       cell            *h_tab[NHASH];
+};
+struct hashdallop      *htab;  /* head of the list */
+int    htabinstall;            /* install the symbol */
 
 double treal;
 double tcpu;
 double tsys;
 double tio;
 double timem;
 
 double treal;
 double tcpu;
 double tsys;
 double tio;
 double timem;
-int    junkp = -1;
+cell   *junkp;
 char   *sname;
 double ncom;
 time_t expand();
 char   *getname();
 
 char   *sname;
 double ncom;
 time_t expand();
 char   *getname();
 
+/*
+ *     usracct saves records of type Olduser.
+ *     There is one record for every possible uid less than
+ *     the largest uid seen in the previous usracct or in savacct.
+ *     uid's that had no activity correspond to zero filled slots;
+ *     thus one can index the file and get the user record out.
+ *     It would be better to save only user information for users
+ *     that the system knows about to save space, but that is not
+ *     upward compatabile with the old system.
+ *
+ *     In the old version of sa, uid's greater than 999 were not handled
+ *     properly; this system will do that.
+ */
+
+#ifdef DEBUG
+#define        USRACCT "./usracct"
+#define        SAVACCT "./savacct"
+#define        ACCT    "./acct"
+#else
+#define        USRACCT "/usr/adm/usracct"
+#define        SAVACCT "/usr/adm/savacct"
+#define        ACCT    "/usr/adm/acct"
+#endif DEBUG
+\f
+
+int    cellcmp();
+cell   *junkp = 0;
+/*
+ *     The threshold is built up from digits in the argv ;
+ *     eg, -v1s0u1
+ *     will build a value of thres of 101.
+ *
+ *     If the threshold is zero after processing argv, it is set to 1
+ */
+int    thres = 0;      
+int    htabinstall = 1;
+int    maxuser = -1;
+int    (*cmp)();
+
 main(argc, argv)
 char **argv;
 {
        FILE *ff;
 main(argc, argv)
 char **argv;
 {
        FILE *ff;
-       int i, j, k;
-       int (*cmp)();
-       extern tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
-       extern double sum();
-       double ft;
-
+       int i, j;
+       extern  tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
+       extern  double sum();
+       double  ft;
+       register        struct  allocbox        *allocwalk;
+       register        cell    *tp, *ub;
+       int     size;
+       int     nchunks;
+       struct  chunkdesc       *chunkvector;
+       int     smallest;
+
+       maxuser = USERSLOP + getmaxuid();
+
+       tabinit();
        cmp = tcmp;
        if (argc>1)
        if (argv[1][0]=='-') {
        cmp = tcmp;
        if (argc>1)
        if (argv[1][0]=='-') {
@@ -152,13 +370,17 @@ char **argv;
                case '7':
                case '8':
                case '9':
                case '7':
                case '8':
                case '9':
-                       thres = argv[0][i]-'0';
+                       thres = thres * 10 + (argv[0][i]-'0');
                        break;
 
                case 'v':
                        vflg++;
                        break;
 
                        break;
 
                case 'v':
                        vflg++;
                        break;
 
+               case 'f':
+                       fflg++; /* force v option; no tty interaction */
+                       break;
+
                case 'u':
                        uflg++;
                        break;
                case 'u':
                        uflg++;
                        break;
@@ -168,10 +390,12 @@ char **argv;
                        break;
                }
        }
                        break;
                }
        }
+       if (thres == 0)
+               thres = 1;
        if (iflg==0)
                init();
        if (argc<2)
        if (iflg==0)
                init();
        if (argc<2)
-               doacct("/usr/adm/acct");
+               doacct(ACCT);
        else while (--argc)
                doacct(*++argv);
        if (uflg) {
        else while (--argc)
                doacct(*++argv);
        if (uflg) {
@@ -186,80 +410,130 @@ char **argv;
        if (vflg)
                strip();
        if(!aflg)
        if (vflg)
                strip();
        if(!aflg)
-       for (i=0; i<size; i++)
-       if (tab[i].name[0]) {
+       PROCESSITERATE(allocwalk, tp, ub){
                for(j=0; j<NC; j++)
                for(j=0; j<NC; j++)
-                       if(tab[i].name[j] == '?')
+                       if(tp->p.name[j] == '?')
                                goto yes;
                                goto yes;
-               if(tab[i].count != 1)
+               if(tp->p.count != 1)
                        continue;
        yes:
                        continue;
        yes:
-               if(junkp == -1)
+               if(junkp == 0)
                        junkp = enter("***other");
                        junkp = enter("***other");
-               tab[junkp].count += tab[i].count;
-               tab[junkp].realt += tab[i].realt;
-               tab[junkp].cput += tab[i].cput;
-               tab[junkp].syst += tab[i].syst;
-               tab[junkp].imem += tab[i].imem;
-               tab[junkp].io += tab[i].io;
-               tab[i].name[0] = 0;
-       }
-       for(i=k=0; i<size; i++)
-       if(tab[i].name[0]) {
-               tab[k] = tab[i];
-               k++;
+               junkp->p.count += tp->p.count;
+               junkp->p.realt += tp->p.realt;
+               junkp->p.cput += tp->p.cput;
+               junkp->p.syst += tp->p.syst;
+               junkp->p.imem += tp->p.imem;
+               junkp->p.io += tp->p.io;
+               tp->p.name[0] = 0;
        }
        if (sflg) {
                signal(SIGINT, SIG_IGN);
        }
        if (sflg) {
                signal(SIGINT, SIG_IGN);
-               if ((ff = fopen("/usr/adm/usracct", "w")) != NULL) {
-                       fwrite((char *)user, sizeof(user), 1, ff);
-                       fclose(ff);
+               if ((ff = fopen(USRACCT, "w")) != NULL) {
+                       static  struct  user ZeroUser = {0};
+                       struct  user    *up;
+                       int     uid;
+                       /*
+                        *      Write out just enough user slots,
+                        *      filling with zero slots for users that
+                        *      weren't found.
+                        *      The file can be indexed directly by uid
+                        *      to get the correct record.
+                        */
+                       for (uid = 0; uid < maxuser; uid++){
+                               if ( (up = wasuser(uid)) != 0)
+                                       fwrite((char *)&(up->oldu),
+                                               sizeof(struct Olduser),1,ff);
+                               else
+                                       fwrite((char *)&(ZeroUser.oldu),
+                                               sizeof(struct Olduser),1,ff);
+                       }
                }
                }
-               if ((ff = fopen("/usr/adm/savacct", "w")) == NULL) {
+               if ((ff = fopen(SAVACCT, "w")) == NULL) {
                        printf("Can't save\n");
                        exit(0);
                }
                        printf("Can't save\n");
                        exit(0);
                }
-               fwrite((char *)tab, sizeof(tab[0]), k, ff);
+               PROCESSITERATE(allocwalk, tp, ub)
+                       fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
                fclose(ff);
                fclose(ff);
-               creat("/usr/adm/acct", 0644);
+               creat(sname, 0644);
                signal(SIGINT, SIG_DFL);
        }
 /*
  * sort and print
  */
                signal(SIGINT, SIG_DFL);
        }
 /*
  * sort and print
  */
-
        if (mflg) {
                printmoney();
                exit(0);
        }
        if (mflg) {
                printmoney();
                exit(0);
        }
-       qsort(tab, k, sizeof(tab[0]), cmp);
        column(ncom, treal, tcpu, tsys, timem, tio);
        printf("\n");
        column(ncom, treal, tcpu, tsys, timem, tio);
        printf("\n");
-       for (i=0; i<k; i++)
-       if (tab[i].name[0]) {
-               ft = tab[i].count;
-               column(ft, tab[i].realt, tab[i].cput, tab[i].syst, tab[i].imem, tab[i].io);
-               printf("   %.14s\n", tab[i].name);
+
+       /*
+        *      the fragmented table is sorted by sorting each fragment
+        *      and then merging.
+        */
+       nchunks = 0;
+       TABCHUNKS(allocwalk, tp, size){
+               qsort(tp, size, sizeof(cell), cellcmp);
+               nchunks ++;
+       }
+       chunkvector = (struct chunkdesc *)calloc(nchunks,
+               sizeof(struct chunkdesc));
+       nchunks = 0;
+       TABCHUNKS(allocwalk, tp, size){
+               chunkvector[nchunks].chunk_tp = tp;
+               chunkvector[nchunks].chunk_n = size;
+               nchunks++;
        }
        }
+       for(; nchunks; ){
+               /*
+                *      Find the smallest element at the head of the queues.
+                */
+               smallest = 0;
+               for (i = 1; i < nchunks; i++){
+                       if (cellcmp(chunkvector[i].chunk_tp,
+                               chunkvector[smallest].chunk_tp) < 0)
+                                       smallest = i;
+               }
+               tp = chunkvector[smallest].chunk_tp++;
+               /*
+                *      If this queue is drained, drop the chunk count,
+                *      and readjust the queues.
+                */
+               if (--chunkvector[smallest].chunk_n == 0){
+                       nchunks--;
+                       for (i = smallest; i < nchunks; i++)
+                               chunkvector[i] = chunkvector[i+1];
+               }
+               if (ISPROCESS(tp)){
+                       ft = tp->p.count;
+                       column(ft, tp->p.realt, tp->p.cput,
+                               tp->p.syst, tp->p.imem, tp->p.io);
+                       printf("   %.14s\n", tp->p.name);
+               }
+       }       /* iterate to merge the lists */
 }
 
 printmoney()
 {
        register i;
 }
 
 printmoney()
 {
        register i;
-       char buf[128];
        register char *cp;
        register char *cp;
-
-       for (i=0; i<sizeof(user)/sizeof(user[0]); i++) {
-               if (user[i].us_cnt && user[i].us_ctime) {
-                       cp = getname(i);
-                       if (cp == 0)
-                               printf("%-8d", i);
-                       else 
-                               printf("%-8s", cp);
-                       printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
-                           user[i].us_cnt, user[i].us_ctime/60,
-                           user[i].us_io,
-                           user[i].us_imem / (60 * 2));
+       register        struct user     *up;
+
+       getnames();             /* fetches all of the names! */
+       for (i = 0; i < maxuser; i++) {
+               if ( (up = wasuser(i)) != 0){
+                       if (up->us_cnt) {
+                               if (up->us_name[0])
+                                       printf("%-8s", up->us_name);
+                               else 
+                                       printf("%-8d", i);
+                               printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
+                                       up->us_cnt, up->us_ctime/60,
+                                       up->us_io,
+                                       up->us_imem / (60 * 2));
+                       }
                }
        }
 }
                }
        }
 }
@@ -312,12 +586,16 @@ char *cp;
 doacct(f)
 char *f;
 {
 doacct(f)
 char *f;
 {
-       int i;
        FILE *ff;
        long x, y, z;
        struct acct fbuf;
        register char *cp;
        register int c;
        FILE *ff;
        long x, y, z;
        struct acct fbuf;
        register char *cp;
        register int c;
+       register        struct  user    *up;
+       register        cell    *tp;
+#ifdef DEBUG
+       int     nrecords = 0;
+#endif DEBUG
 
        if (sflg && sname) {
                printf("Only 1 file with -s\n");
 
        if (sflg && sname) {
                printf("Only 1 file with -s\n");
@@ -330,6 +608,11 @@ char *f;
                return;
        }
        while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
                return;
        }
        while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
+#ifdef DEBUG
+               if (++nrecords % 1000 == 0)
+                       printf("Input record from %s number %d\n",
+                               f, nrecords);
+#endif DEBUG
                if (fbuf.ac_comm[0]==0) {
                        fbuf.ac_comm[0] = '?';
                }
                if (fbuf.ac_comm[0]==0) {
                        fbuf.ac_comm[0] = '?';
                }
@@ -355,50 +638,68 @@ char *f;
                            fbuf.ac_comm);
                        continue;
                }
                            fbuf.ac_comm);
                        continue;
                }
-               c = fbuf.ac_uid;
-               user[c].us_cnt++;
-               user[c].us_ctime += x/60.;
-               user[c].us_imem += x * y;
-               user[c].us_io += z;
+               up = finduser(fbuf.ac_uid);
+               if (up == 0)
+                       continue;       /* preposterous user id */
+               up->us_cnt++;
+               up->us_ctime += x/60.;
+               up->us_imem += x * y;
+               up->us_io += z;
                ncom += 1.0;
                ncom += 1.0;
-               i = enter(fbuf.ac_comm);
-               tab[i].imem += x * y;
+
+               tp = enter(fbuf.ac_comm);
+               tp->p.imem += x * y;
                timem += x * y;
                timem += x * y;
-               tab[i].count++;
+               tp->p.count++;
                x = expand(fbuf.ac_etime)*60;
                x = expand(fbuf.ac_etime)*60;
-               tab[i].realt += x;
+               tp->p.realt += x;
                treal += x;
                x = expand(fbuf.ac_utime);
                treal += x;
                x = expand(fbuf.ac_utime);
-               tab[i].cput += x;
+               tp->p.cput += x;
                tcpu += x;
                x = expand(fbuf.ac_stime);
                tcpu += x;
                x = expand(fbuf.ac_stime);
-               tab[i].syst += x;
+               tp->p.syst += x;
                tsys += x;
                tsys += x;
-               tab[i].io += z;
+               tp->p.io += z;
                tio += z;
        }
        fclose(ff);
 }
 
                tio += z;
        }
        fclose(ff);
 }
 
+/*
+ *     Generalized cell compare routine, to cast out users
+ */
+cellcmp(p1, p2)
+       cell    *p1, *p2;
+{
+       if (ISPROCESS(p1)){
+               if (ISPROCESS(p2))
+                       return(cmp(p1, p2));
+               return(-1);
+       }
+       if (ISPROCESS(p2))
+               return(1);
+       return(0);
+}
 ncmp(p1, p2)
 ncmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
 
 {
 
-       if(p1->count == p2->count)
+       if(p1->p.count == p2->p.count)
                return(tcmp(p1, p2));
        if(rflg)
                return(tcmp(p1, p2));
        if(rflg)
-               return(p1->count - p2->count);
-       return(p2->count - p1->count);
+               return(p1->p.count - p2->p.count);
+       return(p2->p.count - p1->p.count);
 }
 
 bcmp(p1, p2)
 }
 
 bcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
        double f1, f2;
        double sum();
 
 {
        double f1, f2;
        double sum();
 
-       f1 = sum(p1)/p1->count;
-       f2 = sum(p2)/p2->count;
+       f1 = sum(p1)/p1->p.count;
+       f2 = sum(p2)/p2->p.count;
        if(f1 < f2) {
                if(rflg)
                        return(-1);
        if(f1 < f2) {
                if(rflg)
                        return(-1);
@@ -413,15 +714,15 @@ struct tab *p1, *p2;
 }
 
 Kcmp(p1, p2)
 }
 
 Kcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
 
 {
 
-       if (p1->imem < p2->imem) {
+       if (p1->p.imem < p2->p.imem) {
                if(rflg)
                        return(-1);
                return(1);
        }
                if(rflg)
                        return(-1);
                return(1);
        }
-       if (p1->imem > p2->imem) {
+       if (p1->p.imem > p2->p.imem) {
                if(rflg)
                        return(1);
                return(-1);
                if(rflg)
                        return(1);
                return(-1);
@@ -430,12 +731,12 @@ struct tab *p1, *p2;
 }
 
 kcmp(p1, p2)
 }
 
 kcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
        double a1, a2;
 
 {
        double a1, a2;
 
-       a1 = p1->imem / ((p1->cput+p1->syst)?(p1->cput+p1->syst):1);
-       a2 = p2->imem / ((p2->cput+p2->syst)?(p2->cput+p2->syst):1);
+       a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
+       a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
        if (a1 < a2) {
                if(rflg)
                        return(-1);
        if (a1 < a2) {
                if(rflg)
                        return(-1);
@@ -450,12 +751,12 @@ struct tab *p1, *p2;
 }
 
 dcmp(p1, p2)
 }
 
 dcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
        double a1, a2;
 
 {
        double a1, a2;
 
-       a1 = p1->io / (p1->count?p1->count:1);
-       a2 = p2->io / (p2->count?p2->count:1);
+       a1 = p1->p.io / (p1->p.count?p1->p.count:1);
+       a2 = p2->p.io / (p2->p.count?p2->p.count:1);
        if (a1 < a2) {
                if(rflg)
                        return(-1);
        if (a1 < a2) {
                if(rflg)
                        return(-1);
@@ -470,15 +771,15 @@ struct tab *p1, *p2;
 }
 
 Dcmp(p1, p2)
 }
 
 Dcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
 
 {
 
-       if (p1->io < p2->io) {
+       if (p1->p.io < p2->p.io) {
                if(rflg)
                        return(-1);
                return(1);
        }
                if(rflg)
                        return(-1);
                return(1);
        }
-       if (p1->io > p2->io) {
+       if (p1->p.io > p2->p.io) {
                if(rflg)
                        return(1);
                return(-1);
                if(rflg)
                        return(1);
                return(-1);
@@ -487,7 +788,7 @@ struct tab *p1, *p2;
 }
 
 tcmp(p1, p2)
 }
 
 tcmp(p1, p2)
-struct tab *p1, *p2;
+cell *p1, *p2;
 {
        extern double sum();
        double f1, f2;
 {
        extern double sum();
        double f1, f2;
@@ -508,93 +809,83 @@ struct tab *p1, *p2;
 }
 
 double sum(p)
 }
 
 double sum(p)
-struct tab *p;
+cell *p;
 {
 
 {
 
-       if(p->name[0] == 0)
+       if(p->p.name[0] == 0)
                return(0.0);
                return(0.0);
-       return(
-               p->cput+
-               p->syst);
+       return( p->p.cput + p->p.syst);
 }
 
 init()
 {
 }
 
 init()
 {
-       struct tab tbuf;
-       int i;
+       struct  user    userbuf;
+       struct  process tbuf;
+       register        cell    *tp;
+       register        struct  user    *up;
+       int             uid;
        FILE *f;
 
        FILE *f;
 
-       if ((f = fopen("/usr/adm/savacct", "r")) == NULL)
+       if ((f = fopen(SAVACCT, "r")) == NULL)
                goto gshm;
                goto gshm;
-       while (fread((char *)&tbuf, sizeof(tbuf), 1, f) == 1) {
-               i = enter(tbuf.name);
+       while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
+               tp = enter(tbuf.name);
                ncom += tbuf.count;
                ncom += tbuf.count;
-               tab[i].count = tbuf.count;
+               tp->p.count = tbuf.count;
                treal += tbuf.realt;
                treal += tbuf.realt;
-               tab[i].realt = tbuf.realt;
+               tp->p.realt = tbuf.realt;
                tcpu += tbuf.cput;
                tcpu += tbuf.cput;
-               tab[i].cput = tbuf.cput;
+               tp->p.cput = tbuf.cput;
                tsys += tbuf.syst;
                tsys += tbuf.syst;
-               tab[i].syst = tbuf.syst;
+               tp->p.syst = tbuf.syst;
                tio += tbuf.io;
                tio += tbuf.io;
-               tab[i].io = tbuf.io;
+               tp->p.io = tbuf.io;
                timem += tbuf.imem;
                timem += tbuf.imem;
-               tab[i].imem = tbuf.imem;
+               tp->p.imem = tbuf.imem;
        }
        fclose(f);
  gshm:
        }
        fclose(f);
  gshm:
-       if ((f = fopen("/usr/adm/usracct", "r")) == NULL)
+       if ((f = fopen(USRACCT, "r")) == NULL)
                return;
                return;
-       fread((char *)user, sizeof(user), 1, f);
+       for(uid = 0;
+           fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
+           uid++){
+               if (userbuf.us_cnt){
+                       up = finduser(uid);
+                       if (up == 0)
+                               continue;       /* preposterous user id */
+                       up->oldu = userbuf.oldu;
+               }
+       }
        fclose(f);
 }
 
        fclose(f);
 }
 
-enter(np)
-char *np;
-{
-       int i, j;
-
-       for (i=j=0; i<NC; i++) {
-               if (np[i]==0)
-                       j = i;
-               if (j)
-                       np[i] = 0;
-       }
-       for (i=j=0; j<NC; j++) {
-               i = i*7 + np[j];
-       }
-       if (i < 0)
-               i = -i;
-       for (i%=size; tab[i].name[0]; i = (i+1)%size) {
-               for (j=0; j<NC; j++)
-                       if (tab[i].name[j]!=np[j])
-                               goto no;
-               goto yes;
-       no:;
-       }
-       for (j=0; j<NC; j++)
-               tab[i].name[j] = np[j];
-yes:
-       return(i);
-}
-
 strip()
 {
 strip()
 {
-       int i, j, c;
-
-       j = enter("**junk**");
-       for (i = 0; i<size; i++) {
-               if (tab[i].name[0] && tab[i].count<=thres) {
-                       printf("%.14s--", tab[i].name);
-                       if ((c=getchar())=='y') {
-                               tab[i].name[0] = '\0';
-                               tab[j].count += tab[i].count;
-                               tab[j].realt += tab[i].realt;
-                               tab[j].cput += tab[i].cput;
-                               tab[j].syst += tab[i].syst;
+       int     c;
+       register        struct  allocbox        *allocwalk;
+       register        cell    *tp, *ub, *junkp;
+
+       if (fflg)
+               printf("Categorizing commands used %d times or fewer as **junk**\n",
+                       thres);
+       junkp = enter("**junk**");
+       PROCESSITERATE(allocwalk, tp, ub){
+               if (tp->p.name[0] && tp->p.count <= thres) {
+                       if (!fflg)
+                               printf("%.14s--", tp->p.name);
+                       if (fflg || ((c=getchar())=='y')) {
+                               tp->p.name[0] = '\0';
+                               junkp->p.count += tp->p.count;
+                               junkp->p.realt += tp->p.realt;
+                               junkp->p.cput += tp->p.cput;
+                               junkp->p.syst += tp->p.syst;
+                               junkp->p.imem += tp->p.imem;
+                               junkp->p.io += tp->p.io;
                        }
                        }
-                       while (c && c!='\n')
-                               c = getchar();
+                       if (!fflg)
+                               while (c && c!='\n')
+                                       c = getchar();
                }
        }
 }
                }
        }
 }
@@ -614,38 +905,197 @@ unsigned t;
        return(nt);
 }
 
        return(nt);
 }
 
-#include <utmp.h>
-#include <pwd.h>
-
-struct utmp utmp;
-#define        NMAX    sizeof (utmp.ut_name)
-#define        NUID    2048
+static char    UserKey[NAMELG + 2];
+char *makekey(uid)
+       int     uid;
+{
+       sprintf(UserKey+1, "%04x", uid);
+       UserKey[0] = USERKEY;
+       return(UserKey);
+}
 
 
-char   names[NUID][NMAX+1];
+struct user *wasuser(uid)
+       int     uid;
+{
+       struct  user    *tp;
+       htabinstall = 0;
+       tp = finduser(uid);
+       htabinstall = 1;
+       return(tp);
+}
+/*
+ *     Only call this if you really want to insert it in the table!
+ */
+struct user *finduser(uid)
+       int     uid;
+{
+       if (uid > maxuser){
+               fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
+               return(0);
+       } else {
+               return((struct user*)enter(makekey(uid)));
+       }
+}
 
 
-char *
-getname(uid)
+/*
+ *     Set the names of all users in the password file.
+ *     We will later not print those that didn't do anything.
+ */
+getnames()
 {
 {
+       register        struct user     *tp;
        register struct passwd *pw;
        register struct passwd *pw;
-       static init;
        struct passwd *getpwent();
 
        struct passwd *getpwent();
 
-       if (names[uid][0])
-               return (&names[uid][0]);
-       if (init == 2)
-               return (0);
-       if (init == 0)
-               setpwent(), init = 1;
-       while (pw = getpwent()) {
-               if (pw->pw_uid >= NUID)
-                       continue;
-               if (names[pw->pw_uid][0])
-                       continue;
-               strncpy(names[pw->pw_uid], pw->pw_name, NMAX);
-               if (pw->pw_uid == uid)
-                       return (&names[uid][0]);
+       setpwent();
+       while (pw = getpwent()){
+               if ( (tp = wasuser(pw->pw_uid)) != 0)
+                       strncpy(tp->us_name, pw->pw_name, NAMELG);
        }
        }
-       init = 2;
        endpwent();
        endpwent();
-       return (0);
 }
 }
+
+int getmaxuid()
+{
+       register        struct  user    *tp;
+       register        struct  passwd  *pw;
+       struct          passwd  *getpwent();
+       int             maxuid = -1;
+
+       setpwent();
+       while(pw = getpwent()){
+               if (pw->pw_uid > maxuid)
+                       maxuid = pw->pw_uid;
+       }
+       endpwent();
+       return(maxuid);
+}
+
+tabinit()
+{
+       allochead = 0;
+       alloctail = 0;
+       nexttab = 0;
+       tabsleft = 0;
+       htab = 0;
+       ntabs = 0;
+       htaballoc();            /* get the first part of the hash table */
+}
+
+#define ALLOCQTY       sizeof (struct allocbox)
+cell *taballoc()
+{
+       if (tabsleft == 0){
+               newbox = (struct allocbox *)calloc(1, ALLOCQTY);
+               tabsleft = TABDALLOP;
+               nexttab = &newbox->tabslots[0];
+               if (alloctail == 0){
+                       allochead = alloctail = newbox;
+               } else {
+                       alloctail->nextalloc = newbox;
+                       alloctail = newbox;
+               }
+       }
+       --tabsleft;
+       ++ntabs;
+#ifdef DEBUG
+       if (ntabs % 100 == 0)
+               printf("##Accounting table slot # %d\n", ntabs);
+#endif DEBUG
+       return(nexttab++);
+}
+
+htaballoc()
+{
+       register        struct  hashdallop      *new;
+#ifdef DEBUG
+       static  int     ntables = 0;
+       printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
+#endif DEBUG
+       new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
+       if (htab == 0)
+               htab = new;
+       else {          /* add AFTER the 1st slot */
+               new->h_next = htab->h_next;
+               htab->h_next = new;
+       }
+}
+
+#define        HASHCLOGGED     (NHASH / 2)
+/*
+ *     Lookup a symbol passed in as the argument.
+ *
+ *     We take pains to avoid function calls; this function
+ *     is called quite frequently, and the calling overhead
+ *     contributes significantly to the overall execution speed of sa.
+ */
+cell *enter(name)
+       char    *name;  
+{
+       static   int            initialprobe;
+       register cell           **hp;
+       register char           *from;
+       register char           *to;
+       register        int     len;
+       register        int     nprobes;
+       static   struct hashdallop *hdallop;
+       static   cell           **emptyslot;
+       static   struct hashdallop *emptyhd;
+       static   cell           **hp_ub;
+
+       emptyslot = 0;
+       for (nprobes = 0, from = name, len = 0;
+            *from && len < NC;
+            nprobes <<= 2, nprobes += *from++, len++)
+               continue;
+       nprobes += from[-1] << 5;
+       nprobes %= NHASH;
+       if (nprobes < 0)
+               nprobes += NHASH;
+
+       initialprobe = nprobes;
+       for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
+               for (hp = &(hdallop->h_tab[initialprobe]),
+                               nprobes = 1,
+                               hp_ub = &(hdallop->h_tab[NHASH]);
+                    (*hp) && (nprobes < NHASH);
+                               hp += nprobes,
+                               hp -= (hp >= hp_ub) ? NHASH:0,
+                               nprobes += 2)
+               {
+                       from = name;
+                       to = (*hp)->p.name;
+
+                       for (len = 0; (len<NC) && *from; len++)
+                               if (*from++ != *to++)
+                                       goto nextprobe;
+                       if (len >= NC)          /*both are maximal length*/
+                               return(*hp);
+                       if (*to == 0)           /*assert *from == 0*/
+                               return(*hp);
+       nextprobe: ;
+               }
+               if (*hp == 0 && emptyslot == 0 &&
+                   hdallop->h_nused < HASHCLOGGED) {
+                       emptyslot = hp;
+                       emptyhd = hdallop;
+               }
+       }
+       if (emptyslot == 0) {
+               htaballoc();
+               hdallop = htab->h_next;         /* aren't we smart! */
+               hp = &hdallop->h_tab[initialprobe];
+       } else {
+               hdallop = emptyhd;
+               hp = emptyslot;
+       }
+       if (htabinstall){
+               *hp = taballoc();
+               hdallop->h_nused++;
+               for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
+                       if ((*to++ = *from++) == '\0')
+                               break;
+               return(*hp);
+       }
+       return(0);
+}      /*end of lookup*/