[BACK]Return to ukcutil.c CVS log [TXT][DIR] Up to [local] / src / usr.sbin / config

File: [local] / src / usr.sbin / config / ukcutil.c (download)

Revision 1.3, Tue Aug 8 21:42:40 2000 UTC (23 years, 10 months ago) by deraadt
Branch: MAIN
CVS Tags: OPENBSD_2_8_BASE, OPENBSD_2_8
Changes since 1.2: +11 -3 lines

permit changing flags

/*	$OpenBSD: ukcutil.c,v 1.3 2000/08/08 21:42:40 deraadt Exp $ */

/*
 * Copyright (c) 1999 Mats O Jansson.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by Mats O Jansson.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef LINT
static char rcsid[] = "$OpenBSD: ukcutil.c,v 1.3 2000/08/08 21:42:40 deraadt Exp $";
#endif

#include <sys/types.h>
#include <sys/time.h>
#include <sys/device.h>
#include <limits.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cmd.h"
#include "exec.h"
#include "ukc.h"
#include "misc.h"

struct cfdata *
get_cfdata(idx)
	int	idx;
{
	return((struct cfdata *)(adjust((caddr_t)nl[P_CFDATA].n_value) +
				 idx*sizeof(struct cfdata)));
}

short *
get_locnamp(idx)
	int	idx;
{
	return((short *)(adjust((caddr_t)nl[S_LOCNAMP].n_value) +
			 idx*sizeof(short)));
}

caddr_t *
get_locnames(idx)
	int	idx;
{
	return((caddr_t *)(adjust((caddr_t)nl[P_LOCNAMES].n_value) +
			   idx*sizeof(caddr_t)));
}

int *
get_extraloc(idx)
	int	idx;
{
	return((int *)(adjust((caddr_t)nl[IA_EXTRALOC].n_value) +
		       idx*sizeof(int)));
}

int
more()
{
	int quit = 0;
	cmd_t cmd;

	if (cnt != -1) {
		if (cnt == lines) {
			printf("--- more ---");
			fflush(stdout);
			ask_cmd(&cmd);
			cnt = 0;
		}
		cnt++;
		if (cmd.cmd[0] == 'q' || cmd.cmd[0] == 'Q')
			quit = 1;
	}
	
	return (quit);
}

void
pnum(val)
	int val;
{
	if (val > -2 && val < 16) {
		printf("%d",val);
	} else {
		switch (base) {
		case 8:
			printf("0%o",val);
			break;
		case 10:
			printf("%d",val);
			break;
		case 16:
		default:
			printf("0x%x",val);
			break;
		}
	}
}

void
pdevnam(devno)
	short devno;
{
	struct cfdata *cd;
	struct cfdriver *cdrv;

	cd = get_cfdata(devno);

	cdrv = (struct cfdriver *)adjust((caddr_t)cd->cf_driver);

#if defined(OLDSCSIBUS)
	if (strlen(adjust((caddr_t)cdrv->cd_name)) == 0) {
		printf("oldscsibus");
	}
#endif
	printf("%s", adjust((caddr_t)cdrv->cd_name));

	switch (cd->cf_fstate) {
	case FSTATE_NOTFOUND:
	case FSTATE_DNOTFOUND:
		printf("%d", cd->cf_unit);
		break;
	case FSTATE_FOUND:
		printf("*FOUND*");
		break;
	case FSTATE_STAR:
	case FSTATE_DSTAR:
		printf("*");
		break;
	default:
		printf("*UNKNOWN*");
		break;
	}
}

void
pdev(devno)
	short devno;
{
	struct cfdata *cd;
	short	*s,*ln;
	int	*i;
	caddr_t	*p;
	char	c;

	if (devno >  maxdev) {
		printf("Unknown devno (max is %d)\n", maxdev);
		return;
	}

	cd = get_cfdata(devno);

	printf("%3d ", devno);
	pdevnam(devno);
	printf(" at");

	c = ' ';
	s = (short *)adjust((caddr_t)cd->cf_parents);
	if (*s == -1)
		printf(" root");
	while (*s != -1) {
		printf("%c", c);
		pdevnam(*s);
		c = '|';
		s++;
	}
	switch (cd->cf_fstate) {
	case FSTATE_NOTFOUND:
	case FSTATE_FOUND:
	case FSTATE_STAR:
		break;
	case FSTATE_DNOTFOUND:
	case FSTATE_DSTAR:
		printf(" disable");
		break;
	default:
		printf(" ???");
		break;
	}

	i = (int *)adjust((caddr_t)cd->cf_loc);
	ln = get_locnamp(cd->cf_locnames);
	while (*ln != -1) {
		p = get_locnames(*ln);
		printf(" %s ", adjust((caddr_t)*p));
		ln++;
		pnum(*i);
		i++;
	}
	printf(" flags 0x%x\n", cd->cf_flags);
}

int
number(c, val)
	char *c;
	int *val;
{
	u_int num = 0;
	int neg = 0;
	int base = 10;

	if (*c == '-') {
		neg = 1;
		c++;
	}
	if (*c == '0') {
		base = 8;
		c++;
		if (*c == 'x' || *c == 'X') {
			base = 16;
			c++;
		}
	}
	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
		u_char cc = *c;

		if (cc >= '0' && cc <= '9')
			cc = cc - '0';
		else if (cc >= 'a' && cc <= 'f')
			cc = cc - 'a' + 10;
		else if (cc >= 'A' && cc <= 'F')
			cc = cc - 'A' + 10;
		else
			return (-1);

		if (cc > base)
			return (-1);
		num = num * base + cc;
		c++;
	}

	if (neg && num > INT_MAX)	/* overflow */
		return (1);
	*val = neg ? - num : num;
	return (0);
}

