xref: /original-bsd/usr.sbin/sa/sa.c (revision 0b685140)
1 static	char *sccsid = "@(#)sa.c	4.2 (Berkeley) 81/02/28";
2 
3 /*
4  *	Extensive modifications to internal data structures
5  *	to allow arbitrary number of different commands and users added.
6  *
7  *	Also allowed the digit option on the -v flag (interactive
8  *	threshold compress) to be a digit string, so one can
9  *	set the threshold > 9.
10  *
11  *	Also added the -f flag, to force no interactive threshold
12  *	compression with the -v flag.
13  *
14  *	Robert Henry
15  *	UC Berkeley
16  *	31jan81
17  */
18 #include <stdio.h>
19 #include <sys/types.h>
20 #include <sys/acct.h>
21 #include <signal.h>
22 #include <utmp.h>
23 #include <pwd.h>
24 
25 /* interpret command time accounting */
26 
27 #define	NC	sizeof(acctbuf.ac_comm)
28 
29 struct acct acctbuf;
30 int	lflg;
31 int	cflg;
32 int	Dflg;
33 int	dflg;
34 int	iflg;
35 int	jflg;
36 int	Kflg;
37 int	kflg;
38 int	nflg;
39 int	aflg;
40 int	rflg;
41 int	oflg;
42 int	tflg;
43 int	vflg;
44 int	fflg;
45 int	uflg;
46 int	thres;
47 int	sflg;
48 int	bflg;
49 int	mflg;
50 
51 struct	utmp	utmp;
52 #define	NAMELG	(sizeof(utmp.ut_name)+1)
53 
54 struct 	Olduser{
55 	int	Us_cnt;
56 	double	Us_ctime;
57 	double	Us_io;
58 	double	Us_imem;
59 };
60 
61 struct	user {
62 	char	name[NC];		/* this is <\001><user id><\000> */
63 	struct	Olduser	oldu;
64 	char	us_name[NAMELG];
65 };
66 #define	us_cnt		oldu.Us_cnt
67 #define	us_ctime	oldu.Us_ctime
68 #define	us_io		oldu.Us_io
69 #define	us_imem		oldu.Us_imem
70 
71 /*
72  *	We protect ourselves from preposterous user id's by looking
73  *	through the passwd file for the highest uid allocated, and
74  *	then adding 10 to that.
75  *	This prevents the user structure from growing too large.
76  */
77 #define	USERSLOP	10
78 int	maxuser;		/* highest uid from /etc/passwd, + 10 for slop*/
79 
80 struct	process {
81 	char	name[NC];
82 	int	count;
83 	double	realt;
84 	double	cput;
85 	double	syst;
86 	double	imem;
87 	double	io;
88 };
89 
90 union	Tab{
91 	struct	process	p;
92 	struct	user	u;
93 };
94 
95 typedef	union Tab cell;
96 
97 int	(*cmp)();	/* compares 2 cells; set to appropriate func */
98 cell	*enter();
99 struct	user *finduser();
100 struct	user *wasuser();
101 
102 /*
103  *	Table elements are keyed by the name of the file exec'ed.
104  *	Because on large systems, many files can be exec'ed,
105  *	a static table size may grow to be too large.
106  *
107  *	Table elements are allocated in chunks dynamically, linked
108  *	together so that they may be retrieved sequentially.
109  *
110  *	An index into the table structure is provided by hashing through
111  *	a seperate hash table.
112  *	The hash table is segmented, and dynamically extendable.
113  *	Realize that the hash table and accounting information is kept
114  *	in different segments!
115  *
116  *	We have a linked list of hash table segments; within each
117  *	segment we use a quadratic rehash that touches no more than 1/2
118  *	of the buckets in the hash table when probing.
119  *	If the probe does not find the desired symbol, it moves to the
120  *	next segment, or allocates a new segment.
121  *
122  *	Hash table segments are kept on the linked list with the first
123  *	segment always first (that will probably contain the
124  *	most frequently executed commands) and
125  *	the last added segment immediately after the first segment,
126  *	to hopefully gain something by locality of reference.
127  *
128  *	We store the per user information in the same structure as
129  *	the per exec'ed file information.  This allows us to use the
130  *	same managers for both, as the number of user id's may be very
131  *	large.
132  *	User information is keyed by the first character in the name
133  *	being a '\001', followed by four bytes of (long extended)
134  *	user id number, followed by a null byte.
135  *	The actual user names are kept in a seperate field of the
136  *	user structure, and is filled in upon demand later.
137  *	Iteration through all users by low user id to high user id
138  *	is done by just probing the table, which is gross.
139  */
140 #define	USERKEY	'\001'
141 #define	ISPROCESS(tp)	(tp->p.name[0] && (tp->p.name[0] != USERKEY))
142 #define	ISUSER(tp)	(tp->p.name[0] && (tp->p.name[0] == USERKEY))
143 
144 #define	TABDALLOP	500
145 struct 	allocbox{
146 	struct	allocbox	*nextalloc;
147 	cell			tabslots[TABDALLOP];
148 };
149 
150 struct	allocbox	*allochead;	/*head of chunk list*/
151 struct	allocbox	*alloctail;	/*tail*/
152 struct	allocbox	*newbox;	/*for creating a new chunk*/
153 cell			*nexttab;	/*next table element that is free*/
154 int			tabsleft;	/*slots left in current chunk*/
155 int			ntabs;
156 /*
157  *	Iterate through all symbols in the symbol table in declaration
158  *	order.
159  *	struct	allocbox	*allocwalk;
160  *	cell			*sp, *ub;
161  *
162  *	sp points to the desired item, allocwalk and ub are there
163  *	to make the iteration go.
164  */
165 
166 #define DECLITERATE(allocwalk, walkpointer, ubpointer) \
167 	for(allocwalk = allochead; \
168 	    allocwalk != 0; \
169 	    allocwalk = allocwalk->nextalloc) \
170 		for (walkpointer = &allocwalk->tabslots[0],\
171 		        ubpointer = &allocwalk->tabslots[TABDALLOP], \
172 		        ubpointer = ubpointer > ( (cell *)alloctail) \
173 				 ? nexttab : ubpointer ;\
174 		     walkpointer < ubpointer; \
175 		     walkpointer++ )
176 
177 #define TABCHUNKS(allocwalk, tabptr, size) \
178 	for (allocwalk = allochead; \
179 	    allocwalk != 0; \
180 	    allocwalk = allocwalk->nextalloc) \
181 	    if ( \
182 		(tabptr = &allocwalk->tabslots[0]), \
183 		(size = \
184 		 (   (&allocwalk->tabslots[TABDALLOP]) \
185 		   > ((cell *)alloctail) \
186 		 ) \
187 		   ? (nexttab - tabptr) : TABDALLOP \
188 		), \
189 		1 \
190 	    )
191 #define	PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
192 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
193 	if (ISPROCESS(walkpointer))
194 
195 #define	USERITERATE(allocwalk, walkpointer, ubpointer) \
196 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
197 	if (ISUSER(walkpointer))
198 /*
199  *	When we have to sort the segmented accounting table, we
200  *	create a vector of sorted queues that is merged
201  *	to sort the entire accounting table.
202  */
203 struct chunkdesc   {
204 	cell	*chunk_tp;
205 	int	chunk_n;
206 };
207 
208 /*
209  *	Hash table segments and manager
210  */
211 #define	NHASH	1103
212 struct hashdallop {
213 	int	h_nused;
214 	struct	hashdallop	*h_next;
215 	cell		*h_tab[NHASH];
216 };
217 struct	hashdallop	*htab;	/* head of the list */
218 int	htabinstall;		/* install the symbol */
219 
220 double	treal;
221 double	tcpu;
222 double	tsys;
223 double	tio;
224 double	timem;
225 cell	*junkp;
226 char	*sname;
227 double	ncom;
228 time_t	expand();
229 char	*getname();
230 
231 /*
232  *	usracct saves records of type Olduser.
233  *	There is one record for every possible uid less than
234  *	the largest uid seen in the previous usracct or in savacct.
235  *	uid's that had no activity correspond to zero filled slots;
236  *	thus one can index the file and get the user record out.
237  *	It would be better to save only user information for users
238  *	that the system knows about to save space, but that is not
239  *	upward compatabile with the old system.
240  *
241  *	In the old version of sa, uid's greater than 999 were not handled
242  *	properly; this system will do that.
243  */
244 
245 #ifdef	DEBUG
246 #define	USRACCT "./usracct"
247 #define	SAVACCT	"./savacct"
248 #define	ACCT	"./acct"
249 #else
250 #define	USRACCT "/usr/adm/usracct"
251 #define	SAVACCT	"/usr/adm/savacct"
252 #define	ACCT	"/usr/adm/acct"
253 #endif	DEBUG
254 
255 
256 int	cellcmp();
257 cell	*junkp = 0;
258 /*
259  *	The threshold is built up from digits in the argv ;
260  *	eg, -v1s0u1
261  *	will build a value of thres of 101.
262  *
263  *	If the threshold is zero after processing argv, it is set to 1
264  */
265 int	thres = 0;
266 int	htabinstall = 1;
267 int	maxuser = -1;
268 int	(*cmp)();
269 
270 main(argc, argv)
271 char **argv;
272 {
273 	FILE *ff;
274 	int i, j;
275 	extern	tcmp(), ncmp(), bcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
276 	extern	double sum();
277 	double	ft;
278 	register	struct	allocbox	*allocwalk;
279 	register	cell	*tp, *ub;
280 	int	size;
281 	int	nchunks;
282 	struct	chunkdesc	*chunkvector;
283 	int	smallest;
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/60.0, y, z,
638 			    fbuf.ac_comm);
639 			continue;
640 		}
641 		up = finduser(fbuf.ac_uid);
642 		if (up == 0)
643 			continue;	/* preposterous user id */
644 		up->us_cnt++;
645 		up->us_ctime += x/60.;
646 		up->us_imem += x * y;
647 		up->us_io += z;
648 		ncom += 1.0;
649 
650 		tp = enter(fbuf.ac_comm);
651 		tp->p.imem += x * y;
652 		timem += x * y;
653 		tp->p.count++;
654 		x = expand(fbuf.ac_etime)*60;
655 		tp->p.realt += x;
656 		treal += x;
657 		x = expand(fbuf.ac_utime);
658 		tp->p.cput += x;
659 		tcpu += x;
660 		x = expand(fbuf.ac_stime);
661 		tp->p.syst += x;
662 		tsys += x;
663 		tp->p.io += z;
664 		tio += z;
665 	}
666 	fclose(ff);
667 }
668 
669 /*
670  *	Generalized cell compare routine, to cast out users
671  */
672 cellcmp(p1, p2)
673 	cell	*p1, *p2;
674 {
675 	if (ISPROCESS(p1)){
676 		if (ISPROCESS(p2))
677 			return(cmp(p1, p2));
678 		return(-1);
679 	}
680 	if (ISPROCESS(p2))
681 		return(1);
682 	return(0);
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 char *makekey(uid)
910 	int	uid;
911 {
912 	sprintf(UserKey+1, "%04x", uid);
913 	UserKey[0] = USERKEY;
914 	return(UserKey);
915 }
916 
917 struct user *wasuser(uid)
918 	int 	uid;
919 {
920 	struct	user	*tp;
921 	htabinstall = 0;
922 	tp = finduser(uid);
923 	htabinstall = 1;
924 	return(tp);
925 }
926 /*
927  *	Only call this if you really want to insert it in the table!
928  */
929 struct user *finduser(uid)
930 	int	uid;
931 {
932 	if (uid > maxuser){
933 		fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
934 		return(0);
935 	} else {
936 		return((struct user*)enter(makekey(uid)));
937 	}
938 }
939 
940 /*
941  *	Set the names of all users in the password file.
942  *	We will later not print those that didn't do anything.
943  */
944 getnames()
945 {
946 	register	struct user	*tp;
947 	register struct passwd *pw;
948 	struct passwd *getpwent();
949 
950 	setpwent();
951 	while (pw = getpwent()){
952 		if ( (tp = wasuser(pw->pw_uid)) != 0)
953 			strncpy(tp->us_name, pw->pw_name, NAMELG);
954 	}
955 	endpwent();
956 }
957 
958 int getmaxuid()
959 {
960 	register	struct	user	*tp;
961 	register	struct	passwd	*pw;
962 	struct		passwd	*getpwent();
963 	int		maxuid = -1;
964 
965 	setpwent();
966 	while(pw = getpwent()){
967 		if (pw->pw_uid > maxuid)
968 			maxuid = pw->pw_uid;
969 	}
970 	endpwent();
971 	return(maxuid);
972 }
973 
974 tabinit()
975 {
976 	allochead = 0;
977 	alloctail = 0;
978 	nexttab = 0;
979 	tabsleft = 0;
980 	htab = 0;
981 	ntabs = 0;
982 	htaballoc();		/* get the first part of the hash table */
983 }
984 
985 #define ALLOCQTY 	sizeof (struct allocbox)
986 cell *taballoc()
987 {
988 	if (tabsleft == 0){
989 		newbox = (struct allocbox *)calloc(1, ALLOCQTY);
990 		tabsleft = TABDALLOP;
991 		nexttab = &newbox->tabslots[0];
992 		if (alloctail == 0){
993 			allochead = alloctail = newbox;
994 		} else {
995 			alloctail->nextalloc = newbox;
996 			alloctail = newbox;
997 		}
998 	}
999 	--tabsleft;
1000 	++ntabs;
1001 #ifdef DEBUG
1002 	if (ntabs % 100 == 0)
1003 		printf("##Accounting table slot # %d\n", ntabs);
1004 #endif DEBUG
1005 	return(nexttab++);
1006 }
1007 
1008 htaballoc()
1009 {
1010 	register	struct	hashdallop	*new;
1011 #ifdef DEBUG
1012 	static	int	ntables = 0;
1013 	printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
1014 #endif DEBUG
1015 	new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
1016 	if (htab == 0)
1017 		htab = new;
1018 	else {		/* add AFTER the 1st slot */
1019 		new->h_next = htab->h_next;
1020 		htab->h_next = new;
1021 	}
1022 }
1023 
1024 #define 	HASHCLOGGED	(NHASH / 2)
1025 /*
1026  *	Lookup a symbol passed in as the argument.
1027  *
1028  *	We take pains to avoid function calls; this function
1029  *	is called quite frequently, and the calling overhead
1030  *	contributes significantly to the overall execution speed of sa.
1031  */
1032 cell *enter(name)
1033 	char	*name;
1034 {
1035 	static	 int		initialprobe;
1036 	register cell	 	**hp;
1037 	register char 		*from;
1038 	register char		*to;
1039 	register	int	len;
1040 	register	int	nprobes;
1041 	static	 struct hashdallop *hdallop;
1042 	static	 cell		**emptyslot;
1043 	static 	 struct hashdallop *emptyhd;
1044 	static	 cell		**hp_ub;
1045 
1046 	emptyslot = 0;
1047 	for (nprobes = 0, from = name, len = 0;
1048 	     *from && len < NC;
1049 	     nprobes <<= 2, nprobes += *from++, len++)
1050 		continue;
1051 	nprobes += from[-1] << 5;
1052 	nprobes %= NHASH;
1053 	if (nprobes < 0)
1054 		nprobes += NHASH;
1055 
1056 	initialprobe = nprobes;
1057 	for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
1058 		for (hp = &(hdallop->h_tab[initialprobe]),
1059 				nprobes = 1,
1060 				hp_ub = &(hdallop->h_tab[NHASH]);
1061 		     (*hp) && (nprobes < NHASH);
1062 				hp += nprobes,
1063 				hp -= (hp >= hp_ub) ? NHASH:0,
1064 				nprobes += 2)
1065 		{
1066 			from = name;
1067 			to = (*hp)->p.name;
1068 
1069 			for (len = 0; (len<NC) && *from; len++)
1070 				if (*from++ != *to++)
1071 					goto nextprobe;
1072 			if (len >= NC)		/*both are maximal length*/
1073 				return(*hp);
1074 			if (*to == 0)		/*assert *from == 0*/
1075 				return(*hp);
1076 	nextprobe: ;
1077 		}
1078 		if (*hp == 0 && emptyslot == 0 &&
1079 		    hdallop->h_nused < HASHCLOGGED) {
1080 			emptyslot = hp;
1081 			emptyhd = hdallop;
1082 		}
1083 	}
1084 	if (emptyslot == 0) {
1085 		htaballoc();
1086 		hdallop = htab->h_next;		/* aren't we smart! */
1087 		hp = &hdallop->h_tab[initialprobe];
1088 	} else {
1089 		hdallop = emptyhd;
1090 		hp = emptyslot;
1091 	}
1092 	if (htabinstall){
1093 		*hp = taballoc();
1094 		hdallop->h_nused++;
1095 		for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
1096 			if ((*to++ = *from++) == '\0')
1097 				break;
1098 		return(*hp);
1099 	}
1100 	return(0);
1101 }	/*end of lookup*/
1102