xref: /netbsd/sys/kern/subr_userconf.c (revision c4a72b64)
1 /*	$NetBSD: subr_userconf.c,v 1.10 2002/10/22 03:27:47 simonb 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.10 2002/10/22 03:27:47 simonb 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_name; 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_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 	const struct cfparent *cfp;
231 	int   *l;
232 	const char * const *ln;
233 
234 	if (devno > userconf_maxdev) {
235 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
236 		return;
237 	}
238 
239 	cd = &cfdata[devno];
240 
241 	printf("[%3d] ", devno);
242 	userconf_pdevnam(devno);
243 	printf(" at");
244 	cfp = cd->cf_pspec;
245 	if (cfp == NULL)
246 		printf(" root");
247 	else if (cfp->cfp_parent != NULL && cfp->cfp_unit != -1)
248 		printf(" %s%d", cfp->cfp_parent, cfp->cfp_unit);
249 	else
250 		printf(" %s?", cfp->cfp_parent != NULL ? cfp->cfp_parent
251 						       : cfp->cfp_iattr);
252 	switch (cd->cf_fstate) {
253 	case FSTATE_NOTFOUND:
254 	case FSTATE_FOUND:
255 	case FSTATE_STAR:
256 		break;
257 	case FSTATE_DNOTFOUND:
258 	case FSTATE_DSTAR:
259 		printf(" disable");
260 		break;
261 	default:
262 		printf(" ???");
263 		break;
264 	}
265 	l = cd->cf_loc;
266 	ln = cd->cf_locnames;
267 	while (ln && *ln) {
268 		printf(" %s ", *ln++);
269 		userconf_pnum(*l++);
270 	}
271 	printf("\n");
272 }
273 
274 int
275 userconf_number(c, val)
276 	char *c;
277 	int *val;
278 {
279 	u_int num = 0;
280 	int neg = 0;
281 	int base = 10;
282 
283 	if (*c == '-') {
284 		neg = 1;
285 		c++;
286 	}
287 	if (*c == '0') {
288 		base = 8;
289 		c++;
290 		if (*c == 'x' || *c == 'X') {
291 			base = 16;
292 			c++;
293 		}
294 	}
295 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
296 		u_char cc = *c;
297 
298 		if (cc >= '0' && cc <= '9')
299 			cc = cc - '0';
300 		else if (cc >= 'a' && cc <= 'f')
301 			cc = cc - 'a' + 10;
302 		else if (cc >= 'A' && cc <= 'F')
303 			cc = cc - 'A' + 10;
304 		else
305 			return (-1);
306 
307 		if (cc > base)
308 			return (-1);
309 		num = num * base + cc;
310 		c++;
311 	}
312 
313 	if (neg && num > INT_MAX)	/* overflow */
314 		return (1);
315 	*val = neg ? - num : num;
316 	return (0);
317 }
318 
319 int
320 userconf_device(cmd, len, unit, state)
321 	char *cmd;
322 	int *len;
323 	short *unit, *state;
324 {
325 	short u = 0, s = FSTATE_FOUND;
326 	int l = 0;
327 	char *c;
328 
329 	c = cmd;
330 	while (*c >= 'a' && *c <= 'z') {
331 		l++;
332 		c++;
333 	}
334 	if (*c == '*') {
335 		s = FSTATE_STAR;
336 		c++;
337 	} else {
338 		while (*c >= '0' && *c <= '9') {
339 			s = FSTATE_NOTFOUND;
340 			u = u*10 + *c - '0';
341 			c++;
342 		}
343 	}
344 	while (*c == ' ' || *c == '\t' || *c == '\n')
345 		c++;
346 
347 	if (*c == '\0') {
348 		*len = l;
349 		*unit = u;
350 		*state = s;
351 		return(0);
352 	}
353 
354 	return(-1);
355 }
356 
357 void
358 userconf_modify(item, val)
359 	const char *item;
360 	int  *val;
361 {
362 	int ok = 0;
363 	int a;
364 	char *c;
365 
366 	while (!ok) {
367 		printf("%s [", item);
368 		userconf_pnum(*val);
369 		printf("] ? ");
370 
371 		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 * const *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_name != NULL) {
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_name != NULL) {
607 		if (strlen(cfdata[i].cf_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_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 
671 	*val = -1;
672 
673 	while (!ok) {
674 		printf("%s ? ", prompt);
675 
676 		getsn(userconf_argbuf, sizeof(userconf_argbuf));
677 
678 		c = userconf_argbuf;
679 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
680 
681 		if (*c != '\0') {
682 			if (userconf_number(c, &a) == 0) {
683 				if (a > userconf_maxdev) {
684 					printf("Unknown devno (max is %d)\n",
685 					    userconf_maxdev);
686 				} else if (strncasecmp(dev,
687 				    cfdata[a].cf_name, len) != 0 &&
688 					field == 'a') {
689 					printf("Not same device type\n");
690 				} else {
691 					*val = a;
692 					ok = 1;
693 				}
694 			} else if (*c == '?') {
695 				userconf_common_dev(dev, len, 0,
696 				    FSTATE_FOUND, UC_FIND);
697 			} else if (*c == 'q' || *c == 'Q') {
698 				ok = 1;
699 			} else {
700 				printf("Unknown argument\n");
701 			}
702 		} else {
703 			ok = 1;
704 		}
705 	}
706 }
707 
708 int
709 userconf_parse(cmd)
710 	char *cmd;
711 {
712 	char *c, *v;
713 	int i = 0, j = 0, k, a;
714 	short unit, state;
715 
716 	c = cmd;
717 	while (*c == ' ' || *c == '\t')
718 		c++;
719 	v = c;
720 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
721 		c++;
722 		i++;
723 	}
724 
725 	k = -1;
726 	while (*userconf_cmds[j] != '\0') {
727 		if (strlen(userconf_cmds[j]) == i) {
728 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
729 				k = j;
730 		}
731 		j += 2;
732 	}
733 
734 	while (*c == ' ' || *c == '\t' || *c == '\n')
735 		c++;
736 
737 	if (k == -1) {
738 		if (*v != '\n')
739 			printf("Unknown command, try help\n");
740 	} else {
741 		switch (*userconf_cmds[k+1]) {
742 		case 'L':
743 			if (*c == '\0')
744 				printf("Argument expected\n");
745 			else if (userconf_number(c, &a) == 0)
746 				userconf_lines = a;
747 			else
748 				printf("Unknown argument\n");
749 			break;
750 		case 'b':
751 			if (*c == '\0')
752 				printf("8|10|16 expected\n");
753 			else if (userconf_number(c, &a) == 0) {
754 				if (a == 8 || a == 10 || a == 16) {
755 					userconf_base = a;
756 				} else {
757 					printf("8|10|16 expected\n");
758 				}
759 			} else
760 				printf("Unknown argument\n");
761 			break;
762 		case 'c':
763 			if (*c == '\0')
764 				printf("DevNo or Dev expected\n");
765 			else if (userconf_number(c, &a) == 0)
766 				userconf_change(a);
767 			else if (userconf_device(c, &a, &unit, &state) == 0)
768 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
769 			else
770 				printf("Unknown argument\n");
771 			break;
772 		case 'd':
773 			if (*c == '\0')
774 				printf("Attr, DevNo or Dev expected\n");
775 			else if (userconf_number(c, &a) == 0)
776 				userconf_disable(a);
777 			else if (userconf_device(c, &a, &unit, &state) == 0)
778 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
779 			else
780 				printf("Unknown argument\n");
781 			break;
782 		case 'e':
783 			if (*c == '\0')
784 				printf("Attr, DevNo or Dev expected\n");
785 			else if (userconf_number(c, &a) == 0)
786 				userconf_enable(a);
787 			else if (userconf_device(c, &a, &unit, &state) == 0)
788 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
789 			else
790 				printf("Unknown argument\n");
791 			break;
792 		case 'f':
793 			if (*c == '\0')
794 				printf("DevNo or Dev expected\n");
795 			else if (userconf_number(c, &a) == 0)
796 				userconf_pdev(a);
797 			else if (userconf_device(c, &a, &unit, &state) == 0)
798 				userconf_common_dev(c, a, unit, state, UC_FIND);
799 			else
800 				printf("Unknown argument\n");
801 			break;
802 		case 'h':
803 			userconf_help();
804 			break;
805 		case 'l':
806 			if (*c == '\0')
807 				userconf_list();
808 			else
809 				printf("Unknown argument\n");
810 			break;
811 		case 'q':
812 			/* XXX add cmd 'q' eoc */
813 			userconf_hist_cmd('q');
814 			userconf_hist_eoc();
815 			return(-1);
816 		case 's':
817 		default:
818 			printf("Unknown command\n");
819 			break;
820 		}
821 	}
822 	return(0);
823 }
824 
825 extern void user_config __P((void));
826 
827 void
828 user_config()
829 {
830 	char prompt[] = "uc> ";
831 
832 	userconf_init();
833 	printf("userconf: configure system autoconfiguration:\n");
834 
835 	while (1) {
836 		printf(prompt);
837 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
838 		    userconf_parse(userconf_cmdbuf))
839 			break;
840 	}
841 	printf("Continuing...\n");
842 }
843 
844 /*
845  * XXX shouldn't this be a common function?
846  */
847 static int
848 getsn(cp, size)
849 	char *cp;
850 	int size;
851 {
852 	char *lp;
853 	int c, len;
854 
855 	cnpollc(1);
856 
857 	lp = cp;
858 	len = 0;
859 	for (;;) {
860 		c = cngetc();
861 		switch (c) {
862 		case '\n':
863 		case '\r':
864 			printf("\n");
865 			*lp++ = '\0';
866 			cnpollc(0);
867 			return (len);
868 		case '\b':
869 		case '\177':
870 		case '#':
871 			if (len) {
872 				--len;
873 				--lp;
874 				printf("\b \b");
875 			}
876 			continue;
877 		case '@':
878 		case 'u'&037:
879 			len = 0;
880 			lp = cp;
881 			printf("\n");
882 			continue;
883 		default:
884 			if (len + 1 >= size || c < ' ') {
885 				printf("\007");
886 				continue;
887 			}
888 			printf("%c", c);
889 			++len;
890 			*lp++ = c;
891 		}
892 	}
893 }
894