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
36extern char *locnames[];
37extern short locnamp[];
38extern short cfroots[];
39extern int cfroots_size;
40extern int pv_size;
41extern short pv[];
42extern char *pdevnames[];
43extern int pdevnames_size;
44extern struct pdevinit pdevinit[];
45
46int userconf_base = 16;				/* Base for "large" numbers */
47int userconf_maxdev = -1;			/* # of used device slots   */
48int userconf_totdev = -1;			/* # of device slots        */
49int userconf_maxlocnames = -1;			/* # of locnames            */
50int userconf_cnt = -1;				/* Line counter for ...     */
51int userconf_lines = 12;			/* ... # of lines per page  */
52int userconf_histlen = 0;
53int userconf_histcur = 0;
54char userconf_history[1024];
55int userconf_histsz = sizeof(userconf_history);
56char userconf_argbuf[40];			/* Additional input         */
57char userconf_cmdbuf[40];			/* Command line             */
58char userconf_histbuf[40];
59
60void userconf_init(void);
61int userconf_more(void);
62void userconf_modify(char *, long *, long);
63void userconf_hist_cmd(char);
64void userconf_hist_int(long);
65void userconf_hist_eoc(void);
66void userconf_pnum(long);
67void userconf_pdevnam(short);
68void userconf_pdev(short);
69int userconf_number(char *, long *, long);
70int userconf_device(char *, long *, short *, short *);
71int userconf_attr(char *, long *);
72void userconf_change(int);
73void userconf_disable(int);
74void userconf_enable(int);
75void userconf_help(void);
76void userconf_list(void);
77void userconf_show(void);
78void userconf_common_attr_val(short, long *, char);
79void userconf_show_attr(char *);
80void userconf_common_dev(char *, int, short, short, char);
81void userconf_common_attr(char *, int, char);
82void userconf_add_read(char *, char, char *, int, long *);
83void userconf_add(char *, int, short, short);
84int 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
92char *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
113void
114userconf_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
141int
142userconf_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
161void
162userconf_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
171void
172userconf_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
183void
184userconf_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
193void
194userconf_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
215void
216userconf_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
240void
241userconf_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
307int
308userconf_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
350int
351userconf_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
385int
386userconf_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
414void
415userconf_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
445void
446userconf_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
562void
563userconf_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
623void
624userconf_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
683void
684userconf_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
748void
749userconf_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
764void
765userconf_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
780void
781userconf_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
833void
834userconf_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
873void
874userconf_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
962void
963userconf_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
989void
990userconf_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
1035void
1036userconf_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
1179int
1180userconf_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
1324void
1325user_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