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 u

⟦efded125c⟧ TextFile

    Length: 22502 (0x57e6)
    Types: TextFile
    Names: »update.c«

Derivation

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

TextFile

#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;
extern	addr makeusername(), DEF_SHELL;
extern	char *crypt(), *mktemp(), *sprintf(), *when(), *rsalt(), *makepass();
extern	time_t choosedate();

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

/* these are defined in add.c */
extern	struct	list idlist;
extern	struct	list rnlist;
extern	struct	list pwlist;
extern	struct	list mdlist;

#ifdef SENDMAIL
/*
 * Update an alias
 */
updalias(c, v)
int c;
char **v;

{
	struct alias *al, *a;
	struct account *ac;
	struct class *cs;
	struct sig *sg;
	struct groupmap *gm;
	register int i;
	int cc;
	addr *namev;
	char prompt[MEDIUM_BUF];

	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;
	}

	namev = get_gpa(2);
	(void) sprintf(prompt, "Name [%s]: ", al->al_name);
	GetLine(prompt, 1, &cc, namev, &Null_List);
	if (cc == 0 || eq(*namev, al->al_name)) {
	    err("no change");
	    return;
	}
	if (aliasexists((char *)*namev)) {
	    err1("%s: alias exists", (char *)*namev);
	    return;
	}

	critical();

	/*
	 * If this alias name appears in any of the other alias lists
	 * it must be changed there alias well.
	 */
	for (i=0; i < AliasList.l_count; i++) {
	    a = (struct alias *) AliasList.l_list[i];
	    (void) strlistchg(&a->al_addresses, al->al_name, (char *)*namev);
	}
	for (i=0; i < AccountList.l_count; i++) {
	    ac = (struct account *) AccountList.l_list[i];
	    if (strlistchg(&ac->ac_aliases, al->al_name, (char *)*namev))
		ModBits |= AC;
	}
	for (i=0; i < GroupMapList.l_count; i++) {
	    gm = (struct groupmap *) GroupMapList.l_list[i];
	    (void) strlistchg(&gm->gm_aliases, al->al_name, (char *)*namev);
	}
	for (i=0; i < ClassList.l_count; i++) {
	    cs = (struct class *) ClassList.l_list[i];
	    (void) strlistchg(&cs->cs_aliases, al->al_name, (char *)*namev);
	}
	for (i=0; i < SigList.l_count; i++) {
	    sg = (struct sig *) SigList.l_list[i];
	    (void) strlistchg(&sg->sg_aliases, al->al_name, (char *)*namev);
	}

	(void) strlistchg(&Aliases, al->al_name, (char *)*namev);
	FREEMEM(al->al_name);
	savestr(&al->al_name, (char *)*namev);
	sort_list(&AliasList, aliascmp);
	ModBits |= AL;
	puts("updated");
	non_critical();

	return;
}
#endif

/*
 * Update a class
 */
updclass(c, v)
int c;
addr *v;

