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