DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T a

⟦b0108a695⟧ TextFile

    Length: 21617 (0x5471)
    Types: TextFile
    Names: »add.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec8/mcp/src/add.c« 

TextFile

/***************************************************************\
* 							        *
* 	add.c						        *
* 							        *
* Routines to add various things, users, groups, classes, etc.  *
* 							        *
\***************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <strings.h>
#include <ctype.h>
#include <lastlog.h>
#include "sysdep.h"
#include "macros.h"
#include "mem.h"
#include "gpa.h"
#include "lists.h"
#include "account.h"
#ifdef SENDMAIL
#include "alias.h"
#endif
#include "class.h"
#include "groupmap.h"
#include "job.h"
#include "range.h"
#include "sig.h"
#include "sort.h"
#include "save.h"

#define DAY	(4*21600)

#ifdef SENDMAIL
extern	struct list AliasList, Aliases;
#endif
extern	struct list AccountList, Users, ClassList, Classes, GroupMapList;
extern	struct list Groups, RangeList, Ranges, Sigs, SigList, Vigs, Shells;
extern	struct list Null_List;
extern	int ModBits, validint();
extern	addr gethomedir();
extern	addr makeusername(), DEF_SHELL;
extern	char *crypt(), *mktemp(), *sprintf(), *makepass(), *when();
extern	time_t choosedate();

static	char *XXXXXX = "/mcpXXXXXX";
static	char desc[DESCSIZE+1];

static	char *idl[1] = { "exception" };
static	char *pwl[3] = { "generate", "none", "unused" };
static	char *rnl[1];
static	char *mdl[2] = { "exclusive", "shared" };

struct	list idlist = {	1, 1, (addr *)idl };
struct	list pwlist = { 3, 3, (addr *)pwl };
struct	list rnlist = { 1, 1, (addr *)rnl };
struct	list mdlist = { 2, 2, (addr *)mdl };

#ifdef SENDMAIL
/*
 * Add an alias
 */
addalias(c, v)
int c;
addr *v;

{
	struct alias al;
	struct account *ac;
	addr *addressv;
	int cc;
	register int i;

	if (c > 2) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	if (aliasexists((char *)v[1])) {
		err1("%s: alias exists", (char *)v[1]);
		return;
	}
	addressv = get_gpa(257);
	GetLine("Addresses: ", 256, &cc, addressv, &Null_List);

	critical();
	savestr(&al.al_name, (char *)v[1]);
	zerolist(&al.al_addresses);
	zerolist(&al.al_classes);
	zerolist(&al.al_sigs);
	zerolist(&al.al_groups);
	for (i=0; i < cc; i++) {
		strlistadd(&al.al_addresses, (char *)addressv[i]);
		ac = getacnam((char *)addressv[i]);
		if (ac)
			strlistadd(&ac->ac_aliases, (char *)addressv[i]);
	}
	sort_list(&al.al_addresses, pstrcmp);
	strlistadd(&Aliases, (char *)v[1]);
	genlistadd(&AliasList, (addr)&al, sizeof (struct alias));
	sort_list(&Aliases, pstrcmp);
	sort_list(&AliasList, aliascmp);
	ModBits |= AL;
	puts("added");
	non_critical();

	return;
}
#endif
	
/*
 * Add a class
 */
addclass(c, v)
int c;
addr *v;