int
device(cmd, len, unit, state)
	char *cmd;
	int *len;
	short *unit, *state;
{
	short u = 0, s = FSTATE_FOUND;
	int l = 0;
	char *c;

	c = cmd;
	while (*c >= 'a' && *c <= 'z') {
		l++;
		c++;
	}
	if (*c == '*') {
		s = FSTATE_STAR;
		c++;
	} else {
		while (*c >= '0' && *c <= '9') {
			s = FSTATE_NOTFOUND;
			u = u*10 + *c - '0';
			c++;
		}
	}
	while (*c == ' ' || *c == '\t' || *c == '\n')
		c++;

	if (*c == '\0') {
		*len = l;
		*unit = u;
		*state = s;
		return(0);
	}

	return(-1);
}

int
attr(cmd, val)
	char *cmd;
	int *val;
{
	char *c;
	caddr_t *p;
	short attr = -1, i = 0, l = 0;

	c = cmd;
	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
		c++;
		l++;
	}

	p = get_locnames(0);

	while (i <= maxlocnames) {
		if (strlen((char *)adjust((caddr_t)*p)) == l) {
			if (strncasecmp(cmd, adjust((caddr_t)*p), l) == 0)
				attr = i;
		}
		p++;
		i++;
	}

	if (attr == -1) {
		return (-1);
	}

	*val = attr;

	return(0);
}

void
modify(item, val)
	char *item;
	int  *val;
{
	cmd_t cmd;
	int a;

	while(1) {
		printf("%s [", item);
		pnum(*val);
		printf("] ? ");
		fflush(stdout);

		ask_cmd(&cmd);

		if (strlen(cmd.cmd) != 0) {
			if (strlen(cmd.args) == 0) {
				if (number(cmd.cmd, &a) == 0) {
					*val = a;
					break;
				} else
					printf("Unknown argument\n");
			} else
				printf("Too many arguments\n");
		} else {
			break;
		}
	}
}

