Home | History | Annotate | Line # | Download | only in kern
subr_userconf.c revision 1.15.2.1
      1 /*	$NetBSD: subr_userconf.c,v 1.15.2.1 2006/06/21 15:09:38 yamt Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1996 Mats O Jansson <moj (at) stacken.kth.se>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *	This product includes software developed by Mats O Jansson.
     18  * 4. The name of the author may not be used to endorse or promote
     19  *    products derived from this software without specific prior written
     20  *    permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     23  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     24  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     26  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  *
     34  *	OpenBSD: subr_userconf.c,v 1.19 2000/01/08 23:23:37 d Exp
     35  */
     36 
     37 #include <sys/cdefs.h>
     38 __KERNEL_RCSID(0, "$NetBSD: subr_userconf.c,v 1.15.2.1 2006/06/21 15:09:38 yamt Exp $");
     39 
     40 #include "opt_userconf.h"
     41 
     42 #include <sys/param.h>
     43 #include <sys/systm.h>
     44 #include <sys/device.h>
     45 #include <sys/malloc.h>
     46 #include <sys/time.h>
     47 
     48 #include <dev/cons.h>
     49 
     50 extern struct cfdata cfdata[];
     51 
     52 static int userconf_base = 16;			/* Base for "large" numbers */
     53 static int userconf_maxdev = -1;		/* # of used device slots   */
     54 static int userconf_totdev = -1;		/* # of device slots        */
     55 #if 0
     56 static int userconf_maxlocnames = -1;		/* # of locnames            */
     57 #endif
     58 static int userconf_cnt = -1;			/* Line counter for ...     */
     59 static int userconf_lines = 12;			/* ... # of lines per page  */
     60 static int userconf_histlen = 0;
     61 static int userconf_histcur = 0;
     62 static char userconf_history[1024];
     63 static int userconf_histsz = sizeof(userconf_history);
     64 static char userconf_argbuf[40];		/* Additional input         */
     65 static char userconf_cmdbuf[40];		/* Command line             */
     66 static char userconf_histbuf[40];
     67 
     68 static int getsn(char *, int);
     69 
     70 #define UC_CHANGE 'c'
     71 #define UC_DISABLE 'd'
     72 #define UC_ENABLE 'e'
     73 #define UC_FIND 'f'
     74 #define UC_SHOW 's'
     75 
     76 static const char *userconf_cmds[] = {
     77 	"base",		"b",
     78 	"change",	"c",
     79 	"disable",	"d",
     80 	"enable",	"e",
     81 	"exit",		"q",
     82 	"find",		"f",
     83 	"help",		"h",
     84 	"list",		"l",
     85 	"lines",	"L",
     86 	"quit",		"q",
     87 	"?",		"h",
     88 	"",		 "",
     89 };
     90 
     91 static void
     92 userconf_init(void)
     93 {
     94 	int i;
     95 	struct cfdata *cf;
     96 
     97 	i = 0;
     98 	for (cf = cfdata; cf->cf_name; cf++)
     99 		i++;
    100 
    101 	userconf_maxdev = i - 1;
    102 	userconf_totdev = i - 1;
    103 }
    104 
    105 static int
    106 userconf_more(void)
    107 {
    108 	int quit = 0;
    109 	char c = '\0';
    110 
    111 	if (userconf_cnt != -1) {
    112 		if (userconf_cnt == userconf_lines) {
    113 			printf("-- more --");
    114 			c = cngetc();
    115 			userconf_cnt = 0;
    116 			printf("\r            \r");
    117 		}
    118 		userconf_cnt++;
    119 		if (c == 'q' || c == 'Q')
    120 			quit = 1;
    121 	}
    122 	return (quit);
    123 }
    124 
    125 static void
    126 userconf_hist_cmd(char cmd)
    127 {
    128 	userconf_histcur = userconf_histlen;
    129 	if (userconf_histcur < userconf_histsz) {
    130 		userconf_history[userconf_histcur] = cmd;
    131 		userconf_histcur++;
    132 	}
    133 }
    134 
    135 static void
    136 userconf_hist_int(int val)
    137 {
    138 	snprintf(userconf_histbuf, sizeof(userconf_histbuf), " %d", val);
    139 	if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) {
    140 		memcpy(&userconf_history[userconf_histcur],
    141 		      userconf_histbuf,
    142 		      strlen(userconf_histbuf));
    143 		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
    144 	}
    145 }
    146 
    147 static void
    148 userconf_hist_eoc(void)
    149 {
    150 	if (userconf_histcur < userconf_histsz) {
    151 		userconf_history[userconf_histcur] = '\n';
    152 		userconf_histcur++;
    153 		userconf_histlen = userconf_histcur;
    154 	}
    155 }
    156 
    157 static void
    158 userconf_pnum(int val)
    159 {
    160 	if (val > -2 && val < 16) {
    161 		printf("%d",val);
    162 	} else {
    163 		switch (userconf_base) {
    164 		case 8:
    165 			printf("0%o",val);
    166 			break;
    167 		case 10:
    168 			printf("%d",val);
    169 			break;
    170 		case 16:
    171 		default:
    172 			printf("0x%x",val);
    173 			break;
    174 		}
    175 	}
    176 }
    177 
    178 static void
    179 userconf_pdevnam(short dev)
    180 {
    181 	struct cfdata *cd;
    182 
    183 	cd = &cfdata[dev];
    184 	printf("%s", cd->cf_name);
    185 	switch (cd->cf_fstate) {
    186 	case FSTATE_NOTFOUND:
    187 	case FSTATE_DNOTFOUND:
    188 		printf("%d", cd->cf_unit);
    189 		break;
    190 	case FSTATE_FOUND:
    191 		printf("*FOUND*");
    192 		break;
    193 	case FSTATE_STAR:
    194 	case FSTATE_DSTAR:
    195 		printf("*");
    196 		break;
    197 	default:
    198 		printf("*UNKNOWN*");
    199 		break;
    200 	}
    201 }
    202 
    203 static void
    204 userconf_pdev(short devno)
    205 {
    206 	struct cfdata *cd;
    207 	const struct cfparent *cfp;
    208 	int   *l;
    209 	const struct cfiattrdata *ia;
    210 	const struct cflocdesc *ld;
    211 	int nld, i;
    212 
    213 	if (devno > userconf_maxdev) {
    214 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    215 		return;
    216 	}
    217 
    218 	cd = &cfdata[devno];
    219 
    220 	printf("[%3d] ", devno);
    221 	userconf_pdevnam(devno);
    222 	printf(" at");
    223 	cfp = cd->cf_pspec;
    224 	if (cfp == NULL)
    225 		printf(" root");
    226 	else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
    227 		printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
    228 	else
    229 		printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
    230 						       : cfp->cfp_iattr);
    231 	switch (cd->cf_fstate) {
    232 	case FSTATE_NOTFOUND:
    233 	case FSTATE_FOUND:
    234 	case FSTATE_STAR:
    235 		break;
    236 	case FSTATE_DNOTFOUND:
    237 	case FSTATE_DSTAR:
    238 		printf(" disable");
    239 		break;
    240 	default:
    241 		printf(" ???");
    242 		break;
    243 	}
    244 	if (cfp) {
    245 		l = cd->cf_loc;
    246 		ia = cfiattr_lookup(cfp->cfp_iattr, 0);
    247 		KASSERT(ia);
    248 		ld = ia->ci_locdesc;
    249 		nld = ia->ci_loclen;
    250 		for (i = 0; i < nld; i++) {
    251 			printf(" %s ", ld[i].cld_name);
    252 			if (!ld[i].cld_defaultstr
    253 			    || (l[i] != ld[i].cld_default))
    254 				userconf_pnum(l[i]);
    255 			else
    256 				printf("?");
    257 		}
    258 	}
    259 	printf("\n");
    260 }
    261 
    262 static int
    263 userconf_number(char *c, int *val)
    264 {
    265 	u_int num = 0;
    266 	int neg = 0;
    267 	int base = 10;
    268 
    269 	if (*c == '-') {
    270 		neg = 1;
    271 		c++;
    272 	}
    273 	if (*c == '0') {
    274 		base = 8;
    275 		c++;
    276 		if (*c == 'x' || *c == 'X') {
    277 			base = 16;
    278 			c++;
    279 		}
    280 	}
    281 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
    282 		u_char cc = *c;
    283 
    284 		if (cc >= '0' && cc <= '9')
    285 			cc = cc - '0';
    286 		else if (cc >= 'a' && cc <= 'f')
    287 			cc = cc - 'a' + 10;
    288 		else if (cc >= 'A' && cc <= 'F')
    289 			cc = cc - 'A' + 10;
    290 		else
    291 			return (-1);
    292 
    293 		if (cc > base)
    294 			return (-1);
    295 		num = num * base + cc;
    296 		c++;
    297 	}
    298 
    299 	if (neg && num > INT_MAX)	/* overflow */
    300 		return (1);
    301 	*val = neg ? - num : num;
    302 	return (0);
    303 }
    304 
    305 static int
    306 userconf_device(char *cmd, int *len, short *unit, short *state)
    307 {
    308 	short u = 0, s = FSTATE_FOUND;
    309 	int l = 0;
    310 	char *c;
    311 
    312 	c = cmd;
    313 	while (*c >= 'a' && *c <= 'z') {
    314 		l++;
    315 		c++;
    316 	}
    317 	if (*c == '*') {
    318 		s = FSTATE_STAR;
    319 		c++;
    320 	} else {
    321 		while (*c >= '0' && *c <= '9') {
    322 			s = FSTATE_NOTFOUND;
    323 			u = u*10 + *c - '0';
    324 			c++;
    325 		}
    326 	}
    327 	while (*c == ' ' || *c == '\t' || *c == '\n')
    328 		c++;
    329 
    330 	if (*c == '\0') {
    331 		*len = l;
    332 		*unit = u;
    333 		*state = s;
    334 		return(0);
    335 	}
    336 
    337 	return(-1);
    338 }
    339 
    340 static void
    341 userconf_modify(const struct cflocdesc *item, int *val)
    342 {
    343 	int ok = 0;
    344 	int a;
    345 	char *c;
    346 
    347 	while (!ok) {
    348 		printf("%s [", item->cld_name);
    349 		if (item->cld_defaultstr && (*val == item->cld_default))
    350 			printf("?");
    351 		else
    352 			userconf_pnum(*val);
    353 		printf("] ? ");
    354 
    355 		getsn(userconf_argbuf, sizeof(userconf_argbuf));
    356 
    357 		c = userconf_argbuf;
    358 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
    359 
    360 		if (*c != '\0') {
    361 			if (*c == '?') {
    362 				if (item->cld_defaultstr) {
    363 					*val = item->cld_default;
    364 					ok = 1;
    365 				} else
    366 					printf("No default\n");
    367 			} else if (userconf_number(c, &a) == 0) {
    368 				*val = a;
    369 				ok = 1;
    370 			} else {
    371 				printf("Unknown argument\n");
    372 			}
    373 		} else {
    374 			ok = 1;
    375 		}
    376 	}
    377 }
    378 
    379 static void
    380 userconf_change(int devno)
    381 {
    382 	struct cfdata *cd;
    383 	char c = '\0';
    384 	int   *l;
    385 	int   ln;
    386 	const struct cfiattrdata *ia;
    387 	const struct cflocdesc *ld;
    388 	int nld;
    389 
    390 	if (devno <=  userconf_maxdev) {
    391 
    392 		userconf_pdev(devno);
    393 
    394 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
    395 			printf("change (y/n) ?");
    396 			c = cngetc();
    397 			printf("\n");
    398 		}
    399 
    400 		if (c == 'y' || c == 'Y') {
    401 
    402 			/* XXX add cmd 'c' <devno> */
    403 			userconf_hist_cmd('c');
    404 			userconf_hist_int(devno);
    405 
    406 			cd = &cfdata[devno];
    407 			l = cd->cf_loc;
    408 			ia = cfiattr_lookup(cd->cf_pspec->cfp_iattr, 0);
    409 			KASSERT(ia);
    410 			ld = ia->ci_locdesc;
    411 			nld = ia->ci_loclen;
    412 
    413 			for (ln = 0; ln < nld; ln++)
    414 			{
    415 				userconf_modify(&ld[ln], l);
    416 
    417 				/* XXX add *l */
    418 				userconf_hist_int(*l);
    419 
    420 				l++;
    421 			}
    422 
    423 			printf("[%3d] ", devno);
    424 			userconf_pdevnam(devno);
    425 			printf(" changed\n");
    426 			userconf_pdev(devno);
    427 
    428 			/* XXX add eoc */
    429 			userconf_hist_eoc();
    430 
    431 		}
    432 	} else {
    433 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    434 	}
    435 }
    436 
    437 static void
    438 userconf_disable(int devno)
    439 {
    440 	int done = 0;
    441 
    442 	if (devno <= userconf_maxdev) {
    443 		switch (cfdata[devno].cf_fstate) {
    444 		case FSTATE_NOTFOUND:
    445 			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
    446 			break;
    447 		case FSTATE_STAR:
    448 			cfdata[devno].cf_fstate = FSTATE_DSTAR;
    449 			break;
    450 		case FSTATE_DNOTFOUND:
    451 		case FSTATE_DSTAR:
    452 			done = 1;
    453 			break;
    454 		default:
    455 			printf("Error unknown state\n");
    456 			break;
    457 		}
    458 
    459 		printf("[%3d] ", devno);
    460 		userconf_pdevnam(devno);
    461 		if (done) {
    462 			printf(" already");
    463 		} else {
    464 			/* XXX add cmd 'd' <devno> eoc */
    465 			userconf_hist_cmd('d');
    466 			userconf_hist_int(devno);
    467 			userconf_hist_eoc();
    468 		}
    469 		printf(" disabled\n");
    470 	} else {
    471 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    472 	}
    473 }
    474 
    475 static void
    476 userconf_enable(int devno)
    477 {
    478 	int done = 0;
    479 
    480 	if (devno <= userconf_maxdev) {
    481 		switch (cfdata[devno].cf_fstate) {
    482 		case FSTATE_DNOTFOUND:
    483 			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
    484 			break;
    485 		case FSTATE_DSTAR:
    486 			cfdata[devno].cf_fstate = FSTATE_STAR;
    487 			break;
    488 		case FSTATE_NOTFOUND:
    489 		case FSTATE_STAR:
    490 			done = 1;
    491 			break;
    492 		default:
    493 			printf("Error unknown state\n");
    494 			break;
    495 		}
    496 
    497 		printf("[%3d] ", devno);
    498 		userconf_pdevnam(devno);
    499 		if (done) {
    500 			printf(" already");
    501 		} else {
    502 			/* XXX add cmd 'e' <devno> eoc */
    503 			userconf_hist_cmd('d');
    504 			userconf_hist_int(devno);
    505 			userconf_hist_eoc();
    506 		}
    507 		printf(" enabled\n");
    508 	} else {
    509 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    510 	}
    511 }
    512 
    513 static void
    514 userconf_help(void)
    515 {
    516 	int j = 0, k;
    517 
    518 	printf("command   args                description\n");
    519 	while (*userconf_cmds[j] != '\0') {
    520 		printf(userconf_cmds[j]);
    521 		k = strlen(userconf_cmds[j]);
    522 		while (k < 10) {
    523 			printf(" ");
    524 			k++;
    525 		}
    526 		switch (*userconf_cmds[j+1]) {
    527 		case 'L':
    528 			printf("[count]             number of lines before more");
    529 			break;
    530 		case 'b':
    531 			printf("8|10|16             base on large numbers");
    532 			break;
    533 		case 'c':
    534 			printf("devno|dev           change devices");
    535 			break;
    536 		case 'd':
    537 			printf("devno|dev           disable devices");
    538 			break;
    539 		case 'e':
    540 			printf("devno|dev           enable devices");
    541 			break;
    542 		case 'f':
    543 			printf("devno|dev           find devices");
    544 			break;
    545 		case 'h':
    546 			printf("                    this message");
    547 			break;
    548 		case 'l':
    549 			printf("                    list configuration");
    550 			break;
    551 		case 'q':
    552 			printf("                    leave userconf");
    553 			break;
    554 		default:
    555 			printf("                    don't know");
    556 			break;
    557 		}
    558 		printf("\n");
    559 		j += 2;
    560 	}
    561 }
    562 
    563 static void
    564 userconf_list(void)
    565 {
    566 	int i = 0;
    567 
    568 	userconf_cnt = 0;
    569 
    570 	while (cfdata[i].cf_name != NULL) {
    571 		if (userconf_more())
    572 			break;
    573 		userconf_pdev(i++);
    574 	}
    575 
    576 	userconf_cnt = -1;
    577 }
    578 
    579 static void
    580 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
    581 {
    582 	int i = 0;
    583 
    584 	switch (routine) {
    585 	case UC_CHANGE:
    586 		break;
    587 	default:
    588 		userconf_cnt = 0;
    589 		break;
    590 	}
    591 
    592 	while (cfdata[i].cf_name != NULL) {
    593 		if (strlen(cfdata[i].cf_name) == len) {
    594 
    595 			/*
    596 			 * Ok, if device name is correct
    597 			 *  If state == FSTATE_FOUND, look for "dev"
    598 			 *  If state == FSTATE_STAR, look for "dev*"
    599 			 *  If state == FSTATE_NOTFOUND, look for "dev0"
    600 			 */
    601 			if (strncasecmp(dev, cfdata[i].cf_name,
    602 					len) == 0 &&
    603 			    (state == FSTATE_FOUND ||
    604 			     (state == FSTATE_STAR &&
    605 			      (cfdata[i].cf_fstate == FSTATE_STAR ||
    606 			       cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
    607 			     (state == FSTATE_NOTFOUND &&
    608 			      cfdata[i].cf_unit == unit &&
    609 			      (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
    610 			       cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
    611 				if (userconf_more())
    612 					break;
    613 				switch (routine) {
    614 				case UC_CHANGE:
    615 					userconf_change(i);
    616 					break;
    617 				case UC_ENABLE:
    618 					userconf_enable(i);
    619 					break;
    620 				case UC_DISABLE:
    621 					userconf_disable(i);
    622 					break;
    623 				case UC_FIND:
    624 					userconf_pdev(i);
    625 					break;
    626 				default:
    627 					printf("Unknown routine /%c/\n",
    628 					    routine);
    629 					break;
    630 				}
    631 			}
    632 		}
    633 		i++;
    634 	}
    635 
    636 	switch (routine) {
    637 	case UC_CHANGE:
    638 		break;
    639 	default:
    640 		userconf_cnt = -1;
    641 		break;
    642 	}
    643 }
    644 
    645 #if 0
    646 static void
    647 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
    648 {
    649 	int ok = 0;
    650 	int a;
    651 	char *c;
    652 
    653 	*val = -1;
    654 
    655 	while (!ok) {
    656 		printf("%s ? ", prompt);
    657 
    658 		getsn(userconf_argbuf, sizeof(userconf_argbuf));
    659 
    660 		c = userconf_argbuf;
    661 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
    662 
    663 		if (*c != '\0') {
    664 			if (userconf_number(c, &a) == 0) {
    665 				if (a > userconf_maxdev) {
    666 					printf("Unknown devno (max is %d)\n",
    667 					    userconf_maxdev);
    668 				} else if (strncasecmp(dev,
    669 				    cfdata[a].cf_name, len) != 0 &&
    670 					field == 'a') {
    671 					printf("Not same device type\n");
    672 				} else {
    673 					*val = a;
    674 					ok = 1;
    675 				}
    676 			} else if (*c == '?') {
    677 				userconf_common_dev(dev, len, 0,
    678 				    FSTATE_FOUND, UC_FIND);
    679 			} else if (*c == 'q' || *c == 'Q') {
    680 				ok = 1;
    681 			} else {
    682 				printf("Unknown argument\n");
    683 			}
    684 		} else {
    685 			ok = 1;
    686 		}
    687 	}
    688 }
    689 #endif /* 0 */
    690 
    691 static int
    692 userconf_parse(char *cmd)
    693 {
    694 	char *c, *v;
    695 	int i = 0, j = 0, k, a;
    696 	short unit, state;
    697 
    698 	c = cmd;
    699 	while (*c == ' ' || *c == '\t')
    700 		c++;
    701 	v = c;
    702 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
    703 		c++;
    704 		i++;
    705 	}
    706 
    707 	k = -1;
    708 	while (*userconf_cmds[j] != '\0') {
    709 		if (strlen(userconf_cmds[j]) == i) {
    710 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
    711 				k = j;
    712 		}
    713 		j += 2;
    714 	}
    715 
    716 	while (*c == ' ' || *c == '\t' || *c == '\n')
    717 		c++;
    718 
    719 	if (k == -1) {
    720 		if (*v != '\n')
    721 			printf("Unknown command, try help\n");
    722 	} else {
    723 		switch (*userconf_cmds[k+1]) {
    724 		case 'L':
    725 			if (*c == '\0')
    726 				printf("Argument expected\n");
    727 			else if (userconf_number(c, &a) == 0)
    728 				userconf_lines = a;
    729 			else
    730 				printf("Unknown argument\n");
    731 			break;
    732 		case 'b':
    733 			if (*c == '\0')
    734 				printf("8|10|16 expected\n");
    735 			else if (userconf_number(c, &a) == 0) {
    736 				if (a == 8 || a == 10 || a == 16) {
    737 					userconf_base = a;
    738 				} else {
    739 					printf("8|10|16 expected\n");
    740 				}
    741 			} else
    742 				printf("Unknown argument\n");
    743 			break;
    744 		case 'c':
    745 			if (*c == '\0')
    746 				printf("DevNo or Dev expected\n");
    747 			else if (userconf_number(c, &a) == 0)
    748 				userconf_change(a);
    749 			else if (userconf_device(c, &a, &unit, &state) == 0)
    750 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
    751 			else
    752 				printf("Unknown argument\n");
    753 			break;
    754 		case 'd':
    755 			if (*c == '\0')
    756 				printf("Attr, DevNo or Dev expected\n");
    757 			else if (userconf_number(c, &a) == 0)
    758 				userconf_disable(a);
    759 			else if (userconf_device(c, &a, &unit, &state) == 0)
    760 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
    761 			else
    762 				printf("Unknown argument\n");
    763 			break;
    764 		case 'e':
    765 			if (*c == '\0')
    766 				printf("Attr, DevNo or Dev expected\n");
    767 			else if (userconf_number(c, &a) == 0)
    768 				userconf_enable(a);
    769 			else if (userconf_device(c, &a, &unit, &state) == 0)
    770 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
    771 			else
    772 				printf("Unknown argument\n");
    773 			break;
    774 		case 'f':
    775 			if (*c == '\0')
    776 				printf("DevNo or Dev expected\n");
    777 			else if (userconf_number(c, &a) == 0)
    778 				userconf_pdev(a);
    779 			else if (userconf_device(c, &a, &unit, &state) == 0)
    780 				userconf_common_dev(c, a, unit, state, UC_FIND);
    781 			else
    782 				printf("Unknown argument\n");
    783 			break;
    784 		case 'h':
    785 			userconf_help();
    786 			break;
    787 		case 'l':
    788 			if (*c == '\0')
    789 				userconf_list();
    790 			else
    791 				printf("Unknown argument\n");
    792 			break;
    793 		case 'q':
    794 			/* XXX add cmd 'q' eoc */
    795 			userconf_hist_cmd('q');
    796 			userconf_hist_eoc();
    797 			return(-1);
    798 		case 's':
    799 		default:
    800 			printf("Unknown command\n");
    801 			break;
    802 		}
    803 	}
    804 	return(0);
    805 }
    806 
    807 extern void user_config(void);
    808 
    809 void
    810 user_config(void)
    811 {
    812 	char prompt[] = "uc> ";
    813 
    814 	userconf_init();
    815 	printf("userconf: configure system autoconfiguration:\n");
    816 
    817 	while (1) {
    818 		printf(prompt);
    819 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
    820 		    userconf_parse(userconf_cmdbuf))
    821 			break;
    822 	}
    823 	printf("Continuing...\n");
    824 }
    825 
    826 /*
    827  * XXX shouldn't this be a common function?
    828  */
    829 static int
    830 getsn(char *cp, int size)
    831 {
    832 	char *lp;
    833 	int c, len;
    834 
    835 	cnpollc(1);
    836 
    837 	lp = cp;
    838 	len = 0;
    839 	for (;;) {
    840 		c = cngetc();
    841 		switch (c) {
    842 		case '\n':
    843 		case '\r':
    844 			printf("\n");
    845 			*lp++ = '\0';
    846 			cnpollc(0);
    847 			return (len);
    848 		case '\b':
    849 		case '\177':
    850 		case '#':
    851 			if (len) {
    852 				--len;
    853 				--lp;
    854 				printf("\b \b");
    855 			}
    856 			continue;
    857 		case '@':
    858 		case 'u'&037:
    859 			len = 0;
    860 			lp = cp;
    861 			printf("\n");
    862 			continue;
    863 		default:
    864 			if (len + 1 >= size || c < ' ') {
    865 				printf("\007");
    866 				continue;
    867 			}
    868 			printf("%c", c);
    869 			++len;
    870 			*lp++ = c;
    871 		}
    872 	}
    873 }
    874