{
	struct class cl, *cs;
	struct account *ac;
#ifdef SENDMAIL
	struct alias *al;
#endif
	struct stat statbuf;
	addr *namev;
	char tempf[MEDIUM_BUF], errmsg[LONG_BUF], prompt[LONG_BUF];
	FILE *f, *fopen();
	time_t now;
	int i, cc, changed = 0, ch;

	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;
	}
	bcopy(&cl, cs, sizeof (struct class));

	namev = get_gpa(2);

	(void) sprintf(prompt, "Name [%s]: ",  cl.cs_name);
	GetLine(prompt, 1, &cc, namev, &Null_List);
	if (cc) {
		if (eq(*namev, v[1]))
			; /* no change */
		else if (classexists((char *)*namev)) {
			err("that name is taken");
			return;
		}
		else
			changed = 1;
	}
	(void) printf("Class set to end %s\n", when(cl.cs_exptime));
	if (no("Do you wish to change it? [no] ") == 0) {
	    if (!cl.cs_exptime || yesno("Should the class expire? ")) {
		err("Set the expiration date.");
		cl.cs_exptime = choosedate(cl.cs_exptime);
		(void) printf("Class set to end %s\n", when(cl.cs_exptime));
	    }
	    else
		cl.cs_exptime = 0;
	    if (cl.cs_exptime != cs->cs_exptime)
		changed = 1;
	}
	i = no("Edit description? [no] ");

	critical();
	if (i)
		goto finish;
	(void) strcpy(tempf, TMPDIR);
	(void) strcat(tempf, XXXXXX);
	(void) mktemp(tempf);
	f = fopen(tempf, "w");
	if (f == NULL) {
		err1("%s: cannot open (write)", tempf);
		non_critical();
		return;
	}
	fputs(cl.cs_desc, f);
	(void) fclose(f);
	(void) stat(tempf, &statbuf);
	now = statbuf.st_mtime;
	for (;;) {
		edit(tempf);
		if (stat(tempf, &statbuf) == -1) {
			perr(tempf);
			(void) unlink(tempf);
			non_critical();
			return;
		}
		if (statbuf.st_size > DESCSIZE) {
			(void) sprintf(errmsg,
				"description is %d characters too long",
				DESCSIZE - statbuf.st_size);
			err(errmsg);
			continue;
		}
		break;
	}
	if (statbuf.st_mtime == now)
		goto finish;
	changed = 1;
	f = fopen(tempf, "r");
	if (f == NULL) {
		err1("%s: cannot open (read)", tempf);
		non_critical();
		return;
	}
	FREEMEM(cl.cs_desc);
	i = 0;
	while ((ch = getc(f)) != EOF)
		desc[i++] = ch;
	desc[i] = '\0';
	cl.cs_dsize = i;
	savestr(&cl.cs_desc, desc);
	(void) fclose(f);
	(void) unlink(tempf);

finish:
	if (*namev != NIL && !eq(*namev, v[1])) {
		FREEMEM(cl.cs_name);
		savestr(&cl.cs_name, (char *)*namev);
		for (i=0; i < AccountList.l_count; i++) {
		    ac = (struct account *)AccountList.l_list[i];
		    if (strlistchg(&ac->ac_classes, (char *)v[1], cl.cs_name))
			ModBits |= AC;
		}
#ifdef SENDMAIL
		for (i=0; i < AliasList.l_count; i++) {
		    al = (struct alias *) AliasList.l_list[i];
		    if (strlistchg(&al->al_classes, (char *)v[1], cl.cs_name))
			ModBits |= AL;
		}
#endif
		(void) strlistchg(&Classes, (char *)v[1], cl.cs_name);
	}
	if (changed) {
		bcopy(cs, &cl, sizeof (struct class));
		sort_list(&ClassList, classcmp);
		ModBits |= CS;
		puts("updated");
	}
	else
		err("no change");
	non_critical();

	return;
}

/*
 * Update a sig
 */
updsig(c, v)
int c;
addr *v;

