#include "../sccshead/sfile.h"
#include "../sccshead/statbuf.h"
#include "../sccshead/sint.h"
# include "../sccshead/had.h"


int	num_files;
char	had[26];
# define MINR 1
# define MAXR 9999
# define DESCSIZE 100
# define PERSONSIZE 14
# define TYPESIZE 10

char admin_____[] "~|^`admin.c:	3.6";


int irel, ceil, floor, numlocks, numulocks, rdef, fexists, didargs;
int lock[5], unlock[5];
char *desc, *person, *type, *ifile;

/*	aulist is the list of users to be added to Hulist,
*	eulist is the list of users to be erased from Hulist.
*/

char aulist[32]; /* initialized to zero */
char eulist[32] {	0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
			0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
			0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
			0377, 0377, 0377, 0377, 0377, 0377, 0377, 0377,
};
struct Ibufr pbuf;

main(argc,argv)
int argc;
char *argv[];
{
	register int j;
	register char *p;
	char c;
	int testmore, testklt;
	int i;
	int uid, mask, k;
	extern admin();

	opnl(&pbuf,"/etc/passwd");
	testklt = 1;

	for(j=1; j<argc; j++)
		if(argv[j][0] == '-' && (c=argv[j][1])) {
			p = &argv[j][2];
			testmore = 0;
			switch (c) {

			case 'd':
				desc = p;
				if (size(desc) > DESCSIZE)
					fatal("description too large (471)");
				break;

			case 'p':
				person = p;
				if (size(person) > PERSONSIZE)
					fatal("person too large (472)");
				break;

			case 't':
				type = p;
				if (size(type) > TYPESIZE)
					fatal("type too large (473)");
				break;

			case 'i':
				ifile = p;
				break;

			case 'r':
				if ((irel = patoi(p)) == -1)
					fatal("bad r argument (454)");
				if ((irel < MINR) || (irel > MAXR))
					fatal("bad r argument (455)");
				break;

			case 'c':
				if ((ceil = patoi(p)) == -1)
					fatal("bad ceiling (450)");
				if ((ceil < MINR) || (ceil > MAXR))
					fatal("bad ceiling (451)");
				break;

			case 'f':
				if ((floor = patoi(p)) == -1)
					fatal("bad floor (452)");
				if ((floor < MINR) || (floor > MAXR))
					fatal("bad floor (453)");
				break;

			case 'l':
				testklt = 0;
				if (numlocks >= 5)
					fatal("too many locks (456)");
				if ((i = patoi(p)) == -1)
					fatal("bad lock (457)");
				if ((i < MINR) || (i > MAXR))
					fatal ("bad lock (458)");
				chkr(i);
				lock[numlocks++] = i;
				break;

			case 'u':
				testklt = 0;
				if (numulocks >= 5)
					fatal("too many unlocks (459)");
				if ((i = patoi(p)) == -1)
					fatal("bad unlock (460)");
				if ((i < MINR) || (i > MAXR))
					fatal("bad unlock (461)");
				chkr(i);
				unlock[numulocks++] = i;
				break;

			case 'a':
				testklt = 0;
				uid = getluid(p);
				mask = 1;
				for (k=uid&7; k; k--) mask =<< 1;
				aulist[uid>>3] =| mask;
				break;

			case 'e':
				testklt = 0;
				if ((uid = patoi(p))== -1) uid = getluid(p);
				mask = 1;
				for (k=uid&7; k; k--) mask =<< 1;
				eulist[uid>>3] =& ~mask;
				break;

			case 'y':
				if ((rdef = patoi(p)) == -1)
					fatal("bad y argument (462)");
				if ((rdef<MINR || rdef>MAXR) && rdef!=0)
					fatal("bad y argument (463)");
				break;

			case 'z':
			case 'n':
				testmore++;
				break;

			default:
				fatal("unknown key letter (69)");
			}

			if (testmore) {
				testmore = 0;
				if (*p)
					fatal(stringf(
					  "value after %c arg (232)",c));
			}
			if (had[c - 'a']++ && testklt++)
				fatal("key letter twice (464)");
			argv[j] = 0;
		}
		else num_files++;

	if(num_files == 0)
		fatal("missing file arg (49)");
	setsig(fatal,fatal,fatal);
	for (j=1; j<argc; j++)
		if (p=argv[j])
			do_file(p,admin);
	exit(0);
}


/*	`Main' routine.
*	Note that keyletter args are only processed once.
*/

