xref: /netbsd-src/sys/kern/subr_userconf.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: subr_userconf.c,v 1.26 2013/12/23 15:34:16 skrll 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.26 2013/12/23 15:34:16 skrll 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 #define UC_CHANGE 'c'
61 #define UC_DISABLE 'd'
62 #define UC_ENABLE 'e'
63 #define UC_FIND 'f'
64 #define UC_SHOW 's'
65 
66 static const char *userconf_cmds[] = {
67 	"base",		"b",
68 	"change",	"c",
69 	"disable",	"d",
70 	"enable",	"e",
71 	"exit",		"q",
72 	"find",		"f",
73 	"help",		"h",
74 	"list",		"l",
75 	"lines",	"L",
76 	"quit",		"q",
77 	"?",		"h",
78 	"",		 "",
79 };
80 
81 void
82 userconf_init(void)
83 {
84 	int i;
85 	struct cfdata *cf;
86 
87 	i = 0;
88 	for (cf = cfdata; cf->cf_name; cf++)
89 		i++;
90 
91 	userconf_maxdev = i - 1;
92 	userconf_totdev = i - 1;
93 
94 	userconf_bootinfo();
95 }
96 
97 static int
98 userconf_more(void)
99 {
100 	int quit = 0;
101 	char c = '\0';
102 
103 	if (userconf_cnt != -1) {
104 		if (userconf_cnt == userconf_lines) {
105 			printf("-- more --");
106 			c = cngetc();
107 			userconf_cnt = 0;
108 			printf("\r            \r");
109 		}
110 		userconf_cnt++;
111 		if (c == 'q' || c == 'Q')
112 			quit = 1;
113 	}
114 	return (quit);
115 }
116 
117 static void
118 userconf_hist_cmd(char cmd)
119 {
120 	userconf_histcur = userconf_histlen;
121 	if (userconf_histcur < userconf_histsz) {
122 		userconf_history[userconf_histcur] = cmd;
123 		userconf_histcur++;
124 	}
125 }
126 
127 static void
128 userconf_hist_int(int val)
129 {
130 	snprintf(userconf_histbuf, sizeof(userconf_histbuf), " %d", val);
131 	if ((userconf_histcur + strlen(userconf_histbuf)) < userconf_histsz) {
132 		memcpy(&userconf_history[userconf_histcur],
133 		      userconf_histbuf,
134 		      strlen(userconf_histbuf));
135 		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
136 	}
137 }
138 
139 static void
140 userconf_hist_eoc(void)
141 {
142 	if (userconf_histcur < userconf_histsz) {
143 		userconf_history[userconf_histcur] = '\n';
144 		userconf_histcur++;
145 		userconf_histlen = userconf_histcur;
146 	}
147 }
148 
149 static void
150 userconf_pnum(int val)
151 {
152 	if (val > -2 && val < 16) {
153 		printf("%d",val);
154 	} else {
155 		switch (userconf_base) {
156 		case 8:
157 			printf("0%o",val);
158 			break;
159 		case 10:
160 			printf("%d",val);
161 			break;
162 		case 16:
163 		default:
164 			printf("0x%x",val);
165 			break;
166 		}
167 	}
168 }
169 
170 static void
171 userconf_pdevnam(short dev)
172 {
173 	struct cfdata *cd;
174 
175 	cd = &cfdata[dev];
176 	printf("%s", cd->cf_name);
177 	switch (cd->cf_fstate) {
178 	case FSTATE_NOTFOUND:
179 	case FSTATE_DNOTFOUND:
180 		printf("%d", cd->cf_unit);
181 		break;
182 	case FSTATE_FOUND:
183 		printf("*FOUND*");
184 		break;
185 	case FSTATE_STAR:
186 	case FSTATE_DSTAR:
187 		printf("*");
188 		break;
189 	default:
190 		printf("*UNKNOWN*");
191 		break;
192 	}
193 }
194 
195 static void
196 userconf_pdev(short devno)
197 {
198 	struct cfdata *cd;
199 	const struct cfparent *cfp;
200 	int   *l;
201 	const struct cfiattrdata *ia;
202 	const struct cflocdesc *ld;
203 	int nld, i;
204 
205 	if (devno > userconf_maxdev) {
206 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
207 		return;
208 	}
209 
210 	cd = &cfdata[devno];
211 
212 	printf("[%3d] ", devno);
213 	userconf_pdevnam(devno);
214 	printf(" at");
215 	cfp = cd->cf_pspec;
216 	if (cfp == NULL)
217 		printf(" root");
218 	else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
219 		printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
220 	else
221 		printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
222 						       : cfp->cfp_iattr);
223 	switch (cd->cf_fstate) {
224 	case FSTATE_NOTFOUND:
225 	case FSTATE_FOUND:
226 	case FSTATE_STAR:
227 		break;
228 	case FSTATE_DNOTFOUND:
229 	case FSTATE_DSTAR:
230 		printf(" disable");
231 		break;
232 	default:
233 		printf(" ???");
234 		break;
235 	}
236 	if (cfp) {
237 		l = cd->cf_loc;
238 		ia = cfiattr_lookup(cfp->cfp_iattr, 0);
239 		KASSERT(ia);
240 		ld = ia->ci_locdesc;
241 		nld = ia->ci_loclen;
242 		for (i = 0; i < nld; i++) {
243 			printf(" %s ", ld[i].cld_name);
244 			if (!ld[i].cld_defaultstr
245 			    || (l[i] != ld[i].cld_default))
246 				userconf_pnum(l[i]);
247 			else
248 				printf("?");
249 		}
250 	}
251 	printf("\n");
252 }
253 
254 static int
255 userconf_number(char *c, int *val)
256 {
257 	u_int num = 0;
258 	int neg = 0;
259 	int base = 10;
260 
261 	if (*c == '-') {
262 		neg = 1;
263 		c++;
264 	}
265 	if (*c == '0') {
266 		base = 8;
267 		c++;
268 		if (*c == 'x' || *c == 'X') {
269 			base = 16;
270 			c++;
271 		}
272 	}
273 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
274 		u_char cc = *c;
275 
276 		if (cc >= '0' && cc <= '9')
277 			cc = cc - '0';
278 		else if (cc >= 'a' && cc <= 'f')
279 			cc = cc - 'a' + 10;
280 		else if (cc >= 'A' && cc <= 'F')
281 			cc = cc - 'A' + 10;
282 		else
283 			return (-1);
284 
285 		if (cc > base)
286 			return (-1);
287 		num = num * base + cc;
288 		c++;
289 	}
290 
291 	if (neg && num > INT_MAX)	/* overflow */
292 		return (1);
293 	*val = neg ? - num : num;
294 	return (0);
295 }
296 
297 static int
298 userconf_device(char *cmd, int *len, short *unit, short *state)
299 {
300 	short u = 0, s = FSTATE_FOUND;
301 	int l = 0;
302 	char *c;
303 
304 	c = cmd;
305 	while (!(!*c || *c == ' ' || *c == '\t' || *c == '\n'))
306 		c++;
307 	while (c > cmd) {
308 		c--;
309 		if (!((*c >= '0' && *c <= '9') || *c == '*')) {
310 			c++;
311 			break;
312 		}
313 	}
314 	l = c - cmd;
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 		cngetsn(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 (cngetsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
815 		    userconf_parse(userconf_cmdbuf))
816 			break;
817 	}
818 	printf("Continuing...\n");
819 }
820