{
	struct sig s, *sg;
#ifdef SENDMAIL
	struct alias *al;
#endif
	struct account *ac;
	struct stat statbuf;
	addr *namev;
	char tempf[MEDIUM_BUF], errmsg[LONG_BUF], prompt[LONG_BUF];
	FILE *f, *fopen();
	time_t now;
	int i, cc, changed = 0, ch;

	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;
	}
	bcopy(&s, sg, sizeof (struct sig));

	namev = get_gpa(2);

	(void) sprintf(prompt, "Name [%s]: ",  s.sg_name);
	GetLine(prompt, 1, &cc, namev, &Null_List);
	if (cc) {
		if (eq(*namev, v[1]))
			; /* no change */
		else if (sigexists((char *)*namev)) {
			err("that name is taken");
			return;
		}
		else
			changed = 1;
	}
	(void) printf("Sig set to end %s\n", when(s.sg_exptime));
	if (no("Do you wish to change it? [no] ") == 0) {
	    if (!s.sg_exptime || yesno("Should the sig expire? ")) {
		err("Set the expiration date.");
		s.sg_exptime = choosedate(s.sg_exptime);
		(void) printf("Project set to end %s\n", when(s.sg_exptime));
	    }
	    else
		s.sg_exptime = 0;
	    if (s.sg_exptime != sg->sg_exptime)
		changed = 1;
	}
	i = no("Edit description? [no] ");
	
	critical();
	if (i)
		goto finish;
	(void) strcpy(tempf, TMPDIR);
	(void) strcat(tempf, XXXXXX);
	(void) mktemp(tempf);
	f = fopen(tempf, "w");
	if (f == NULL) {
		err1("%s: cannot open (write)", tempf);
		non_critical();
		return;
	}
	fputs(s.sg_desc, f);
	(void) fclose(f);
	(void) stat(tempf, &statbuf);
	now = statbuf.st_mtime;
	for (;;) {
		edit(tempf);
		if (stat(tempf, &statbuf) == -1) {
			perr(tempf);
			(void) unlink(tempf);
			non_critical();
			return;
		}
		if (statbuf.st_size > DESCSIZE) {
			(void) sprintf(errmsg,
				"description is %d characters too long",
				DESCSIZE - statbuf.st_size);
			err(errmsg);
			continue;
		}
		break;
	}
	if (statbuf.st_mtime == now)
		goto finish;
	changed = 1;
	f = fopen(tempf, "r");
	if (f == NULL) {
		err1("%s: cannot open (read)", tempf);
		non_critical();
		return;
	}
	FREEMEM(s.sg_desc);
	i = 0;
	while ((ch = getc(f)) != EOF)
		desc[i++] = ch;
	desc[i] = '\0';
	s.sg_dsize = i;
	savestr(&s.sg_desc, desc);
	(void) fclose(f);
	(void) unlink(tempf);

finish:
	if (*namev != NIL && !eq(*namev, v[1])) {
		FREEMEM(s.sg_name);
		savestr(&s.sg_name, (char *)*namev);
		for (i=0; i < AccountList.l_count; i++) {
		    ac = (struct account *)AccountList.l_list[i];
		    if (strlistchg(&ac->ac_sigs, (char *)v[1], s.sg_name))
			ModBits |= AC;
		}
#ifdef SENDMAIL
		for (i=0; i < AliasList.l_count; i++) {
		    al = (struct alias *) AliasList.l_list[i];
		    if (strlistchg(&al->al_sigs, (char *)v[1], s.sg_name))
			ModBits |= AL;
		}
#endif
		(void) strlistchg(&Sigs, (char *)v[1], s.sg_name);
	}
	if (changed) {
		bcopy(sg, &s, sizeof (struct sig));
		sort_list(&SigList, sigcmp);
		ModBits |= SG;
		puts("updated");
	}
	else
		err("no change");
	non_critical();

	return;
}

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