void
change(devno)
	int devno;
{
	struct cfdata *cd,*c;
	caddr_t	*p;
	int	 i,share = 0,*j,*k,*l;
	short	*ln,*lk;

	j = k = NULL;

	if (devno <=  maxdev) {

		pdev(devno);

		if (ask_yn("change")) {

			cd = get_cfdata(devno);

			/*
			 * Search for some other driver sharing this
			 * locator table. if one does, we may need to
			 * replace the locators with a new copy.
			 */

			c = get_cfdata(0);
			for (i = 0; c->cf_driver; i++) {
				if (i != devno && c->cf_loc == cd->cf_loc)
					share = 1;
				c++;
			}

			ln = get_locnamp(cd->cf_locnames);
			l = (int *)adjust((caddr_t)cd->cf_loc);
			
			if (share) {
				if (oldkernel) {
					printf("Can't do that on this kernel\n");
					return;
				}

				lk = ln; 
				i = 0;
				while (*lk != -1) {
					lk++; i++;
				}
				lk = ln;
				
				j = (int *)adjust((caddr_t)nl[I_NEXTRALOC].n_value);
				k = (int *)adjust((caddr_t)nl[I_UEXTRALOC].n_value);
				if ((i + *k) > *j) {
					printf("Not enough space to change device.\n");
					return;
				}

				j = l = get_extraloc(*k);

				bcopy(adjust((caddr_t)cd->cf_loc),
				      l, sizeof(int) * i);
			}

			while (*ln != -1) {
				p = get_locnames(*ln);
				modify((char *)adjust(*p),l);
				ln++;
				l++;
			}
			modify("flags", &cd->cf_flags);

			if (share) {
				if (bcmp(adjust((caddr_t)cd->cf_loc),
					 j, sizeof(int) * i)) {
					cd->cf_loc = (int *)readjust((caddr_t)j);
					*k = *k + i;
				}
			}

			printf("%3d ", devno);
			pdevnam(devno);
			printf(" changed\n");
			pdev(devno);
		}
	} else {
		printf("Unknown devno (max is %d)\n", maxdev);
	}
}

void
change_history(devno,str)
	int devno;
	char *str;
{
	struct cfdata *cd,*c;
	caddr_t	*p;
	int	 i,share = 0,*j,*k,*l;
	short	*ln,*lk;

	j = k = NULL;

	if (devno <=  maxdev) {

		pdev(devno);

		cd = get_cfdata(devno);

		/*
		 * Search for some other driver sharing this
		 * locator table. if one does, we may need to
		 * replace the locators with a new copy.
		 */

		c = get_cfdata(0);
		for (i = 0; c->cf_driver; i++) {
			if (i != devno && c->cf_loc == cd->cf_loc)
				share = 1;
			c++;
		}

		ln = get_locnamp(cd->cf_locnames);
		l = (int *)adjust((caddr_t)cd->cf_loc);
			
		if (share) {
			if (oldkernel) {
				printf("Can't do that on this kernel\n");
				return;
			}

			lk = ln; 
			i = 0;
			while (*lk != -1) {
				lk++; i++;
			}
			lk = ln;
				
			j = (int *)adjust((caddr_t)nl[I_NEXTRALOC].n_value);
			k = (int *)adjust((caddr_t)nl[I_UEXTRALOC].n_value);
			if ((i + *k) > *j) {
				printf("Not enough space to change device.\n");
				return;
			}

			j = l = get_extraloc(*k);

			bcopy(adjust((caddr_t)cd->cf_loc),
			      l, sizeof(int) * i);
		}

		while (*ln != -1) {
			p = get_locnames(*ln);
			*l = atoi(str);
			if (*str == '-') str++;
			while ((*str >= '0') && (*str <= '9')) str++;
			if (*str == ' ') str++;
			ln++;
			l++;
		}

		if (*str) {
			cd->cf_flags = atoi(str);
			if (*str == '-') str++;
			while ((*str >= '0') && (*str <= '9')) str++;
			if (*str == ' ') str++;
		}

		if (share) {
			if (bcmp(adjust((caddr_t)cd->cf_loc),
				 j, sizeof(int) * i)) {
				cd->cf_loc = (int *)readjust((caddr_t)j);
				*k = *k + i;
			}
		}

		printf("%3d ", devno);
		pdevnam(devno);
		printf(" changed\n");
		pdev(devno);
		
	} else {
		printf("Unknown devno (max is %d)\n", maxdev);
	}
}

void
disable(devno)
	int devno;
{
	struct cfdata *cd;
	int done = 0;

	if (devno <= maxdev) {
		
		cd = get_cfdata(devno);

		switch (cd->cf_fstate) {
		case FSTATE_NOTFOUND:
			cd->cf_fstate = FSTATE_DNOTFOUND;
			break;
		case FSTATE_STAR:
			cd->cf_fstate = FSTATE_DSTAR;
			break;
		case FSTATE_DNOTFOUND:
		case FSTATE_DSTAR:
			done = 1;
			break;
		default:
			printf("Error unknown state\n");
			break;
		}

		printf("%3d ", devno);
		pdevnam(devno);
		if (done)
			printf(" already");
		printf(" disabled\n");
	} else {
		printf("Unknown devno (max is %d)\n", maxdev);
	}
}