{
	struct class cs;
	struct stat statbuf;
	char tempf[MEDIUM_BUF], errmsg[LONG_BUF];
	FILE *f, *fopen();
	time_t now, time();
	int tries, i, ch;

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if ( c != 2 ) {
		err1("usage: %s <class>", (char *)v[0]);
		return;
	}
	if (classexists((char *)v[1])) {
		err1("%s: class exists", (char *)v[1]);
		return;
	}
	if (yesno("Should the class expire? ") == 0)
		cs.cs_exptime = (time_t) 0;
	else {
		(void) time(&now);
		err("Set the expiration date.");
		cs.cs_exptime = choosedate(now);
		(void) printf("Ends %s\n", when(cs.cs_exptime));
	}
	(void) strcpy(tempf, TMPDIR);
	(void) strcat(tempf, XXXXXX);
	(void) mktemp(tempf);
	tries = 0;
edit_it:
	tries++;
	f = fopen(tempf, "w");
	if (f == NULL) {
		err1("%s: cannot open (write)", tempf);
		return;
	}
	(void) fprintf(f, "Instructor: \n\n");
	(void) fprintf(f, "...\n");
	(void) fclose(f);
re_edit:
	edit(tempf);
	if (stat(tempf, &statbuf) == -1) {
		perr(tempf);
		if (tries < 5) {
			sleep(2);
			goto edit_it;
		}
		else {
			err1("%s aborted", (char *)v[0]);
			(void) unlink(tempf);
			return;
		}
	}
	if (statbuf.st_size > DESCSIZE) {
		(void)sprintf(errmsg, "description is %d characters too long",
			DESCSIZE - statbuf.st_size);
		err(errmsg);
		sleep(2);
		goto re_edit;
	}

	critical();
	f = fopen(tempf, "r");
	if (f == NULL) {
		err1("%s: cannot open (read)", tempf);
		non_critical();
		return;
	}
	savestr(&cs.cs_name, (char *)v[1]);
	i = 0;
	while ((ch = getc(f)) != EOF)
		desc[i++] = (char) ch;
	desc[i] = '\0';
	cs.cs_dsize = i;
	savestr(&cs.cs_desc, desc);
	(void) fclose(f);
#ifdef SENDMAIL
	zerolist(&cs.cs_aliases);
#endif
	genlistadd(&ClassList, (addr) &cs, sizeof (struct class));
	strlistadd(&Classes, cs.cs_name);
	sort_list(&ClassList, classcmp);
	sort_list(&Classes, pstrcmp);
	(void) unlink(tempf);
	ModBits |= CS;
	puts("added");
	non_critical();

	return;
}

/*
 * Add a group
 */
addgroup(c, v)
int c;
addr *v;

{
	struct groupmap gm;
	char prompt[SHORT_BUF];
	addr *gidv;
	int cc, gid;

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	if (groupexists((char *)v[1])) {
		err1("%s: group exists", (char *)v[1]);
		return;
	}
	gm.gm_gid = nextgid();
	(void) sprintf(prompt, "Gid [%d]: ", gm.gm_gid);
	gidv = get_gpa(2);
	do {
	    GetLine(prompt, 1, &cc, gidv, &Null_List);
	    if (cc) {
		if (!validint((char *)*gidv)) {
		    err1("%s makes no sense to me", (char *)*gidv);
		    continue;
		}
		gid = atoi((char *)*gidv);
		if (gidexists(gid))
		    err("that gid is taken");
		else {
		    gm.gm_gid = gid;
		    break;
		}
	    }
	    else
			break;
	} while (clear_gpa(gidv, 2));

	critical();
	savestr(&gm.gm_name, (char *)v[1]);
	savestr(&gm.gm_passwd, "*");
	zerolist(&gm.gm_mem);
#ifdef SENDMAIL
	zerolist(&gm.gm_aliases);
#endif
	genlistadd(&GroupMapList, (addr) &gm, sizeof (struct groupmap));
	sort_list(&GroupMapList, gmapcmp);
	strlistadd(&Groups, gm.gm_name);
	sort_list(&Groups, pstrcmp);
	ModBits |= GR;
	puts("added");
	non_critical();

	return;
}

int nextgid()

{
	register int i, next = 0;
	struct groupmap *gm;

	for (i=0; i<GroupMapList.l_count; i++) {
		gm = (struct groupmap *) GroupMapList.l_list[i];
		if (gm->gm_gid > next)
			return next;
		/*
		 * Since gid's may be shared (gag) by two or more group names
		 * the seemingly obvious next++ actually must be...
		 */
		next = gm->gm_gid + 1;
	}
	return next;
}

/*
 * Add a range
 */
addrange(c, v)
int c;
addr *v;

