xref: /netbsd-src/sys/kern/subr_userconf.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*	$NetBSD: subr_userconf.c,v 1.19 2009/10/20 00:51:13 snj Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 Mats O Jansson <moj@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 2009/10/20 00:51:13 snj 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/malloc.h>
40 #include <sys/time.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 static 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 
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(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 static 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 extern void user_config(void);
802 
803 void
804 user_config(void)
805 {
806 	char prompt[] = "uc> ";
807 
808 	userconf_init();
809 	printf("userconf: configure system autoconfiguration:\n");
810 
811 	while (1) {
812 		printf(prompt);
813 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
814 		    userconf_parse(userconf_cmdbuf))
815 			break;
816 	}
817 	printf("Continuing...\n");
818 }
819 
820 /*
821  * XXX shouldn't this be a common function?
822  */
823 static int
824 getsn(char *cp, int size)
825 {
826 	char *lp;
827 	int c, len;
828 
829 	cnpollc(1);
830 
831 	lp = cp;
832 	len = 0;
833 	for (;;) {
834 		c = cngetc();
835 		switch (c) {
836 		case '\n':
837 		case '\r':
838 			printf("\n");
839 			*lp++ = '\0';
840 			cnpollc(0);
841 			return (len);
842 		case '\b':
843 		case '\177':
844 		case '#':
845 			if (len) {
846 				--len;
847 				--lp;
848 				printf("\b \b");
849 			}
850 			continue;
851 		case '@':
852 		case 'u'&037:
853 			len = 0;
854 			lp = cp;
855 			printf("\n");
856 			continue;
857 		default:
858 			if (len + 1 >= size || c < ' ') {
859 				printf("\007");
860 				continue;
861 			}
862 			printf("%c", c);
863 			++len;
864 			*lp++ = c;
865 		}
866 	}
867 }
868