void
enable(devno)
	int devno;
{
	struct cfdata *cd;
	int done = 0;
	
	if (devno <= maxdev) {
		
		cd = get_cfdata(devno);

		switch (cd->cf_fstate) {
		case FSTATE_DNOTFOUND:
			cd->cf_fstate = FSTATE_NOTFOUND;
			break;
		case FSTATE_DSTAR:
			cd->cf_fstate = FSTATE_STAR;
			break;
		case FSTATE_NOTFOUND:
		case FSTATE_STAR:
			done = 1;
			break;
		default:
			printf("Error unknown state\n");
			break;
		}

		printf("%3d ", devno);
		pdevnam(devno);
		if (done)
			printf(" already");
		printf(" enabled\n");
	} else {
		printf("Unknown devno (max is %d)\n", maxdev);
	}
}

void
show()
{
	caddr_t *p;
	int	i = 0;

	cnt = 0;

	p = get_locnames(0);

	while (i <= maxlocnames) {
		if (more())
			break;
		printf("%s\n", (char *)adjust(*p));
		p++;
		i++;
	}

	cnt = -1;
}

void
common_attr_val(attr, val, routine)
	short attr;
	int   *val;
	char  routine;
{
	int	i = 0;
	struct cfdata *cd;
	int   *l;
	short *ln;
	int quit = 0;

	cnt = 0;

	cd = get_cfdata(0);

	while(cd->cf_attach != 0) {
		l = (int *)adjust((caddr_t)cd->cf_loc);
		ln = get_locnamp(cd->cf_locnames);
		while(*ln != -1) {
			if (*ln == attr) {
				if (val == NULL) {
					quit = more();
					pdev(i);			
				} else {
					if (*val == *l) {
						quit = more();
						switch (routine) {
						case UC_ENABLE:
							enable(i);
							break;
						case UC_DISABLE:
							disable(i);
							break;
						case UC_SHOW:
							pdev(i);
							break;
						default:
							printf("Unknown routine /%c/\n",
							    routine);
							break;
						}
					}
				}
			}
			if (quit)
				break;
			ln++;
			l++;
		}
		if (quit)
			break;
		i++;
		cd++;
	}

	cnt = -1;
}

void
show_attr(cmd)
	char *cmd;
{
	char *c;
	caddr_t *p;
	short attr = -1, i = 0, l = 0;
	int a;

	c = cmd;
	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
		c++;
		l++;
	}
	while (*c == ' ' || *c == '\t' || *c == '\n') {
		c++;
	}

	p = get_locnames(0);

	while (i <= maxlocnames) {
		if (strlen((char *)adjust(*p)) == l) {
			if (strncasecmp(cmd, adjust(*p), l) == 0) {
				attr = i;
			}
		}
		p++;
		i++;
	}

	if (attr == -1) {
		printf("Unknown attribute\n");
		return;
	}

	if (*c == '\0') {
		common_attr_val(attr, NULL, UC_SHOW);
	} else {
		if (number(c, &a) == 0) {
			common_attr_val(attr, &a, UC_SHOW);
		} else {
			printf("Unknown argument\n");
		}
	}
}

void
common_dev(dev, len, unit, state, routine)
	char *dev;
	int len;
	short unit, state;
	char routine;
{
	struct cfdata *cd;
	struct cfdriver *cdrv;
	int i = 0;

	switch (routine) {
	case UC_CHANGE:
		break;
	default:
		cnt = 0;
		break;
	}

	cnt = 0;

	cd = get_cfdata(0);

	while(cd->cf_attach != 0) {
		cdrv = (struct cfdriver *)adjust((caddr_t)cd->cf_driver);

		if (strlen((char *)adjust(cdrv->cd_name)) == len) {
			/*
			 * Ok, if device name is correct
			 *  If state == FSTATE_FOUND, look for "dev"
			 *  If state == FSTATE_STAR, look for "dev*"
			 *  If state == FSTATE_NOTFOUND, look for "dev0"
			 */
			if (strncasecmp(dev,(char *)adjust(cdrv->cd_name),
					len) == 0 &&
			    (state == FSTATE_FOUND ||
			     (state == FSTATE_STAR &&
			      (cd->cf_fstate == FSTATE_STAR ||
			       cd->cf_fstate == FSTATE_DSTAR)) ||
			     (state == FSTATE_NOTFOUND &&
			      cd->cf_unit == unit &&
			      (cd->cf_fstate == FSTATE_NOTFOUND ||
			       cd->cf_fstate == FSTATE_DNOTFOUND)))) {
				if (more())
					break;
				switch (routine) {
				case UC_CHANGE:
					change(i);
					break;
				case UC_ENABLE:
					enable(i);
					break;
				case UC_DISABLE:
					disable(i);
					break;
				case UC_FIND:
					pdev(i);
					break;
				default:
					printf("Unknown routine /%c/\n",
					    routine);
					break;
				}
			}
		}
		i++;
		cd++;
	}

	switch (routine) {
	case UC_CHANGE:
		break;
	default:
		cnt = -1;
		break;
	}
}

