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