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