{
	struct range r, *rg;
	char prompt[SHORT_BUF];
	addr *fromv, *tov, *modev;
	int cc, indx;

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	if (!groupexists((char *)v[1])) {
		err1("%s: no such group", (char *)v[1]);
		return;
	}
	(void) strcpy(prompt, "From: ");
	fromv = get_gpa(2);
	do {
		GetLine(prompt, 1, &cc, fromv, &Null_List);
		if (cc && !validint((char *)*fromv)) {
			cc = 0;
			continue;
		}
	} while (cc == 0 && clear_gpa(fromv, 2));

	(void) strcpy(prompt, "To  : ");
	tov = get_gpa(2);
	do {
		GetLine(prompt, 1, &cc, tov, &Null_List);
		if (cc && !validint((char *)*tov)) {
			cc = 0;
			continue;
		}
	} while (cc == 0 && clear_gpa(tov, 2));

	(void) strcpy(prompt, "Mode: ");
	modev = get_gpa(2);
	do {
		GetLine(prompt, 1, &cc, modev, &mdlist);
	} while (cc == 0 && clear_gpa(modev, 2));

	if (eq(*modev, "shared"))
		r.rg_mode = RG_SHARED;
	else if (eq(*modev, "exclusive"))
		r.rg_mode = RG_EXCLUSIVE;
	else {
		err1("%s: unknown mode", (char *)*modev);
		return;
	}
	r.rg_from = atoi((char *)*fromv);
	r.rg_to = atoi((char *)*tov);

	for (indx=0; indx < RangeList.l_count; indx++) {
		rg = (struct range *) RangeList.l_list[indx];
		if (rg->rg_mode == RG_SHARED && r.rg_mode == RG_SHARED)
			continue;
		if (INRANGE(r.rg_from, rg->rg_from, rg->rg_to)) {
			err1("conflicts with range of group %s", rg->rg_name);
			return;
		}
		if (INRANGE(r.rg_to, rg->rg_from, rg->rg_to)) {
			err1("conflicts with range of group %s", rg->rg_name);
			return;
		}
	}

	critical();
	savestr(&r.rg_name, (char *)v[1]);
	genlistadd(&RangeList, (addr) &r, sizeof (struct range));
	sort_list(&RangeList, rangecmp);
	strlistadd(&Ranges, (char *)v[1]);
	sort_list(&Ranges, pstrcmp);
	ModBits |= RG;
	puts("added");
	non_critical();
	return;
}

/*
 * Add a sig
 */
addsig(c, v)
int c;
addr *v;

{
	struct sig sg;
	struct stat statbuf;
	FILE *f, *fopen();
	time_t now, time();
	int tries, i, ch;
	char tempf[SHORT_BUF+1], errmsg[LONG_BUF];

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if ( c != 2 ) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	if (sigexists((char *)v[1])) {
		err1("%s: sig exists", (char *)v[1]);
		return;
	}
	if (yesno("Should the sig expire? ") == 0)
		sg.sg_exptime = (time_t) 0;
	else {
		(void) time(&now);
		err("Set the expiration date.");
		sg.sg_exptime = choosedate(now);
		(void) printf("Sig ends %s\n", when(sg.sg_exptime));
	}
	(void) strcpy(tempf, TMPDIR);
	(void) strcat(tempf, XXXXXX);
	(void) mktemp(tempf);
	tries = 0;
edit_it:
	tries++;
	f = fopen(tempf, "w");
	if (f == NULL) {
		err1("%s: cannot open (write)", tempf);
		return;
	}
	(void) fprintf(f, "Guru: \n\n");
	(void) fprintf(f, "...\n");
	(void) fclose(f);
re_edit:
	edit(tempf);
	if (stat(tempf, &statbuf) == -1) {
		perr(tempf);
		if (tries < 5) {
			sleep(2);
			goto edit_it;
		}
		else {
			err1("%s aborted", (char *)v[0]);
			(void) unlink(tempf);
			return;
		}
	}
	if (statbuf.st_size > DESCSIZE) {
		(void) sprintf(errmsg, 
				"description is %d characters too long",
				DESCSIZE - statbuf.st_size);
		err(errmsg);
		sleep(2);
		goto re_edit;
	}

	critical();
	f = fopen(tempf, "r");
	if (f == NULL) {
		err1("%s: cannot open (read)", tempf);
		non_critical();
		return;
	}
	savestr(&sg.sg_name, (char *)v[1]);
	i = 0;
	while ((ch = getc(f)) != EOF)
		desc[i++] = (char) ch;
	desc[i] = '\0';
	sg.sg_dsize = i;
	savestr(&sg.sg_desc, desc);
	(void) fclose(f);
#ifdef SENDMAIL
	zerolist(&sg.sg_aliases);
#endif
	genlistadd(&SigList, (addr) &sg, sizeof (struct sig));
	strlistadd(&Sigs, sg.sg_name);
	sort_list(&SigList, sigcmp);
	sort_list(&Sigs, pstrcmp);
	(void) unlink(tempf);
	ModBits |= SG;
	puts("added");
	non_critical();

	return;
}

