Home | History | Annotate | Line # | Download | only in kern
subr_userconf.c revision 1.15
      1 /*	$NetBSD: subr_userconf.c,v 1.15 2005/06/23 18:44:44 thorpej 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 2005/06/23 18:44:44 thorpej 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 char * const *ln;
    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 	l = cd->cf_loc;
    243 	ln = cd->cf_locnames;
    244 	while (ln && *ln) {
    245 		printf(" %s ", *ln++);
    246 		userconf_pnum(*l++);
    247 	}
    248 	printf("\n");
    249 }
    250 
    251 static int
    252 userconf_number(char *c, int *val)
    253 {
    254 	u_int num = 0;
    255 	int neg = 0;
    256 	int base = 10;
    257 
    258 	if (*c == '-') {
    259 		neg = 1;
    260 		c++;
    261 	}
    262 	if (*c == '0') {
    263 		base = 8;
    264 		c++;
    265 		if (*c == 'x' || *c == 'X') {
    266 			base = 16;
    267 			c++;
    268 		}
    269 	}
    270 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
    271 		u_char cc = *c;
    272 
    273 		if (cc >= '0' && cc <= '9')
    274 			cc = cc - '0';
    275 		else if (cc >= 'a' && cc <= 'f')
    276 			cc = cc - 'a' + 10;
    277 		else if (cc >= 'A' && cc <= 'F')
    278 			cc = cc - 'A' + 10;
    279 		else
    280 			return (-1);
    281 
    282 		if (cc > base)
    283 			return (-1);
    284 		num = num * base + cc;
    285 		c++;
    286 	}
    287 
    288 	if (neg && num > INT_MAX)	/* overflow */
    289 		return (1);
    290 	*val = neg ? - num : num;
    291 	return (0);
    292 }
    293 
    294 static int
    295 userconf_device(char *cmd, int *len, short *unit, short *state)
    296 {
    297 	short u = 0, s = FSTATE_FOUND;
    298 	int l = 0;
    299 	char *c;
    300 
    301 	c = cmd;
    302 	while (*c >= 'a' && *c <= 'z') {
    303 		l++;
    304 		c++;
    305 	}
    306 	if (*c == '*') {
    307 		s = FSTATE_STAR;
    308 		c++;
    309 	} else {
    310 		while (*c >= '0' && *c <= '9') {
    311 			s = FSTATE_NOTFOUND;
    312 			u = u*10 + *c - '0';
    313 			c++;
    314 		}
    315 	}
    316 	while (*c == ' ' || *c == '\t' || *c == '\n')
    317 		c++;
    318 
    319 	if (*c == '\0') {
    320 		*len = l;
    321 		*unit = u;
    322 		*state = s;
    323 		return(0);
    324 	}
    325 
    326 	return(-1);
    327 }
    328 
    329 static void
    330 userconf_modify(const char *item, int *val)
    331 {
    332 	int ok = 0;
    333 	int a;
    334 	char *c;
    335 
    336 	while (!ok) {
    337 		printf("%s [", item);
    338 		userconf_pnum(*val);
    339 		printf("] ? ");
    340 
    341 		getsn(userconf_argbuf, sizeof(userconf_argbuf));
    342 
    343 		c = userconf_argbuf;
    344 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
    345 
    346 		if (*c != '\0') {
    347 			if (userconf_number(c, &a) == 0) {
    348 				*val = a;
    349 				ok = 1;
    350 			} else {
    351 				printf("Unknown argument\n");
    352 			}
    353 		} else {
    354 			ok = 1;
    355 		}
    356 	}
    357 }
    358 
    359 static void
    360 userconf_change(int devno)
    361 {
    362 	struct cfdata *cd;
    363 	char c = '\0';
    364 	int   *l;
    365 	int   ln;
    366 	const char * const *locnames;
    367 
    368 	if (devno <=  userconf_maxdev) {
    369 
    370 		userconf_pdev(devno);
    371 
    372 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
    373 			printf("change (y/n) ?");
    374 			c = cngetc();
    375 			printf("\n");
    376 		}
    377 
    378 		if (c == 'y' || c == 'Y') {
    379 
    380 			/* XXX add cmd 'c' <devno> */
    381 			userconf_hist_cmd('c');
    382 			userconf_hist_int(devno);
    383 
    384 			cd = &cfdata[devno];
    385 			l = cd->cf_loc;
    386 			locnames = cd->cf_locnames;
    387 			ln = 0;
    388 
    389 			while (locnames[ln])
    390 			{
    391 				userconf_modify(locnames[ln], l);
    392 
    393 				/* XXX add *l */
    394 				userconf_hist_int(*l);
    395 
    396 				ln++;
    397 				l++;
    398 			}
    399 
    400 			printf("[%3d] ", devno);
    401 			userconf_pdevnam(devno);
    402 			printf(" changed\n");
    403 			userconf_pdev(devno);
    404 
    405 			/* XXX add eoc */
    406 			userconf_hist_eoc();
    407 
    408 		}
    409 	} else {
    410 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    411 	}
    412 }
    413 
    414 static void
    415 userconf_disable(int devno)
    416 {
    417 	int done = 0;
    418 
    419 	if (devno <= userconf_maxdev) {
    420 		switch (cfdata[devno].cf_fstate) {
    421 		case FSTATE_NOTFOUND:
    422 			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
    423 			break;
    424 		case FSTATE_STAR:
    425 			cfdata[devno].cf_fstate = FSTATE_DSTAR;
    426 			break;
    427 		case FSTATE_DNOTFOUND:
    428 		case FSTATE_DSTAR:
    429 			done = 1;
    430 			break;
    431 		default:
    432 			printf("Error unknown state\n");
    433 			break;
    434 		}
    435 
    436 		printf("[%3d] ", devno);
    437 		userconf_pdevnam(devno);
    438 		if (done) {
    439 			printf(" already");
    440 		} else {
    441 			/* XXX add cmd 'd' <devno> eoc */
    442 			userconf_hist_cmd('d');
    443 			userconf_hist_int(devno);
    444 			userconf_hist_eoc();
    445 		}
    446 		printf(" disabled\n");
    447 	} else {
    448 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    449 	}
    450 }
    451 
    452 static void
    453 userconf_enable(int devno)
    454 {
    455 	int done = 0;
    456 
    457 	if (devno <= userconf_maxdev) {
    458 		switch (cfdata[devno].cf_fstate) {
    459 		case FSTATE_DNOTFOUND:
    460 			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
    461 			break;
    462 		case FSTATE_DSTAR:
    463 			cfdata[devno].cf_fstate = FSTATE_STAR;
    464 			break;
    465 		case FSTATE_NOTFOUND:
    466 		case FSTATE_STAR:
    467 			done = 1;
    468 			break;
    469 		default:
    470 			printf("Error unknown state\n");
    471 			break;
    472 		}
    473 
    474 		printf("[%3d] ", devno);
    475 		userconf_pdevnam(devno);
    476 		if (done) {
    477 			printf(" already");
    478 		} else {
    479 			/* XXX add cmd 'e' <devno> eoc */
    480 			userconf_hist_cmd('d');
    481 			userconf_hist_int(devno);
    482 			userconf_hist_eoc();
    483 		}
    484 		printf(" enabled\n");
    485 	} else {
    486 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
    487 	}
    488 }
    489 
    490 static void
    491 userconf_help(void)
    492 {
    493 	int j = 0, k;
    494 
    495 	printf("command   args                description\n");
    496 	while (*userconf_cmds[j] != '\0') {
    497 		printf(userconf_cmds[j]);
    498 		k = strlen(userconf_cmds[j]);
    499 		while (k < 10) {
    500 			printf(" ");
    501 			k++;
    502 		}
    503 		switch (*userconf_cmds[j+1]) {
    504 		case 'L':
    505 			printf("[count]             number of lines before more");
    506 			break;
    507 		case 'b':
    508 			printf("8|10|16             base on large numbers");
    509 			break;
    510 		case 'c':
    511 			printf("devno|dev           change devices");
    512 			break;
    513 		case 'd':
    514 			printf("devno|dev           disable devices");
    515 			break;
    516 		case 'e':
    517 			printf("devno|dev           enable devices");
    518 			break;
    519 		case 'f':
    520 			printf("devno|dev           find devices");
    521 			break;
    522 		case 'h':
    523 			printf("                    this message");
    524 			break;
    525 		case 'l':
    526 			printf("                    list configuration");
    527 			break;
    528 		case 'q':
    529 			printf("                    leave userconf");
    530 			break;
    531 		default:
    532 			printf("                    don't know");
    533 			break;
    534 		}
    535 		printf("\n");
    536 		j += 2;
    537 	}
    538 }
    539 
    540 static void
    541 userconf_list(void)
    542 {
    543 	int i = 0;
    544 
    545 	userconf_cnt = 0;
    546 
    547 	while (cfdata[i].cf_name != NULL) {
    548 		if (userconf_more())
    549 			break;
    550 		userconf_pdev(i++);
    551 	}
    552 
    553 	userconf_cnt = -1;
    554 }
    555 
    556 static void
    557 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
    558 {
    559 	int i = 0;
    560 
    561 	switch (routine) {
    562 	case UC_CHANGE:
    563 		break;
    564 	default:
    565 		userconf_cnt = 0;
    566 		break;
    567 	}
    568 
    569 	while (cfdata[i].cf_name != NULL) {
    570 		if (strlen(cfdata[i].cf_name) == len) {
    571 
    572 			/*
    573 			 * Ok, if device name is correct
    574 			 *  If state == FSTATE_FOUND, look for "dev"
    575 			 *  If state == FSTATE_STAR, look for "dev*"
    576 			 *  If state == FSTATE_NOTFOUND, look for "dev0"
    577 			 */
    578 			if (strncasecmp(dev, cfdata[i].cf_name,
    579 					len) == 0 &&
    580 			    (state == FSTATE_FOUND ||
    581 			     (state == FSTATE_STAR &&
    582 			      (cfdata[i].cf_fstate == FSTATE_STAR ||
    583 			       cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
    584 			     (state == FSTATE_NOTFOUND &&
    585 			      cfdata[i].cf_unit == unit &&
    586 			      (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
    587 			       cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
    588 				if (userconf_more())
    589 					break;
    590 				switch (routine) {
    591 				case UC_CHANGE:
    592 					userconf_change(i);
    593 					break;
    594 				case UC_ENABLE:
    595 					userconf_enable(i);
    596 					break;
    597 				case UC_DISABLE:
    598 					userconf_disable(i);
    599 					break;
    600 				case UC_FIND:
    601 					userconf_pdev(i);
    602 					break;
    603 				default:
    604 					printf("Unknown routine /%c/\n",
    605 					    routine);
    606 					break;
    607 				}
    608 			}
    609 		}
    610 		i++;
    611 	}
    612 
    613 	switch (routine) {
    614 	case UC_CHANGE:
    615 		break;
    616 	default:
    617 		userconf_cnt = -1;
    618 		break;
    619 	}
    620 }
    621 
    622 #if 0
    623 static void
    624 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
    625 {
    626 	int ok = 0;
    627 	int a;
    628 	char *c;
    629 
    630 	*val = -1;
    631 
    632 	while (!ok) {
    633 		printf("%s ? ", prompt);
    634 
    635 		getsn(userconf_argbuf, sizeof(userconf_argbuf));
    636 
    637 		c = userconf_argbuf;
    638 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
    639 
    640 		if (*c != '\0') {
    641 			if (userconf_number(c, &a) == 0) {
    642 				if (a > userconf_maxdev) {
    643 					printf("Unknown devno (max is %d)\n",
    644 					    userconf_maxdev);
    645 				} else if (strncasecmp(dev,
    646 				    cfdata[a].cf_name, len) != 0 &&
    647 					field == 'a') {
    648 					printf("Not same device type\n");
    649 				} else {
    650 					*val = a;
    651 					ok = 1;
    652 				}
    653 			} else if (*c == '?') {
    654 				userconf_common_dev(dev, len, 0,
    655 				    FSTATE_FOUND, UC_FIND);
    656 			} else if (*c == 'q' || *c == 'Q') {
    657 				ok = 1;
    658 			} else {
    659 				printf("Unknown argument\n");
    660 			}
    661 		} else {
    662 			ok = 1;
    663 		}
    664 	}
    665 }
    666 #endif /* 0 */
    667 
    668 static int
    669 userconf_parse(char *cmd)
    670 {
    671 	char *c, *v;
    672 	int i = 0, j = 0, k, a;
    673 	short unit, state;
    674 
    675 	c = cmd;
    676 	while (*c == ' ' || *c == '\t')
    677 		c++;
    678 	v = c;
    679 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
    680 		c++;
    681 		i++;
    682 	}
    683 
    684 	k = -1;
    685 	while (*userconf_cmds[j] != '\0') {
    686 		if (strlen(userconf_cmds[j]) == i) {
    687 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
    688 				k = j;
    689 		}
    690 		j += 2;
    691 	}
    692 
    693 	while (*c == ' ' || *c == '\t' || *c == '\n')
    694 		c++;
    695 
    696 	if (k == -1) {
    697 		if (*v != '\n')
    698 			printf("Unknown command, try help\n");
    699 	} else {
    700 		switch (*userconf_cmds[k+1]) {
    701 		case 'L':
    702 			if (*c == '\0')
    703 				printf("Argument expected\n");
    704 			else if (userconf_number(c, &a) == 0)
    705 				userconf_lines = a;
    706 			else
    707 				printf("Unknown argument\n");
    708 			break;
    709 		case 'b':
    710 			if (*c == '\0')
    711 				printf("8|10|16 expected\n");
    712 			else if (userconf_number(c, &a) == 0) {
    713 				if (a == 8 || a == 10 || a == 16) {
    714 					userconf_base = a;
    715 				} else {
    716 					printf("8|10|16 expected\n");
    717 				}
    718 			} else
    719 				printf("Unknown argument\n");
    720 			break;
    721 		case 'c':
    722 			if (*c == '\0')
    723 				printf("DevNo or Dev expected\n");
    724 			else if (userconf_number(c, &a) == 0)
    725 				userconf_change(a);
    726 			else if (userconf_device(c, &a, &unit, &state) == 0)
    727 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
    728 			else
    729 				printf("Unknown argument\n");
    730 			break;
    731 		case 'd':
    732 			if (*c == '\0')
    733 				printf("Attr, DevNo or Dev expected\n");
    734 			else if (userconf_number(c, &a) == 0)
    735 				userconf_disable(a);
    736 			else if (userconf_device(c, &a, &unit, &state) == 0)
    737 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
    738 			else
    739 				printf("Unknown argument\n");
    740 			break;
    741 		case 'e':
    742 			if (*c == '\0')
    743 				printf("Attr, DevNo or Dev expected\n");
    744 			else if (userconf_number(c, &a) == 0)
    745 				userconf_enable(a);
    746 			else if (userconf_device(c, &a, &unit, &state) == 0)
    747 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
    748 			else
    749 				printf("Unknown argument\n");
    750 			break;
    751 		case 'f':
    752 			if (*c == '\0')
    753 				printf("DevNo or Dev expected\n");
    754 			else if (userconf_number(c, &a) == 0)
    755 				userconf_pdev(a);
    756 			else if (userconf_device(c, &a, &unit, &state) == 0)
    757 				userconf_common_dev(c, a, unit, state, UC_FIND);
    758 			else
    759 				printf("Unknown argument\n");
    760 			break;
    761 		case 'h':
    762 			userconf_help();
    763 			break;
    764 		case 'l':
    765 			if (*c == '\0')
    766 				userconf_list();
    767 			else
    768 				printf("Unknown argument\n");
    769 			break;
    770 		case 'q':
    771 			/* XXX add cmd 'q' eoc */
    772 			userconf_hist_cmd('q');
    773 			userconf_hist_eoc();
    774 			return(-1);
    775 		case 's':
    776 		default:
    777 			printf("Unknown command\n");
    778 			break;
    779 		}
    780 	}
    781 	return(0);
    782 }
    783 
    784 extern void user_config(void);
    785 
    786 void
    787 user_config(void)
    788 {
    789 	char prompt[] = "uc> ";
    790 
    791 	userconf_init();
    792 	printf("userconf: configure system autoconfiguration:\n");
    793 
    794 	while (1) {
    795 		printf(prompt);
    796 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
    797 		    userconf_parse(userconf_cmdbuf))
    798 			break;
    799 	}
    800 	printf("Continuing...\n");
    801 }
    802 
    803 /*
    804  * XXX shouldn't this be a common function?
    805  */
    806 static int
    807 getsn(char *cp, int size)
    808 {
    809 	char *lp;
    810 	int c, len;
    811 
    812 	cnpollc(1);
    813 
    814 	lp = cp;
    815 	len = 0;
    816 	for (;;) {
    817 		c = cngetc();
    818 		switch (c) {
    819 		case '\n':
    820 		case '\r':
    821 			printf("\n");
    822 			*lp++ = '\0';
    823 			cnpollc(0);
    824 			return (len);
    825 		case '\b':
    826 		case '\177':
    827 		case '#':
    828 			if (len) {
    829 				--len;
    830 				--lp;
    831 				printf("\b \b");
    832 			}
    833 			continue;
    834 		case '@':
    835 		case 'u'&037:
    836 			len = 0;
    837 			lp = cp;
    838 			printf("\n");
    839 			continue;
    840 		default:
    841 			if (len + 1 >= size || c < ' ') {
    842 				printf("\007");
    843 				continue;
    844 			}
    845 			printf("%c", c);
    846 			++len;
    847 			*lp++ = c;
    848 		}
    849 	}
    850 }
    851