xref: /openbsd-src/sys/kern/subr_userconf.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: subr_userconf.c,v 1.34 2005/12/09 09:09:52 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 1996-2001 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 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/device.h>
32 #include <sys/malloc.h>
33 #include <sys/time.h>
34 
35 #include <dev/cons.h>
36 
37 extern char *locnames[];
38 extern short locnamp[];
39 extern short cfroots[];
40 extern int cfroots_size;
41 extern int pv_size;
42 extern short pv[];
43 extern struct timezone tz;
44 extern char *pdevnames[];
45 extern int pdevnames_size;
46 extern struct pdevinit pdevinit[];
47 
48 int userconf_base = 16;				/* Base for "large" numbers */
49 int userconf_maxdev = -1;			/* # of used device slots   */
50 int userconf_totdev = -1;			/* # of device slots        */
51 int userconf_maxlocnames = -1;			/* # of locnames            */
52 int userconf_cnt = -1;				/* Line counter for ...     */
53 int userconf_lines = 12;			/* ... # of lines per page  */
54 int userconf_histlen = 0;
55 int userconf_histcur = 0;
56 char userconf_history[1024];
57 int userconf_histsz = sizeof(userconf_history);
58 char userconf_argbuf[40];			/* Additional input         */
59 char userconf_cmdbuf[40];			/* Command line             */
60 char userconf_histbuf[40];
61 
62 void userconf_init(void);
63 int userconf_more(void);
64 void userconf_modify(char *, int *);
65 void userconf_hist_cmd(char);
66 void userconf_hist_int(int);
67 void userconf_hist_eoc(void);
68 void userconf_pnum(int);
69 void userconf_pdevnam(short);
70 void userconf_pdev(short);
71 int userconf_number(char *, int *);
72 int userconf_device(char *, int *, short *, short *);
73 int userconf_attr(char *, int *);
74 void userconf_modify(char *, int *);
75 void userconf_change(int);
76 void userconf_disable(int);
77 void userconf_enable(int);
78 void userconf_help(void);
79 void userconf_list(void);
80 void userconf_show(void);
81 void userconf_common_attr_val(short, int *, char);
82 void userconf_show_attr(char *);
83 void userconf_common_dev(char *, int, short, short, char);
84 void userconf_common_attr(char *, int, char);
85 void userconf_add_read(char *, char, char *, int, int *);
86 void userconf_add(char *, int, short, short);
87 int userconf_parse(char *);
88 
89 #define UC_CHANGE 'c'
90 #define UC_DISABLE 'd'
91 #define UC_ENABLE 'e'
92 #define UC_FIND 'f'
93 #define UC_SHOW 's'
94 
95 char *userconf_cmds[] = {
96 	"add",		"a",
97 	"base",		"b",
98 	"change",	"c",
99 #if defined(DDB)
100 	"ddb",		"D",
101 #endif
102 	"disable",	"d",
103 	"enable",	"e",
104 	"exit",		"q",
105 	"find",		"f",
106 	"help",		"h",
107 	"list",		"l",
108 	"lines",	"L",
109 	"quit",		"q",
110 	"show",		"s",
111 	"timezone",	"t",
112 	"verbose",	"v",
113 	"?",		"h",
114 	"",		 "",
115 };
116 
117 void
118 userconf_init(void)
119 {
120 	int i = 0;
121 	struct cfdata *cd;
122 	int   ln;
123 
124 	while (cfdata[i].cf_attach != 0) {
125 		userconf_maxdev = i;
126 		userconf_totdev = i;
127 
128 		cd = &cfdata[i];
129 		ln = cd->cf_locnames;
130 		while (locnamp[ln] != -1) {
131 			if (locnamp[ln] > userconf_maxlocnames)
132 				userconf_maxlocnames = locnamp[ln];
133 			ln++;
134 		}
135 		i++;
136 	}
137 
138 	while (cfdata[i].cf_attach == 0) {
139 		userconf_totdev = i;
140 		i++;
141 	}
142 	userconf_totdev = userconf_totdev - 1;
143 }
144 
145 int
146 userconf_more(void)
147 {
148 	int quit = 0;
149 	char c = '\0';
150 
151 	if (userconf_cnt != -1) {
152 		if (userconf_cnt == userconf_lines) {
153 			printf("--- more ---");
154 			c = cngetc();
155 			userconf_cnt = 0;
156 			printf("\r            \r");
157 		}
158 		userconf_cnt++;
159 		if (c == 'q' || c == 'Q')
160 			quit = 1;
161 	}
162 	return (quit);
163 }
164 
165 void
166 userconf_hist_cmd(char cmd)
167 {
168 	userconf_histcur = userconf_histlen;
169 	if (userconf_histcur < userconf_histsz) {
170 		userconf_history[userconf_histcur] = cmd;
171 		userconf_histcur++;
172 	}
173 }
174 
175 void
176 userconf_hist_int(int val)
177 {
178 	snprintf(userconf_histbuf, sizeof userconf_histbuf, " %d",val);
179 	if (userconf_histcur + strlen(userconf_histbuf) < userconf_histsz) {
180 		bcopy(userconf_histbuf,
181 		    &userconf_history[userconf_histcur],
182 		    strlen(userconf_histbuf));
183 		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
184 	}
185 }
186 
187 void
188 userconf_hist_eoc(void)
189 {
190 	if (userconf_histcur < userconf_histsz) {
191 		userconf_history[userconf_histcur] = '\n';
192 		userconf_histcur++;
193 		userconf_histlen = userconf_histcur;
194 	}
195 }
196 
197 void
198 userconf_pnum(int val)
199 {
200 	if (val > -2 && val < 16) {
201 		printf("%d",val);
202 		return;
203 	}
204 
205 	switch (userconf_base) {
206 	case 8:
207 		printf("0%o",val);
208 		break;
209 	case 10:
210 		printf("%d",val);
211 		break;
212 	case 16:
213 	default:
214 		printf("0x%x",val);
215 		break;
216 	}
217 }
218 
219 void
220 userconf_pdevnam(short dev)
221 {
222 	struct cfdata *cd;
223 
224 	cd = &cfdata[dev];
225 	printf("%s", cd->cf_driver->cd_name);
226 	switch (cd->cf_fstate) {
227 	case FSTATE_NOTFOUND:
228 	case FSTATE_DNOTFOUND:
229 		printf("%d", cd->cf_unit);
230 		break;
231 	case FSTATE_FOUND:
232 		printf("*FOUND*");
233 		break;
234 	case FSTATE_STAR:
235 	case FSTATE_DSTAR:
236 		printf("*");
237 		break;
238 	default:
239 		printf("*UNKNOWN*");
240 		break;
241 	}
242 }
243 
244 void
245 userconf_pdev(short devno)
246 {
247 	struct cfdata *cd;
248 	short *p;
249 	int   *l;
250 	int   ln;
251 	char c;
252 
253 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
254 		printf("%3d free slot (for add)\n", devno);
255 		return;
256 	}
257 
258 	if (devno > userconf_totdev &&
259 	    devno <= userconf_totdev+pdevnames_size) {
260 		printf("%3d %s count %d (pseudo device)\n", devno,
261 		    pdevnames[devno-userconf_totdev-1],
262 		    pdevinit[devno-userconf_totdev-1].pdev_count);
263 		return;
264 	}
265 
266 	if (devno >  userconf_maxdev) {
267 		printf("Unknown devno (max is %d)\n", userconf_maxdev);
268 		return;
269 	}
270 
271 	cd = &cfdata[devno];
272 
273 	printf("%3d ", devno);
274 	userconf_pdevnam(devno);
275 	printf(" at");
276 	c = ' ';
277 	p = cd->cf_parents;
278 	if (*p == -1)
279 		printf(" root");
280 	while (*p != -1) {
281 		printf("%c", c);
282 		userconf_pdevnam(*p++);
283 		c = '|';
284 	}
285 	switch (cd->cf_fstate) {
286 	case FSTATE_NOTFOUND:
287 	case FSTATE_FOUND:
288 	case FSTATE_STAR:
289 		break;
290 	case FSTATE_DNOTFOUND:
291 	case FSTATE_DSTAR:
292 		printf(" disable");
293 		break;
294 	default:
295 		printf(" ???");
296 		break;
297 	}
298 	l = cd->cf_loc;
299 	ln = cd->cf_locnames;
300 	while (locnamp[ln] != -1) {
301 		printf(" %s ", locnames[locnamp[ln]]);
302 		ln++;
303 		userconf_pnum(*l++);
304 	}
305 	printf(" flags 0x%x\n", cd->cf_flags);
306 }
307 
308 int
309 userconf_number(char *c, int *val)
310 {
311 	u_int num = 0;
312 	int neg = 0;
313 	int base = 10;
314 
315 	if (*c == '-') {
316 		neg = 1;
317 		c++;
318 	}
319 	if (*c == '0') {
320 		base = 8;
321 		c++;
322 		if (*c == 'x' || *c == 'X') {
323 			base = 16;
324 			c++;
325 		}
326 	}
327 	while (*c != '\n' && *c != '\t' && *c != ' ' && *c != '\0') {
328 		u_char cc = *c;
329 
330 		if (cc >= '0' && cc <= '9')
331 			cc = cc - '0';
332 		else if (cc >= 'a' && cc <= 'f')
333 			cc = cc - 'a' + 10;
334 		else if (cc >= 'A' && cc <= 'F')
335 			cc = cc - 'A' + 10;
336 		else
337 			return (-1);
338 
339 		if (cc > base)
340 			return (-1);
341 		num = num * base + cc;
342 		c++;
343 	}
344 
345 	if (neg && num > INT_MAX)	/* overflow */
346 		return (1);
347 	*val = neg ? - num : num;
348 	return (0);
349 }
350 
351 int
352 userconf_device(char *cmd, int *len, short *unit, short *state)
353 {
354 	short u = 0, s = FSTATE_FOUND;
355 	int l = 0;
356 	char *c;
357 
358 	c = cmd;
359 	while (*c >= 'a' && *c <= 'z') {
360 		l++;
361 		c++;
362 	}
363 	if (*c == '*') {
364 		s = FSTATE_STAR;
365 		c++;
366 	} else {
367 		while (*c >= '0' && *c <= '9') {
368 			s = FSTATE_NOTFOUND;
369 			u = u*10 + *c - '0';
370 			c++;
371 		}
372 	}
373 	while (*c == ' ' || *c == '\t' || *c == '\n')
374 		c++;
375 
376 	if (*c == '\0') {
377 		*len = l;
378 		*unit = u;
379 		*state = s;
380 		return(0);
381 	}
382 
383 	return(-1);
384 }
385 
386 int
387 userconf_attr(char *cmd, int *val)
388 {
389 	char *c;
390 	short attr = -1, i = 0, l = 0;
391 
392 	c = cmd;
393 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
394 		c++;
395 		l++;
396 	}
397 
398 	while (i <= userconf_maxlocnames) {
399 		if (strlen(locnames[i]) == l) {
400 			if (strncasecmp(cmd, locnames[i], l) == 0)
401 				attr = i;
402 		}
403 		i++;
404 	}
405 
406 	if (attr == -1) {
407 		return (-1);
408 	}
409 
410 	*val = attr;
411 
412 	return(0);
413 }
414 
415 void
416 userconf_modify(char *item, int *val)
417 {
418 	int ok = 0;
419 	int a;
420 	char *c;
421 	int i;
422 
423 	while (!ok) {
424 		printf("%s [", item);
425 		userconf_pnum(*val);
426 		printf("] ? ");
427 
428 		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
429 
430 		c = userconf_argbuf;
431 		while (*c == ' ' || *c == '\t' || *c == '\n') c++;
432 
433 		if (*c != '\0') {
434 			if (userconf_number(c, &a) == 0) {
435 				*val = a;
436 				ok = 1;
437 			} else {
438 				printf("Unknown argument\n");
439 			}
440 		} else {
441 			ok = 1;
442 		}
443 	}
444 }
445 
446 void
447 userconf_change(int devno)
448 {
449 	struct cfdata *cd;
450 	char c = '\0';
451 	int   *l;
452 	int   ln;
453 
454 	if (devno <=  userconf_maxdev) {
455 		userconf_pdev(devno);
456 
457 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
458 			printf("change (y/n) ?");
459 			c = cngetc();
460 			printf("\n");
461 		}
462 
463 		if (c == 'y' || c == 'Y') {
464 			int share = 0, i, *lk;
465 
466 			/* XXX add cmd 'c' <devno> */
467 			userconf_hist_cmd('c');
468 			userconf_hist_int(devno);
469 
470 			cd = &cfdata[devno];
471 			l = cd->cf_loc;
472 			ln = cd->cf_locnames;
473 
474 			/*
475 			 * Search for some other driver sharing this
476 			 * locator table. if one does, we may need to
477 			 * replace the locators with a malloc'd copy.
478 			 */
479 			for (i = 0; cfdata[i].cf_driver; i++)
480 				if (i != devno && cfdata[i].cf_loc == l)
481 					share = 1;
482 			if (share) {
483 				for (i = 0; locnamp[ln+i] != -1 ; i++)
484 					;
485 				lk = l = (int *)malloc(sizeof(int) * i,
486 				    M_TEMP, M_NOWAIT);
487 				if (lk == NULL) {
488 					printf("out of memory.\n");
489 					return;
490 				}
491 				bcopy(cd->cf_loc, l, sizeof(int) * i);
492 			}
493 
494 			while (locnamp[ln] != -1) {
495 				userconf_modify(locnames[locnamp[ln]], l);
496 
497 				/* XXX add *l */
498 				userconf_hist_int(*l);
499 
500 				ln++;
501 				l++;
502 			}
503 			userconf_modify("flags", &cd->cf_flags);
504 			userconf_hist_int(cd->cf_flags);
505 
506 			if (share) {
507 				if (bcmp(cd->cf_loc, lk, sizeof(int) * i))
508 					cd->cf_loc = lk;
509 				else
510 					free(lk, M_TEMP);
511 			}
512 
513 			printf("%3d ", devno);
514 			userconf_pdevnam(devno);
515 			printf(" changed\n");
516 			userconf_pdev(devno);
517 		}
518 		return;
519 	}
520 
521 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
522 		printf("%3d can't change free slot\n", devno);
523 		return;
524 	}
525 
526 	if (devno > userconf_totdev &&
527 	    devno <= userconf_totdev+pdevnames_size) {
528 		userconf_pdev(devno);
529 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
530 			printf("change (y/n) ?");
531 			c = cngetc();
532 			printf("\n");
533 		}
534 
535 		if (c == 'y' || c == 'Y') {
536 			/* XXX add cmd 'c' <devno> */
537 			userconf_hist_cmd('c');
538 			userconf_hist_int(devno);
539 
540 			userconf_modify("count",
541 			    &pdevinit[devno-userconf_totdev-1].pdev_count);
542 			userconf_hist_int(pdevinit[devno-userconf_totdev-1].pdev_count);
543 
544 			printf("%3d %s changed\n", devno,
545 			    pdevnames[devno-userconf_totdev-1]);
546 			userconf_pdev(devno);
547 
548 			/* XXX add eoc */
549 			userconf_hist_eoc();
550 		}
551 		return;
552 	}
553 
554 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
555 }
556 
557 void
558 userconf_disable(int devno)
559 {
560 	int done = 0;
561 
562 	if (devno <= userconf_maxdev) {
563 		switch (cfdata[devno].cf_fstate) {
564 		case FSTATE_NOTFOUND:
565 			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
566 			break;
567 		case FSTATE_STAR:
568 			cfdata[devno].cf_fstate = FSTATE_DSTAR;
569 			break;
570 		case FSTATE_DNOTFOUND:
571 		case FSTATE_DSTAR:
572 			done = 1;
573 			break;
574 		default:
575 			printf("Error unknown state\n");
576 			break;
577 		}
578 
579 		printf("%3d ", devno);
580 		userconf_pdevnam(devno);
581 		if (done) {
582 			printf(" already");
583 		} else {
584 			/* XXX add cmd 'd' <devno> eoc */
585 			userconf_hist_cmd('d');
586 			userconf_hist_int(devno);
587 			userconf_hist_eoc();
588 		}
589 		printf(" disabled\n");
590 
591 		return;
592 	}
593 
594 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
595 		printf("%3d can't disable free slot\n", devno);
596 		return;
597 	}
598 
599 	if (devno > userconf_totdev &&
600 	    devno <= userconf_totdev+pdevnames_size) {
601 		printf("%3d %s can't disable pseudo device\n", devno,
602 		    pdevnames[devno-userconf_totdev-1]);
603 		return;
604 	}
605 
606 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
607 }
608 
609 void
610 userconf_enable(int devno)
611 {
612 	int done = 0;
613 
614 	if (devno <= userconf_maxdev) {
615 		switch (cfdata[devno].cf_fstate) {
616 		case FSTATE_DNOTFOUND:
617 			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
618 			break;
619 		case FSTATE_DSTAR:
620 			cfdata[devno].cf_fstate = FSTATE_STAR;
621 			break;
622 		case FSTATE_NOTFOUND:
623 		case FSTATE_STAR:
624 			done = 1;
625 			break;
626 		default:
627 			printf("Error unknown state\n");
628 			break;
629 		}
630 
631 		printf("%3d ", devno);
632 		userconf_pdevnam(devno);
633 		if (done) {
634 			printf(" already");
635 		} else {
636 			/* XXX add cmd 'e' <devno> eoc */
637 			userconf_hist_cmd('e');
638 			userconf_hist_int(devno);
639 			userconf_hist_eoc();
640 		}
641 		printf(" enabled\n");
642 		return;
643 	}
644 
645 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
646 		printf("%3d can't enable free slot\n", devno);
647 		return;
648 	}
649 
650 	if (devno > userconf_totdev &&
651 	    devno <= userconf_totdev+pdevnames_size) {
652 		printf("%3d %s can't enable pseudo device\n", devno,
653 		    pdevnames[devno-userconf_totdev-1]);
654 		return;
655 	}
656 
657 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
658 }
659 
660 void
661 userconf_help(void)
662 {
663 	int j = 0, k;
664 
665 	printf("command   args                description\n");
666 	while (*userconf_cmds[j] != '\0') {
667 		printf(userconf_cmds[j]);
668 		k = strlen(userconf_cmds[j]);
669 		while (k < 10) {
670 			printf(" ");
671 			k++;
672 		}
673 		switch (*userconf_cmds[j+1]) {
674 		case 'L':
675 			printf("[count]             number of lines before more");
676 			break;
677 		case 'a':
678 			printf("dev                 add a device");
679 			break;
680 		case 'b':
681 			printf("8|10|16             base on large numbers");
682 			break;
683 		case 'c':
684 			printf("devno|dev           change devices");
685 			break;
686 #if defined(DDB)
687 		case 'D':
688 			printf("                    enter ddb");
689 			break;
690 #endif
691 		case 'd':
692 			printf("attr val|devno|dev  disable devices");
693 			break;
694 		case 'e':
695 			printf("attr val|devno|dev  enable devices");
696 			break;
697 		case 'f':
698 			printf("devno|dev           find devices");
699 			break;
700 		case 'h':
701 			printf("                    this message");
702 			break;
703 		case 'l':
704 			printf("                    list configuration");
705 			break;
706 		case 'q':
707 			printf("                    leave UKC");
708 			break;
709 		case 's':
710 			printf("[attr [val]]        "
711 			   "show attributes (or devices with an attribute)");
712 			break;
713 		case 't':
714 			printf("[mins [dst]]        set timezone/dst");
715 			break;
716 		case 'v':
717 			printf("                    toggle verbose booting");
718 			break;
719 		default:
720 			printf("                    don't know");
721 			break;
722 		}
723 		printf("\n");
724 		j += 2;
725 	}
726 }
727 
728 void
729 userconf_list(void)
730 {
731 	int i = 0;
732 
733 	userconf_cnt = 0;
734 
735 	while (i <= (userconf_totdev+pdevnames_size)) {
736 		if (userconf_more())
737 			break;
738 		userconf_pdev(i++);
739 	}
740 
741 	userconf_cnt = -1;
742 }
743 
744 void
745 userconf_show(void)
746 {
747 	int i = 0;
748 
749 	userconf_cnt = 0;
750 
751 	while (i <= userconf_maxlocnames) {
752 		if (userconf_more())
753 			break;
754 		printf("%s\n", locnames[i++]);
755 	}
756 
757 	userconf_cnt = -1;
758 }
759 
760 void
761 userconf_common_attr_val(short attr, int *val, char routine)
762 {
763 	struct cfdata *cd;
764 	int   *l;
765 	int   ln;
766 	int i = 0, quit = 0;
767 
768 	userconf_cnt = 0;
769 
770 	while (i <= userconf_maxdev) {
771 		cd = &cfdata[i];
772 		l = cd->cf_loc;
773 		ln = cd->cf_locnames;
774 		while (locnamp[ln] != -1) {
775 			if (locnamp[ln] == attr) {
776 				if (val == NULL) {
777 					quit = userconf_more();
778 					userconf_pdev(i);
779 				} else {
780 					if (*val == *l) {
781 						quit = userconf_more();
782 						switch (routine) {
783 						case UC_ENABLE:
784 							userconf_enable(i);
785 							break;
786 						case UC_DISABLE:
787 							userconf_disable(i);
788 							break;
789 						case UC_SHOW:
790 							userconf_pdev(i);
791 							break;
792 						default:
793 							printf("Unknown routine /%c/\n",
794 							    routine);
795 							break;
796 						}
797 					}
798 				}
799 			}
800 			if (quit)
801 				break;
802 			ln++;
803 			l++;
804 		}
805 		if (quit)
806 			break;
807 		i++;
808 	}
809 
810 	userconf_cnt = -1;
811 }
812 
813 void
814 userconf_show_attr(char *cmd)
815 {
816 	char *c;
817 	short attr = -1, i = 0, l = 0;
818 	int a;
819 
820 	c = cmd;
821 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
822 		c++;
823 		l++;
824 	}
825 	while (*c == ' ' || *c == '\t' || *c == '\n') {
826 		c++;
827 	}
828 	while (i <= userconf_maxlocnames) {
829 		if (strlen(locnames[i]) == l) {
830 			if (strncasecmp(cmd, locnames[i], l) == 0) {
831 				attr = i;
832 			}
833 		}
834 		i++;
835 	}
836 
837 	if (attr == -1) {
838 		printf("Unknown attribute\n");
839 		return;
840 	}
841 
842 	if (*c == '\0') {
843 		userconf_common_attr_val(attr, NULL, UC_SHOW);
844 	} else {
845 		if (userconf_number(c, &a) == 0) {
846 			userconf_common_attr_val(attr, &a, UC_SHOW);
847 		} else {
848 			printf("Unknown argument\n");
849 		}
850 	}
851 }
852 
853 void
854 userconf_common_dev(char *dev, int len, short unit, short state, char routine)
855 {
856 	int i = 0;
857 
858 	switch (routine) {
859 	case UC_CHANGE:
860 		break;
861 	default:
862 		userconf_cnt = 0;
863 		break;
864 	}
865 
866 	while (cfdata[i].cf_attach != 0) {
867 		if (strlen(cfdata[i].cf_driver->cd_name) == len) {
868 
869 			/*
870 			 * Ok, if device name is correct
871 			 *  If state == FSTATE_FOUND, look for "dev"
872 			 *  If state == FSTATE_STAR, look for "dev*"
873 			 *  If state == FSTATE_NOTFOUND, look for "dev0"
874 			 */
875 			if (strncasecmp(dev, cfdata[i].cf_driver->cd_name,
876 					len) == 0 &&
877 			    (state == FSTATE_FOUND ||
878 			     (state == FSTATE_STAR &&
879 			      (cfdata[i].cf_fstate == FSTATE_STAR ||
880 			       cfdata[i].cf_fstate == FSTATE_DSTAR)) ||
881 			     (state == FSTATE_NOTFOUND &&
882 			      cfdata[i].cf_unit == unit &&
883 			      (cfdata[i].cf_fstate == FSTATE_NOTFOUND ||
884 			       cfdata[i].cf_fstate == FSTATE_DNOTFOUND)))) {
885 				if (userconf_more())
886 					break;
887 				switch (routine) {
888 				case UC_CHANGE:
889 					userconf_change(i);
890 					break;
891 				case UC_ENABLE:
892 					userconf_enable(i);
893 					break;
894 				case UC_DISABLE:
895 					userconf_disable(i);
896 					break;
897 				case UC_FIND:
898 					userconf_pdev(i);
899 					break;
900 				default:
901 					printf("Unknown routine /%c/\n",
902 					    routine);
903 					break;
904 				}
905 			}
906 		}
907 		i++;
908 	}
909 
910 	for (i = 0; i < pdevnames_size; i++) {
911 		if (strncasecmp(dev, pdevnames[i], len) == 0 &&
912 		    state == FSTATE_FOUND) {
913 			switch(routine) {
914 			case UC_CHANGE:
915 				userconf_change(userconf_totdev+1+i);
916 				break;
917 			case UC_ENABLE:
918 				userconf_enable(userconf_totdev+1+i);
919 				break;
920 			case UC_DISABLE:
921 				userconf_disable(userconf_totdev+1+i);
922 				break;
923 			case UC_FIND:
924 				userconf_pdev(userconf_totdev+1+i);
925 				break;
926 			default:
927 				printf("Unknown pseudo routine /%c/\n",routine);
928 				break;
929 			}
930 		}
931 	}
932 
933 	switch (routine) {
934 	case UC_CHANGE:
935 		break;
936 	default:
937 		userconf_cnt = -1;
938 		break;
939 	}
940 }
941 
942 void
943 userconf_common_attr(char *cmd, int attr, char routine)
944 {
945 	char *c;
946 	short l = 0;
947 	int a;
948 
949 	c = cmd;
950 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
951 		c++;
952 		l++;
953 	}
954 	while (*c == ' ' || *c == '\t' || *c == '\n')
955 		c++;
956 
957 	if (*c == '\0') {
958 		printf("Value missing for attribute\n");
959 		return;
960 	}
961 
962 	if (userconf_number(c, &a) == 0) {
963 		userconf_common_attr_val(attr, &a, routine);
964 	} else {
965 		printf("Unknown argument\n");
966 	}
967 }
968 
969 void
970 userconf_add_read(char *prompt, char field, char *dev, int len, int *val)
971 {
972 	int ok = 0;
973 	int a;
974 	char *c;
975 	int i;
976 
977 	*val = -1;
978 
979 	while (!ok) {
980 		printf("%s ? ", prompt);
981 
982 		i = getsn(userconf_argbuf, sizeof(userconf_argbuf));
983 
984 		c = userconf_argbuf;
985 		while (*c == ' ' || *c == '\t' || *c == '\n')
986 			c++;
987 
988 		if (*c != '\0') {
989 			if (userconf_number(c, &a) == 0) {
990 				if (a > userconf_maxdev) {
991 					printf("Unknown devno (max is %d)\n",
992 					    userconf_maxdev);
993 				} else if (strncasecmp(dev,
994 				    cfdata[a].cf_driver->cd_name, len) != 0 &&
995 				    field == 'a') {
996 					printf("Not same device type\n");
997 				} else {
998 					*val = a;
999 					ok = 1;
1000 				}
1001 			} else if (*c == '?') {
1002 				userconf_common_dev(dev, len, 0,
1003 				    FSTATE_FOUND, UC_FIND);
1004 			} else if (*c == 'q' || *c == 'Q') {
1005 				ok = 1;
1006 			} else {
1007 				printf("Unknown argument\n");
1008 			}
1009 		} else {
1010 			ok = 1;
1011 		}
1012 	}
1013 }
1014 
1015 void
1016 userconf_add(char *dev, int len, short unit, short state)
1017 {
1018 	int i = 0, found = 0;
1019 	struct cfdata new;
1020 	int  val, max_unit, star_unit, orig;
1021 
1022 	bzero(&new, sizeof(struct cfdata));
1023 
1024 	if (userconf_maxdev == userconf_totdev) {
1025 		printf("No more space for new devices.\n");
1026 		return;
1027 	}
1028 
1029 	if (state == FSTATE_FOUND) {
1030 		printf("Device not complete number or * is missing\n");
1031 		return;
1032 	}
1033 
1034 	for (i = 0; cfdata[i].cf_driver; i++)
1035 		if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1036 		    strncasecmp(dev, cfdata[i].cf_driver->cd_name, len) == 0)
1037 			found = 1;
1038 
1039 	if (!found) {
1040 		printf("No device of this type exists.\n");
1041 		return;
1042 	}
1043 
1044 	userconf_add_read("Clone Device (DevNo, 'q' or '?')",
1045 	    'a', dev, len, &val);
1046 
1047 	if (val != -1) {
1048 		orig = val;
1049 		new = cfdata[val];
1050 		new.cf_unit = unit;
1051 		new.cf_fstate = state;
1052 		userconf_add_read("Insert before Device (DevNo, 'q' or '?')",
1053 		    'i', dev, len, &val);
1054 	}
1055 
1056 	if (val != -1) {
1057 		/* XXX add cmd 'a' <orig> <val> eoc */
1058 		userconf_hist_cmd('a');
1059 		userconf_hist_int(orig);
1060 		userconf_hist_int(unit);
1061 		userconf_hist_int(state);
1062 		userconf_hist_int(val);
1063 		userconf_hist_eoc();
1064 
1065 		/* Insert the new record */
1066 		for (i = userconf_maxdev; val <= i; i--)
1067 			cfdata[i+1] = cfdata[i];
1068 		cfdata[val] = new;
1069 
1070 		/* Fix indexs in pv */
1071 		for (i = 0; i < pv_size; i++) {
1072 			if (pv[i] != -1 && pv[i] >= val)
1073 				pv[i]++;
1074 		}
1075 
1076 		/* Fix indexs in cfroots */
1077 		for (i = 0; i < cfroots_size; i++) {
1078 			if (cfroots[i] != -1 && cfroots[i] >= val)
1079 				cfroots[i]++;
1080 		}
1081 
1082 		userconf_maxdev++;
1083 
1084 		max_unit = -1;
1085 
1086 		/* Find max unit number of the device type */
1087 
1088 		i = 0;
1089 		while (cfdata[i].cf_attach != 0) {
1090 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1091 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1092 			    len) == 0) {
1093 				switch (cfdata[i].cf_fstate) {
1094 				case FSTATE_NOTFOUND:
1095 				case FSTATE_DNOTFOUND:
1096 					if (cfdata[i].cf_unit > max_unit)
1097 						max_unit = cfdata[i].cf_unit;
1098 					break;
1099 				default:
1100 					break;
1101 				}
1102 			}
1103 			i++;
1104 		}
1105 
1106 		/*
1107 		 * For all * entries set unit number to max+1, and update
1108 		 * cf_starunit1 if necessary.
1109 		 */
1110 		max_unit++;
1111 		star_unit = -1;
1112 
1113 		i = 0;
1114 		while (cfdata[i].cf_attach != 0) {
1115 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1116 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1117 			    len) == 0) {
1118 				switch (cfdata[i].cf_fstate) {
1119 				case FSTATE_NOTFOUND:
1120 				case FSTATE_DNOTFOUND:
1121 					if (cfdata[i].cf_unit > star_unit)
1122 						star_unit = cfdata[i].cf_unit;
1123 					break;
1124 				default:
1125 					break;
1126 				}
1127 			}
1128 			i++;
1129 		}
1130 		star_unit++;
1131 
1132 		i = 0;
1133 		while (cfdata[i].cf_attach != 0) {
1134 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1135 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1136 			    len) == 0) {
1137 				switch (cfdata[i].cf_fstate) {
1138 				case FSTATE_STAR:
1139 				case FSTATE_DSTAR:
1140 					cfdata[i].cf_unit = max_unit;
1141 					if (cfdata[i].cf_starunit1 < star_unit)
1142 						cfdata[i].cf_starunit1 =
1143 						    star_unit;
1144 					break;
1145 				default:
1146 					break;
1147 				}
1148 			}
1149 			i++;
1150 		}
1151 		userconf_pdev(val);
1152 	}
1153 
1154 	/* cf_attach, cf_driver, cf_unit, cf_fstate, cf_loc, cf_flags,
1155 	   cf_parents, cf_locnames, cf_locnames and cf_ivstubs */
1156 }
1157 
1158 int
1159 userconf_parse(char *cmd)
1160 {
1161 	char *c, *v;
1162 	int i = 0, j = 0, k, a;
1163 	short unit, state;
1164 
1165 	c = cmd;
1166 	while (*c == ' ' || *c == '\t')
1167 		c++;
1168 	v = c;
1169 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
1170 		c++;
1171 		i++;
1172 	}
1173 
1174 	k = -1;
1175 	while (*userconf_cmds[j] != '\0') {
1176 		if (strlen(userconf_cmds[j]) == i) {
1177 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
1178 				k = j;
1179 		}
1180 		j += 2;
1181 	}
1182 
1183 	while (*c == ' ' || *c == '\t' || *c == '\n')
1184 		c++;
1185 
1186 	if (k == -1) {
1187 		if (*v != '\n')
1188 			printf("Unknown command, try help\n");
1189 	} else {
1190 		switch (*userconf_cmds[k+1]) {
1191 		case 'L':
1192 			if (*c == '\0')
1193 				printf("Argument expected\n");
1194 			else if (userconf_number(c, &a) == 0)
1195 				userconf_lines = a;
1196 			else
1197 				printf("Unknown argument\n");
1198 			break;
1199 		case 'a':
1200 			if (*c == '\0')
1201 				printf("Dev expected\n");
1202 			else if (userconf_device(c, &a, &unit, &state) == 0)
1203 				userconf_add(c, a, unit, state);
1204 			else
1205 				printf("Unknown argument\n");
1206 			break;
1207 		case 'b':
1208 			if (*c == '\0')
1209 				printf("8|10|16 expected\n");
1210 			else if (userconf_number(c, &a) == 0) {
1211 				if (a == 8 || a == 10 || a == 16) {
1212 					userconf_base = a;
1213 				} else {
1214 					printf("8|10|16 expected\n");
1215 				}
1216 			} else
1217 				printf("Unknown argument\n");
1218 			break;
1219 		case 'c':
1220 			if (*c == '\0')
1221 				printf("DevNo or Dev expected\n");
1222 			else if (userconf_number(c, &a) == 0)
1223 				userconf_change(a);
1224 			else if (userconf_device(c, &a, &unit, &state) == 0)
1225 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
1226 			else
1227 				printf("Unknown argument\n");
1228 			break;
1229 #if defined(DDB)
1230 		case 'D':
1231 			Debugger();
1232 			break;
1233 #endif
1234 		case 'd':
1235 			if (*c == '\0')
1236 				printf("Attr, DevNo or Dev expected\n");
1237 			else if (userconf_attr(c, &a) == 0)
1238 				userconf_common_attr(c, a, UC_DISABLE);
1239 			else if (userconf_number(c, &a) == 0)
1240 				userconf_disable(a);
1241 			else if (userconf_device(c, &a, &unit, &state) == 0)
1242 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
1243 			else
1244 				printf("Unknown argument\n");
1245 			break;
1246 		case 'e':
1247 			if (*c == '\0')
1248 				printf("Attr, DevNo or Dev expected\n");
1249 			else if (userconf_attr(c, &a) == 0)
1250 				userconf_common_attr(c, a, UC_ENABLE);
1251 			else if (userconf_number(c, &a) == 0)
1252 				userconf_enable(a);
1253 			else if (userconf_device(c, &a, &unit, &state) == 0)
1254 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
1255 			else
1256 				printf("Unknown argument\n");
1257 			break;
1258 		case 'f':
1259 			if (*c == '\0')
1260 				printf("DevNo or Dev expected\n");
1261 			else if (userconf_number(c, &a) == 0)
1262 				userconf_pdev(a);
1263 			else if (userconf_device(c, &a, &unit, &state) == 0)
1264 				userconf_common_dev(c, a, unit, state, UC_FIND);
1265 			else
1266 				printf("Unknown argument\n");
1267 			break;
1268 		case 'h':
1269 			userconf_help();
1270 			break;
1271 		case 'l':
1272 			if (*c == '\0')
1273 				userconf_list();
1274 			else
1275 				printf("Unknown argument\n");
1276 			break;
1277 		case 'q':
1278 			/* XXX add cmd 'q' eoc */
1279 			userconf_hist_cmd('q');
1280 			userconf_hist_eoc();
1281 			return(-1);
1282 			break;
1283 		case 's':
1284 			if (*c == '\0')
1285 				userconf_show();
1286 			else
1287 				userconf_show_attr(c);
1288 			break;
1289 		case 't':
1290 			if (*c == '\0' || userconf_number(c, &a) == 0) {
1291 				if (*c != '\0') {
1292 					tz.tz_minuteswest = a;
1293 					while (*c != '\n' && *c != '\t' &&
1294 					    *c != ' ' && *c != '\0')
1295 						c++;
1296 					while (*c == '\t' || *c == ' ')
1297 						c++;
1298 					if (*c != '\0' &&
1299 					    userconf_number(c, &a) == 0)
1300 						tz.tz_dsttime = a;
1301 					userconf_hist_cmd('t');
1302 					userconf_hist_int(tz.tz_minuteswest);
1303 					userconf_hist_int(tz.tz_dsttime);
1304 					userconf_hist_eoc();
1305 				}
1306 				printf("timezone = %d, dst = %d\n",
1307 				    tz.tz_minuteswest, tz.tz_dsttime);
1308 			} else
1309 				printf("Unknown argument\n");
1310 			break;
1311 		case 'v':
1312 			autoconf_verbose = !autoconf_verbose;
1313 			printf("autoconf verbose %sabled\n",
1314 			    autoconf_verbose ? "en" : "dis");
1315 			break;
1316 		default:
1317 			printf("Unknown command\n");
1318 			break;
1319 		}
1320 	}
1321 	return(0);
1322 }
1323 
1324 void
1325 user_config(void)
1326 {
1327 	userconf_init();
1328 	printf("User Kernel Config\n");
1329 
1330 	while (1) {
1331 		printf("UKC> ");
1332 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
1333 		    userconf_parse(userconf_cmdbuf))
1334 			break;
1335 	}
1336 	printf("Continuing...\n");
1337 }
1338