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