void
common_attr(cmd, attr, routine)
	char *cmd;
	int attr;
	char routine;
{
	char *c;
	short l = 0;
	int a;

	c = cmd;
	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
		c++;
		l++;
	}
	while (*c == ' ' || *c == '\t' || *c == '\n') {
		c++;
	}
	if (*c == '\0') {
		printf("Value missing for attribute\n");
		return;
	}

	if (number(c, &a) == 0) {
		common_attr_val(attr, &a, routine);
	} else {
		printf("Unknown argument\n");
	}
}

void
add_read(prompt, field, dev, len, val)
	char *prompt;
	char field;
	char *dev;
	int len;
	int *val;
{
	int ok = 0;
	int a;
	cmd_t cmd;
	struct cfdata *cd;
	struct cfdriver *cdrv;

	while(!ok) {
		printf("%s ? ", prompt);
		fflush(stdout);
		
		ask_cmd(&cmd);

		if (strlen(cmd.cmd) != 0) {
			if (number(cmd.cmd, &a) == 0) {
				if (a > maxdev) {
					printf("Unknown devno (max is %d)\n",
					    maxdev);
				} else {
					cd = get_cfdata(a);
					cdrv = (struct cfdriver *)
					  adjust((caddr_t)cd->cf_driver);
					if (strncasecmp(dev,
							(char *)adjust(cdrv->cd_name),
							len) != 0 &&
					    field == 'a') {
						printf("Not same device type\n");
					} else {
						*val = a;
						ok = 1;
					}
				}
			} else if (cmd.cmd[0] == '?') {
				common_dev(dev, len, 0,
				    FSTATE_FOUND, UC_FIND);
			} else if (cmd.cmd[0] == 'q' ||
				   cmd.cmd[0] == 'Q') {
				ok = 1;
			} else {
				printf("Unknown argument\n");
			}
		} else {
			ok = 1;
		}
	}

}

