]> git.saurik.com Git - apple/boot.git/blob - i386/libsaio/stringTable.c
1be07b01a21ab38d792775ac142552516c43641f
[apple/boot.git] / i386 / libsaio / stringTable.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Copyright 1993 NeXT, Inc.
26 * All rights reserved.
27 */
28
29 #include "bootstruct.h"
30 #include "libsaio.h"
31 #include "stringConstants.h"
32 #include "legacy/configTablePrivate.h"
33 #include "xml.h"
34
35 extern char *Language;
36 extern char *LoadableFamilies;
37
38 static TagPtr gConfigDict;
39
40 static void eatThru(char val, const char **table_p);
41
42 static inline int isspace(char c)
43 {
44 return (c == ' ' || c == '\t');
45 }
46
47 /*
48 * Compare a string to a key with quoted characters
49 */
50 static inline int
51 keyncmp(const char *str, const char *key, int n)
52 {
53 int c;
54 while (n--) {
55 c = *key++;
56 if (c == '\\') {
57 switch(c = *key++) {
58 case 'n':
59 c = '\n';
60 break;
61 case 'r':
62 c = '\r';
63 break;
64 case 't':
65 c = '\t';
66 break;
67 default:
68 break;
69 }
70 } else if (c == '\"') {
71 /* Premature end of key */
72 return 1;
73 }
74 if (c != *str++) {
75 return 1;
76 }
77 }
78 return 0;
79 }
80
81 static void eatThru(char val, const char **table_p)
82 {
83 register const char *table = *table_p;
84 register BOOL found = NO;
85
86 while (*table && !found)
87 {
88 if (*table == '\\') table += 2;
89 else
90 {
91 if (*table == val) found = YES;
92 table++;
93 }
94 }
95 *table_p = table;
96 }
97
98 /* Remove key and its associated value from the table. */
99
100 BOOL
101 removeKeyFromTable(const char *key, char *table)
102 {
103 register int len;
104 register char *tab;
105 char *buf;
106
107 len = strlen(key);
108 tab = (char *)table;
109 buf = (char *)malloc(len + 3);
110
111 sprintf(buf, "\"%s\"", key);
112 len = strlen(buf);
113
114 while(*tab) {
115 if(strncmp(buf, tab, len) == 0) {
116 char c;
117
118 while((c = *(tab + len)) != ';') {
119 if(c == 0) {
120 len = -1;
121 goto out;
122 }
123 len++;
124 }
125 len++;
126 if(*(tab + len) == '\n') len++;
127 goto out;
128 }
129 tab++;
130 }
131 len = -1;
132 out:
133 free(buf);
134
135 if(len == -1) return NO;
136
137 while((*tab = *(tab + len))) {
138 tab++;
139 }
140
141 return YES;
142 }
143
144 char *
145 newStringFromList(
146 char **list,
147 int *size
148 )
149 {
150 char *begin = *list, *end;
151 char *newstr;
152 int newsize = *size;
153 int bufsize;
154
155 while (*begin && newsize && isspace(*begin)) {
156 begin++;
157 newsize--;
158 }
159 end = begin;
160 while (*end && newsize && !isspace(*end)) {
161 end++;
162 newsize--;
163 }
164 if (begin == end)
165 return 0;
166 bufsize = end - begin + 1;
167 newstr = malloc(bufsize);
168 strlcpy(newstr, begin, bufsize);
169 *list = end;
170 *size = newsize;
171 return newstr;
172 }
173
174 /*
175 * compress == compress escaped characters to one character
176 */
177 int stringLength(const char *table, int compress)
178 {
179 int ret = 0;
180
181 while (*table)
182 {
183 if (*table == '\\')
184 {
185 table += 2;
186 ret += 1 + (compress ? 0 : 1);
187 }
188 else
189 {
190 if (*table == '\"') return ret;
191 ret++;
192 table++;
193 }
194 }
195 return ret;
196 }
197
198 BOOL getValueForConfigTableKey(const char *table, const char *key, const char **val, int *size)
199 {
200 int keyLength;
201 const char *tableKey;
202
203 if (gConfigDict != 0 ) {
204 /* Look up key in XML dictionary */
205 TagPtr value;
206 value = XMLGetProperty(gConfigDict, key);
207 if (value != 0) {
208 if (value->type != kTagTypeString) {
209 error("Non-string tag '%s' found in config file\n",
210 key);
211 return NO;
212 }
213 *val = value->string;
214 *size = strlen(value->string);
215 return YES;
216 }
217 } else {
218 /* Legacy plist-style table */
219 do
220 {
221 eatThru('\"',&table);
222 tableKey = table;
223 keyLength = strlen(key);
224 if (keyLength &&
225 (stringLength(table,1) == keyLength) &&
226 (keyncmp(key, table, keyLength) == 0))
227 {
228 int c;
229
230 /* found the key; now look for either
231 * '=' or ';'
232 */
233 while (c = *table) {
234 ++table;
235 if (c == '\\') {
236 ++table;
237 continue;
238 } else if (c == '=' || c == ';') {
239 break;
240 }
241 }
242 if (c == ';') {
243 table = tableKey;
244 } else {
245 eatThru('\"',&table);
246 }
247 *val = table;
248 *size = stringLength(table,0);
249 return YES;
250 }
251
252 eatThru(';',&table);
253
254 } while (*table);
255 }
256
257 return NO;
258 }
259
260 /*
261 * Returns a new malloc'ed string if one is found
262 * in the string table matching 'key'. Also translates
263 * \n escapes in the string.
264 */
265 char *newStringForStringTableKey(
266 char *table,
267 char *key
268 )
269 {
270 const char *val;
271 char *newstr, *p;
272 int size;
273
274 if (getValueForConfigTableKey(table, key, &val, &size)) {
275 newstr = (char *)malloc(size+1);
276 for (p = newstr; size; size--, p++, val++) {
277 if ((*p = *val) == '\\') {
278 switch (*++val) {
279 case 'r':
280 *p = '\r';
281 break;
282 case 'n':
283 *p = '\n';
284 break;
285 case 't':
286 *p = '\t';
287 break;
288 default:
289 *p = *val;
290 break;
291 }
292 size--;
293 }
294 }
295 *p = '\0';
296 return newstr;
297 } else {
298 return 0;
299 }
300 }
301
302 char *
303 newStringForKey(char *key)
304 {
305 const char *val;
306 char *newstr;
307 int size;
308
309 if (getValueForKey(key, &val, &size) && size) {
310 newstr = (char *)malloc(size + 1);
311 strlcpy(newstr, val, size + 1);
312 return newstr;
313 } else {
314 return 0;
315 }
316 }
317
318 /* parse a command line
319 * in the form: [<argument> ...] [<option>=<value> ...]
320 * both <option> and <value> must be either composed of
321 * non-whitespace characters, or enclosed in quotes.
322 */
323
324 static const char *getToken(const char *line, const char **begin, int *len)
325 {
326 if (*line == '\"') {
327 *begin = ++line;
328 while (*line && *line != '\"')
329 line++;
330 *len = line++ - *begin;
331 } else {
332 *begin = line;
333 while (*line && !isspace(*line) && *line != '=')
334 line++;
335 *len = line - *begin;
336 }
337 return line;
338 }
339
340 BOOL getValueForBootKey(const char *line, const char *match, const char **matchval, int *len)
341 {
342 const char *key, *value;
343 int key_len, value_len;
344
345 while (*line) {
346 /* look for keyword or argument */
347 while (isspace(*line)) line++;
348
349 /* now look for '=' or whitespace */
350 line = getToken(line, &key, &key_len);
351 /* line now points to '=' or space */
352 if (*line && !isspace(*line)) {
353 line = getToken(++line, &value, &value_len);
354 } else {
355 value = line;
356 value_len = 0;
357 }
358 if ((strlen(match) == key_len)
359 && strncmp(match, key, key_len) == 0) {
360 *matchval = value;
361 *len = value_len;
362 return YES;
363 }
364 }
365 return NO;
366 }
367
368 BOOL getBoolForKey(
369 const char *key
370 )
371 {
372 const char *val;
373 int size;
374
375 if (getValueForKey(key, &val, &size) && (size >= 1) &&
376 val[0] == 'Y' || val[0] == 'y')
377 return YES;
378 return NO;
379 }
380
381 BOOL getIntForKey(
382 const char *key,
383 int *value
384 )
385 {
386 const char *val;
387 int size, sum;
388
389 if (getValueForKey(key, &val, &size)) {
390 for (sum = 0; size > 0; size--) {
391 sum = (sum * 10) + (*val++ - '0');
392 }
393 *value = sum;
394 return YES;
395 }
396 return NO;
397 }
398
399 BOOL getValueForKey(
400 const char *key,
401 const char **val,
402 int *size
403 )
404 {
405 if (getValueForBootKey(bootArgs->bootString, key, val, size))
406 return YES;
407 else if (getValueForConfigTableKey(bootArgs->config, key, val, size))
408 return YES;
409
410 return NO;
411 }
412
413 int sysConfigValid;
414
415 #define TABLE_EXPAND_SIZE 192
416
417 /*
418 * Returns 0 if file loaded OK,
419 * -1 if file was not loaded
420 * Does not print error messages.
421 * Returns pointer to table in memory in *table.
422 * Allocates an extra number of bytes for table expansion.
423 */
424 int
425 loadConfigFile(const char *configFile, const char **table, BOOL allocTable)
426 {
427 char *configPtr = bootArgs->configEnd;
428 int fd, count;
429
430 /* Read config file into memory */
431 if ((fd = open(configFile, 0)) >= 0)
432 {
433 if (allocTable) {
434 configPtr = malloc(file_size(fd)+2+TABLE_EXPAND_SIZE);
435 } else {
436 if ((configPtr - bootArgs->config) > CONFIG_SIZE) {
437 error("No room in memory for config files\n");
438 close(fd);
439 return -1;
440 }
441 verbose("Reading configuration file '%s'.\n",configFile);
442 }
443 if (table) *table = configPtr;
444 count = read(fd, configPtr, IO_CONFIG_DATA_SIZE);
445 close(fd);
446
447 configPtr += count;
448 *configPtr++ = 0;
449 *configPtr = 0;
450 if (!allocTable)
451 bootArgs->configEnd = configPtr;
452
453 return 0;
454 } else {
455 return -1;
456 }
457 }
458
459 /* Returns 0 if requested config files were loaded,
460 * 1 if default files were loaded,
461 * -1 if no files were loaded.
462 * Prints error message if files cannot be loaded.
463 */
464
465 int
466 loadConfigDir(
467 const char *bundleName, // bundle directory name (e.g. "System")
468 BOOL useDefault, // use Default.table instead of instance tables
469 const char **table, // returns pointer to config table
470 BOOL allocTable // malloc the table and return in *table
471 )
472 {
473 char *buf;
474 int i, max, ret;
475 const char *device_dir = usrDevices();
476
477 buf = malloc(256);
478 ret = 0;
479
480 // load up to 99 instance tables
481 if (allocTable)
482 max = 1;
483 else
484 max = 99;
485 for (i=0; i < max; i++) {
486 sprintf(buf, "%s/%s.config/Instance%d.table",
487 device_dir,
488 bundleName, i);
489 if (useDefault || (loadConfigFile(buf, table, allocTable) != 0)) {
490 if (i == 0) {
491 // couldn't load first instance table;
492 // try the default table
493 sprintf(buf, "%s/%s.config/%s",
494 device_dir,
495 bundleName,
496 IO_DEFAULT_TABLE_FILENAME);
497 if (loadConfigFile(buf, table, allocTable) == 0) {
498 ret = 1;
499 } else {
500 if (!allocTable) {
501 error("Config file \"%s\" not found\n", buf);
502 sleep(1); // let the message be seen!
503 }
504 ret = -1;
505 }
506 }
507 // we must be done.
508 break;
509 }
510 }
511 free(buf);
512 return ret;
513 }
514
515
516 #define USR_SYSTEM_CONFIG \
517 USR_DEVICES "/System.config"
518 #define USR_SYSTEM_DEFAULT_FILE \
519 USR_SYSTEM_CONFIG "/Default.table"
520 #define ARCH_SYSTEM_CONFIG \
521 ARCH_DEVICES "/System.config"
522 #define ARCH_SYSTEM_DEFAULT_FILE \
523 ARCH_SYSTEM_CONFIG "/Default.table"
524 #define SYSTEM_CONFIG "System"
525 #define LP '('
526 #define RP ')'
527
528 #define SYSTEM_CONFIG_DIR "/Library/Preferences/SystemConfiguration"
529 #define SYSTEM_CONFIG_FILE "/com.apple.Boot.plist"
530 #define SYSTEM_CONFIG_PATH SYSTEM_CONFIG_DIR SYSTEM_CONFIG_FILE
531 #define CONFIG_EXT ".plist"
532
533 #if 1
534 void
535 printSystemConfig(void)
536 {
537 char *p1 = bootArgs->config;
538 char *p2 = p1, tmp;
539
540 while (*p1 != '\0') {
541 while (*p2 != '\0' && *p2 != '\n') p2++;
542 tmp = *p2;
543 *p2 = '\0';
544 printf("%s\n", p1);
545 *p2 = tmp;
546 if (tmp == '\0') break;
547 p1 = ++p2;
548 }
549 }
550 #endif
551
552 static int sysconfig_dev;
553
554 //==========================================================================
555 // ParseXMLFile
556 // Modifies the input buffer.
557 // Expects to see one dictionary in the XML file.
558 // Puts the first dictionary it finds in the
559 // tag pointer and returns 0, or returns -1 if not found
560 // (and does not modify dict pointer).
561 //
562 static long
563 ParseXMLFile( char * buffer, TagPtr * dict )
564 {
565 long length, pos;
566 TagPtr tag;
567 pos = 0;
568 char *configBuffer;
569
570 configBuffer = malloc(strlen(buffer)+1);
571 strcpy(configBuffer, buffer);
572
573 while (1)
574 {
575 length = XMLParseNextTag(configBuffer + pos, &tag);
576 if (length == -1) break;
577
578 pos += length;
579
580 if (tag == 0) continue;
581 if (tag->type == kTagTypeDict) break;
582
583 XMLFreeTag(tag);
584 }
585 free(configBuffer);
586 if (length < 0) {
587 return -1;
588 }
589 *dict = tag;
590 return 0;
591 }
592
593 /* Returns 0 if requested config files were loaded,
594 * 1 if default files were loaded,
595 * -1 if no files were loaded.
596 * Prints error message if files cannot be loaded.
597 */
598 int
599 loadSystemConfig(
600 const char *which,
601 int size
602 )
603 {
604 char *buf, *bp;
605 const char *cp;
606 int ret, len, doDefault=0;
607
608 buf = bp = malloc(512);
609 if (which && size) {
610 for(cp = which, len = size; len && *cp && *cp != LP; cp++, len--) ;
611 if (*cp == LP) {
612 while (len-- && *cp && *cp++ != RP) ;
613 /* cp now points past device */
614 strlcpy(buf,which,cp - which);
615 bp += cp - which;
616 } else {
617 cp = which;
618 len = size;
619 }
620 if (*cp != '/') {
621 strcpy(bp, systemConfigDir());
622 strcat(bp, "/");
623 strncat(bp, cp, len);
624 if (strncmp(cp + len - strlen(CONFIG_EXT),
625 CONFIG_EXT, strlen(CONFIG_EXT)) != 0)
626 strcat(bp, CONFIG_EXT);
627 } else {
628 strlcpy(bp, cp, len);
629 }
630 if ((strcmp(bp, SYSTEM_CONFIG_PATH) == 0)) {
631 doDefault = 1;
632 }
633 ret = loadConfigFile(bp = buf, 0, 0);
634 } else {
635 strcpy(bp, systemConfigDir());
636 strcat(bp, SYSTEM_CONFIG_FILE);
637 ret = loadConfigFile(bp, 0, 0);
638 if (ret < 0) {
639 ret = loadConfigDir((bp = SYSTEM_CONFIG), 0, 0, 0);
640 }
641 }
642 sysconfig_dev = currentdev();
643 if (ret < 0) {
644 error("System config file '%s' not found\n", bp);
645 sleep(1);
646 } else {
647 sysConfigValid = 1;
648 // Check for XML file;
649 // if not XML, gConfigDict will remain 0.
650 ParseXMLFile(bootArgs->config, &gConfigDict);
651 }
652 free(buf);
653 return (ret < 0 ? ret : doDefault);
654 }
655
656
657 char * newString(const char * oldString)
658 {
659 if ( oldString )
660 return strcpy(malloc(strlen(oldString)+1), oldString);
661 else
662 return NULL;
663 }