{
	struct groupmap g, *gm;
#ifdef SENDMAIL
	struct alias *al;
#endif
	struct account *ac;
	struct range *rg;
	char prompt[SHORT_BUF];
	addr *tempv, *namev;
	int i, cc, gid, changed = 0;

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	gm = getgmnam((char *)v[1]);
	if (!gm) {
		err1("%s: group exists", (char *)v[1]);
		return;
	}
	bcopy(&g, gm, sizeof (struct groupmap));

	namev = get_gpa(2);
	tempv = get_gpa(2);

	(void) sprintf(prompt, "Name [%s]: ",  g.gm_name);
	GetLine(prompt, 1, &cc, namev, &Null_List);
	if (cc) {
		if (!eq(*namev, v[1]))
			; /* no change */
		else if (groupexists((char *)*namev)) {
			err("that name is taken");
			return;
		}
		else
			changed = 1;
	}

	(void) sprintf(prompt, "Gid [%d]: ", g.gm_gid);
	GetLine(prompt, 1, &cc, tempv, &Null_List);
	if (cc) {
		if (!validint((char *)*tempv)) {
			err1("%s makes no sense to me", (char *)tempv);
			return;
		}
		gid = atoi((char *)*tempv);
		if (gidexists(g.gm_gid)) {
			err("that gid is taken");
			return;
		}
		else {
			g.gm_gid = gid;
			changed = 1;
		}
	}

	critical();
	if (g.gm_gid != gm->gm_gid) {
		changed = 1;
		for (i=0; i < AccountList.l_count; i++) {
			ac = (struct account *) AccountList.l_list[i];
			if (ac->ac_gid == gm->gm_gid) {
				ac->ac_gid = g.gm_gid;
				ModBits |= AC;
			}
		}
	}
	if (*namev != NIL && !eq(*namev, v[1])) {
	    changed = 1;
	    FREEMEM(g.gm_name);
	    savestr(&g.gm_name, (char *)*namev);
	    (void) strlistchg(&Groups, (char *)v[1], g.gm_name);
	    for (i=0; i < AccountList.l_count; i++) {
		ac = (struct account *) AccountList.l_list[i];
		if (strlistchg(&ac->ac_groups, (char *)v[1], g.gm_name))
		    ModBits |= AC;
	    }
#ifdef SENDMAIL
	    for (i=0; i < AliasList.l_count; i++) {
		al = (struct alias *) AliasList.l_list[i];
		if (strlistchg(&al->al_groups, (char *)v[1], g.gm_name))
		    ModBits |= AL;
	    }
#endif
	    rg = getrgnam((char *)v[1]);
	    if (rg) {
		FREEMEM(rg->rg_name);
		savestr(&rg->rg_name, g.gm_name);
		(void) strlistchg(&Ranges, (char *)v[1], g.gm_name);
		sort_list(&RangeList, rangecmp);
		ModBits |= RG;
	    }
	    if (vigexists((char *)v[1])) {
		(void) strlistchg(&Vigs, (char *)v[1], g.gm_name);
		ModBits |= VG;
	    }
	}
	if (changed) {
		bcopy(gm, &g, sizeof (struct groupmap));
		sort_list(&GroupMapList, gmapcmp);
		ModBits |= GR;
		puts("updated");
	}
	else
		err("no change");

	non_critical();
	return;
}

/*
 * Update a range
 */
updrange(c, v)
int c;
addr *v;