void
add(dev, len, unit, state)
	char *dev;
	int len;
	short unit, state;
{
	int i = 0, found = 0, *p;
	short *pv;
	struct cfdata new,*cd,*cdp;
	struct cfdriver *cdrv;
	int  val, max_unit;

	bzero(&new, sizeof(struct cfdata));

	if (maxdev == totdev) {
		printf("No more space for new devices.\n");
		return;
	}

	if (state == FSTATE_FOUND) {
		printf("Device not complete number or * is missing/n");
		return;
	}

	cd = get_cfdata(0);

	while(cd->cf_attach != 0) {
		cdrv = (struct cfdriver *)adjust((caddr_t)cd->cf_driver);

		if (strlen((char *)adjust(cdrv->cd_name)) == len &&
		    strncasecmp(dev, (char *)adjust(cdrv->cd_name), len) == 0)
			found = 1;
		cd++;
	}

	if (!found) {
		printf("No device of this type exists.\n");
		return;
	}

	add_read("Clone Device (DevNo, 'q' or '?')", 'a', dev, len, &val);
	
	if (val != -1) {
		cd = get_cfdata(val);
		new = *cd;
		new.cf_unit = unit;
		new.cf_fstate = state;
		add_read("Insert before Device (DevNo, 'q' or '?')",
		    'i', dev, len, &val);
	}

	if (val != -1) {
		
		/* Insert the new record */
		cdp = cd = get_cfdata(maxdev+1);
		cdp--;
		for (i = maxdev; val <= i; i--) {
			*cd-- = *cdp--;
		}
		cd = get_cfdata(val);
		*cd = new;

		/* Fix indexs in pv */
		p = (int *)adjust((caddr_t)nl[I_PV_SIZE].n_value);
		pv = (short *)adjust((caddr_t)nl[SA_PV].n_value);
		for (i = 0; i < *p; i++) {
			if ((*pv != 1) && (*pv >= val))
				*pv = *pv + 1;
			pv++;
		}

		/* Fix indexs in cfroots */
		p = (int *)adjust((caddr_t)nl[I_CFROOTS_SIZE].n_value);
		pv = (short *)adjust((caddr_t)nl[SA_CFROOTS].n_value);
		for (i = 0; i < *p; i++) {
			if ((*pv != 1) && (*pv >= val))
				*pv = *pv + 1;
			pv++;
		}
		
		maxdev++;

		max_unit = -1;

		/* Find max unit number of the device type */

		cd = get_cfdata(0);

		while(cd->cf_attach != 0) {
			cdrv = (struct cfdriver *)
			  adjust((caddr_t)cd->cf_driver);

			if (strlen((char *)adjust(cdrv->cd_name)) == len &&
			    strncasecmp(dev, (char *)adjust(cdrv->cd_name),
					len) == 0) {
				switch (cd->cf_fstate) {
				case FSTATE_NOTFOUND:
				case FSTATE_DNOTFOUND:
					if (cd->cf_unit > max_unit)
						max_unit = cd->cf_unit;
					break;
				default:
					break;
				}
			}
			cd++;
		}


		/* For all * entries set unit number to max+1 */

		max_unit++;

		cd = get_cfdata(0);

		while(cd->cf_attach != 0) {
			cdrv = (struct cfdriver *)
			  adjust((caddr_t)cd->cf_driver);

			if (strlen((char *)adjust(cdrv->cd_name)) == len &&
			    strncasecmp(dev, (char *)adjust(cdrv->cd_name),
					len) == 0) {
				switch (cd->cf_fstate) {
				case FSTATE_STAR:
				case FSTATE_DSTAR:
					cd->cf_unit = max_unit;
					break;
				default:
					break;
				}
			}
			cd++;
		}

		pdev(val);
	}

	/* cf_attach, cf_driver, cf_unit, cf_state, cf_loc, cf_flags,
	   cf_parents, cf_locnames, cf_locnames and cf_ivstubs */
}

void
add_history(devno, unit, state, newno)
	int devno, newno;
	short unit, state;
{
	int i = 0, *p;
	short *pv;
	struct cfdata new,*cd,*cdp;
	struct cfdriver *cdrv;
	int  val, max_unit;
	int  len;
	char *dev;

	bzero(&new, sizeof(struct cfdata));

	cd = get_cfdata(devno);
	new = *cd;
	new.cf_unit = unit;
	new.cf_fstate = state;
	
	val = newno;

	cdrv = (struct cfdriver *) adjust((caddr_t)cd->cf_driver);
	dev = adjust((caddr_t)cdrv->cd_name);
	len = strlen(dev);

	/* Insert the new record */
	cdp = cd = get_cfdata(maxdev+1);
	cdp--;
	for (i = maxdev; val <= i; i--) {
		*cd-- = *cdp--;
	}
	cd = get_cfdata(val);
	*cd = new;
	
	/* Fix indexs in pv */
	p = (int *)adjust((caddr_t)nl[I_PV_SIZE].n_value);
	pv = (short *)adjust((caddr_t)nl[SA_PV].n_value);
	for (i = 0; i < *p; i++) {
		if ((*pv != 1) && (*pv >= val))
			*pv = *pv + 1;
		pv++;
	}

	/* Fix indexs in cfroots */
	p = (int *)adjust((caddr_t)nl[I_CFROOTS_SIZE].n_value);
	pv = (short *)adjust((caddr_t)nl[SA_CFROOTS].n_value);
	for (i = 0; i < *p; i++) {
		if ((*pv != 1) && (*pv >= val))
			*pv = *pv + 1;
		pv++;
	}
		
	maxdev++;
	
	max_unit = -1;

	/* Find max unit number of the device type */

	cd = get_cfdata(0);

	while(cd->cf_attach != 0) {
		cdrv = (struct cfdriver *)
		  adjust((caddr_t)cd->cf_driver);

		if (strlen((char *)adjust(cdrv->cd_name)) == len &&
		    strncasecmp(dev, (char *)adjust(cdrv->cd_name),
				len) == 0) {
			switch (cd->cf_fstate) {
			case FSTATE_NOTFOUND:
			case FSTATE_DNOTFOUND:
				if (cd->cf_unit > max_unit)
					max_unit = cd->cf_unit;
				break;
			default:
				break;
			}
		}
		cd++;
	}


	/* For all * entries set unit number to max+1 */

	max_unit++;

	cd = get_cfdata(0);

	while(cd->cf_attach != 0) {
		cdrv = (struct cfdriver *)
		  adjust((caddr_t)cd->cf_driver);

		if (strlen((char *)adjust(cdrv->cd_name)) == len &&
		    strncasecmp(dev, (char *)adjust(cdrv->cd_name),
				len) == 0) {
			switch (cd->cf_fstate) {
			case FSTATE_STAR:
			case FSTATE_DSTAR:
				cd->cf_unit = max_unit;
				break;
			default:
				break;
			}
		}
		cd++;
	}


	printf("%3d ", newno);
	pdevnam(newno);
	printf(" added\n");
	pdev(val);

}

