xref: /original-bsd/usr.sbin/sa/sa.c (revision 8251a00e)
1 #ifndef lint
2 static	char *sccsid = "@(#)sa.c	4.4 (Berkeley) 83/08/11";
3 #endif
4 
5 /*
6  *	Extensive modifications to internal data structures
7  *	to allow arbitrary number of different commands and users added.
8  *
9  *	Also allowed the digit option on the -v flag (interactive
10  *	threshold compress) to be a digit string, so one can
11  *	set the threshold > 9.
12  *
13  *	Also added the -f flag, to force no interactive threshold
14  *	compression with the -v flag.
15  *
16  *	Robert Henry
17  *	UC Berkeley
18  *	31jan81
19  */
20 #include <stdio.h>
21 #include <sys/types.h>
22 #include <sys/acct.h>
23 #include <signal.h>
24 #include <utmp.h>
25 #include <pwd.h>
26 
27 /* interpret command time accounting */
28 
29 #define	NC	sizeof(acctbuf.ac_comm)
30 
31 struct acct acctbuf;
32 int	lflg;
33 int	cflg;
34 int	Dflg;
35 int	dflg;
36 int	iflg;
37 int	jflg;
38 int	Kflg;
39 int	kflg;
40 int	nflg;
41 int	aflg;
42 int	rflg;
43 int	oflg;
44 int	tflg;
45 int	vflg;
46 int	fflg;
47 int	uflg;
48 int	thres;
49 int	sflg;
50 int	bflg;
51 int	mflg;
52 
53 struct	utmp	utmp;
54 #define	NAMELG	(sizeof(utmp.ut_name)+1)
55 
56 struct 	Olduser{
57 	int	Us_cnt;
58 	double	Us_ctime;
59 	double	Us_io;
60 	double	Us_imem;
61 };
62 
63 struct	user {
64 	char	name[NC];		/* this is <\001><user id><\000> */
65 	struct	Olduser	oldu;
66 	char	us_name[NAMELG];
67 };
68 #define	us_cnt		oldu.Us_cnt
69 #define	us_ctime	oldu.Us_ctime
70 #define	us_io		oldu.Us_io
71 #define	us_imem		oldu.Us_imem
72 
73 /*
74  *	We protect ourselves from preposterous user id's by looking
75  *	through the passwd file for the highest uid allocated, and
76  *	then adding 10 to that.
77  *	This prevents the user structure from growing too large.
78  */
79 #define	USERSLOP	10
80 int	maxuser;		/* highest uid from /etc/passwd, + 10 for slop*/
81 
82 struct	process {
83 	char	name[NC];
84 	int	count;
85 	double	realt;
86 	double	cput;
87 	double	syst;
88 	double	imem;
89 	double	io;
90 };
91 
92 union	Tab{
93 	struct	process	p;
94 	struct	user	u;
95 };
96 
97 typedef	union Tab cell;
98 
99 int	(*cmp)();	/* compares 2 cells; set to appropriate func */
100 cell	*enter();
101 struct	user *finduser();
102 struct	user *wasuser();
103 
104 /*
105  *	Table elements are keyed by the name of the file exec'ed.
106  *	Because on large systems, many files can be exec'ed,
107  *	a static table size may grow to be too large.
108  *
109  *	Table elements are allocated in chunks dynamically, linked
110  *	together so that they may be retrieved sequentially.
111  *
112  *	An index into the table structure is provided by hashing through
113  *	a seperate hash table.
114  *	The hash table is segmented, and dynamically extendable.
115  *	Realize that the hash table and accounting information is kept
116  *	in different segments!
117  *
118  *	We have a linked list of hash table segments; within each
119  *	segment we use a quadratic rehash that touches no more than 1/2
120  *	of the buckets in the hash table when probing.
121  *	If the probe does not find the desired symbol, it moves to the
122  *	next segment, or allocates a new segment.
123  *
124  *	Hash table segments are kept on the linked list with the first
125  *	segment always first (that will probably contain the
126  *	most frequently executed commands) and
127  *	the last added segment immediately after the first segment,
128  *	to hopefully gain something by locality of reference.
129  *
130  *	We store the per user information in the same structure as
131  *	the per exec'ed file information.  This allows us to use the
132  *	same managers for both, as the number of user id's may be very
133  *	large.
134  *	User information is keyed by the first character in the name
135  *	being a '\001', followed by four bytes of (long extended)
136  *	user id number, followed by a null byte.
137  *	The actual user names are kept in a seperate field of the
138  *	user structure, and is filled in upon demand later.
139  *	Iteration through all users by low user id to high user id
140  *	is done by just probing the table, which is gross.
141  */
142 #define	USERKEY	'\001'
143 #define	ISPROCESS(tp)	(tp->p.name[0] && (tp->p.name[0] != USERKEY))
144 #define	ISUSER(tp)	(tp->p.name[0] && (tp->p.name[0] == USERKEY))
145 
146 #define	TABDALLOP	500
147 struct 	allocbox{
148 	struct	allocbox	*nextalloc;
149 	cell			tabslots[TABDALLOP];
150 };
151 
152 struct	allocbox	*allochead;	/*head of chunk list*/
153 struct	allocbox	*alloctail;	/*tail*/
154 struct	allocbox	*newbox;	/*for creating a new chunk*/
155 cell			*nexttab;	/*next table element that is free*/
156 int			tabsleft;	/*slots left in current chunk*/
157 int			ntabs;
158 /*
159  *	Iterate through all symbols in the symbol table in declaration
160  *	order.
161  *	struct	allocbox	*allocwalk;
162  *	cell			*sp, *ub;
163  *
164  *	sp points to the desired item, allocwalk and ub are there
165  *	to make the iteration go.
166  */
167 
168 #define DECLITERATE(allocwalk, walkpointer, ubpointer) \
169 	for(allocwalk = allochead; \
170 	    allocwalk != 0; \
171 	    allocwalk = allocwalk->nextalloc) \
172 		for (walkpointer = &allocwalk->tabslots[0],\
173 		        ubpointer = &allocwalk->tabslots[TABDALLOP], \
174 		        ubpointer = ubpointer > ( (cell *)alloctail) \
175 				 ? nexttab : ubpointer ;\
176 		     walkpointer < ubpointer; \
177 		     walkpointer++ )
178 
179 #define TABCHUNKS(allocwalk, tabptr, size) \
180 	for (allocwalk = allochead; \
181 	    allocwalk != 0; \
182 	    allocwalk = allocwalk->nextalloc) \
183 	    if ( \
184 		(tabptr = &allocwalk->tabslots[0]), \
185 		(size = \
186 		 (   (&allocwalk->tabslots[TABDALLOP]) \
187 		   > ((cell *)alloctail) \
188 		 ) \
189 		   ? (nexttab - tabptr) : TABDALLOP \
190 		), \
191 		1 \
192 	    )
193 #define	PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
194 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
195 	if (ISPROCESS(walkpointer))
196 
197 #define	USERITERATE(allocwalk, walkpointer, ubpointer) \
198 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
199 	if (ISUSER(walkpointer))
200 /*
201  *	When we have to sort the segmented accounting table, we
202  *	create a vector of sorted queues that is merged
203  *	to sort the entire accounting table.
204  */
205 struct chunkdesc   {
206 	cell	*chunk_tp;
207 	int	chunk_n;
208 };
209 
210 /*
211  *	Hash table segments and manager
212  */
213 #define	NHASH	1103
214 struct hashdallop {
215 	int	h_nused;
216 	struct	hashdallop	*h_next;
217 	cell		*h_tab[NHASH];
218 };
219 struct	hashdallop	*htab;	/* head of the list */
220 int	htabinstall;		/* install the symbol */
221 
222 double	treal;
223 double	tcpu;
224 double	tsys;
225 double	tio;
226 double	timem;
227 cell	*junkp;
228 char	*sname;
229 double	ncom;
230 time_t	expand();
231 char	*getname();
232 
233 /*
234  *	usracct saves records of type Olduser.
235  *	There is one record for every possible uid less than
236  *	the largest uid seen in the previous usracct or in savacct.
237  *	uid's that had no activity correspond to zero filled slots;
238  *	thus one can index the file and get the user record out.
239  *	It would be better to save only user information for users
240  *	that the system knows about to save space, but that is not
241  *	upward compatabile with the old system.
242  *
243  *	In the old version of sa, uid's greater than 999 were not handled
244  *	properly; this system will do that.
245  */
246 
247 #ifdef	DEBUG
248 #define	USRACCT "./usracct"
249 #define	SAVACCT	"./savacct"
250 #define	ACCT	"./acct"
251 #else
252 #define	USRACCT "/usr/adm/usracct"
253 #define	SAVACCT	"/usr/adm/savacct"
254 #define	ACCT	"/usr/adm/acct"
255 #endif	DEBUG
256 
257 
258 int	cellcmp();
259 cell	*junkp = 0;
260 /*
261  *	The threshold is built up from digits in the argv ;
262  *	eg, -v1s0u1
263  *	will build a value of thres of 101.
264  *
265  *	If the threshold is zero after processing argv, it is set to 1
266  */
267 int	thres = 0;
268 int	htabinstall = 1;
269 int	maxuser = -1;
270 int	(*cmp)();
271 
272 extern	tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
273 extern	double sum();
274 
275 main(argc, argv)
276 	char **argv;
277 {
278 	FILE *ff;
279 	double ft;
280 	register struct	allocbox *allocwalk;
281 	register cell *tp, *ub;
282 	int i, j, size, nchunks, smallest;
283 	struct chunkdesc *chunkvector;
284 
285 	maxuser = USERSLOP + getmaxuid();
286 
287 	tabinit();
288 	cmp = tcmp;
289 	if (argc>1)
290 	if (argv[1][0]=='-') {
291 		argv++;
292 		argc--;
293 		for(i=1; argv[0][i]; i++)
294 		switch(argv[0][i]) {
295 
296 		case 'o':
297 			oflg++;
298 			break;
299 
300 		case 'i':
301 			iflg++;
302 			break;
303 
304 		case 'b':
305 			bflg++;
306 			cmp = bcmp;
307 			break;
308 
309 		case 'l':
310 			lflg++;
311 			break;
312 
313 		case 'c':
314 			cflg++;
315 			break;
316 
317 		case 'd':
318 			dflg++;
319 			cmp = dcmp;
320 			break;
321 
322 		case 'D':
323 			Dflg++;
324 			cmp = Dcmp;
325 			break;
326 
327 		case 'j':
328 			jflg++;
329 			break;
330 
331 		case 'k':
332 			kflg++;
333 			cmp = kcmp;
334 			break;
335 
336 		case 'K':
337 			Kflg++;
338 			cmp = Kcmp;
339 			break;
340 
341 		case 'n':
342 			nflg++;
343 			cmp = ncmp;
344 			break;
345 
346 		case 'a':
347 			aflg++;
348 			break;
349 
350 		case 'r':
351 			rflg++;
352 			break;
353 
354 		case 't':
355 			tflg++;
356 			break;
357 
358 		case 's':
359 			sflg++;
360 			aflg++;
361 			break;
362 
363 		case '0':
364 		case '1':
365 		case '2':
366 		case '3':
367 		case '4':
368 		case '5':
369 		case '6':
370 		case '7':
371 		case '8':
372 		case '9':
373 			thres = thres * 10 + (argv[0][i]-'0');
374 			break;
375 
376 		case 'v':
377 			vflg++;
378 			break;
379 
380 		case 'f':
381 			fflg++;	/* force v option; no tty interaction */
382 			break;
383 
384 		case 'u':
385 			uflg++;
386 			break;
387 
388 		case 'm':
389 			mflg++;
390 			break;
391 		}
392 	}
393 	if (thres == 0)
394 		thres = 1;
395 	if (iflg==0)
396 		init();
397 	if (argc<2)
398 		doacct(ACCT);
399 	else while (--argc)
400 		doacct(*++argv);
401 	if (uflg) {
402 		return;
403 	}
404 
405 /*
406  * cleanup pass
407  * put junk together
408  */
409 
410 	if (vflg)
411 		strip();
412 	if(!aflg)
413 	PROCESSITERATE(allocwalk, tp, ub){
414 		for(j=0; j<NC; j++)
415 			if(tp->p.name[j] == '?')
416 				goto yes;
417 		if(tp->p.count != 1)
418 			continue;
419 	yes:
420 		if(junkp == 0)
421 			junkp = enter("***other");
422 		junkp->p.count += tp->p.count;
423 		junkp->p.realt += tp->p.realt;
424 		junkp->p.cput += tp->p.cput;
425 		junkp->p.syst += tp->p.syst;
426 		junkp->p.imem += tp->p.imem;
427 		junkp->p.io += tp->p.io;
428 		tp->p.name[0] = 0;
429 	}
430 	if (sflg) {
431 		signal(SIGINT, SIG_IGN);
432 		if ((ff = fopen(USRACCT, "w")) != NULL) {
433 			static	struct	user ZeroUser = {0};
434 			struct 	user	*up;
435 			int	uid;
436 			/*
437 			 *	Write out just enough user slots,
438 			 *	filling with zero slots for users that
439 			 *	weren't found.
440 			 *	The file can be indexed directly by uid
441 			 *	to get the correct record.
442 			 */
443 			for (uid = 0; uid < maxuser; uid++){
444 				if ( (up = wasuser(uid)) != 0)
445 					fwrite((char *)&(up->oldu),
446 						sizeof(struct Olduser),1,ff);
447 				else
448 					fwrite((char *)&(ZeroUser.oldu),
449 						sizeof(struct Olduser),1,ff);
450 			}
451 		}
452 		if ((ff = fopen(SAVACCT, "w")) == NULL) {
453 			printf("Can't save\n");
454 			exit(0);
455 		}
456 		PROCESSITERATE(allocwalk, tp, ub)
457 			fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
458 		fclose(ff);
459 		creat(sname, 0644);
460 		signal(SIGINT, SIG_DFL);
461 	}
462 /*
463  * sort and print
464  */
465 	if (mflg) {
466 		printmoney();
467 		exit(0);
468 	}
469 	column(ncom, treal, tcpu, tsys, timem, tio);
470 	printf("\n");
471 
472 	/*
473 	 *	the fragmented table is sorted by sorting each fragment
474 	 *	and then merging.
475 	 */
476 	nchunks = 0;
477 	TABCHUNKS(allocwalk, tp, size){
478 		qsort(tp, size, sizeof(cell), cellcmp);
479 		nchunks ++;
480 	}
481 	chunkvector = (struct chunkdesc *)calloc(nchunks,
482 		sizeof(struct chunkdesc));
483 	nchunks = 0;
484 	TABCHUNKS(allocwalk, tp, size){
485 		chunkvector[nchunks].chunk_tp = tp;
486 		chunkvector[nchunks].chunk_n = size;
487 		nchunks++;
488 	}
489 	for(; nchunks; ){
490 		/*
491 		 *	Find the smallest element at the head of the queues.
492 		 */
493 		smallest = 0;
494 		for (i = 1; i < nchunks; i++){
495 			if (cellcmp(chunkvector[i].chunk_tp,
496 				chunkvector[smallest].chunk_tp) < 0)
497 					smallest = i;
498 		}
499 		tp = chunkvector[smallest].chunk_tp++;
500 		/*
501 		 *	If this queue is drained, drop the chunk count,
502 		 *	and readjust the queues.
503 		 */
504 		if (--chunkvector[smallest].chunk_n == 0){
505 			nchunks--;
506 			for (i = smallest; i < nchunks; i++)
507 				chunkvector[i] = chunkvector[i+1];
508 		}
509 		if (ISPROCESS(tp)){
510 			ft = tp->p.count;
511 			column(ft, tp->p.realt, tp->p.cput,
512 				tp->p.syst, tp->p.imem, tp->p.io);
513 			printf("   %.14s\n", tp->p.name);
514 		}
515 	}	/* iterate to merge the lists */
516 }
517 
518 printmoney()
519 {
520 	register i;
521 	register char *cp;
522 	register	struct user	*up;
523 
524 	getnames();		/* fetches all of the names! */
525 	for (i = 0; i < maxuser; i++) {
526 		if ( (up = wasuser(i)) != 0){
527 			if (up->us_cnt) {
528 				if (up->us_name[0])
529 					printf("%-8s", up->us_name);
530 				else
531 					printf("%-8d", i);
532 				printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
533 					up->us_cnt, up->us_ctime / 60,
534 					up->us_io,
535 					up->us_imem / (60 * 2));
536 			}
537 		}
538 	}
539 }
540 
541 column(n, a, b, c, d, e)
542 	double n, a, b, c, d, e;
543 {
544 
545 	printf("%8.0f", n);
546 	if(cflg) {
547 		if(n == ncom)
548 			printf("%9s", ""); else
549 			printf("%8.2f%%", 100.*n/ncom);
550 	}
551 	col(n, a, treal, "re");
552 	if (oflg)
553 		col(n, 3600*(b/(b+c)), tcpu+tsys, "u/s");
554 	else if(lflg) {
555 		col(n, b, tcpu, "u");
556 		col(n, c, tsys, "s");
557 	} else
558 		col(n, b+c, tcpu+tsys, "cp");
559 	if(tflg)
560 		printf("%8.1f", a/(b+c), "re/cp");
561 	if(dflg || !Dflg)
562 		printf("%10.0favio", e/(n?n:1));
563 	else
564 		printf("%10.0ftio", e);
565 	if (kflg || !Kflg)
566 		printf("%10.0fk", d/(2*((b+c)!=0.0?(b+c):1.0)));
567 	else
568 		printf("%10.0fk*sec", d/(2*60));
569 }
570 
571 col(n, a, m, cp)
572 	double n, a, m;
573 	char *cp;
574 {
575 
576 	if(jflg)
577 		printf("%11.2f%s", a/(n*60.), cp); else
578 		printf("%11.2f%s", a/3600., cp);
579 	if(cflg) {
580 		if(a == m)
581 			printf("%9s", ""); else
582 			printf("%8.2f%%", 100.*a/m);
583 	}
584 }
585 
586 doacct(f)
587 char *f;
588 {
589 	FILE *ff;
590 	long x, y, z;
591 	struct acct fbuf;
592 	register char *cp;
593 	register int c;
594 	register struct	user *up;
595 	register cell *tp;
596 #ifdef DEBUG
597 	int	nrecords = 0;
598 #endif DEBUG
599 
600 	if (sflg && sname) {
601 		printf("Only 1 file with -s\n");
602 		exit(0);
603 	}
604 	if (sflg)
605 		sname = f;
606 	if ((ff = fopen(f, "r"))==NULL) {
607 		printf("Can't open %s\n", f);
608 		return;
609 	}
610 	while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
611 #ifdef DEBUG
612 		if (++nrecords % 1000 == 0)
613 			printf("Input record from %s number %d\n",
614 				f, nrecords);
615 #endif DEBUG
616 		if (fbuf.ac_comm[0]==0) {
617 			fbuf.ac_comm[0] = '?';
618 		}
619 		for (cp = fbuf.ac_comm; cp < &fbuf.ac_comm[NC]; cp++) {
620 			c = *cp & 0377;
621 			if (c && (c < ' ' || c >= 0200)) {
622 				*cp = '?';
623 			}
624 		}
625 		if (fbuf.ac_flag&AFORK) {
626 			for (cp=fbuf.ac_comm; cp < &fbuf.ac_comm[NC]; cp++)
627 				if (*cp==0) {
628 					*cp = '*';
629 					break;
630 				}
631 		}
632 		x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
633 		y = fbuf.ac_mem;
634 		z = expand(fbuf.ac_io);
635 		if (uflg) {
636 			printf("%3d%6.1fcp %6dmem %6dio %.14s\n",
637 			    fbuf.ac_uid, x, y, z, fbuf.ac_comm);
638 			continue;
639 		}
640 		up = finduser(fbuf.ac_uid);
641 		if (up == 0)
642 			continue;	/* preposterous user id */
643 		up->us_cnt++;
644 		up->us_ctime += x;
645 		up->us_imem += x * y;
646 		up->us_io += z;
647 		ncom += 1.0;
648 
649 		tp = enter(fbuf.ac_comm);
650 		tp->p.imem += x * y;
651 		timem += x * y;
652 		tp->p.count++;
653 		x = expand(fbuf.ac_etime);
654 		tp->p.realt += x;
655 		treal += x;
656 		x = expand(fbuf.ac_utime);
657 		tp->p.cput += x;
658 		tcpu += x;
659 		x = expand(fbuf.ac_stime);
660 		tp->p.syst += x;
661 		tsys += x;
662 		tp->p.io += z;
663 		tio += z;
664 	}
665 	fclose(ff);
666 }
667 
668 /*
669  *	Generalized cell compare routine, to cast out users
670  */
671 cellcmp(p1, p2)
672 	cell *p1, *p2;
673 {
674 	if (ISPROCESS(p1)){
675 		if (ISPROCESS(p2))
676 			return((*cmp)(p1, p2));
677 		return(-1);
678 	}
679 	if (ISPROCESS(p2))
680 		return(1);
681 	return(0);
682 }
683 
684 ncmp(p1, p2)
685 	cell *p1, *p2;
686 {
687 
688 	if(p1->p.count == p2->p.count)
689 		return(tcmp(p1, p2));
690 	if(rflg)
691 		return(p1->p.count - p2->p.count);
692 	return(p2->p.count - p1->p.count);
693 }
694 
695 bcmp(p1, p2)
696 	cell *p1, *p2;
697 {
698 	double f1, f2;
699 	double sum();
700 
701 	f1 = sum(p1)/p1->p.count;
702 	f2 = sum(p2)/p2->p.count;
703 	if(f1 < f2) {
704 		if(rflg)
705 			return(-1);
706 		return(1);
707 	}
708 	if(f1 > f2) {
709 		if(rflg)
710 			return(1);
711 		return(-1);
712 	}
713 	return(0);
714 }
715 
716 Kcmp(p1, p2)
717 	cell *p1, *p2;
718 {
719 
720 	if (p1->p.imem < p2->p.imem) {
721 		if(rflg)
722 			return(-1);
723 		return(1);
724 	}
725 	if (p1->p.imem > p2->p.imem) {
726 		if(rflg)
727 			return(1);
728 		return(-1);
729 	}
730 	return(0);
731 }
732 
733 kcmp(p1, p2)
734 	cell *p1, *p2;
735 {
736 	double a1, a2;
737 
738 	a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
739 	a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
740 	if (a1 < a2) {
741 		if(rflg)
742 			return(-1);
743 		return(1);
744 	}
745 	if (a1 > a2) {
746 		if(rflg)
747 			return(1);
748 		return(-1);
749 	}
750 	return(0);
751 }
752 
753 dcmp(p1, p2)
754 	cell *p1, *p2;
755 {
756 	double a1, a2;
757 
758 	a1 = p1->p.io / (p1->p.count?p1->p.count:1);
759 	a2 = p2->p.io / (p2->p.count?p2->p.count:1);
760 	if (a1 < a2) {
761 		if(rflg)
762 			return(-1);
763 		return(1);
764 	}
765 	if (a1 > a2) {
766 		if(rflg)
767 			return(1);
768 		return(-1);
769 	}
770 	return(0);
771 }
772 
773 Dcmp(p1, p2)
774 	cell *p1, *p2;
775 {
776 
777 	if (p1->p.io < p2->p.io) {
778 		if(rflg)
779 			return(-1);
780 		return(1);
781 	}
782 	if (p1->p.io > p2->p.io) {
783 		if(rflg)
784 			return(1);
785 		return(-1);
786 	}
787 	return(0);
788 }
789 
790 tcmp(p1, p2)
791 	cell *p1, *p2;
792 {
793 	extern double sum();
794 	double f1, f2;
795 
796 	f1 = sum(p1);
797 	f2 = sum(p2);
798 	if(f1 < f2) {
799 		if(rflg)
800 			return(-1);
801 		return(1);
802 	}
803 	if(f1 > f2) {
804 		if(rflg)
805 			return(1);
806 		return(-1);
807 	}
808 	return(0);
809 }
810 
811 double sum(p)
812 	cell *p;
813 {
814 
815 	if(p->p.name[0] == 0)
816 		return(0.0);
817 	return( p->p.cput + p->p.syst);
818 }
819 
820 init()
821 {
822 	struct user userbuf;
823 	struct process	tbuf;
824 	register cell *tp;
825 	register struct user *up;
826 	int uid;
827 	FILE *f;
828 
829 	if ((f = fopen(SAVACCT, "r")) == NULL)
830 		goto gshm;
831 	while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
832 		tp = enter(tbuf.name);
833 		ncom += tbuf.count;
834 		tp->p.count = tbuf.count;
835 		treal += tbuf.realt;
836 		tp->p.realt = tbuf.realt;
837 		tcpu += tbuf.cput;
838 		tp->p.cput = tbuf.cput;
839 		tsys += tbuf.syst;
840 		tp->p.syst = tbuf.syst;
841 		tio += tbuf.io;
842 		tp->p.io = tbuf.io;
843 		timem += tbuf.imem;
844 		tp->p.imem = tbuf.imem;
845 	}
846 	fclose(f);
847  gshm:
848 	if ((f = fopen(USRACCT, "r")) == NULL)
849 		return;
850 	for(uid = 0;
851 	    fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
852 	    uid++){
853 		if (userbuf.us_cnt){
854 			up = finduser(uid);
855 			if (up == 0)
856 				continue;	/* preposterous user id */
857 			up->oldu = userbuf.oldu;
858 		}
859 	}
860 	fclose(f);
861 }
862 
863 strip()
864 {
865 	int c;
866 	register struct allocbox *allocwalk;
867 	register cell *tp, *ub, *junkp;
868 
869 	if (fflg)
870 		printf("Categorizing commands used %d times or fewer as **junk**\n",
871 			thres);
872 	junkp = enter("**junk**");
873 	PROCESSITERATE(allocwalk, tp, ub){
874 		if (tp->p.name[0] && tp->p.count <= thres) {
875 			if (!fflg)
876 				printf("%.14s--", tp->p.name);
877 			if (fflg || ((c=getchar())=='y')) {
878 				tp->p.name[0] = '\0';
879 				junkp->p.count += tp->p.count;
880 				junkp->p.realt += tp->p.realt;
881 				junkp->p.cput += tp->p.cput;
882 				junkp->p.syst += tp->p.syst;
883 				junkp->p.imem += tp->p.imem;
884 				junkp->p.io += tp->p.io;
885 			}
886 			if (!fflg)
887 				while (c && c!='\n')
888 					c = getchar();
889 		}
890 	}
891 }
892 
893 time_t
894 expand(t)
895 	unsigned t;
896 {
897 	register time_t nt;
898 
899 	nt = t&017777;
900 	t >>= 13;
901 	while (t!=0) {
902 		t--;
903 		nt <<= 3;
904 	}
905 	return(nt);
906 }
907 
908 static	char UserKey[NAMELG + 2];
909 
910 char *
911 makekey(uid)
912 	int uid;
913 {
914 	sprintf(UserKey+1, "%04x", uid);
915 	UserKey[0] = USERKEY;
916 	return(UserKey);
917 }
918 
919 struct user *
920 wasuser(uid)
921 	int uid;
922 {
923 	struct user *tp;
924 
925 	htabinstall = 0;
926 	tp = finduser(uid);
927 	htabinstall = 1;
928 	return(tp);
929 }
930 
931 /*
932  *	Only call this if you really want to insert it in the table!
933  */
934 struct user *
935 finduser(uid)
936 	int uid;
937 {
938 
939 	if (uid > maxuser){
940 		fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
941 		return(0);
942 	}
943 	return((struct user*)enter(makekey(uid)));
944 }
945 
946 /*
947  *	Set the names of all users in the password file.
948  *	We will later not print those that didn't do anything.
949  */
950 getnames()
951 {
952 	register struct user *tp;
953 	register struct passwd *pw;
954 	struct passwd *getpwent();
955 
956 	setpwent();
957 	while (pw = getpwent()){
958 		if ( (tp = wasuser(pw->pw_uid)) != 0)
959 			strncpy(tp->us_name, pw->pw_name, NAMELG);
960 	}
961 	endpwent();
962 }
963 
964 int
965 getmaxuid()
966 {
967 	register struct user *tp;
968 	register struct passwd *pw;
969 	struct passwd *getpwent();
970 	int maxuid = -1;
971 
972 	setpwent();
973 	while(pw = getpwent()){
974 		if (pw->pw_uid > maxuid)
975 			maxuid = pw->pw_uid;
976 	}
977 	endpwent();
978 	return(maxuid);
979 }
980 
981 tabinit()
982 {
983 	allochead = 0;
984 	alloctail = 0;
985 	nexttab = 0;
986 	tabsleft = 0;
987 	htab = 0;
988 	ntabs = 0;
989 	htaballoc();		/* get the first part of the hash table */
990 }
991 
992 #define ALLOCQTY 	sizeof (struct allocbox)
993 cell *
994 taballoc()
995 {
996 
997 	if (tabsleft == 0){
998 		newbox = (struct allocbox *)calloc(1, ALLOCQTY);
999 		tabsleft = TABDALLOP;
1000 		nexttab = &newbox->tabslots[0];
1001 		if (alloctail == 0){
1002 			allochead = alloctail = newbox;
1003 		} else {
1004 			alloctail->nextalloc = newbox;
1005 			alloctail = newbox;
1006 		}
1007 	}
1008 	--tabsleft;
1009 	++ntabs;
1010 #ifdef DEBUG
1011 	if (ntabs % 100 == 0)
1012 		printf("##Accounting table slot # %d\n", ntabs);
1013 #endif DEBUG
1014 	return(nexttab++);
1015 }
1016 
1017 htaballoc()
1018 {
1019 	register struct hashdallop *new;
1020 #ifdef DEBUG
1021 	static int ntables = 0;
1022 
1023 	printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
1024 #endif DEBUG
1025 	new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
1026 	if (htab == 0)
1027 		htab = new;
1028 	else {		/* add AFTER the 1st slot */
1029 		new->h_next = htab->h_next;
1030 		htab->h_next = new;
1031 	}
1032 }
1033 
1034 #define 	HASHCLOGGED	(NHASH / 2)
1035 /*
1036  *	Lookup a symbol passed in as the argument.
1037  *
1038  *	We take pains to avoid function calls; this function
1039  *	is called quite frequently, and the calling overhead
1040  *	contributes significantly to the overall execution speed of sa.
1041  */
1042 cell *
1043 enter(name)
1044 	char *name;
1045 {
1046 	static int initialprobe;
1047 	register cell **hp;
1048 	register char *from, *to;
1049 	register int len, nprobes;
1050 	static struct hashdallop *hdallop, *emptyhd;
1051 	static cell **emptyslot, **hp_ub;
1052 
1053 	emptyslot = 0;
1054 	for (nprobes = 0, from = name, len = 0;
1055 	     *from && len < NC;
1056 	     nprobes <<= 2, nprobes += *from++, len++)
1057 		continue;
1058 	nprobes += from[-1] << 5;
1059 	nprobes %= NHASH;
1060 	if (nprobes < 0)
1061 		nprobes += NHASH;
1062 
1063 	initialprobe = nprobes;
1064 	for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
1065 		for (hp = &(hdallop->h_tab[initialprobe]),
1066 				nprobes = 1,
1067 				hp_ub = &(hdallop->h_tab[NHASH]);
1068 		     (*hp) && (nprobes < NHASH);
1069 				hp += nprobes,
1070 				hp -= (hp >= hp_ub) ? NHASH:0,
1071 				nprobes += 2)
1072 		{
1073 			from = name;
1074 			to = (*hp)->p.name;
1075 
1076 			for (len = 0; (len<NC) && *from; len++)
1077 				if (*from++ != *to++)
1078 					goto nextprobe;
1079 			if (len >= NC)		/*both are maximal length*/
1080 				return(*hp);
1081 			if (*to == 0)		/*assert *from == 0*/
1082 				return(*hp);
1083 	nextprobe: ;
1084 		}
1085 		if (*hp == 0 && emptyslot == 0 &&
1086 		    hdallop->h_nused < HASHCLOGGED) {
1087 			emptyslot = hp;
1088 			emptyhd = hdallop;
1089 		}
1090 	}
1091 	if (emptyslot == 0) {
1092 		htaballoc();
1093 		hdallop = htab->h_next;		/* aren't we smart! */
1094 		hp = &hdallop->h_tab[initialprobe];
1095 	} else {
1096 		hdallop = emptyhd;
1097 		hp = emptyslot;
1098 	}
1099 	if (htabinstall){
1100 		*hp = taballoc();
1101 		hdallop->h_nused++;
1102 		for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
1103 			if ((*to++ = *from++) == '\0')
1104 				break;
1105 		return(*hp);
1106 	}
1107 	return(0);
1108 }
1109