xref: /csrg-svn/usr.sbin/sa/sa.c (revision 61875)
149240Sbostic /*-
2*61875Sbostic  * Copyright (c) 1991, 1993
3*61875Sbostic  *	The Regents of the University of California.  All rights reserved.
449240Sbostic  *
549240Sbostic  * %sccs.include.proprietary.c%
649240Sbostic  */
749240Sbostic 
811840Ssam #ifndef lint
9*61875Sbostic static char copyright[] =
10*61875Sbostic "@(#) Copyright (c) 1991, 1993\n\
11*61875Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1249240Sbostic #endif /* not lint */
132813Swnj 
1449240Sbostic #ifndef lint
15*61875Sbostic static char sccsid[] = "@(#)sa.c	8.1 (Berkeley) 06/06/93";
1649240Sbostic #endif /* not lint */
1749240Sbostic 
182813Swnj /*
192813Swnj  *	Extensive modifications to internal data structures
202813Swnj  *	to allow arbitrary number of different commands and users added.
212813Swnj  *
222813Swnj  *	Also allowed the digit option on the -v flag (interactive
232813Swnj  *	threshold compress) to be a digit string, so one can
242813Swnj  *	set the threshold > 9.
252813Swnj  *
262813Swnj  *	Also added the -f flag, to force no interactive threshold
272813Swnj  *	compression with the -v flag.
282813Swnj  *
292813Swnj  *	Robert Henry
302813Swnj  *	UC Berkeley
312813Swnj  *	31jan81
322813Swnj  */
331087Sbill #include <stdio.h>
3416717Ssam #include <ctype.h>
351087Sbill #include <sys/types.h>
361087Sbill #include <sys/acct.h>
371087Sbill #include <signal.h>
382813Swnj #include <utmp.h>
392813Swnj #include <pwd.h>
4037297Sbostic #include "pathnames.h"
411087Sbill 
421087Sbill /* interpret command time accounting */
431087Sbill 
441087Sbill #define	NC	sizeof(acctbuf.ac_comm)
452813Swnj 
461087Sbill struct acct acctbuf;
471087Sbill int	lflg;
481087Sbill int	cflg;
491087Sbill int	Dflg;
501087Sbill int	dflg;
511087Sbill int	iflg;
521087Sbill int	jflg;
531087Sbill int	Kflg;
541087Sbill int	kflg;
551087Sbill int	nflg;
561087Sbill int	aflg;
571087Sbill int	rflg;
581087Sbill int	oflg;
591087Sbill int	tflg;
601087Sbill int	vflg;
612813Swnj int	fflg;
621087Sbill int	uflg;
632813Swnj int	thres;
641087Sbill int	sflg;
651087Sbill int	bflg;
661087Sbill int	mflg;
671087Sbill 
682813Swnj struct	utmp	utmp;
692813Swnj #define	NAMELG	(sizeof(utmp.ut_name)+1)
702813Swnj 
712813Swnj struct 	Olduser{
722813Swnj 	int	Us_cnt;
732813Swnj 	double	Us_ctime;
742813Swnj 	double	Us_io;
752813Swnj 	double	Us_imem;
762813Swnj };
772813Swnj 
781087Sbill struct	user {
792813Swnj 	char	name[NC];		/* this is <\001><user id><\000> */
802813Swnj 	struct	Olduser	oldu;
812813Swnj 	char	us_name[NAMELG];
822813Swnj };
832813Swnj #define	us_cnt		oldu.Us_cnt
842813Swnj #define	us_ctime	oldu.Us_ctime
852813Swnj #define	us_io		oldu.Us_io
862813Swnj #define	us_imem		oldu.Us_imem
871087Sbill 
882813Swnj /*
892813Swnj  *	We protect ourselves from preposterous user id's by looking
902813Swnj  *	through the passwd file for the highest uid allocated, and
912813Swnj  *	then adding 10 to that.
922813Swnj  *	This prevents the user structure from growing too large.
932813Swnj  */
942813Swnj #define	USERSLOP	10
952813Swnj int	maxuser;		/* highest uid from /etc/passwd, + 10 for slop*/
962813Swnj 
972813Swnj struct	process {
981087Sbill 	char	name[NC];
991087Sbill 	int	count;
1001087Sbill 	double	realt;
1011087Sbill 	double	cput;
1021087Sbill 	double	syst;
1031087Sbill 	double	imem;
1041087Sbill 	double	io;
1052813Swnj };
1061087Sbill 
1072813Swnj union	Tab{
1082813Swnj 	struct	process	p;
1092813Swnj 	struct	user	u;
1102813Swnj };
1112813Swnj 
1122813Swnj typedef	union Tab cell;
1132813Swnj 
1142813Swnj int	(*cmp)();	/* compares 2 cells; set to appropriate func */
1152813Swnj cell	*enter();
1162813Swnj struct	user *finduser();
1172813Swnj struct	user *wasuser();
1182813Swnj 
1192813Swnj /*
1202813Swnj  *	Table elements are keyed by the name of the file exec'ed.
1212813Swnj  *	Because on large systems, many files can be exec'ed,
1222813Swnj  *	a static table size may grow to be too large.
1232813Swnj  *
1242813Swnj  *	Table elements are allocated in chunks dynamically, linked
1252813Swnj  *	together so that they may be retrieved sequentially.
1262813Swnj  *
1272813Swnj  *	An index into the table structure is provided by hashing through
1282813Swnj  *	a seperate hash table.
1292813Swnj  *	The hash table is segmented, and dynamically extendable.
1302813Swnj  *	Realize that the hash table and accounting information is kept
1312813Swnj  *	in different segments!
1322813Swnj  *
1332813Swnj  *	We have a linked list of hash table segments; within each
1342813Swnj  *	segment we use a quadratic rehash that touches no more than 1/2
1352813Swnj  *	of the buckets in the hash table when probing.
1362813Swnj  *	If the probe does not find the desired symbol, it moves to the
1372813Swnj  *	next segment, or allocates a new segment.
1382813Swnj  *
1392813Swnj  *	Hash table segments are kept on the linked list with the first
1402813Swnj  *	segment always first (that will probably contain the
1412813Swnj  *	most frequently executed commands) and
1422813Swnj  *	the last added segment immediately after the first segment,
1432813Swnj  *	to hopefully gain something by locality of reference.
1442813Swnj  *
1452813Swnj  *	We store the per user information in the same structure as
1462813Swnj  *	the per exec'ed file information.  This allows us to use the
1472813Swnj  *	same managers for both, as the number of user id's may be very
1482813Swnj  *	large.
1492813Swnj  *	User information is keyed by the first character in the name
1502813Swnj  *	being a '\001', followed by four bytes of (long extended)
1512813Swnj  *	user id number, followed by a null byte.
1522813Swnj  *	The actual user names are kept in a seperate field of the
1532813Swnj  *	user structure, and is filled in upon demand later.
1542813Swnj  *	Iteration through all users by low user id to high user id
1552813Swnj  *	is done by just probing the table, which is gross.
1562813Swnj  */
1572813Swnj #define	USERKEY	'\001'
1582813Swnj #define	ISPROCESS(tp)	(tp->p.name[0] && (tp->p.name[0] != USERKEY))
1592813Swnj #define	ISUSER(tp)	(tp->p.name[0] && (tp->p.name[0] == USERKEY))
1602813Swnj 
1612813Swnj #define	TABDALLOP	500
1622813Swnj struct 	allocbox{
1632813Swnj 	struct	allocbox	*nextalloc;
1642813Swnj 	cell			tabslots[TABDALLOP];
1652813Swnj };
1662813Swnj 
1672813Swnj struct	allocbox	*allochead;	/*head of chunk list*/
1682813Swnj struct	allocbox	*alloctail;	/*tail*/
1692813Swnj struct	allocbox	*newbox;	/*for creating a new chunk*/
1702813Swnj cell			*nexttab;	/*next table element that is free*/
1712813Swnj int			tabsleft;	/*slots left in current chunk*/
1722813Swnj int			ntabs;
1732813Swnj /*
1742813Swnj  *	Iterate through all symbols in the symbol table in declaration
1752813Swnj  *	order.
1762813Swnj  *	struct	allocbox	*allocwalk;
1772813Swnj  *	cell			*sp, *ub;
1782813Swnj  *
1792813Swnj  *	sp points to the desired item, allocwalk and ub are there
1802813Swnj  *	to make the iteration go.
1812813Swnj  */
1822813Swnj 
1832813Swnj #define DECLITERATE(allocwalk, walkpointer, ubpointer) \
1842813Swnj 	for(allocwalk = allochead; \
1852813Swnj 	    allocwalk != 0; \
1862813Swnj 	    allocwalk = allocwalk->nextalloc) \
1872813Swnj 		for (walkpointer = &allocwalk->tabslots[0],\
1882813Swnj 		        ubpointer = &allocwalk->tabslots[TABDALLOP], \
1892813Swnj 		        ubpointer = ubpointer > ( (cell *)alloctail) \
1902813Swnj 				 ? nexttab : ubpointer ;\
1912813Swnj 		     walkpointer < ubpointer; \
1922813Swnj 		     walkpointer++ )
1932813Swnj 
1942813Swnj #define TABCHUNKS(allocwalk, tabptr, size) \
1952813Swnj 	for (allocwalk = allochead; \
1962813Swnj 	    allocwalk != 0; \
1972813Swnj 	    allocwalk = allocwalk->nextalloc) \
1982813Swnj 	    if ( \
1992813Swnj 		(tabptr = &allocwalk->tabslots[0]), \
2002813Swnj 		(size = \
2012813Swnj 		 (   (&allocwalk->tabslots[TABDALLOP]) \
2022813Swnj 		   > ((cell *)alloctail) \
2032813Swnj 		 ) \
2042813Swnj 		   ? (nexttab - tabptr) : TABDALLOP \
2052813Swnj 		), \
2062813Swnj 		1 \
2072813Swnj 	    )
2082813Swnj #define	PROCESSITERATE(allocwalk, walkpointer, ubpointer) \
2092813Swnj 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
2102813Swnj 	if (ISPROCESS(walkpointer))
2112813Swnj 
2122813Swnj #define	USERITERATE(allocwalk, walkpointer, ubpointer) \
2132813Swnj 	DECLITERATE(allocwalk, walkpointer, ubpointer) \
2142813Swnj 	if (ISUSER(walkpointer))
2152813Swnj /*
2162813Swnj  *	When we have to sort the segmented accounting table, we
2172813Swnj  *	create a vector of sorted queues that is merged
2182813Swnj  *	to sort the entire accounting table.
2192813Swnj  */
2202813Swnj struct chunkdesc   {
2212813Swnj 	cell	*chunk_tp;
2222813Swnj 	int	chunk_n;
2232813Swnj };
2242813Swnj 
2252813Swnj /*
2262813Swnj  *	Hash table segments and manager
2272813Swnj  */
2282813Swnj #define	NHASH	1103
2292813Swnj struct hashdallop {
2302813Swnj 	int	h_nused;
2312813Swnj 	struct	hashdallop	*h_next;
2322813Swnj 	cell		*h_tab[NHASH];
2332813Swnj };
2342813Swnj struct	hashdallop	*htab;	/* head of the list */
2352813Swnj int	htabinstall;		/* install the symbol */
2362813Swnj 
2371087Sbill double	treal;
2381087Sbill double	tcpu;
2391087Sbill double	tsys;
2401087Sbill double	tio;
2411087Sbill double	timem;
2422813Swnj cell	*junkp;
2431087Sbill char	*sname;
2441087Sbill double	ncom;
2451087Sbill time_t	expand();
2461087Sbill char	*getname();
2471087Sbill 
2482813Swnj /*
2492813Swnj  *	usracct saves records of type Olduser.
2502813Swnj  *	There is one record for every possible uid less than
2512813Swnj  *	the largest uid seen in the previous usracct or in savacct.
2522813Swnj  *	uid's that had no activity correspond to zero filled slots;
2532813Swnj  *	thus one can index the file and get the user record out.
2542813Swnj  *	It would be better to save only user information for users
2552813Swnj  *	that the system knows about to save space, but that is not
2562813Swnj  *	upward compatabile with the old system.
2572813Swnj  *
2582813Swnj  *	In the old version of sa, uid's greater than 999 were not handled
2592813Swnj  *	properly; this system will do that.
2602813Swnj  */
2612813Swnj 
2622813Swnj #ifdef	DEBUG
2632813Swnj #define	USRACCT "./usracct"
2642813Swnj #define	SAVACCT	"./savacct"
2652813Swnj #define	ACCT	"./acct"
2662813Swnj #else
26737297Sbostic #define	USRACCT _PATH_USRACCT
26837297Sbostic #define	SAVACCT	_PATH_SAVACCT
26937297Sbostic #define	ACCT	_PATH_ACCT
2702813Swnj #endif	DEBUG
2712813Swnj 
2722813Swnj 
27317504Skarels char *usracct = USRACCT;
27417504Skarels char *savacct = SAVACCT;
27517504Skarels 
2762813Swnj int	cellcmp();
2772813Swnj cell	*junkp = 0;
2782813Swnj /*
2792813Swnj  *	The threshold is built up from digits in the argv ;
2802813Swnj  *	eg, -v1s0u1
2812813Swnj  *	will build a value of thres of 101.
2822813Swnj  *
2832813Swnj  *	If the threshold is zero after processing argv, it is set to 1
2842813Swnj  */
2852813Swnj int	thres = 0;
2862813Swnj int	htabinstall = 1;
2872813Swnj int	maxuser = -1;
2882813Swnj int	(*cmp)();
2892813Swnj 
29016717Ssam /* we assume pagesize is at least 1k */
29116717Ssam int	pgdiv;
29245019Smckusick #define	pgtok(x)	((x) * pgdiv)
29316717Ssam 
29437851Sbostic extern	tcmp(), ncmp(), bflgcmp(), dcmp(), Dcmp(), kcmp(), Kcmp();
29511840Ssam extern	double sum();
29611840Ssam 
main(argc,argv)2971087Sbill main(argc, argv)
29811840Ssam 	char **argv;
2991087Sbill {
3001087Sbill 	FILE *ff;
30111840Ssam 	double ft;
30211840Ssam 	register struct	allocbox *allocwalk;
30311840Ssam 	register cell *tp, *ub;
30411840Ssam 	int i, j, size, nchunks, smallest;
30511840Ssam 	struct chunkdesc *chunkvector;
3061087Sbill 
30716717Ssam 	pgdiv = getpagesize() / 1024;
30816717Ssam 	if (pgdiv == 0)
30916717Ssam 		pgdiv = 1;
3102813Swnj 	maxuser = USERSLOP + getmaxuid();
3112813Swnj 
3122813Swnj 	tabinit();
3131087Sbill 	cmp = tcmp;
3141087Sbill 	if (argc>1)
3151087Sbill 	if (argv[1][0]=='-') {
3161087Sbill 		argv++;
3171087Sbill 		argc--;
3181087Sbill 		for(i=1; argv[0][i]; i++)
3191087Sbill 		switch(argv[0][i]) {
3201087Sbill 
3211087Sbill 		case 'o':
3221087Sbill 			oflg++;
3231087Sbill 			break;
3241087Sbill 
3251087Sbill 		case 'i':
3261087Sbill 			iflg++;
3271087Sbill 			break;
3281087Sbill 
3291087Sbill 		case 'b':
3301087Sbill 			bflg++;
33137851Sbostic 			cmp = bflgcmp;
3321087Sbill 			break;
3331087Sbill 
3341087Sbill 		case 'l':
3351087Sbill 			lflg++;
3361087Sbill 			break;
3371087Sbill 
3381087Sbill 		case 'c':
3391087Sbill 			cflg++;
3401087Sbill 			break;
3411087Sbill 
3421087Sbill 		case 'd':
3431087Sbill 			dflg++;
3441087Sbill 			cmp = dcmp;
3451087Sbill 			break;
3461087Sbill 
3471087Sbill 		case 'D':
3481087Sbill 			Dflg++;
3491087Sbill 			cmp = Dcmp;
3501087Sbill 			break;
3511087Sbill 
3521087Sbill 		case 'j':
3531087Sbill 			jflg++;
3541087Sbill 			break;
3551087Sbill 
3561087Sbill 		case 'k':
3571087Sbill 			kflg++;
3581087Sbill 			cmp = kcmp;
3591087Sbill 			break;
3601087Sbill 
3611087Sbill 		case 'K':
3621087Sbill 			Kflg++;
3631087Sbill 			cmp = Kcmp;
3641087Sbill 			break;
3651087Sbill 
3661087Sbill 		case 'n':
3671087Sbill 			nflg++;
3681087Sbill 			cmp = ncmp;
3691087Sbill 			break;
3701087Sbill 
3711087Sbill 		case 'a':
3721087Sbill 			aflg++;
3731087Sbill 			break;
3741087Sbill 
3751087Sbill 		case 'r':
3761087Sbill 			rflg++;
3771087Sbill 			break;
3781087Sbill 
3791087Sbill 		case 't':
3801087Sbill 			tflg++;
3811087Sbill 			break;
3821087Sbill 
3831087Sbill 		case 's':
3841087Sbill 			sflg++;
3851087Sbill 			aflg++;
3861087Sbill 			break;
3871087Sbill 
3881087Sbill 		case '0':
3891087Sbill 		case '1':
3901087Sbill 		case '2':
3911087Sbill 		case '3':
3921087Sbill 		case '4':
3931087Sbill 		case '5':
3941087Sbill 		case '6':
3951087Sbill 		case '7':
3961087Sbill 		case '8':
3971087Sbill 		case '9':
3982813Swnj 			thres = thres * 10 + (argv[0][i]-'0');
3991087Sbill 			break;
4001087Sbill 
4011087Sbill 		case 'v':
4021087Sbill 			vflg++;
4031087Sbill 			break;
4041087Sbill 
4052813Swnj 		case 'f':
4062813Swnj 			fflg++;	/* force v option; no tty interaction */
4072813Swnj 			break;
4082813Swnj 
4091087Sbill 		case 'u':
4101087Sbill 			uflg++;
4111087Sbill 			break;
4121087Sbill 
4131087Sbill 		case 'm':
4141087Sbill 			mflg++;
4151087Sbill 			break;
41617504Skarels 
41717504Skarels 		case 'U':
41817504Skarels 		case 'S':
41917504Skarels 			if (i != 1 || argv[0][2]) {	/* gross! */
42017504Skarels 				fprintf(stderr, "-U and -S options must be separate\n");
42117504Skarels 				exit(1);
42217504Skarels 			}
42317504Skarels 			argc++, argv--;			/* backup - yuk */
42417504Skarels 			goto doUS;
42517504Skarels 
42617504Skarels 		default:
42717504Skarels 		    	fprintf(stderr, "Invalid option %c\n", argv[0][1]);
42817504Skarels 			exit(1);
4291087Sbill 		}
4301087Sbill 	}
43117504Skarels 
43217504Skarels #define optfile(f) {if (argc < 2) \
43317504Skarels 			{ fprintf(stderr, "Missing filename\n"); exit(1); } \
43417504Skarels 			argc--, argv++; f = argv[0]; }
43517504Skarels 
43617504Skarels doUS:
43717504Skarels 	for (argc--, argv++; argc && argv[0][0] == '-'; argc--, argv++) {
43817504Skarels 		switch(argv[0][1]) {
43917504Skarels 		    case 'U':
44017504Skarels 		    	optfile(usracct);
44117504Skarels 			break;
44217504Skarels 
44317504Skarels 		    case 'S':
44417504Skarels 		    	optfile(savacct);
44517504Skarels 			break;
44617504Skarels 
44717504Skarels 		    default:
44817504Skarels 		    	fprintf(stderr, "Invalid option %c\n", argv[0][1]);
44917504Skarels 			exit(1);
45017504Skarels 		}
45117504Skarels 	}
45217504Skarels 
4532813Swnj 	if (thres == 0)
4542813Swnj 		thres = 1;
4551087Sbill 	if (iflg==0)
4561087Sbill 		init();
45717504Skarels 	if (argc<1)
4582813Swnj 		doacct(ACCT);
45917504Skarels 	else while (argc--)
46017504Skarels 		doacct(*argv++);
4611087Sbill 	if (uflg) {
4621087Sbill 		return;
4631087Sbill 	}
4641087Sbill 
4651087Sbill /*
4661087Sbill  * cleanup pass
4671087Sbill  * put junk together
4681087Sbill  */
4691087Sbill 
4701087Sbill 	if (vflg)
4711087Sbill 		strip();
4721087Sbill 	if(!aflg)
4732813Swnj 	PROCESSITERATE(allocwalk, tp, ub){
4741087Sbill 		for(j=0; j<NC; j++)
4752813Swnj 			if(tp->p.name[j] == '?')
4761087Sbill 				goto yes;
4772813Swnj 		if(tp->p.count != 1)
4781087Sbill 			continue;
4791087Sbill 	yes:
4802813Swnj 		if(junkp == 0)
4811087Sbill 			junkp = enter("***other");
4822813Swnj 		junkp->p.count += tp->p.count;
4832813Swnj 		junkp->p.realt += tp->p.realt;
4842813Swnj 		junkp->p.cput += tp->p.cput;
4852813Swnj 		junkp->p.syst += tp->p.syst;
4862813Swnj 		junkp->p.imem += tp->p.imem;
4872813Swnj 		junkp->p.io += tp->p.io;
4882813Swnj 		tp->p.name[0] = 0;
4891087Sbill 	}
4901087Sbill 	if (sflg) {
4911087Sbill 		signal(SIGINT, SIG_IGN);
49217504Skarels 		if ((ff = fopen(usracct, "w")) != NULL) {
4932813Swnj 			static	struct	user ZeroUser = {0};
4942813Swnj 			struct 	user	*up;
4952813Swnj 			int	uid;
4962813Swnj 			/*
4972813Swnj 			 *	Write out just enough user slots,
4982813Swnj 			 *	filling with zero slots for users that
4992813Swnj 			 *	weren't found.
5002813Swnj 			 *	The file can be indexed directly by uid
5012813Swnj 			 *	to get the correct record.
5022813Swnj 			 */
5032813Swnj 			for (uid = 0; uid < maxuser; uid++){
5042813Swnj 				if ( (up = wasuser(uid)) != 0)
5052813Swnj 					fwrite((char *)&(up->oldu),
5062813Swnj 						sizeof(struct Olduser),1,ff);
5072813Swnj 				else
5082813Swnj 					fwrite((char *)&(ZeroUser.oldu),
5092813Swnj 						sizeof(struct Olduser),1,ff);
5102813Swnj 			}
5111087Sbill 		}
51217504Skarels 		if ((ff = fopen(savacct, "w")) == NULL) {
5131087Sbill 			printf("Can't save\n");
5141087Sbill 			exit(0);
5151087Sbill 		}
5162813Swnj 		PROCESSITERATE(allocwalk, tp, ub)
5172813Swnj 			fwrite((char *)&(tp->p), sizeof(struct process), 1, ff);
5181087Sbill 		fclose(ff);
5192813Swnj 		creat(sname, 0644);
5201087Sbill 		signal(SIGINT, SIG_DFL);
5211087Sbill 	}
5221087Sbill /*
5231087Sbill  * sort and print
5241087Sbill  */
5251087Sbill 	if (mflg) {
5261087Sbill 		printmoney();
5271087Sbill 		exit(0);
5281087Sbill 	}
5291087Sbill 	column(ncom, treal, tcpu, tsys, timem, tio);
5301087Sbill 	printf("\n");
5312813Swnj 
5322813Swnj 	/*
5332813Swnj 	 *	the fragmented table is sorted by sorting each fragment
5342813Swnj 	 *	and then merging.
5352813Swnj 	 */
5362813Swnj 	nchunks = 0;
5372813Swnj 	TABCHUNKS(allocwalk, tp, size){
5382813Swnj 		qsort(tp, size, sizeof(cell), cellcmp);
5392813Swnj 		nchunks ++;
5401087Sbill 	}
5412813Swnj 	chunkvector = (struct chunkdesc *)calloc(nchunks,
5422813Swnj 		sizeof(struct chunkdesc));
5432813Swnj 	nchunks = 0;
5442813Swnj 	TABCHUNKS(allocwalk, tp, size){
5452813Swnj 		chunkvector[nchunks].chunk_tp = tp;
5462813Swnj 		chunkvector[nchunks].chunk_n = size;
5472813Swnj 		nchunks++;
5482813Swnj 	}
5492813Swnj 	for(; nchunks; ){
5502813Swnj 		/*
5512813Swnj 		 *	Find the smallest element at the head of the queues.
5522813Swnj 		 */
5532813Swnj 		smallest = 0;
5542813Swnj 		for (i = 1; i < nchunks; i++){
5552813Swnj 			if (cellcmp(chunkvector[i].chunk_tp,
5562813Swnj 				chunkvector[smallest].chunk_tp) < 0)
5572813Swnj 					smallest = i;
5582813Swnj 		}
5592813Swnj 		tp = chunkvector[smallest].chunk_tp++;
5602813Swnj 		/*
5612813Swnj 		 *	If this queue is drained, drop the chunk count,
5622813Swnj 		 *	and readjust the queues.
5632813Swnj 		 */
5642813Swnj 		if (--chunkvector[smallest].chunk_n == 0){
5652813Swnj 			nchunks--;
5662813Swnj 			for (i = smallest; i < nchunks; i++)
5672813Swnj 				chunkvector[i] = chunkvector[i+1];
5682813Swnj 		}
5692813Swnj 		if (ISPROCESS(tp)){
5702813Swnj 			ft = tp->p.count;
5712813Swnj 			column(ft, tp->p.realt, tp->p.cput,
5722813Swnj 				tp->p.syst, tp->p.imem, tp->p.io);
5732813Swnj 			printf("   %.14s\n", tp->p.name);
5742813Swnj 		}
5752813Swnj 	}	/* iterate to merge the lists */
5761087Sbill }
5771087Sbill 
printmoney()5781087Sbill printmoney()
5791087Sbill {
5801087Sbill 	register i;
5811087Sbill 	register char *cp;
5822813Swnj 	register	struct user	*up;
5831087Sbill 
5842813Swnj 	getnames();		/* fetches all of the names! */
5852813Swnj 	for (i = 0; i < maxuser; i++) {
5862813Swnj 		if ( (up = wasuser(i)) != 0){
5872813Swnj 			if (up->us_cnt) {
5882813Swnj 				if (up->us_name[0])
5892813Swnj 					printf("%-8s", up->us_name);
5902813Swnj 				else
5912813Swnj 					printf("%-8d", i);
5922813Swnj 				printf("%7u %9.2fcpu %10.0ftio %12.0fk*sec\n",
59311840Ssam 					up->us_cnt, up->us_ctime / 60,
5942813Swnj 					up->us_io,
59517504Skarels 					up->us_imem / AHZ);
5962813Swnj 			}
5971087Sbill 		}
5981087Sbill 	}
5991087Sbill }
6001087Sbill 
column(n,a,b,c,d,e)6011087Sbill column(n, a, b, c, d, e)
60211840Ssam 	double n, a, b, c, d, e;
6031087Sbill {
6041087Sbill 
6051087Sbill 	printf("%8.0f", n);
6061087Sbill 	if(cflg) {
6071087Sbill 		if(n == ncom)
6081087Sbill 			printf("%9s", ""); else
6091087Sbill 			printf("%8.2f%%", 100.*n/ncom);
6101087Sbill 	}
6111087Sbill 	col(n, a, treal, "re");
6121087Sbill 	if (oflg)
61317504Skarels 		col(n, 60*AHZ*(b/(b+c)), tcpu+tsys, "u/s");
6141087Sbill 	else if(lflg) {
6151087Sbill 		col(n, b, tcpu, "u");
6161087Sbill 		col(n, c, tsys, "s");
6171087Sbill 	} else
6181087Sbill 		col(n, b+c, tcpu+tsys, "cp");
6191087Sbill 	if(tflg)
62017504Skarels 		printf("%8.1fre/cp", a/(b+c));
6211087Sbill 	if(dflg || !Dflg)
6221087Sbill 		printf("%10.0favio", e/(n?n:1));
6231087Sbill 	else
6241087Sbill 		printf("%10.0ftio", e);
6251087Sbill 	if (kflg || !Kflg)
62616717Ssam 		printf("%10.0fk", d/((b+c)!=0.0?(b+c):1.0));
6271087Sbill 	else
62817504Skarels 		printf("%10.0fk*sec", d/AHZ);
6291087Sbill }
6301087Sbill 
col(n,a,m,cp)6311087Sbill col(n, a, m, cp)
63211840Ssam 	double n, a, m;
63311840Ssam 	char *cp;
6341087Sbill {
6351087Sbill 
6361087Sbill 	if(jflg)
63717504Skarels 		printf("%11.2f%s", a/(n*(double)AHZ), cp); else
63817504Skarels 		printf("%11.2f%s", a/(60.*(double)AHZ), cp);
6391087Sbill 	if(cflg) {
6401087Sbill 		if(a == m)
6411087Sbill 			printf("%9s", ""); else
6421087Sbill 			printf("%8.2f%%", 100.*a/m);
6431087Sbill 	}
6441087Sbill }
6451087Sbill 
doacct(f)6461087Sbill doacct(f)
6471087Sbill char *f;
6481087Sbill {
6491087Sbill 	FILE *ff;
6501087Sbill 	long x, y, z;
6511087Sbill 	struct acct fbuf;
6521087Sbill 	register char *cp;
6531087Sbill 	register int c;
65411840Ssam 	register struct	user *up;
65511840Ssam 	register cell *tp;
6562813Swnj #ifdef DEBUG
6572813Swnj 	int	nrecords = 0;
6582813Swnj #endif DEBUG
6591087Sbill 
6601087Sbill 	if (sflg && sname) {
6611087Sbill 		printf("Only 1 file with -s\n");
6621087Sbill 		exit(0);
6631087Sbill 	}
6641087Sbill 	if (sflg)
6651087Sbill 		sname = f;
6661087Sbill 	if ((ff = fopen(f, "r"))==NULL) {
6671087Sbill 		printf("Can't open %s\n", f);
6681087Sbill 		return;
6691087Sbill 	}
6701087Sbill 	while (fread((char *)&fbuf, sizeof(fbuf), 1, ff) == 1) {
6712813Swnj #ifdef DEBUG
6722813Swnj 		if (++nrecords % 1000 == 0)
6732813Swnj 			printf("Input record from %s number %d\n",
6742813Swnj 				f, nrecords);
6752813Swnj #endif DEBUG
67616717Ssam 		for (cp = fbuf.ac_comm; *cp && cp < &fbuf.ac_comm[NC]; cp++)
67716717Ssam 			if (!isascii(*cp) || iscntrl(*cp))
6781087Sbill 				*cp = '?';
67916717Ssam 		if (cp == fbuf.ac_comm)
68016717Ssam 			*cp++ = '?';
6811087Sbill 		if (fbuf.ac_flag&AFORK) {
68216717Ssam 			if (cp >= &fbuf.ac_comm[NC])
68316729Ssam 				cp = &fbuf.ac_comm[NC-1];
68416717Ssam 			*cp++ = '*';
6851087Sbill 		}
68616717Ssam 		if (cp < &fbuf.ac_comm[NC])
68716717Ssam 			*cp = '\0';
6881087Sbill 		x = expand(fbuf.ac_utime) + expand(fbuf.ac_stime);
68916717Ssam 		y = pgtok((u_short)fbuf.ac_mem);
69017504Skarels 		z = expand(fbuf.ac_io) / AHZ;
6911087Sbill 		if (uflg) {
69217504Skarels 			printf("%3d %6.2f cpu %8luk mem %6ld io %.*s\n",
69317504Skarels 			    fbuf.ac_uid, x/(double)AHZ, y, z, NC, fbuf.ac_comm);
6941087Sbill 			continue;
6951087Sbill 		}
6962813Swnj 		up = finduser(fbuf.ac_uid);
6972813Swnj 		if (up == 0)
6982813Swnj 			continue;	/* preposterous user id */
6992813Swnj 		up->us_cnt++;
70017504Skarels 		up->us_ctime += x/(double)AHZ;
7012813Swnj 		up->us_imem += x * y;
7022813Swnj 		up->us_io += z;
7031087Sbill 		ncom += 1.0;
7042813Swnj 
7052813Swnj 		tp = enter(fbuf.ac_comm);
7062813Swnj 		tp->p.imem += x * y;
7071087Sbill 		timem += x * y;
7082813Swnj 		tp->p.count++;
70911840Ssam 		x = expand(fbuf.ac_etime);
7102813Swnj 		tp->p.realt += x;
7111087Sbill 		treal += x;
7121087Sbill 		x = expand(fbuf.ac_utime);
7132813Swnj 		tp->p.cput += x;
7141087Sbill 		tcpu += x;
7151087Sbill 		x = expand(fbuf.ac_stime);
7162813Swnj 		tp->p.syst += x;
7171087Sbill 		tsys += x;
7182813Swnj 		tp->p.io += z;
7191087Sbill 		tio += z;
7201087Sbill 	}
7211087Sbill 	fclose(ff);
7221087Sbill }
7231087Sbill 
7242813Swnj /*
7252813Swnj  *	Generalized cell compare routine, to cast out users
7262813Swnj  */
cellcmp(p1,p2)7272813Swnj cellcmp(p1, p2)
72811840Ssam 	cell *p1, *p2;
7292813Swnj {
7302813Swnj 	if (ISPROCESS(p1)){
7312813Swnj 		if (ISPROCESS(p2))
73214451Smckusick 			return((*cmp)(p1, p2));
7332813Swnj 		return(-1);
7342813Swnj 	}
7352813Swnj 	if (ISPROCESS(p2))
7362813Swnj 		return(1);
7372813Swnj 	return(0);
7382813Swnj }
73911840Ssam 
ncmp(p1,p2)7401087Sbill ncmp(p1, p2)
74111840Ssam 	cell *p1, *p2;
7421087Sbill {
7431087Sbill 
7442813Swnj 	if(p1->p.count == p2->p.count)
7451087Sbill 		return(tcmp(p1, p2));
7461087Sbill 	if(rflg)
7472813Swnj 		return(p1->p.count - p2->p.count);
7482813Swnj 	return(p2->p.count - p1->p.count);
7491087Sbill }
7501087Sbill 
bflgcmp(p1,p2)75137851Sbostic bflgcmp(p1, p2)
75211840Ssam 	cell *p1, *p2;
7531087Sbill {
7541087Sbill 	double f1, f2;
7551087Sbill 	double sum();
7561087Sbill 
7572813Swnj 	f1 = sum(p1)/p1->p.count;
7582813Swnj 	f2 = sum(p2)/p2->p.count;
7591087Sbill 	if(f1 < f2) {
7601087Sbill 		if(rflg)
7611087Sbill 			return(-1);
7621087Sbill 		return(1);
7631087Sbill 	}
7641087Sbill 	if(f1 > f2) {
7651087Sbill 		if(rflg)
7661087Sbill 			return(1);
7671087Sbill 		return(-1);
7681087Sbill 	}
7691087Sbill 	return(0);
7701087Sbill }
7711087Sbill 
Kcmp(p1,p2)7721087Sbill Kcmp(p1, p2)
77311840Ssam 	cell *p1, *p2;
7741087Sbill {
7751087Sbill 
7762813Swnj 	if (p1->p.imem < p2->p.imem) {
7771087Sbill 		if(rflg)
7781087Sbill 			return(-1);
7791087Sbill 		return(1);
7801087Sbill 	}
7812813Swnj 	if (p1->p.imem > p2->p.imem) {
7821087Sbill 		if(rflg)
7831087Sbill 			return(1);
7841087Sbill 		return(-1);
7851087Sbill 	}
7861087Sbill 	return(0);
7871087Sbill }
7881087Sbill 
kcmp(p1,p2)7891087Sbill kcmp(p1, p2)
79011840Ssam 	cell *p1, *p2;
7911087Sbill {
7921087Sbill 	double a1, a2;
7931087Sbill 
7942813Swnj 	a1 = p1->p.imem / ((p1->p.cput+p1->p.syst)?(p1->p.cput+p1->p.syst):1);
7952813Swnj 	a2 = p2->p.imem / ((p2->p.cput+p2->p.syst)?(p2->p.cput+p2->p.syst):1);
7961087Sbill 	if (a1 < a2) {
7971087Sbill 		if(rflg)
7981087Sbill 			return(-1);
7991087Sbill 		return(1);
8001087Sbill 	}
8011087Sbill 	if (a1 > a2) {
8021087Sbill 		if(rflg)
8031087Sbill 			return(1);
8041087Sbill 		return(-1);
8051087Sbill 	}
8061087Sbill 	return(0);
8071087Sbill }
8081087Sbill 
dcmp(p1,p2)8091087Sbill dcmp(p1, p2)
81011840Ssam 	cell *p1, *p2;
8111087Sbill {
8121087Sbill 	double a1, a2;
8131087Sbill 
8142813Swnj 	a1 = p1->p.io / (p1->p.count?p1->p.count:1);
8152813Swnj 	a2 = p2->p.io / (p2->p.count?p2->p.count:1);
8161087Sbill 	if (a1 < a2) {
8171087Sbill 		if(rflg)
8181087Sbill 			return(-1);
8191087Sbill 		return(1);
8201087Sbill 	}
8211087Sbill 	if (a1 > a2) {
8221087Sbill 		if(rflg)
8231087Sbill 			return(1);
8241087Sbill 		return(-1);
8251087Sbill 	}
8261087Sbill 	return(0);
8271087Sbill }
8281087Sbill 
Dcmp(p1,p2)8291087Sbill Dcmp(p1, p2)
83011840Ssam 	cell *p1, *p2;
8311087Sbill {
8321087Sbill 
8332813Swnj 	if (p1->p.io < p2->p.io) {
8341087Sbill 		if(rflg)
8351087Sbill 			return(-1);
8361087Sbill 		return(1);
8371087Sbill 	}
8382813Swnj 	if (p1->p.io > p2->p.io) {
8391087Sbill 		if(rflg)
8401087Sbill 			return(1);
8411087Sbill 		return(-1);
8421087Sbill 	}
8431087Sbill 	return(0);
8441087Sbill }
8451087Sbill 
tcmp(p1,p2)8461087Sbill tcmp(p1, p2)
84711840Ssam 	cell *p1, *p2;
8481087Sbill {
8491087Sbill 	extern double sum();
8501087Sbill 	double f1, f2;
8511087Sbill 
8521087Sbill 	f1 = sum(p1);
8531087Sbill 	f2 = sum(p2);
8541087Sbill 	if(f1 < f2) {
8551087Sbill 		if(rflg)
8561087Sbill 			return(-1);
8571087Sbill 		return(1);
8581087Sbill 	}
8591087Sbill 	if(f1 > f2) {
8601087Sbill 		if(rflg)
8611087Sbill 			return(1);
8621087Sbill 		return(-1);
8631087Sbill 	}
8641087Sbill 	return(0);
8651087Sbill }
8661087Sbill 
sum(p)8671087Sbill double sum(p)
86811840Ssam 	cell *p;
8691087Sbill {
8701087Sbill 
8712813Swnj 	if(p->p.name[0] == 0)
8721087Sbill 		return(0.0);
8732813Swnj 	return( p->p.cput + p->p.syst);
8741087Sbill }
8751087Sbill 
init()8761087Sbill init()
8771087Sbill {
87811840Ssam 	struct user userbuf;
87911840Ssam 	struct process	tbuf;
88011840Ssam 	register cell *tp;
88111840Ssam 	register struct user *up;
88211840Ssam 	int uid;
8831087Sbill 	FILE *f;
8841087Sbill 
88517504Skarels 	if ((f = fopen(savacct, "r")) == NULL)
8861087Sbill 		goto gshm;
8872813Swnj 	while (fread((char *)&tbuf, sizeof(struct process), 1, f) == 1) {
8882813Swnj 		tp = enter(tbuf.name);
8891087Sbill 		ncom += tbuf.count;
8902813Swnj 		tp->p.count = tbuf.count;
8911087Sbill 		treal += tbuf.realt;
8922813Swnj 		tp->p.realt = tbuf.realt;
8931087Sbill 		tcpu += tbuf.cput;
8942813Swnj 		tp->p.cput = tbuf.cput;
8951087Sbill 		tsys += tbuf.syst;
8962813Swnj 		tp->p.syst = tbuf.syst;
8971087Sbill 		tio += tbuf.io;
8982813Swnj 		tp->p.io = tbuf.io;
8991087Sbill 		timem += tbuf.imem;
9002813Swnj 		tp->p.imem = tbuf.imem;
9011087Sbill 	}
9021087Sbill 	fclose(f);
9031087Sbill  gshm:
90417504Skarels 	if ((f = fopen(usracct, "r")) == NULL)
9051087Sbill 		return;
9062813Swnj 	for(uid = 0;
9072813Swnj 	    fread((char *)&(userbuf.oldu), sizeof(struct Olduser), 1, f) == 1;
9082813Swnj 	    uid++){
9092813Swnj 		if (userbuf.us_cnt){
9102813Swnj 			up = finduser(uid);
9112813Swnj 			if (up == 0)
9122813Swnj 				continue;	/* preposterous user id */
9132813Swnj 			up->oldu = userbuf.oldu;
9142813Swnj 		}
9152813Swnj 	}
9161087Sbill 	fclose(f);
9171087Sbill }
9181087Sbill 
strip()9191087Sbill strip()
9201087Sbill {
92111840Ssam 	int c;
92211840Ssam 	register struct allocbox *allocwalk;
92311840Ssam 	register cell *tp, *ub, *junkp;
9241087Sbill 
9252813Swnj 	if (fflg)
9262813Swnj 		printf("Categorizing commands used %d times or fewer as **junk**\n",
9272813Swnj 			thres);
9282813Swnj 	junkp = enter("**junk**");
9292813Swnj 	PROCESSITERATE(allocwalk, tp, ub){
9302813Swnj 		if (tp->p.name[0] && tp->p.count <= thres) {
9312813Swnj 			if (!fflg)
9322813Swnj 				printf("%.14s--", tp->p.name);
9332813Swnj 			if (fflg || ((c=getchar())=='y')) {
9342813Swnj 				tp->p.name[0] = '\0';
9352813Swnj 				junkp->p.count += tp->p.count;
9362813Swnj 				junkp->p.realt += tp->p.realt;
9372813Swnj 				junkp->p.cput += tp->p.cput;
9382813Swnj 				junkp->p.syst += tp->p.syst;
9392813Swnj 				junkp->p.imem += tp->p.imem;
9402813Swnj 				junkp->p.io += tp->p.io;
9411087Sbill 			}
9422813Swnj 			if (!fflg)
9432813Swnj 				while (c && c!='\n')
9442813Swnj 					c = getchar();
9451087Sbill 		}
9461087Sbill 	}
9471087Sbill }
9481087Sbill 
9491087Sbill time_t
expand(t)9501087Sbill expand(t)
95111840Ssam 	unsigned t;
9521087Sbill {
9531087Sbill 	register time_t nt;
9541087Sbill 
9551087Sbill 	nt = t&017777;
9561087Sbill 	t >>= 13;
9571087Sbill 	while (t!=0) {
9581087Sbill 		t--;
9591087Sbill 		nt <<= 3;
9601087Sbill 	}
9611087Sbill 	return(nt);
9621087Sbill }
9631087Sbill 
96411840Ssam static	char UserKey[NAMELG + 2];
96511840Ssam 
96611840Ssam char *
makekey(uid)96711840Ssam makekey(uid)
96811840Ssam 	int uid;
9692813Swnj {
97032450Sbostic 	(void)sprintf(UserKey+1, "%04x", uid);
9712813Swnj 	UserKey[0] = USERKEY;
9722813Swnj 	return(UserKey);
9732813Swnj }
9741087Sbill 
97511840Ssam struct user *
wasuser(uid)97611840Ssam wasuser(uid)
97711840Ssam 	int uid;
9782813Swnj {
97911840Ssam 	struct user *tp;
98011840Ssam 
9812813Swnj 	htabinstall = 0;
9822813Swnj 	tp = finduser(uid);
9832813Swnj 	htabinstall = 1;
9842813Swnj 	return(tp);
9852813Swnj }
98611840Ssam 
9872813Swnj /*
9882813Swnj  *	Only call this if you really want to insert it in the table!
9892813Swnj  */
99011840Ssam struct user *
finduser(uid)99111840Ssam finduser(uid)
99211840Ssam 	int uid;
9932813Swnj {
99411840Ssam 
9952813Swnj 	if (uid > maxuser){
9962813Swnj 		fprintf(stderr, "Preposterous user id, %d: ignored\n", uid);
9972813Swnj 		return(0);
9982813Swnj 	}
99911840Ssam 	return((struct user*)enter(makekey(uid)));
10002813Swnj }
10011087Sbill 
10022813Swnj /*
10032813Swnj  *	Set the names of all users in the password file.
10042813Swnj  *	We will later not print those that didn't do anything.
10052813Swnj  */
getnames()10062813Swnj getnames()
10071087Sbill {
100811840Ssam 	register struct user *tp;
10091087Sbill 	register struct passwd *pw;
10101087Sbill 	struct passwd *getpwent();
10111087Sbill 
10122813Swnj 	setpwent();
10132813Swnj 	while (pw = getpwent()){
101417504Skarels 		/* use first name in passwd file for duplicate uid's */
101517504Skarels 		if ((tp = wasuser(pw->pw_uid)) != 0 && !isalpha(tp->us_name[0]))
10162813Swnj 			strncpy(tp->us_name, pw->pw_name, NAMELG);
10171087Sbill 	}
10181087Sbill 	endpwent();
10191087Sbill }
10202813Swnj 
102111840Ssam int
getmaxuid()102211840Ssam getmaxuid()
10232813Swnj {
102411840Ssam 	register struct user *tp;
102511840Ssam 	register struct passwd *pw;
102611840Ssam 	struct passwd *getpwent();
102711840Ssam 	int maxuid = -1;
10282813Swnj 
10292813Swnj 	setpwent();
10302813Swnj 	while(pw = getpwent()){
10312813Swnj 		if (pw->pw_uid > maxuid)
10322813Swnj 			maxuid = pw->pw_uid;
10332813Swnj 	}
10342813Swnj 	endpwent();
10352813Swnj 	return(maxuid);
10362813Swnj }
10372813Swnj 
tabinit()10382813Swnj tabinit()
10392813Swnj {
10402813Swnj 	allochead = 0;
10412813Swnj 	alloctail = 0;
10422813Swnj 	nexttab = 0;
10432813Swnj 	tabsleft = 0;
10442813Swnj 	htab = 0;
10452813Swnj 	ntabs = 0;
10462813Swnj 	htaballoc();		/* get the first part of the hash table */
10472813Swnj }
10482813Swnj 
10492813Swnj #define ALLOCQTY 	sizeof (struct allocbox)
105011840Ssam cell *
taballoc()105111840Ssam taballoc()
10522813Swnj {
105311840Ssam 
10542813Swnj 	if (tabsleft == 0){
10552813Swnj 		newbox = (struct allocbox *)calloc(1, ALLOCQTY);
10562813Swnj 		tabsleft = TABDALLOP;
10572813Swnj 		nexttab = &newbox->tabslots[0];
10582813Swnj 		if (alloctail == 0){
10592813Swnj 			allochead = alloctail = newbox;
10602813Swnj 		} else {
10612813Swnj 			alloctail->nextalloc = newbox;
10622813Swnj 			alloctail = newbox;
10632813Swnj 		}
10642813Swnj 	}
10652813Swnj 	--tabsleft;
10662813Swnj 	++ntabs;
10672813Swnj #ifdef DEBUG
10682813Swnj 	if (ntabs % 100 == 0)
10692813Swnj 		printf("##Accounting table slot # %d\n", ntabs);
10702813Swnj #endif DEBUG
10712813Swnj 	return(nexttab++);
10722813Swnj }
10732813Swnj 
htaballoc()10742813Swnj htaballoc()
10752813Swnj {
107611840Ssam 	register struct hashdallop *new;
10772813Swnj #ifdef DEBUG
107811840Ssam 	static int ntables = 0;
107911840Ssam 
10802813Swnj 	printf("%%%New hash table chunk allocated, number %d\n", ++ntables);
10812813Swnj #endif DEBUG
10822813Swnj 	new = (struct hashdallop *)calloc(1, sizeof (struct hashdallop));
10832813Swnj 	if (htab == 0)
10842813Swnj 		htab = new;
10852813Swnj 	else {		/* add AFTER the 1st slot */
10862813Swnj 		new->h_next = htab->h_next;
10872813Swnj 		htab->h_next = new;
10882813Swnj 	}
10892813Swnj }
10902813Swnj 
10912813Swnj #define 	HASHCLOGGED	(NHASH / 2)
10922813Swnj /*
10932813Swnj  *	Lookup a symbol passed in as the argument.
10942813Swnj  *
10952813Swnj  *	We take pains to avoid function calls; this function
10962813Swnj  *	is called quite frequently, and the calling overhead
10972813Swnj  *	contributes significantly to the overall execution speed of sa.
10982813Swnj  */
109911840Ssam cell *
enter(name)110011840Ssam enter(name)
110111840Ssam 	char *name;
11022813Swnj {
110311840Ssam 	static int initialprobe;
110411840Ssam 	register cell **hp;
110511840Ssam 	register char *from, *to;
110611840Ssam 	register int len, nprobes;
110711840Ssam 	static struct hashdallop *hdallop, *emptyhd;
110811840Ssam 	static cell **emptyslot, **hp_ub;
11092813Swnj 
11102813Swnj 	emptyslot = 0;
11112813Swnj 	for (nprobes = 0, from = name, len = 0;
11122813Swnj 	     *from && len < NC;
11132813Swnj 	     nprobes <<= 2, nprobes += *from++, len++)
11142813Swnj 		continue;
11152813Swnj 	nprobes += from[-1] << 5;
11162813Swnj 	nprobes %= NHASH;
11172813Swnj 	if (nprobes < 0)
11182813Swnj 		nprobes += NHASH;
11192813Swnj 
11202813Swnj 	initialprobe = nprobes;
11212813Swnj 	for (hdallop = htab; hdallop != 0; hdallop = hdallop->h_next){
11222813Swnj 		for (hp = &(hdallop->h_tab[initialprobe]),
11232813Swnj 				nprobes = 1,
11242813Swnj 				hp_ub = &(hdallop->h_tab[NHASH]);
11252813Swnj 		     (*hp) && (nprobes < NHASH);
11262813Swnj 				hp += nprobes,
11272813Swnj 				hp -= (hp >= hp_ub) ? NHASH:0,
11282813Swnj 				nprobes += 2)
11292813Swnj 		{
11302813Swnj 			from = name;
11312813Swnj 			to = (*hp)->p.name;
11322813Swnj 
11332813Swnj 			for (len = 0; (len<NC) && *from; len++)
11342813Swnj 				if (*from++ != *to++)
11352813Swnj 					goto nextprobe;
11362813Swnj 			if (len >= NC)		/*both are maximal length*/
11372813Swnj 				return(*hp);
11382813Swnj 			if (*to == 0)		/*assert *from == 0*/
11392813Swnj 				return(*hp);
11402813Swnj 	nextprobe: ;
11412813Swnj 		}
11422813Swnj 		if (*hp == 0 && emptyslot == 0 &&
11432813Swnj 		    hdallop->h_nused < HASHCLOGGED) {
11442813Swnj 			emptyslot = hp;
11452813Swnj 			emptyhd = hdallop;
11462813Swnj 		}
11472813Swnj 	}
11482813Swnj 	if (emptyslot == 0) {
11492813Swnj 		htaballoc();
11502813Swnj 		hdallop = htab->h_next;		/* aren't we smart! */
11512813Swnj 		hp = &hdallop->h_tab[initialprobe];
11522813Swnj 	} else {
11532813Swnj 		hdallop = emptyhd;
11542813Swnj 		hp = emptyslot;
11552813Swnj 	}
11562813Swnj 	if (htabinstall){
11572813Swnj 		*hp = taballoc();
11582813Swnj 		hdallop->h_nused++;
11592813Swnj 		for(len = 0, from = name, to = (*hp)->p.name; (len<NC); len++)
11602813Swnj 			if ((*to++ = *from++) == '\0')
11612813Swnj 				break;
11622813Swnj 		return(*hp);
11632813Swnj 	}
11642813Swnj 	return(0);
116511840Ssam }
1166