#ifdef SENDMAIL
addtoalias(c, v)
int c;
addr *v;

{
	struct alias *al;
	struct account *ac;
	addr *addressv;
	int cc, added = 0, notes = 0;
	register int indx;

	if (c > 2) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <alias>", (char *)v[0]);
		return;
	}
	al = getalnam((char *)v[1]);
	if (!al) {
		err1("%s: no such alias", (char *)v[1]);
		return;
	}

	addressv = get_gpa(65);
	GetLine("Addresses: ", 64, &cc, addressv, &Null_List);
	if (cc == 0) {
		err("no change");
		return;
	}

	critical();
	for (indx=0; indx < cc; indx++) {
	    ac = getacnam((char *)addressv[indx]);
	    if (ac) {
		if (!instrlist(&ac->ac_aliases, (char *)v[1])) {
		    strlistadd(&ac->ac_aliases, (char *)v[1]);
		    sort_list(&ac->ac_aliases, pstrcmp);
		    ModBits |= AC;
		}
		else {
		    err1("%s: already in alias", (char *)ac->ac_name);
		    continue;
		}
	    }
	    if (!instrlist(&al->al_addresses, (char *)addressv[indx])) {
		strlistadd(&al->al_addresses, (char *)addressv[indx]);
		added++;
	    }
	    else if (!ac)
		err1("%s: already in alias", (char *)addressv[indx]);
	    else {
		err1("%s: unique membership noted", (char *)ac->ac_name);
		notes++;
	    }
	}
	if (added) {
		sort_list(&al->al_addresses, pstrcmp);
		ModBits |= AL;
		(void) printf("%d added\n", added);
	}
	else if (!notes)
		err("no change");
	non_critical();

	return;
}
#endif

addtoclass(c, v)
int c;
addr *v;

{
	struct account *ac;
	struct class *cs;
	addr *userv;
	int cc, added = 0;
	register int indx;
#ifdef SENDMAIL
	struct alias *al;
	register int j;
#endif

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <class>", (char *)v[0]);
		return;
	}
	cs = getcsnam((char *)v[1]);
	if (!cs) {
		err1("%s: no such class", (char *)v[1]);
		return;
	}
	userv = get_gpa(65);
	GetLine("Users: ", 64, &cc, userv, &Users);
	if (cc == 0) {
		err("no change");
		return;
	}

	critical();
	for (indx=0; indx < cc; indx++) {
		ac = getacnam((char *)userv[indx]);
		if (!ac) {
			err1("%s: no such user", (char *)userv[indx]);
			continue;
		}
		if (instrlist(&ac->ac_classes, (char *)v[1])) {
			err1("%s: already is in class\n",
				(char *)userv[indx]);
			continue;
		}
		strlistadd(&ac->ac_classes, (char *)v[1]);
		sort_list(&ac->ac_classes, pstrcmp);
#ifdef SENDMAIL
		for (j=0; j < cs->cs_aliases.l_count; j++) {
		    al = getalnam((char *)cs->cs_aliases.l_list[j]);
		    if (!al) continue;	/* trouble */
		    if (!instrlist(&al->al_addresses, (char *)ac->ac_name)) {
			strlistadd(&al->al_addresses, (char *)ac->ac_name);
			sort_list(&al->al_addresses, pstrcmp);
			ModBits |= AL;
		    }
		}
#endif
		added++;
	}
	if (added) {
	    ModBits |= AC;
	    (void) printf("%d added\n", added);
	}
	else
	    err("no change");
	non_critical();

	return;
}

