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