xref: /netbsd-src/sys/kern/subr_userconf.c (revision ba65fde2d7fefa7d39838fa5fa855e62bd606b5e)
1 /*	$NetBSD: subr_userconf.c,v 1.25 2011/08/01 10:33:26 drochner 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.25 2011/08/01 10:33:26 drochner 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 || *c == ' ' || *c == '\t' || *c == '\n'))
308 		c++;
309 	while (c > cmd) {
310 		c--;
311 		if (!((*c >= '0' && *c <= '9') || *c == '*')) {
312 			c++;
313 			break;
314 		}
315 	}
316 	l = c - cmd;
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("%s", 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 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 void
808 userconf_prompt(void)
809 {
810 	const char prompt[] = "uc> ";
811 
812 	printf("userconf: configure system autoconfiguration:\n");
813 
814 	while (1) {
815 		printf(prompt);
816 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
817 		    userconf_parse(userconf_cmdbuf))
818 			break;
819 	}
820 	printf("Continuing...\n");
821 }
822 
823 /*
824  * XXX shouldn't this be a common function?
825  */
826 static int
827 getsn(char *cp, int size)
828 {
829 	char *lp;
830 	int c, len;
831 
832 	cnpollc(1);
833 
834 	lp = cp;
835 	len = 0;
836 	for (;;) {
837 		c = cngetc();
838 		switch (c) {
839 		case '\n':
840 		case '\r':
841 			printf("\n");
842 			*lp++ = '\0';
843 			cnpollc(0);
844 			return (len);
845 		case '\b':
846 		case '\177':
847 		case '#':
848 			if (len) {
849 				--len;
850 				--lp;
851 				printf("\b \b");
852 			}
853 			continue;
854 		case '@':
855 		case 'u'&037:
856 			len = 0;
857 			lp = cp;
858 			printf("\n");
859 			continue;
860 		default:
861 			if (len + 1 >= size || c < ' ') {
862 				printf("\007");
863 				continue;
864 			}
865 			printf("%c", c);
866 			++len;
867 			*lp++ = c;
868 		}
869 	}
870 }
871