addtogroup(c, v)
int c;
addr *v;

{
	struct account *ac;
	struct groupmap *gm;
	addr *userv;
	int cc, added = 0;
	register int indx;
#ifdef SENDMAIL
	struct alias *al;
	register int j;
#endif

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <group>", (char *)v[0]);
		return;
	}
	gm = getgmnam((char *)v[1]);
	if (!gm) {
		err1("%s: no such group", (char *)v[1]);
		return;
	}
	userv = get_gpa(65);
	GetLine("Users: ", 64, &cc, userv, &Users);
	if (cc == 0) {
		err("no change");
		return;
	}

	critical();
	for (indx=0; indx < cc; indx++) {
		if (instrlist(&gm->gm_mem, (char *)userv[indx])) {
			err1("%s: is in group", (char *)userv[indx]);
			continue;
		}
		ac = getacnam((char *)userv[indx]);
		if (!ac) {
			err1("%s: no such user", (char *)userv[indx]);
			continue;
		}
		strlistadd(&ac->ac_groups, (char *)v[1]);
		strlistadd(&gm->gm_mem, (char *)userv[indx]);
		sort_list(&ac->ac_groups, pstrcmp);
#ifdef SENDMAIL
		for (j=0; j < gm->gm_aliases.l_count; j++) {
		    al = getalnam((char *)gm->gm_aliases.l_list[j]);
		    if (!al) continue;	/* trouble */
		    if (!instrlist(&al->al_addresses, (char *)ac->ac_name)) {
			strlistadd(&al->al_addresses, (char *)ac->ac_name);
			sort_list(&al->al_addresses, pstrcmp);
			ModBits |= AL;
		    }
		}
#endif
		added++;
	}
	if (added) {
		ModBits |= AC|GR;
		sort_list(&gm->gm_mem, pstrcmp);
		(void) printf("%d added\n", added);
	}
	else
		err("no change");
	non_critical();

	return;
}

addtosig(c, v)
int c;
addr *v;

{
	struct account *ac;
	struct sig *sg;
	addr *userv;
	int cc, added = 0;
	register int indx;
#ifdef SENDMAIL
	struct alias *al;
	register int j;
#endif

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <sig>", (char *)v[0]);
		return;
	}
	sg = getsgnam((char *)v[1]);
	if (!sg) {
		err1("%s: no such sig", (char *)v[1]);
		return;
	}
	userv = get_gpa(65);
	GetLine("Users: ", 64, &cc, userv, &Users);
	if (cc == 0) {
		err("no change");
		return;
	}

	critical();

	for (indx=0; indx < cc; indx++) {
		ac = getacnam((char *)userv[indx]);
		if (!ac) {
			err1("%s: no such user", (char *)userv[indx]);
			continue;
		}
		if (instrlist(&ac->ac_sigs, (char *)v[1])) {
			err1("%s: already is in sig", (char *)userv[indx]);
			continue;
		}
		strlistadd(&ac->ac_sigs, (char *)v[1]);
		sort_list(&ac->ac_sigs, pstrcmp);
#ifdef SENDMAIL
		for (j=0; j < sg->sg_aliases.l_count; j++) {
		    al = getalnam((char *)sg->sg_aliases.l_list[j]);
		    if (!al) continue;	/* trouble */
		    if (!instrlist(&al->al_addresses, (char *)ac->ac_name)) {
			strlistadd(&al->al_addresses, (char *)ac->ac_name);
			sort_list(&al->al_addresses, pstrcmp);
			ModBits |= AL;
		    }
		}
#endif
		added++;
	}
	if (added) {
	    ModBits |= AC;
	    (void) printf("%d added\n", added);
	}
	else
	    err("no change");
	non_critical();

	return;
}