int
config()
{
	cmd_t cmd;
	int i, st;

	/* Set up command table pointer */
	cmd.table = cmd_table;

	printf("Enter 'help' for information\n");

	/* Edit cycle */
	do {
again:
		printf("ukc> ");
		fflush(stdout);
		ask_cmd(&cmd);

		if (cmd.cmd[0] == '\0')
			goto again;
		for (i = 0; cmd_table[i].cmd != NULL; i++)
			if (strstr(cmd_table[i].cmd, cmd.cmd)==cmd_table[i].cmd)
				break;

		/* Quick hack to put in '?' == 'help' */
		if (!strcmp(cmd.cmd, "?"))
			i = 0;

		/* Check for valid command */
		if (cmd_table[i].cmd == NULL) {
			printf("Invalid command '%s'.  Try 'help'.\n", cmd.cmd);
			continue;
		} else
			strcpy(cmd.cmd, cmd_table[i].cmd);

		/* Call function */
		st = cmd_table[i].fcn(&cmd);

		/* Update status */
		if (st == CMD_EXIT)
			break;
		if (st == CMD_SAVE)
			break;
	} while (1);

	return (st == CMD_SAVE);
}

void
process_history(len,buf)
	int len;
	char *buf;
{
	char *c;
	int devno,newno;
	short unit,state;
	struct timezone *tz;

	if (len == 0) {
		printf("History is empty\n");
		return;
	}

	printf("Processing history...\n");
	
	buf[len] = 0;

	c = buf;
	
	while (*c != NULL) {
		switch (*c) {
		case 'a':
			c++; c++;
			devno = atoi(c);
			while ((*c >= '0') && (*c <= '9')) c++; c++;
			unit = atoi(c);
			if (*c == '-') c++;
			while ((*c >= '0') && (*c <= '9')) c++; c++;
			state = atoi(c);
			if (*c == '-') c++;
			while ((*c >= '0') && (*c <= '9')) c++; c++;
			newno = atoi(c);
			while ((*c >= '0') && (*c <= '9')) c++;
			add_history(devno,unit,state,newno);
			while (*c != '\n') c++; c++;
			break;
		case 'c':
			c++; c++;
			devno = atoi(c);
			while ((*c >= '0') && (*c <= '9')) c++;
			if (*c == ' ') c++;
			if (*c != '\n') { 
				change_history(devno,c);
			}
			while (*c != '\n') c++; c++;
			break;
		case 'd':
			c++;
			devno = atoi(c);
			disable(devno);
			while (*c != '\n') c++; c++;
			break;
		case 'e':
			c++;
			devno = atoi(c);
			enable(devno);
			while (*c != '\n') c++; c++;
			break;
		case 't':
			c++; c++;
			tz = (struct timezone *)adjust((caddr_t)nl[TZ_TZ].
			    n_value);
			tz->tz_minuteswest = atoi(c);
			while (*c != ' ') c++; c++;
			tz->tz_dsttime = atoi(c);
			while (*c != '\n') c++; c++;
			break;
		case 'q':
			while (*c != NULL) c++;
			break;
		default:
			printf("unknown command %c\n",*c);
			while ((*c != NULL) && (*c != '\n')) c++;
			break;
		}
	}
}