xref: /openbsd/sys/kern/subr_userconf.c (revision 5a38ef86)
1 /*	$OpenBSD: subr_userconf.c,v 1.47 2021/10/24 00:02:25 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 char *pdevnames[];
44 extern int pdevnames_size;
45 extern struct pdevinit pdevinit[];
46 
47 int userconf_base = 16;				/* Base for "large" numbers */
48 int userconf_maxdev = -1;			/* # of used device slots   */
49 int userconf_totdev = -1;			/* # of device slots        */
50 int userconf_maxlocnames = -1;			/* # of locnames            */
51 int userconf_cnt = -1;				/* Line counter for ...     */
52 int userconf_lines = 12;			/* ... # of lines per page  */
53 int userconf_histlen = 0;
54 int userconf_histcur = 0;
55 char userconf_history[1024];
56 int userconf_histsz = sizeof(userconf_history);
57 char userconf_argbuf[40];			/* Additional input         */
58 char userconf_cmdbuf[40];			/* Command line             */
59 char userconf_histbuf[40];
60 
61 void userconf_init(void);
62 int userconf_more(void);
63 void userconf_modify(char *, long *, long);
64 void userconf_hist_cmd(char);
65 void userconf_hist_int(long);
66 void userconf_hist_eoc(void);
67 void userconf_pnum(long);
68 void userconf_pdevnam(short);
69 void userconf_pdev(short);
70 int userconf_number(char *, long *, long);
71 int userconf_device(char *, long *, short *, short *);
72 int userconf_attr(char *, long *);
73 void userconf_change(int);
74 void userconf_disable(int);
75 void userconf_enable(int);
76 void userconf_help(void);
77 void userconf_list(void);
78 void userconf_show(void);
79 void userconf_common_attr_val(short, long *, char);
80 void userconf_show_attr(char *);
81 void userconf_common_dev(char *, int, short, short, char);
82 void userconf_common_attr(char *, int, char);
83 void userconf_add_read(char *, char, char *, int, long *);
84 void userconf_add(char *, int, short, short);
85 int userconf_parse(char *);
86 
87 #define UC_CHANGE 'c'
88 #define UC_DISABLE 'd'
89 #define UC_ENABLE 'e'
90 #define UC_FIND 'f'
91 #define UC_SHOW 's'
92 
93 char *userconf_cmds[] = {
94 	"add",		"a",
95 	"base",		"b",
96 	"change",	"c",
97 #if defined(DDB)
98 	"ddb",		"D",
99 #endif
100 	"disable",	"d",
101 	"enable",	"e",
102 	"exit",		"q",
103 	"find",		"f",
104 	"help",		"h",
105 	"list",		"l",
106 	"lines",	"L",
107 	"quit",		"q",
108 	"show",		"s",
109 	"verbose",	"v",
110 	"?",		"h",
111 	"",		 "",
112 };
113 
114 void
115 userconf_init(void)
116 {
117 	int i = 0;
118 	struct cfdata *cd;
119 	int   ln;
120 
121 	while (cfdata[i].cf_attach != NULL) {
122 		userconf_maxdev = i;
123 		userconf_totdev = i;
124 
125 		cd = &cfdata[i];
126 		ln = cd->cf_locnames;
127 		while (locnamp[ln] != -1) {
128 			if (locnamp[ln] > userconf_maxlocnames)
129 				userconf_maxlocnames = locnamp[ln];
130 			ln++;
131 		}
132 		i++;
133 	}
134 
135 	while (cfdata[i].cf_attach == NULL) {
136 		userconf_totdev = i;
137 		i++;
138 	}
139 	userconf_totdev = userconf_totdev - 1;
140 }
141 
142 int
143 userconf_more(void)
144 {
145 	int quit = 0;
146 	char c = '\0';
147 
148 	if (userconf_cnt != -1) {
149 		if (userconf_cnt == userconf_lines) {
150 			printf("--- more ---");
151 			c = cngetc();
152 			userconf_cnt = 0;
153 			printf("\r            \r");
154 		}
155 		userconf_cnt++;
156 		if (c == 'q' || c == 'Q')
157 			quit = 1;
158 	}
159 	return (quit);
160 }
161 
162 void
163 userconf_hist_cmd(char cmd)
164 {
165 	userconf_histcur = userconf_histlen;
166 	if (userconf_histcur < userconf_histsz) {
167 		userconf_history[userconf_histcur] = cmd;
168 		userconf_histcur++;
169 	}
170 }
171 
172 void
173 userconf_hist_int(long val)
174 {
175 	snprintf(userconf_histbuf, sizeof userconf_histbuf, " %ld", val);
176 	if (userconf_histcur + strlen(userconf_histbuf) < userconf_histsz) {
177 		bcopy(userconf_histbuf,
178 		    &userconf_history[userconf_histcur],
179 		    strlen(userconf_histbuf));
180 		userconf_histcur = userconf_histcur + strlen(userconf_histbuf);
181 	}
182 }
183 
184 void
185 userconf_hist_eoc(void)
186 {
187 	if (userconf_histcur < userconf_histsz) {
188 		userconf_history[userconf_histcur] = '\n';
189 		userconf_histcur++;
190 		userconf_histlen = userconf_histcur;
191 	}
192 }
193 
194 void
195 userconf_pnum(long val)
196 {
197 	if (val > -2 && val < 16) {
198 		printf("%ld",val);
199 		return;
200 	}
201 
202 	switch (userconf_base) {
203 	case 8:
204 		printf("0%lo",val);
205 		break;
206 	case 10:
207 		printf("%ld",val);
208 		break;
209 	case 16:
210 	default:
211 		printf("0x%lx",val);
212 		break;
213 	}
214 }
215 
216 void
217 userconf_pdevnam(short dev)
218 {
219 	struct cfdata *cd;
220 
221 	cd = &cfdata[dev];
222 	printf("%s", cd->cf_driver->cd_name);
223 	switch (cd->cf_fstate) {
224 	case FSTATE_NOTFOUND:
225 	case FSTATE_DNOTFOUND:
226 		printf("%d", cd->cf_unit);
227 		break;
228 	case FSTATE_FOUND:
229 		printf("*FOUND*");
230 		break;
231 	case FSTATE_STAR:
232 	case FSTATE_DSTAR:
233 		printf("*");
234 		break;
235 	default:
236 		printf("*UNKNOWN*");
237 		break;
238 	}
239 }
240 
241 void
242 userconf_pdev(short devno)
243 {
244 	struct cfdata *cd;
245 	short *p;
246 	long  *l;
247 	int   ln;
248 	char c;
249 
250 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
251 		printf("%3d free slot (for add)\n", devno);
252 		return;
253 	}
254 
255 	if (devno > userconf_totdev &&
256 	    devno <= userconf_totdev+pdevnames_size) {
257 		printf("%3d %s count %d", devno,
258 		    pdevnames[devno-userconf_totdev-1],
259 		    abs(pdevinit[devno-userconf_totdev-1].pdev_count));
260 		if (pdevinit[devno-userconf_totdev-1].pdev_count < 1)
261 			printf(" disable");
262 		printf(" (pseudo device)\n");
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, long *val, long limit)
310 {
311 	u_long 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 > limit)	/* overflow */
346 		return (1);
347 	*val = neg ? - num : num;
348 	return (0);
349 }
350 
351 int
352 userconf_device(char *cmd, long *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, long *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, long *val, long limit)
417 {
418 	int ok = 0;
419 	long 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, limit) == 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 	long  *l, tmp;
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, lklen;
465 			long *lk;
466 
467 			/* XXX add cmd 'c' <devno> */
468 			userconf_hist_cmd('c');
469 			userconf_hist_int(devno);
470 
471 			cd = &cfdata[devno];
472 			l = cd->cf_loc;
473 			ln = cd->cf_locnames;
474 
475 			/*
476 			 * Search for some other driver sharing this
477 			 * locator table. if one does, we may need to
478 			 * replace the locators with a malloc'd copy.
479 			 */
480 			for (i = 0; cfdata[i].cf_driver; i++)
481 				if (i != devno && cfdata[i].cf_loc == l)
482 					share = 1;
483 			if (share) {
484 				for (i = 0; locnamp[ln+i] != -1 ; i++)
485 					;
486 				lk = l = mallocarray(i, sizeof(long),
487 				    M_TEMP, M_NOWAIT);
488 				if (lk == NULL) {
489 					printf("out of memory.\n");
490 					return;
491 				}
492 				lklen = i * sizeof(long);
493 				bcopy(cd->cf_loc, l, lklen);
494 			}
495 
496 			while (locnamp[ln] != -1) {
497 				userconf_modify(locnames[locnamp[ln]], l,
498 				    LONG_MAX);
499 
500 				/* XXX add *l */
501 				userconf_hist_int(*l);
502 
503 				ln++;
504 				l++;
505 			}
506 			tmp = cd->cf_flags;
507 			userconf_modify("flags", &tmp, INT_MAX);
508 			userconf_hist_int(tmp);
509 			cd->cf_flags = tmp;
510 
511 			if (share) {
512 				if (memcmp(cd->cf_loc, lk, lklen))
513 					cd->cf_loc = lk;
514 				else
515 					free(lk, M_TEMP, lklen);
516 			}
517 
518 			printf("%3d ", devno);
519 			userconf_pdevnam(devno);
520 			printf(" changed\n");
521 			userconf_pdev(devno);
522 		}
523 		return;
524 	}
525 
526 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
527 		printf("%3d can't change free slot\n", devno);
528 		return;
529 	}
530 
531 	if (devno > userconf_totdev &&
532 	    devno <= userconf_totdev+pdevnames_size) {
533 		userconf_pdev(devno);
534 		while (c != 'y' && c != 'Y' && c != 'n' && c != 'N') {
535 			printf("change (y/n) ?");
536 			c = cngetc();
537 			printf("\n");
538 		}
539 
540 		if (c == 'y' || c == 'Y') {
541 			/* XXX add cmd 'c' <devno> */
542 			userconf_hist_cmd('c');
543 			userconf_hist_int(devno);
544 
545 			tmp = pdevinit[devno-userconf_totdev-1].pdev_count;
546 			userconf_modify("count", &tmp, INT_MAX);
547 			userconf_hist_int(tmp);
548 			pdevinit[devno-userconf_totdev-1].pdev_count = tmp;
549 
550 			printf("%3d %s changed\n", devno,
551 			    pdevnames[devno-userconf_totdev-1]);
552 			userconf_pdev(devno);
553 
554 			/* XXX add eoc */
555 			userconf_hist_eoc();
556 		}
557 		return;
558 	}
559 
560 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
561 }
562 
563 void
564 userconf_disable(int devno)
565 {
566 	int done = 0;
567 
568 	if (devno <= userconf_maxdev) {
569 		switch (cfdata[devno].cf_fstate) {
570 		case FSTATE_NOTFOUND:
571 			cfdata[devno].cf_fstate = FSTATE_DNOTFOUND;
572 			break;
573 		case FSTATE_STAR:
574 			cfdata[devno].cf_fstate = FSTATE_DSTAR;
575 			break;
576 		case FSTATE_DNOTFOUND:
577 		case FSTATE_DSTAR:
578 			done = 1;
579 			break;
580 		default:
581 			printf("Error unknown state\n");
582 			break;
583 		}
584 
585 		printf("%3d ", devno);
586 		userconf_pdevnam(devno);
587 		if (done) {
588 			printf(" already");
589 		} else {
590 			/* XXX add cmd 'd' <devno> eoc */
591 			userconf_hist_cmd('d');
592 			userconf_hist_int(devno);
593 			userconf_hist_eoc();
594 		}
595 		printf(" disabled\n");
596 
597 		return;
598 	}
599 
600 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
601 		printf("%3d can't disable free slot\n", devno);
602 		return;
603 	}
604 
605 	if (devno > userconf_totdev &&
606 	    devno <= userconf_totdev+pdevnames_size) {
607 		printf("%3d %s", devno, pdevnames[devno-userconf_totdev-1]);
608 		if (pdevinit[devno-userconf_totdev-1].pdev_count < 1) {
609 			printf(" already ");
610 		} else {
611 			pdevinit[devno-userconf_totdev-1].pdev_count *= -1;
612 			/* XXX add cmd 'd' <devno> eoc */
613 			userconf_hist_cmd('d');
614 			userconf_hist_int(devno);
615 			userconf_hist_eoc();
616 		}
617 		printf(" disabled\n");
618 		return;
619 	}
620 
621 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
622 }
623 
624 void
625 userconf_enable(int devno)
626 {
627 	int done = 0;
628 
629 	if (devno <= userconf_maxdev) {
630 		switch (cfdata[devno].cf_fstate) {
631 		case FSTATE_DNOTFOUND:
632 			cfdata[devno].cf_fstate = FSTATE_NOTFOUND;
633 			break;
634 		case FSTATE_DSTAR:
635 			cfdata[devno].cf_fstate = FSTATE_STAR;
636 			break;
637 		case FSTATE_NOTFOUND:
638 		case FSTATE_STAR:
639 			done = 1;
640 			break;
641 		default:
642 			printf("Error unknown state\n");
643 			break;
644 		}
645 
646 		printf("%3d ", devno);
647 		userconf_pdevnam(devno);
648 		if (done) {
649 			printf(" already");
650 		} else {
651 			/* XXX add cmd 'e' <devno> eoc */
652 			userconf_hist_cmd('e');
653 			userconf_hist_int(devno);
654 			userconf_hist_eoc();
655 		}
656 		printf(" enabled\n");
657 		return;
658 	}
659 
660 	if (devno > userconf_maxdev && devno <= userconf_totdev) {
661 		printf("%3d can't enable free slot\n", devno);
662 		return;
663 	}
664 
665 	if (devno > userconf_totdev &&
666 	    devno <= userconf_totdev+pdevnames_size) {
667 		printf("%3d %s", devno, pdevnames[devno-userconf_totdev-1]);
668 		if (pdevinit[devno-userconf_totdev-1].pdev_count > 0) {
669 			printf(" already");
670 		} else {
671 			pdevinit[devno-userconf_totdev-1].pdev_count *= -1;
672 			/* XXX add cmd 'e' <devno> eoc */
673 			userconf_hist_cmd('e');
674 			userconf_hist_int(devno);
675 			userconf_hist_eoc();
676 		}
677 		printf(" enabled\n");
678 		return;
679 	}
680 
681 	printf("Unknown devno (max is %d)\n", userconf_totdev+pdevnames_size);
682 }
683 
684 void
685 userconf_help(void)
686 {
687 	int j = 0, k;
688 
689 	printf("command   args                description\n");
690 	while (*userconf_cmds[j] != '\0') {
691 		printf("%s", userconf_cmds[j]);
692 		k = strlen(userconf_cmds[j]);
693 		while (k < 10) {
694 			printf(" ");
695 			k++;
696 		}
697 		switch (*userconf_cmds[j+1]) {
698 		case 'L':
699 			printf("[count]             number of lines before more");
700 			break;
701 		case 'a':
702 			printf("dev                 add a device");
703 			break;
704 		case 'b':
705 			printf("8|10|16             base on large numbers");
706 			break;
707 		case 'c':
708 			printf("devno|dev           change devices");
709 			break;
710 #if defined(DDB)
711 		case 'D':
712 			printf("                    enter ddb");
713 			break;
714 #endif
715 		case 'd':
716 			printf("attr val|devno|dev  disable devices");
717 			break;
718 		case 'e':
719 			printf("attr val|devno|dev  enable devices");
720 			break;
721 		case 'f':
722 			printf("devno|dev           find devices");
723 			break;
724 		case 'h':
725 			printf("                    this message");
726 			break;
727 		case 'l':
728 			printf("                    list configuration");
729 			break;
730 		case 'q':
731 			printf("                    leave UKC");
732 			break;
733 		case 's':
734 			printf("[attr [val]]        "
735 			   "show attributes (or devices with an attribute)");
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, long *val, char routine)
783 {
784 	struct cfdata *cd;
785 	long  *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 	long 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, INT_MAX) == 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 != NULL) {
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 	long 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, INT_MAX) == 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, long *val)
992 {
993 	int ok = 0;
994 	long 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, INT_MAX) == 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 found = 0;
1040 	struct cfdata new;
1041 	int max_unit, star_unit;
1042 	long i = 0, val, orig;
1043 
1044 	memset(&new, 0, sizeof(struct cfdata));
1045 
1046 	if (userconf_maxdev == userconf_totdev) {
1047 		printf("No more space for new devices.\n");
1048 		return;
1049 	}
1050 
1051 	if (state == FSTATE_FOUND) {
1052 		printf("Device not complete number or * is missing\n");
1053 		return;
1054 	}
1055 
1056 	for (i = 0; cfdata[i].cf_driver; i++)
1057 		if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1058 		    strncasecmp(dev, cfdata[i].cf_driver->cd_name, len) == 0)
1059 			found = 1;
1060 
1061 	if (!found) {
1062 		printf("No device of this type exists.\n");
1063 		return;
1064 	}
1065 
1066 	userconf_add_read("Clone Device (DevNo, 'q' or '?')",
1067 	    'a', dev, len, &val);
1068 
1069 	if (val != -1) {
1070 		orig = val;
1071 		new = cfdata[val];
1072 		new.cf_unit = unit;
1073 		new.cf_fstate = state;
1074 		userconf_add_read("Insert before Device (DevNo, 'q' or '?')",
1075 		    'i', dev, len, &val);
1076 	}
1077 
1078 	if (val != -1) {
1079 		/* XXX add cmd 'a' <orig> <val> eoc */
1080 		userconf_hist_cmd('a');
1081 		userconf_hist_int(orig);
1082 		userconf_hist_int(unit);
1083 		userconf_hist_int(state);
1084 		userconf_hist_int(val);
1085 		userconf_hist_eoc();
1086 
1087 		/* Insert the new record */
1088 		for (i = userconf_maxdev; val <= i; i--)
1089 			cfdata[i+1] = cfdata[i];
1090 		cfdata[val] = new;
1091 
1092 		/* Fix indexs in pv */
1093 		for (i = 0; i < pv_size; i++) {
1094 			if (pv[i] != -1 && pv[i] >= val)
1095 				pv[i]++;
1096 		}
1097 
1098 		/* Fix indexs in cfroots */
1099 		for (i = 0; i < cfroots_size; i++) {
1100 			if (cfroots[i] != -1 && cfroots[i] >= val)
1101 				cfroots[i]++;
1102 		}
1103 
1104 		userconf_maxdev++;
1105 
1106 		max_unit = -1;
1107 
1108 		/* Find max unit number of the device type */
1109 
1110 		i = 0;
1111 		while (cfdata[i].cf_attach != NULL) {
1112 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1113 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1114 			    len) == 0) {
1115 				switch (cfdata[i].cf_fstate) {
1116 				case FSTATE_NOTFOUND:
1117 				case FSTATE_DNOTFOUND:
1118 					if (cfdata[i].cf_unit > max_unit)
1119 						max_unit = cfdata[i].cf_unit;
1120 					break;
1121 				default:
1122 					break;
1123 				}
1124 			}
1125 			i++;
1126 		}
1127 
1128 		/*
1129 		 * For all * entries set unit number to max+1, and update
1130 		 * cf_starunit1 if necessary.
1131 		 */
1132 		max_unit++;
1133 		star_unit = -1;
1134 
1135 		i = 0;
1136 		while (cfdata[i].cf_attach != NULL) {
1137 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1138 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1139 			    len) == 0) {
1140 				switch (cfdata[i].cf_fstate) {
1141 				case FSTATE_NOTFOUND:
1142 				case FSTATE_DNOTFOUND:
1143 					if (cfdata[i].cf_unit > star_unit)
1144 						star_unit = cfdata[i].cf_unit;
1145 					break;
1146 				default:
1147 					break;
1148 				}
1149 			}
1150 			i++;
1151 		}
1152 		star_unit++;
1153 
1154 		i = 0;
1155 		while (cfdata[i].cf_attach != NULL) {
1156 			if (strlen(cfdata[i].cf_driver->cd_name) == len &&
1157 			    strncasecmp(dev, cfdata[i].cf_driver->cd_name,
1158 			    len) == 0) {
1159 				switch (cfdata[i].cf_fstate) {
1160 				case FSTATE_STAR:
1161 				case FSTATE_DSTAR:
1162 					cfdata[i].cf_unit = max_unit;
1163 					if (cfdata[i].cf_starunit1 < star_unit)
1164 						cfdata[i].cf_starunit1 =
1165 						    star_unit;
1166 					break;
1167 				default:
1168 					break;
1169 				}
1170 			}
1171 			i++;
1172 		}
1173 		userconf_pdev(val);
1174 	}
1175 
1176 	/* cf_attach, cf_driver, cf_unit, cf_fstate, cf_loc, cf_flags,
1177 	   cf_parents, cf_locnames, and cf_locnames */
1178 }
1179 
1180 int
1181 userconf_parse(char *cmd)
1182 {
1183 	char *c, *v;
1184 	int i = 0, j = 0, k;
1185 	long a;
1186 	short unit, state;
1187 
1188 	c = cmd;
1189 	while (*c == ' ' || *c == '\t')
1190 		c++;
1191 	v = c;
1192 	while (*c != ' ' && *c != '\t' && *c != '\n' && *c != '\0') {
1193 		c++;
1194 		i++;
1195 	}
1196 
1197 	k = -1;
1198 	while (*userconf_cmds[j] != '\0') {
1199 		if (strlen(userconf_cmds[j]) == i) {
1200 			if (strncasecmp(v, userconf_cmds[j], i) == 0)
1201 				k = j;
1202 		}
1203 		j += 2;
1204 	}
1205 
1206 	while (*c == ' ' || *c == '\t' || *c == '\n')
1207 		c++;
1208 
1209 	if (k == -1) {
1210 		if (*v != '\n')
1211 			printf("Unknown command, try help\n");
1212 	} else {
1213 		switch (*userconf_cmds[k+1]) {
1214 		case 'L':
1215 			if (*c == '\0')
1216 				printf("Argument expected\n");
1217 			else if (userconf_number(c, &a, INT_MAX) == 0)
1218 				userconf_lines = a;
1219 			else
1220 				printf("Unknown argument\n");
1221 			break;
1222 		case 'a':
1223 			if (*c == '\0')
1224 				printf("Dev expected\n");
1225 			else if (userconf_device(c, &a, &unit, &state) == 0)
1226 				userconf_add(c, a, unit, state);
1227 			else
1228 				printf("Unknown argument\n");
1229 			break;
1230 		case 'b':
1231 			if (*c == '\0')
1232 				printf("8|10|16 expected\n");
1233 			else if (userconf_number(c, &a, INT_MAX) == 0) {
1234 				if (a == 8 || a == 10 || a == 16) {
1235 					userconf_base = a;
1236 				} else {
1237 					printf("8|10|16 expected\n");
1238 				}
1239 			} else
1240 				printf("Unknown argument\n");
1241 			break;
1242 		case 'c':
1243 			if (*c == '\0')
1244 				printf("DevNo or Dev expected\n");
1245 			else if (userconf_number(c, &a, INT_MAX) == 0)
1246 				userconf_change(a);
1247 			else if (userconf_device(c, &a, &unit, &state) == 0)
1248 				userconf_common_dev(c, a, unit, state, UC_CHANGE);
1249 			else
1250 				printf("Unknown argument\n");
1251 			break;
1252 #if defined(DDB)
1253 		case 'D':
1254 			db_enter();
1255 			break;
1256 #endif
1257 		case 'd':
1258 			if (*c == '\0')
1259 				printf("Attr, DevNo or Dev expected\n");
1260 			else if (userconf_attr(c, &a) == 0)
1261 				userconf_common_attr(c, a, UC_DISABLE);
1262 			else if (userconf_number(c, &a, INT_MAX) == 0)
1263 				userconf_disable(a);
1264 			else if (userconf_device(c, &a, &unit, &state) == 0)
1265 				userconf_common_dev(c, a, unit, state, UC_DISABLE);
1266 			else
1267 				printf("Unknown argument\n");
1268 			break;
1269 		case 'e':
1270 			if (*c == '\0')
1271 				printf("Attr, DevNo or Dev expected\n");
1272 			else if (userconf_attr(c, &a) == 0)
1273 				userconf_common_attr(c, a, UC_ENABLE);
1274 			else if (userconf_number(c, &a, INT_MAX) == 0)
1275 				userconf_enable(a);
1276 			else if (userconf_device(c, &a, &unit, &state) == 0)
1277 				userconf_common_dev(c, a, unit, state, UC_ENABLE);
1278 			else
1279 				printf("Unknown argument\n");
1280 			break;
1281 		case 'f':
1282 			if (*c == '\0')
1283 				printf("DevNo or Dev expected\n");
1284 			else if (userconf_number(c, &a, INT_MAX) == 0)
1285 				userconf_pdev(a);
1286 			else if (userconf_device(c, &a, &unit, &state) == 0)
1287 				userconf_common_dev(c, a, unit, state, UC_FIND);
1288 			else
1289 				printf("Unknown argument\n");
1290 			break;
1291 		case 'h':
1292 			userconf_help();
1293 			break;
1294 		case 'l':
1295 			if (*c == '\0')
1296 				userconf_list();
1297 			else
1298 				printf("Unknown argument\n");
1299 			break;
1300 		case 'q':
1301 			/* XXX add cmd 'q' eoc */
1302 			userconf_hist_cmd('q');
1303 			userconf_hist_eoc();
1304 			return(-1);
1305 			break;
1306 		case 's':
1307 			if (*c == '\0')
1308 				userconf_show();
1309 			else
1310 				userconf_show_attr(c);
1311 			break;
1312 		case 'v':
1313 			autoconf_verbose = !autoconf_verbose;
1314 			printf("autoconf verbose %sabled\n",
1315 			    autoconf_verbose ? "en" : "dis");
1316 			break;
1317 		default:
1318 			printf("Unknown command\n");
1319 			break;
1320 		}
1321 	}
1322 	return(0);
1323 }
1324 
1325 void
1326 user_config(void)
1327 {
1328 	userconf_init();
1329 	printf("User Kernel Config\n");
1330 
1331 	cnpollc(1);
1332 	while (1) {
1333 		printf("UKC> ");
1334 		if (getsn(userconf_cmdbuf, sizeof(userconf_cmdbuf)) > 0 &&
1335 		    userconf_parse(userconf_cmdbuf))
1336 			break;
1337 	}
1338 	cnpollc(0);
1339 
1340 	printf("Continuing...\n");
1341 }
1342