adduser(c, v)
int c;
addr *v;

{
	struct account *aa;
	struct groupmap *gm;
	addr *realnamev, *idv, *passwdv, *groupv, *uidv, *shellv;
	addr username, def_dir, *dirv;
	addr shell, dir;
	addr password[SHORT_BUF];
	char prompt[MEDIUM_BUF], prompt2[MEDIUM_BUF], *salt;
	int cc, uid, n;
	addr_t cap[SHORT_BUF];

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c > 1 && userexists((char *)v[1])) {
		err1("%s: user exists", (char *) v[1]);
		return;
	}
	if (c > 1) {
		(void) strcpy((char *)cap, (char *)v[1]);
		capitalize((char *)cap);
		rnlist.l_count = 1;
		rnlist.l_list[0] = cap;
	}
	else
		rnlist.l_count = 0;
	
	realnamev = get_gpa(17);
	do {
		GetLine("Real Name: ", 16, &cc, realnamev, &rnlist);
	} while (cc == 0 && clear_gpa(realnamev, 17));

	/*
	 * If we were handed a username, take it.  If not, we must manufacture
	 * one from the Real Name.
	 */
	if (c > 1)
		username = v[1];
	else {
		username = makeusername(cc, realnamev);
		(void) printf("login name is \"%s\"\n", username);
	}

	/*
	 * Unique identification, like SSN
	 */
	idv = get_gpa(2);
	do {
		GetLine("Id: ", 1, &cc, idv, &idlist);
	} while (cc == 0 && clear_gpa(idv, 2));
	if (!eq(*idv, "exception") && (aa = getacid((char *)*idv))) {
		(void) sprintf(prompt, "%s shares that id.  continue? [no] ",
			(char *)aa->ac_name);
		if (no(prompt))
			return;
	}

	/*
	 * Give the user a password
	 */
	(void) sprintf(prompt, "Password [%s]: ", *idv);
	passwdv = get_gpa(2);
	GetLine(prompt, 1, &cc, passwdv, &pwlist);
	if (cc)
		if (eq(*passwdv, "none"))
			(void) strcpy((char *)password, "");
		else if (eq(*passwdv, "unused"))
			(void) strcpy((char *)password, "*");
		else if (eq(*passwdv, "generate")) {
			(void) strcpy((char *)password, makepass());
			(void) printf("password is \"%s\"\n", password);
		}
		else {
			salt = CRYPT_SALT;
			(void) strcpy((char *)password,
					crypt((char *)*passwdv, salt));
		}
	else {
		salt = CRYPT_SALT;
		(void) strcpy((char *)password, crypt((char *)*idv, salt));
	}

	(void) sprintf(prompt, "Group [%s]: ", DEF_GROUP);
	groupv = get_gpa(2);
	do {
		GetLine(prompt, 1, &cc, groupv, &Groups);
		if (cc) {
			gm = getgmnam((char *)*groupv);
			if (!gm)
				err1("%s: no such group", (char *)*groupv);
			else
				break;
		}
		else {
			gm = getgmnam(DEF_GROUP);
			if (!gm) {
				err1("%s: no such group", DEF_GROUP);
				continue;
			}
			break;
		}
	} while (clear_gpa(groupv, 2));

	uid = findnextuid(gm->gm_name);
	if (uid == NOMORE) {
		err1("no more free uids for group %s!",
			gm->gm_name);
		return;
	}
	(void) sprintf(prompt, "Uid [%d]: ", uid);
	uidv = get_gpa(2);
	do {
	    GetLine(prompt, 1, &cc, uidv, &Null_List);
	    if (cc) {
		if (!validint((char *)*uidv)) {
		    err1("%s makes no sense to me", (char *)*uidv);
		    continue;
		}
		n = atoi((char *)*uidv);
		aa = getacuid(n);
		if (aa) {
		    (void) sprintf(prompt2,
					"%s shares that uid, use anyway? ",
					aa->ac_name);
		    if (yesno(prompt2)) {
			uid = n;
			break;
		    }
		}
		else {
		    uid = n;
		    break;
		}
	    }
	    else
			break;
	} while (clear_gpa(uidv, 2));

	/*
	 * Shell
	 */
	(void) sprintf(prompt, "Shell [%s]: ", DEF_SHELL);
	shellv = get_gpa(2);
	GetLine(prompt, 1, &cc, shellv, &Shells);
	shell = (cc == 0) ? DEF_SHELL : *shellv;

	/*
	 * Home directory.
	 */
	def_dir = gethomedir((char *)username, gm->gm_name);
	(void) sprintf(prompt, "Home [%s]: ", def_dir);
	dirv = get_gpa(2);
	GetFilenames(prompt, 1, &cc, dirv);
	dir = (cc == 0) ? def_dir : *dirv;

	addu(uid, gm->gm_gid, username, glob(realnamev),
		(addr)password, *idv, dir, shell);

