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