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