#ifndef DOFILES
	err("Don't forget to create this user's directory!");
#endif
	puts("added");
	return;
}

addvig(c, v)
int c;
addr *v;

{
	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	if (!groupexists((char *)v[1])) {
		err1("%s: no such group", (char *)v[1]);
		return;
	}

	critical();
	strlistadd(&Vigs, (char *)v[1]);
	sort_list(&Vigs, pstrcmp);
	ModBits |= VG;
	puts("added");
	non_critical();

	return;
}


addu(uid, gid, username, realname, password, id, dir, shell)
int uid, gid;
addr username, realname, password, id, dir, shell;

{
	struct account ac;
#ifdef SENDMAIL
	struct groupmap *gm;
	struct alias *al;
	register int j;
#endif
#ifdef DOFILES
	int zero = 0;
#endif

	critical();
#ifdef SENDMAIL
	zerolist(&ac.ac_aliases);
#endif
	zerolist(&ac.ac_groups);
	zerolist(&ac.ac_sigs);		zerolist(&ac.ac_classes);
	ac.ac_uid = uid;
	ac.ac_gid = gid;
	savestr((char **)&ac.ac_name, (char *)username);
	savestr((char **)&ac.ac_realname, (char *)realname);
	savestr((char **)&ac.ac_gecos, (char *)realname);
	savestr((char **)&ac.ac_passwd, (char *)password);
	savestr((char **)&ac.ac_id, (char *)id);
	savestr((char **)&ac.ac_dir, (char *)dir);
	savestr((char **)&ac.ac_shell, (char *)shell);
	ac.ac_ll.ll_time = (time_t) 0;
	(void) strncpy(ac.ac_ll.ll_line, "", sizeof ac.ac_ll.ll_line);
	(void) strncpy(ac.ac_ll.ll_host, "", sizeof ac.ac_ll.ll_host);
	genlistadd(&AccountList, (addr) &ac, sizeof (struct account));
	sort_list(&AccountList, acctcmp);
	strlistadd(&Users, (char *)ac.ac_name);
	sort_list(&Users, pstrcmp);
#ifdef DOFILES
	add_job(JB_MKDIR, ac.ac_dir, (addr)&ac.ac_uid,
		(addr)&zero);
#endif
	add_job(JB_LASTLOG, (addr) &ac.ac_uid,
		(addr)&ac.ac_ll, NIL);
	ModBits |= (AC|PW);
#ifdef SENDMAIL
	gm = getgmgid(ac.ac_gid);
	for (j=0; j < gm->gm_aliases.l_count; j++) {
	    al = getalnam((char *)gm->gm_aliases.l_list[j]);
	    if (!al) continue;	/* trouble */
	    if (!instrlist(&al->al_addresses, (char *)ac.ac_name)) {
		strlistadd(&al->al_addresses, (char *)ac.ac_name);
		sort_list(&al->al_addresses, pstrcmp);
		ModBits |= AL;
	    }
	}
#endif
	non_critical();

	return;
}