admin(afile)
char *afile;
{
	register struct Header *hdr;
	register int i, k;
	int fd, rel, ohash, nhash;
	int chkunlk[5];
	char *cp;
	struct Reltab rt;
	struct Deltab dt;
	struct Control ctl;
	struct Ibufr ibuf;
	struct Obufr obuf;
	struct Header ahdr;
	extern char *fatal_file;
	extern nfiles;

	hdr = &ahdr;
	if (!same(sname(afile),"s.",2))
		fatal("not an SCCS file (301)");
	fexists = exists(afile);
	if (HADI && nfiles > 0)
		fatal("more than one file (465)");

	if (HADI) HADN = 1;
	if (HADN && fexists)
		fatal("file exists (466)");
	if (!HADN && !fexists)
		fatal("file does not exist (467)");

	if (fexists) {
		chmod(afile,fexists|(IREAD|IWRITE));
		fd = xopen(afile,2);
		chmod(afile,fexists);
		seek(fd,2,0);
		if (read(fd,hdr,sizeof(*hdr))!=sizeof(*hdr))
			fatal("read error (468)");
		ohash = hash(hdr,&hdr->Hash);
		if (hdr->Hash==0) HADZ = 1;
	}
	else {
		crtr(&obuf,afile,0444);
		hdr->Hmagicno = MAGICNO;
	}

	if (HADD) copy(desc,hdr->Hdesc);
	if (HADP) copy(person,hdr->Hpers);
	if (HADT) copy(type,hdr->Htype);
	if (HADC) hdr->Hceil = ceil;
	if (HADF) hdr->Hfloor = floor;

	if (HADU) {
		for (k=0; k<numulocks; k++) {
			chkunlk[k] = unlock[k];
			for (i=0; i<5; i++)
				if (hdr->Hsw[i]==unlock[k])
					hdr->Hsw[i] = chkunlk[k] = 0;
		}
		for (k=0; k<numulocks; k++) if (chkunlk[k])
			msg2(stringf("[%s]: Release %d wasn't locked (469)\n",
				fatal_file,chkunlk[k]));
		for (k=0; k<5; k++)
			if (hdr->Hsw[k]==0)
				for (i=k+1; i<5; i++)
					if (hdr->Hsw[k] = hdr->Hsw[i]) {
						hdr->Hsw[i] = 0;
						break;
					}
	}
	if (HADL) {
		for (k=0; k<5; k++)
			if (hdr->Hsw[k]==0) break;
		for (i=0; i<numlocks; i++) {
			if (k==5)
				fatal("too many locks (470)");
			hdr->Hsw[k++] = lock[i];
		}
	}
	if (HADA)
		for (i=0; i<32; i++) hdr->Hulist[i] =| aulist[i];
	if (HADE)
		for (i=0; i<32; i++) hdr->Hulist[i] =& eulist[i];
	if (HADY) hdr->Hrdef = rdef;

	if (fexists) {
		nhash = hash(hdr,&hdr->Hash);
		hdr->Hash =+ nhash-ohash;
		if (HADZ) hdr->Hash = 0;
		seek(fd,2,0);
		if (write(fd,hdr,sizeof(*hdr))!=sizeof(*hdr))
			fatal("write error (474)");
		close(fd);
		return;
	}

/*	Thie following is really a large else clause
*	to complement the `if (fexists)' above.
*	I.e., we get here if the file did not exist.
*/

	putr(&obuf,hdr,sizeof(*hdr));
	if (HADI) {
		if (HADR) rel = rt.Rrel = irel;
		else rel = rt.Rrel = 1;
		rt.Rlevs = 1;
		putr(&obuf,&rt,sizeof(rt));
	}
	rt.Rrel = rt.Rlevs = 0;
	putr(&obuf,&rt,sizeof(rt));
	dt.Dtype = 'D';
	dt.Dfill = ' ';
	time(&dt.Ddatetime);
	if ((cp=getlnam())==0)
		fatal("Who are you? (75)");
	copy(cp,dt.Dpgmr);
	dt.Dhist[0] = '\0';
	if (HADI) {
		dt.Drel = rel;
		dt.Dlev = 1;
		putr(&obuf,&dt,sizeof(dt)-(sizeof(dt.Dhist)-1));
	}
	dt.Drel = dt.Dlev = 0;
	putr(&obuf,&dt,sizeof(dt)-(sizeof(dt.Dhist)-1));

	if (HADI) {
		ctl.Crel = rel;
		ctl.Clev = 1;
		ctl.Cctl = INS;
		putr(&obuf,&ctl,SIZEOFCONTROL);
		if (*ifile) opnl(&ibuf,ifile);
		else opnl(&ibuf,0,0);
		for (k = 1; getl(&ibuf) != 1; k++) {
			cp = ibuf.Irecptr;
			i = ibuf.Ilen - 1;
			cp[i] = 0;
			if (ctlrec(cp,i))
				fatal(stringf("illegal data on line %d (312)",
					k));
			putr(&obuf,cp,i);
		}
		close(ibuf.Ifildes);
		ctl.Cctl = END;
		putr(&obuf,&ctl,SIZEOFCONTROL);
	}
	ctl.Crel = ctl.Clev = 0;
	ctl.Cctl = END;
	putr(&obuf,&ctl,SIZEOFCONTROL);
	flshr(&obuf,HADZ);
	close(obuf.Ofildes);
}


/*	This is really a typo check (if you type the same release
*	number twice this routine catches it).
*/

chkr(r)
{
	register int i;

	for (i=0; i<numlocks; i++)
		if (r==lock[i])
			fatal(stringf("release %d twice (475)",r));
	for (i=0; i<numulocks; i++)
		if (r==unlock[i])
			fatal(stringf("release %d twice (476)",r));
}


/*	Computes the integer sum of memory in the interval [ip1,ip2).
*/

hash(ip1,ip2)
int *ip1,*ip2;
{
	register int *rip, sum;

	sum = 0;
	for (rip=ip1; rip<ip2; rip++) sum =+ *rip;
	return(sum);
}


/*	Returns the numerical user ID of the argument string (s).
*/

getluid(s)
char *s;
{
	register char *p;
	register int i,uid;

	seek(pbuf.Ifildes,0,0);
	opnl(&pbuf,0,pbuf.Ifildes);
	i = size(s) - 1;
	while ((p=getl(&pbuf))!=1) {
		if (p[i]==':') {
			p[i] = '\0';
			if (!equal(p,s)) continue;
			for (p= &p[i+1]; *p++ !=':'; );
			uid = 0;
			for (; *p!=':'; p++) uid = uid*10+(*p-'0');
			return(uid);
		}
	}
	fatal(stringf("%s not in passwd file (477)",s));
}
