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