{
	struct range r, *rr, *rg;
	char prompt[SHORT_BUF];
	addr *tempv, *namev;
	int cc, indx, changed = 0;

	if ( c > 2 ) {
		err1("%s: too many arguments", (char *)v[0]);
		return;
	}
	if (c != 2) {
		err1("usage: %s <name>", (char *)v[0]);
		return;
	}
	rr = getrgnam((char *)v[1]);
	if (!rr) {
		err1("%s: no such range", (char *)v[1]);
		return;
	}
	bcopy(&r, rr, sizeof (struct range));

	namev = get_gpa(2);
	tempv = get_gpa(2);

	/*
	 * New name?
	 */
	(void) sprintf(prompt, "Name [%s]: ",  r.rg_name);
	GetLine(prompt, 1, &cc, namev, &Groups);
	if (cc) {
		if (!groupexists((char *)*namev)) {
			err1("%s: no such group", (char *)*namev);
			return;
		}
		if (eq(*namev, v[1]))
			; /* no change */
		else if (rangeexists((char *)namev)) {
			err("that name is taken");
			return;
		}
		else
			changed = 1;
	}

	/*
	 * From?
	 */
	(void) sprintf(prompt, "From [%d]: ", r.rg_from);
	GetLine(prompt, 1, &cc, tempv, &Null_List);
	if (cc) {
		if (!validint((char *)*tempv)) {
			err1("%s makes no sense to me", (char *)*tempv);
			return;
		}
		r.rg_from = atoi((char *)*tempv);
		(void) clear_gpa(tempv, 2);
		changed = 1;
	}

	/*
	 * To?
	 */
	(void) sprintf(prompt, "To [%d]: ", r.rg_to);
	GetLine(prompt, 1, &cc, tempv, &Null_List);
	if (cc) {
		if (!validint((char *)*tempv)) {
			err1("%s makes no sense to me", (char *)*tempv);
			return;
		}
		r.rg_to = atoi((char *)*tempv);
		(void) clear_gpa(tempv, 2);
		changed = 1;
	}

	/*
	 * New mode?
	 */
	(void) sprintf(prompt, "Mode [%s] : ",
			(r.rg_mode == RG_SHARED ? "shared" : "exclusive"));
	GetLine(prompt, 1, &cc, tempv, &mdlist);
	if (!cc)
		; /* no change */
	else if (eq(*tempv, "shared")) {
		r.rg_mode = RG_SHARED;
		changed = 1;
	}
	else if (eq(*tempv, "exclusive")) {
		r.rg_mode = RG_EXCLUSIVE;
		changed = 1;
	}
	else {
		err1("%s: unknown mode", (char *)*tempv);
		return;
	}

	/*
	 * Check to see if the new range conflicts with existing ranges
	 */
	for (indx=0; indx < RangeList.l_count; indx++) {
		rg = (struct range *) RangeList.l_list[indx];
		if (rg == rr)
			continue;
		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();
	if (*namev != NIL && !eq(*namev, v[1])) {
		FREEMEM(r.rg_name);
		savestr(&r.rg_name, (char *)*tempv);
		(void) strlistchg(&Ranges, (char *)v[1], r.rg_name);
	}
	if (changed) {
		bcopy(rr, &r, sizeof (struct range));
		sort_list(&RangeList, rangecmp);
		ModBits |= RG;
		puts("updated");
	}
	else
		err("no change");
	non_critical();

	return;
}

upduser(c, v)
int c;
char **v;

{
	struct account *ac, *ac2;
	struct groupmap *gm;
#ifdef SENDMAIL
	struct alias *al;
	struct class *cs;
	struct sig *sg;
	int ogid, j;
#endif
	addr *namev, *realnamev, *idv, *uidv, *gidv, *dirv, *passwdv, *shellv;
	int uid, gid, changed = 0;
#ifdef DOFILES
	int mvdir = 0;
#endif
	int cc;
	register int i;
	char *cp, prompt[LONG_BUF], errmsg[LONG_BUF];

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

	namev = get_gpa(2);
	realnamev = get_gpa(17);
	idv = get_gpa(2);
	passwdv = get_gpa(2);
	uidv = get_gpa(2);
	gidv = get_gpa(2);
	dirv = get_gpa(2);
	shellv = get_gpa(2);

	/*
	 * Change login name?
	 */
	(void) sprintf(prompt, "Login name [%s]: ", ac->ac_name);
	GetLine(prompt, 1, &cc, namev, &Null_List);

	/*
	 * Change real name?
	 */
	(void) sprintf(prompt, "Real Name [%s]: ", ac->ac_realname);
	GetLine(prompt, 16, &cc, realnamev, &Null_List);

	/*
	 * Change id?
	 */
	(void) sprintf(prompt, "Id [%s]: ", ac->ac_id);
	GetLine(prompt, 1, &cc, idv, &idlist);

	/*
	 * Change password?
	 */
	(void) sprintf(prompt, "Password (RETURN means no change): ");
	GetLine(prompt, 1, &cc, passwdv, &pwlist);

	/*
	 * Change uid?
	 */
	(void) sprintf(prompt, "Uid [%d]: ", ac->ac_uid);
	GetLine(prompt, 1, &cc, uidv, &Null_List);
	if (cc && !validint((char *)*uidv)) {
	    err1("%s makes no sense to me", (char *)*uidv);
	    return;
	}

	/*
	 * Change gid?
	 */
	(void) sprintf(prompt, "Gid [%d]: ", ac->ac_gid);
	GetLine(prompt, 1, &cc, gidv, &Null_List);
	if (cc && !validint((char *)*gidv)) {
	    err1("%s makes no sense to me", (char *)*gidv);
	    return;
	}

	/*
	 * Rename home directory?
	 */
	(void) sprintf(prompt, "Home [%s]: ", ac->ac_dir);
	GetFilenames(prompt, 1, &cc, dirv);
#ifdef DOFILES
	if (cc && !eq(*dirv, ac->ac_dir) && fileexists((char *)*dirv))
	  err2("%s already exists, so I won't move %s", (char *)*dirv,
	       (char *)ac->ac_dir);
	else
	  mvdir = 1;
#endif

	/*
	 * New shell?
	 */
	(void) sprintf(prompt, "Shell [%s]: ", ac->ac_shell);
	GetLine(prompt, 1, &cc, shellv, &Shells);
	if (cc && !fileexists((char *)*shellv))
	    err1("Warning: %s does not exist", (char *)*shellv);

	critical();

	/*
	 * If given a different user name, use it.  No duplicate
	 * user names allowed (of course).
	 */
	if (*namev != NIL && !eq(*namev, ac->ac_name)) {
	    if (!userexists((char *)*namev)) {
		/*
		 * Update group member lists
		 */
		for (i=0; i < GroupMapList.l_count; i++) {
		    gm = (struct groupmap *)GroupMapList.l_list[i];
		    if (strlistchg(&gm->gm_mem, (char *)v[1], (char *)*namev))
			ModBits |= GR;
		}
#ifdef SENDMAIL
		/*
		 * Update aliases
		 */
		for (i=0; i < AliasList.l_count; i++) {
		    al = (struct alias *) AliasList.l_list[i];
		    if (strlistchg(&al->al_addresses, (char *)v[1],
				(char *)*namev))
			ModBits |= AL;
		}
		al = getalnam((char *)ac->ac_name);
		if (al) {
		    ModBits |= AL;
		    for (i=0; i < AccountList.l_count; i++) {
			ac2 = (struct account *) AccountList.l_list[i];
			if (!strlistchg(&ac2->ac_aliases, al->al_name,
				(char *)*namev))
			    continue;
			ModBits |= AC;
		    }
		    for (i=0; i < GroupMapList.l_count; i++) {
			gm = (struct groupmap *) GroupMapList.l_list[i];
			(void) strlistchg(&gm->gm_aliases, al->al_name,
					(char *)*namev);
		    }
		    for (i=0; i < ClassList.l_count; i++) {
			cs = (struct class *) ClassList.l_list[i];
			(void) strlistchg(&cs->cs_aliases, al->al_name,
					(char *)*namev);
		    }
		    for (i=0; i < SigList.l_count; i++) {
			sg = (struct sig *) SigList.l_list[i];
			(void) strlistchg(&sg->sg_aliases, al->al_name,
					(char *)*namev);
		    }
		    (void) strlistchg(&Aliases, al->al_name, (char *)*namev);
		    FREEMEM(al->al_name);
		    savestr(&al->al_name, (char *)*namev);
		    sort_list(&AliasList, aliascmp);
		}
#endif
		/*
		 * Now fix the accounts struct and the users
		 * completion list
		 */
		(void) strlistchg(&Users, (char *)ac->ac_name, (char *)*namev);
		FREEMEM((char *)ac->ac_name);
		savestr((char **)&ac->ac_name, (char *)*namev);
		ModBits |= (AC|PW);
		changed++;
	    }
	    else {
		err1("%s: user exists", (char *)*namev);
		err("login name unchanged");
	    }
	}

	/*
	 * If given a new real name, use it.
	 */
	if (*realnamev != NIL) {
		cp = (char *)glob(realnamev);
		if (!eq(cp, ac->ac_realname)) {
			FREEMEM((char *)ac->ac_realname);
			savestr((char **)&ac->ac_realname, cp);
			ModBits |= AC;
			changed++;
		}
	}

	/*
	 * If id changed, record it.  Since the user already has an
	 * account we don't care if his id matches anyone else's
	 * The check for duplicate ids is done at when a user is added.
	 */
	if (*idv != NIL && !eq(*idv, ac->ac_id)) {
		FREEMEM((char *)ac->ac_id);
		savestr((char **)&ac->ac_id, (char *)*idv);
		ModBits |= AC;
		changed++;
	}

	/*
	 * Handle change of password
	 */
	if (*passwdv != NIL) {
		FREEMEM((char *)ac->ac_passwd);
		if (eq(*passwdv, "unused"))
			savestr((char **)&ac->ac_passwd, "*");
		else if (eq(*passwdv, "none"))
			savestr((char **)&ac->ac_passwd, "");
		else if (eq(*passwdv, "generate")) {
			cp = makepass();
			savestr((char **)&ac->ac_passwd, crypt(cp, rsalt()));
			(void) printf("password is \"%s\"\n", cp);
		}
		else
			savestr((char **)&ac->ac_passwd,
				crypt((char *)*passwdv, rsalt()));
		ModBits |= PW;
		changed++;
	}

	/*
	 * Note home directory change, if any.  This must be before
	 * checking for a change in uid so that the omni_chown isn't
	 * suddenly left high and dry if user's directory is moved.
	 */
	if (*dirv != NIL && !eq(*dirv, ac->ac_dir)) {
#ifdef DOFILES
		if (mvdir)
			add_job(JB_MV, ac->ac_dir, *dirv, NIL);
#else
		err("Don't forget to move the user's home directory");
#endif
		FREEMEM((char *)ac->ac_dir);
		savestr((char **)&ac->ac_dir, (char *)*dirv);
		ModBits |= PW;
		changed++;
	}

	/*
	 * Handle a change of uid.  This entails changing the ownership
	 * of this user's files when savechanges() is called.  Sharing
	 * of uids is permitted but a warning message is printed.
	 */
	if (*uidv != NIL) {
	    uid = atoi((char *)*uidv);
	    if (uid <= 0)
		err("uid is out of range");
	    else if (uid != ac->ac_uid && uid >= 0) {
		ac2 = getacuid(uid);
		if (ac2)
		    (void) printf("warning: uid %d is shared by %s\n",
				uid, ac2->ac_name);
#ifdef DOFILES
		add_job(JB_OMNICHOWN, &ac->ac_uid, &uid, NIL);
#else
		err("Do not forget to chown the user files.");
#endif
		ac->ac_uid = uid;
		sort_list(&AccountList, acctcmp);
		ModBits |= (AC|PW);
		add_job(JB_LASTLOG, &ac->ac_uid, (addr)&ac->ac_ll, NIL);
		changed++;
	    }
	}

	/*
	 * Handle a change of gid.  Must make sure there is group
	 * associated with the gid.
	 */
	if (*gidv != NIL) {
		gid = atoi((char *)*gidv);
		if (gid < 0)
			err("gid is out of range");
		else if (!(gm = getgmgid(gid))) {
			(void) sprintf(errmsg, 
				"no group associated with gid %d",
				gid);
			err(errmsg);
		}
		else if (gid != ac->ac_gid) {
#ifdef SENDMAIL
			ogid = ac->ac_gid;
#endif
			ac->ac_gid = gid;
			ModBits |= (AC|PW);
#ifdef SENDMAIL
			if (gm->gm_aliases.l_count)
			    RXBindings(ac);
			gm = getgmgid(ogid);
			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
			changed++;
		}
	}

	/*
	 * Make change in shell if necessary.
	 */
	if (*shellv != NIL && !eq(*shellv, ac->ac_shell)) {
		FREEMEM((char *)ac->ac_shell);
		savestr((char **)&ac->ac_shell, (char *)*shellv);
		ModBits |= PW;
		changed++;
	}

	if (changed)
		puts("updated");
	else
		err("no change");
	non_critical();

	return;
}