#include "u.h" #include "lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "fs.h" /* * Where configuration info is left for the loaded programme. * This will turn into a structure as more is done by the boot loader * (e.g. why parse the .ini file twice?). * There are 3584 bytes available at CONFADDR. * * The low-level boot routines in l.s leave data for us at CONFADDR, * which we pick up before reading the plan9.ini file. */ #define BOOTLINELEN 64 #define BOOTARGS ((char*)(CONFADDR+BOOTLINELEN)) #define BOOTARGSLEN (3584-0x200-BOOTLINELEN) #define MAXCONF 100 static char *confname[MAXCONF]; static char *confval[MAXCONF]; static int nconf; extern char **ini; typedef struct { char* name; int start; int end; } Mblock; typedef struct { char* tag; Mblock* mb; } Mitem; static Mblock mblock[MAXCONF]; static int nmblock; static Mitem mitem[MAXCONF]; static int nmitem; static char* mdefault; static char mdefaultbuf[10]; static int mtimeout; static char* comma(char* line, char** residue) { char *q, *r; if((q = strchr(line, ',')) != nil){ *q++ = 0; if(*q == ' ') q++; } *residue = q; if((r = strchr(line, ' ')) != nil) *r = 0; if(*line == ' ') line++; return line; } static Mblock* findblock(char* name, char** residue) { int i; char *p; p = comma(name, residue); for(i = 0; i < nmblock; i++){ if(strcmp(p, mblock[i].name) == 0) return &mblock[i]; } return nil; } static Mitem* finditem(char* name, char** residue) { int i; char *p; p = comma(name, residue); for(i = 0; i < nmitem; i++){ if(strcmp(p, mitem[i].mb->name) == 0) return &mitem[i]; } return nil; } static void parsemenu(char* str, char* scratch, int len) { Mitem *mi; Mblock *mb, *menu; char buf[20], *p, *q, *line[MAXCONF]; int i, inblock, n, show; inblock = 0; menu = nil; memmove(scratch, str, len); n = getfields(scratch, line, MAXCONF, '\n'); if(n >= MAXCONF) print("warning: possibly too many lines in plan9.ini\n"); for(i = 0; i < n; i++){ p = line[i]; if(inblock && *p == '['){ mblock[nmblock].end = i; if(strcmp(mblock[nmblock].name, "menu") == 0) menu = &mblock[nmblock]; nmblock++; inblock = 0; } if(*p == '['){ if(nmblock == 0 && i != 0){ mblock[nmblock].name = "common"; mblock[nmblock].start = 0; mblock[nmblock].end = i; nmblock++; } q = strchr(p+1, ']'); if(q == nil || *(q+1) != 0){ print("malformed menu block header - %s\n", p); return; } *q = 0; mblock[nmblock].name = p+1; mblock[nmblock].start = i+1; inblock = 1; } } if(inblock){ mblock[nmblock].end = i; nmblock++; } if(menu == nil) return; if(nmblock < 2){ print("incomplete menu specification\n"); return; } for(i = menu->start; i < menu->end; i++){ p = line[i]; if(cistrncmp(p, "menuitem=", 9) == 0){ p += 9; if((mb = findblock(p, &q)) == nil){ print("no block for menuitem %s\n", p); return; } if(q != nil) mitem[nmitem].tag = q; else mitem[nmitem].tag = mb->name; mitem[nmitem].mb = mb; nmitem++; } else if(cistrncmp(p, "menudefault=", 12) == 0){ p += 12; if((mi = finditem(p, &q)) == nil){ print("no item for menudefault %s\n", p); return; } if(q != nil) mtimeout = strtol(q, 0, 0); sprint(mdefaultbuf, "%ld", mi-mitem+1); mdefault = mdefaultbuf; } else if(cistrncmp(p, "menuconsole=", 12) == 0){ p += 12; p = comma(p, &q); consinit(p, q); } else{ print("invalid line in [menu] block - %s\n", p); return; } } again: print("\nPlan 9 Startup Menu:\n====================\n"); for(i = 0; i < nmitem; i++) print(" %d. %s\n", i+1, mitem[i].tag); for(;;){ getstr("Selection", buf, sizeof(buf), mdefault, mtimeout); mtimeout = 0; i = strtol(buf, &p, 0)-1; if(i < 0 || i >= nmitem) goto again; switch(*p){ case 'p': case 'P': show = 1; print("\n"); break; case 0: show = 0; break; default: continue; } mi = &mitem[i]; p = str; p += sprint(p, "menuitem=%s\n", mi->mb->name); for(i = 0; i < nmblock; i++){ mb = &mblock[i]; if(mi->mb != mb && cistrcmp(mb->name, "common") != 0) continue; for(n = mb->start; n < mb->end; n++) p += sprint(p, "%s\n", line[n]); } if(show){ for(q = str; q < p; q += i){ if((i = print(q)) <= 0) break; } goto again; } break; } print("\n"); } /* static void msleep(int msec) { ulong start; for(start = m->ticks; TK2MS(m->ticks - start) < msec; ) ; } */ void readlsconf(void) { uchar *p; p = (uchar*)CONFADDR; for(;;) { if(strcmp((char*)p, "APM") == 0){ apm.haveinfo = 1; apm.ax = *(ushort*)(p+4); apm.cx = *(ushort*)(p+6); apm.dx = *(ushort*)(p+8); apm.di = *(ushort*)(p+10); apm.ebx = *(ulong*)(p+12); apm.esi = *(ulong*)(p+16); print("apm ax=%x cx=%x dx=%x di=%x ebx=%x esi=%x\n", apm.ax, apm.cx, apm.dx, apm.di, apm.ebx, apm.esi); p += 20; continue; } break; } } char* getconf(char *name) { int i, n, nmatch; char buf[20]; nmatch = 0; for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) nmatch++; switch(nmatch) { default: print("\n"); nmatch = 0; for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) print("%d. %s\n", ++nmatch, confval[i]); print("%d. none of the above\n", ++nmatch); do { getstr(name, buf, sizeof(buf), nil, 0); n = atoi(buf); } while(n < 1 || n > nmatch); for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) if(--n == 0) return confval[i]; break; case 1: for(i = 0; i < nconf; i++) if(cistrcmp(confname[i], name) == 0) return confval[i]; break; case 0: break; } return nil; } void addconf(char *fmt, ...) { donprint(BOOTARGS+strlen(BOOTARGS), BOOTARGS+BOOTARGSLEN, fmt, (&fmt+1)); } void changeconf(char *fmt, ...) { char *p, *q, pref[20], buf[128]; donprint(buf, buf+sizeof buf, fmt, (&fmt+1)); strncpy(pref+1, buf, 19); pref[19] = '\0'; if(p = strchr(pref, '=')) *(p+1) = '\0'; else print("warning: did not change %s in plan9.ini\n", buf); /* find old line by looking for \nwhat= */ pref[0] = '\n'; if(strncmp(BOOTARGS, pref+1, strlen(pref+1)) == 0) p = BOOTARGS; else if(p = strstr(BOOTARGS, pref)) p++; else p = nil; /* move rest of args up, deleting what= line. */ if(p != nil && (q = strchr(p, '\n')) != nil) memmove(p, q+1, strlen(q+1)+1); /* add replacement to end */ addconf("%s", buf); } /* * read configuration file */ static char inibuf[BOOTARGSLEN]; static char id[8] = "ZORT 0\r\n"; int dotini(Fs *fs) { File rc; int blankline, i, incomment, inspace, n; char *cp, *p, *q, *line[MAXCONF]; if(fswalk(fs, *ini, &rc) <= 0) return -1; cp = inibuf; *cp = 0; n = fsread(&rc, cp, BOOTARGSLEN-1); if(n <= 0) return -1; cp[n] = 0; /* * Strip out '\r', change '\t' -> ' '. * Change runs of spaces into single spaces. * Strip out trailing spaces, blank lines. * * We do this before we make the copy so that if we * need to change the copy, it is already fairly clean. * The main need is in the case when plan9.ini has been * padded with lots of trailing spaces, as is the case * for those created during a distribution install. */ p = cp; blankline = 1; incomment = inspace = 0; for(q = cp; *q; q++){ if(*q == '\r') continue; if(*q == '\t') *q = ' '; if(*q == ' '){ inspace = 1; continue; } if(*q == '\n'){ if(!blankline){ if(!incomment) *p++ = '\n'; blankline = 1; } incomment = inspace = 0; continue; } if(inspace){ if(!blankline && !incomment) *p++ = ' '; inspace = 0; } if(blankline && *q == '#') incomment = 1; blankline = 0; if(!incomment) *p++ = *q; } if(p > cp && p[-1] != '\n') *p++ = '\n'; *p++ = 0; n = p-cp; parsemenu(cp, BOOTARGS, n); /* * Keep a copy. * We could change this to pass the parsed strings * to the booted programme instead of the raw * string, then it only gets done once. */ if(strncmp(cp, id, sizeof(id))){ memmove(BOOTARGS, id, sizeof(id)); if(n+1+sizeof(id) >= BOOTARGSLEN) n -= sizeof(id); memmove(BOOTARGS+sizeof(id), cp, n+1); } else memmove(BOOTARGS, cp, n+1); n = getfields(cp, line, MAXCONF, '\n'); for(i = 0; i < n; i++){ cp = strchr(line[i], '='); if(cp == 0) continue; *cp++ = 0; if(cp - line[i] >= NAMELEN+1) *(line[i]+NAMELEN-1) = 0; confname[nconf] = line[i]; confval[nconf] = cp; nconf++; } return 0; } static int parseether(uchar *to, char *from) { char nip[4]; char *p; int i; p = from; while(*p == ' ') ++p; for(i = 0; i < 6; i++){ if(*p == 0) return -1; nip[0] = *p++; if(*p == 0) return -1; nip[1] = *p++; nip[2] = 0; to[i] = strtoul(nip, 0, 16); if(*p == ':') p++; } return 0; } int isaconfig(char *class, int ctlrno, ISAConf *isa) { char cc[NAMELEN], *p, *q, *r; int n; sprint(cc, "%s%d", class, ctlrno); for(n = 0; n < nconf; n++){ if(cistrncmp(confname[n], cc, NAMELEN)) continue; isa->nopt = 0; p = confval[n]; while(*p){ while(*p == ' ' || *p == '\t') p++; if(*p == '\0') break; if(cistrncmp(p, "type=", 5) == 0){ p += 5; for(q = isa->type; q < &isa->type[NAMELEN-1]; q++){ if(*p == '\0' || *p == ' ' || *p == '\t') break; *q = *p++; } *q = '\0'; } else if(cistrncmp(p, "port=", 5) == 0) isa->port = strtoul(p+5, &p, 0); else if(cistrncmp(p, "irq=", 4) == 0) isa->irq = strtoul(p+4, &p, 0); else if(cistrncmp(p, "mem=", 4) == 0) isa->mem = strtoul(p+4, &p, 0); else if(cistrncmp(p, "size=", 5) == 0) isa->size = strtoul(p+5, &p, 0); else if(cistrncmp(p, "ea=", 3) == 0){ if(parseether(isa->ea, p+3) == -1) memset(isa->ea, 0, 6); } else if(isa->nopt < NISAOPT){ r = isa->opt[isa->nopt]; while(*p && *p != ' ' && *p != '\t'){ *r++ = *p++; if(r-isa->opt[isa->nopt] >= ISAOPTLEN-1) break; } *r = '\0'; isa->nopt++; } while(*p && *p != ' ' && *p != '\t') p++; } return 1; } return 0; }