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