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