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