Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Copyright (c) 1997 Metro Link Incorporated | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
17 | * THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
18 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF | |
19 | * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | * SOFTWARE. | |
21 | * | |
22 | * Except as contained in this notice, the name of the Metro Link shall not be | |
23 | * used in advertising or otherwise to promote the sale, use or other dealings | |
24 | * in this Software without prior written authorization from Metro Link. | |
25 | * | |
26 | */ | |
27 | /* | |
28 | * Copyright (c) 1997-2003 by The XFree86 Project, Inc. | |
29 | * | |
30 | * Permission is hereby granted, free of charge, to any person obtaining a | |
31 | * copy of this software and associated documentation files (the "Software"), | |
32 | * to deal in the Software without restriction, including without limitation | |
33 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
34 | * and/or sell copies of the Software, and to permit persons to whom the | |
35 | * Software is furnished to do so, subject to the following conditions: | |
36 | * | |
37 | * The above copyright notice and this permission notice shall be included in | |
38 | * all copies or substantial portions of the Software. | |
39 | * | |
40 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
41 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
42 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
43 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
44 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
45 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
46 | * OTHER DEALINGS IN THE SOFTWARE. | |
47 | * | |
48 | * Except as contained in this notice, the name of the copyright holder(s) | |
49 | * and author(s) shall not be used in advertising or otherwise to promote | |
50 | * the sale, use or other dealings in this Software without prior written | |
51 | * authorization from the copyright holder(s) and author(s). | |
52 | */ | |
53 | ||
54 | #ifdef HAVE_XORG_CONFIG_H | |
55 | #include <xorg-config.h> | |
56 | #endif | |
57 | ||
58 | #include <ctype.h> | |
59 | #include <stdio.h> | |
60 | #include <stdlib.h> | |
61 | #include <string.h> | |
62 | #include <sys/types.h> | |
63 | #include <dirent.h> | |
64 | #include <unistd.h> | |
65 | #include <stdarg.h> | |
66 | #include <X11/Xdefs.h> | |
67 | #include <X11/Xfuncproto.h> | |
68 | ||
69 | #if defined(_POSIX_SOURCE) | |
70 | #include <limits.h> | |
71 | #else | |
72 | #define _POSIX_SOURCE | |
73 | #include <limits.h> | |
74 | #undef _POSIX_SOURCE | |
75 | #endif /* _POSIX_SOURCE */ | |
76 | ||
77 | #if !defined(MAXHOSTNAMELEN) | |
78 | #define MAXHOSTNAMELEN 32 | |
79 | #endif /* !MAXHOSTNAMELEN */ | |
80 | ||
81 | /* For PATH_MAX */ | |
82 | #include "misc.h" | |
83 | ||
84 | #include "Configint.h" | |
85 | #include "xf86tokens.h" | |
86 | ||
87 | #define CONFIG_BUF_LEN 1024 | |
88 | #define CONFIG_MAX_FILES 64 | |
89 | ||
90 | static int StringToToken(const char *, xf86ConfigSymTabRec *); | |
91 | ||
92 | static struct { | |
93 | FILE *file; | |
94 | char *path; | |
95 | } configFiles[CONFIG_MAX_FILES]; | |
96 | static const char **builtinConfig = NULL; | |
97 | static int builtinIndex = 0; | |
98 | static int configPos = 0; /* current readers position */ | |
99 | static int configLineNo = 0; /* linenumber */ | |
100 | static char *configBuf, *configRBuf; /* buffer for lines */ | |
101 | static char *configSection = NULL; /* name of current section being parsed */ | |
102 | static int numFiles = 0; /* number of config files */ | |
103 | static int curFileIndex = 0; /* index of current config file */ | |
104 | static int pushToken = LOCK_TOKEN; | |
105 | static int eol_seen = 0; /* private state to handle comments */ | |
106 | LexRec val; | |
107 | ||
108 | /* | |
109 | * xf86getNextLine -- | |
110 | * | |
111 | * read from the configFiles FILE stream until we encounter a new | |
112 | * line; this is effectively just a big wrapper for fgets(3). | |
113 | * | |
114 | * xf86getToken() assumes that we will read up to the next | |
115 | * newline; we need to grow configBuf and configRBuf as needed to | |
116 | * support that. | |
117 | */ | |
118 | ||
119 | static char * | |
120 | xf86getNextLine(void) | |
121 | { | |
122 | static int configBufLen = CONFIG_BUF_LEN; | |
123 | char *tmpConfigBuf, *tmpConfigRBuf; | |
124 | int c, i, pos = 0, eolFound = 0; | |
125 | char *ret = NULL; | |
126 | ||
127 | /* | |
128 | * reallocate the string if it was grown last time (i.e., is no | |
129 | * longer CONFIG_BUF_LEN); we malloc the new strings first, so | |
130 | * that if either of the mallocs fail, we can fall back on the | |
131 | * existing buffer allocations | |
132 | */ | |
133 | ||
134 | if (configBufLen != CONFIG_BUF_LEN) { | |
135 | ||
136 | tmpConfigBuf = malloc(CONFIG_BUF_LEN); | |
137 | tmpConfigRBuf = malloc(CONFIG_BUF_LEN); | |
138 | ||
139 | if (!tmpConfigBuf || !tmpConfigRBuf) { | |
140 | ||
141 | /* | |
142 | * at least one of the mallocs failed; keep the old buffers | |
143 | * and free any partial allocations | |
144 | */ | |
145 | ||
146 | free(tmpConfigBuf); | |
147 | free(tmpConfigRBuf); | |
148 | ||
149 | } | |
150 | else { | |
151 | ||
152 | /* | |
153 | * malloc succeeded; free the old buffers and use the new | |
154 | * buffers | |
155 | */ | |
156 | ||
157 | configBufLen = CONFIG_BUF_LEN; | |
158 | ||
159 | free(configBuf); | |
160 | free(configRBuf); | |
161 | ||
162 | configBuf = tmpConfigBuf; | |
163 | configRBuf = tmpConfigRBuf; | |
164 | } | |
165 | } | |
166 | ||
167 | /* read in another block of chars */ | |
168 | ||
169 | do { | |
170 | ret = fgets(configBuf + pos, configBufLen - pos - 1, | |
171 | configFiles[curFileIndex].file); | |
172 | ||
173 | if (!ret) { | |
174 | /* | |
175 | * if the file doesn't end in a newline, add one | |
176 | * and trigger another read | |
177 | */ | |
178 | if (pos != 0) { | |
179 | strcpy(&configBuf[pos], "\n"); | |
180 | ret = configBuf; | |
181 | } | |
182 | else | |
183 | break; | |
184 | } | |
185 | ||
186 | /* search for EOL in the new block of chars */ | |
187 | ||
188 | for (i = pos; i < (configBufLen - 1); i++) { | |
189 | c = configBuf[i]; | |
190 | ||
191 | if (c == '\0') | |
192 | break; | |
193 | ||
194 | if ((c == '\n') || (c == '\r')) { | |
195 | eolFound = 1; | |
196 | break; | |
197 | } | |
198 | } | |
199 | ||
200 | /* | |
201 | * if we didn't find EOL, then grow the string and | |
202 | * read in more | |
203 | */ | |
204 | ||
205 | if (!eolFound) { | |
206 | ||
207 | tmpConfigBuf = realloc(configBuf, configBufLen + CONFIG_BUF_LEN); | |
208 | tmpConfigRBuf = realloc(configRBuf, configBufLen + CONFIG_BUF_LEN); | |
209 | ||
210 | if (!tmpConfigBuf || !tmpConfigRBuf) { | |
211 | ||
212 | /* | |
213 | * at least one of the reallocations failed; use the | |
214 | * new allocation that succeeded, but we have to | |
215 | * fallback to the previous configBufLen size and use | |
216 | * the string we have, even though we don't have an | |
217 | * EOL | |
218 | */ | |
219 | ||
220 | if (tmpConfigBuf) | |
221 | configBuf = tmpConfigBuf; | |
222 | if (tmpConfigRBuf) | |
223 | configRBuf = tmpConfigRBuf; | |
224 | ||
225 | break; | |
226 | ||
227 | } | |
228 | else { | |
229 | ||
230 | /* reallocation succeeded */ | |
231 | ||
232 | configBuf = tmpConfigBuf; | |
233 | configRBuf = tmpConfigRBuf; | |
234 | pos = i; | |
235 | configBufLen += CONFIG_BUF_LEN; | |
236 | } | |
237 | } | |
238 | ||
239 | } while (!eolFound); | |
240 | ||
241 | return ret; | |
242 | } | |
243 | ||
244 | /* | |
245 | * xf86getToken -- | |
246 | * Read next Token from the config file. Handle the global variable | |
247 | * pushToken. | |
248 | */ | |
249 | int | |
250 | xf86getToken(xf86ConfigSymTabRec * tab) | |
251 | { | |
252 | int c, i; | |
253 | ||
254 | /* | |
255 | * First check whether pushToken has a different value than LOCK_TOKEN. | |
256 | * In this case rBuf[] contains a valid STRING/TOKEN/NUMBER. But in the | |
257 | * oth * case the next token must be read from the input. | |
258 | */ | |
259 | if (pushToken == EOF_TOKEN) | |
260 | return EOF_TOKEN; | |
261 | else if (pushToken == LOCK_TOKEN) { | |
262 | /* | |
263 | * eol_seen is only set for the first token after a newline. | |
264 | */ | |
265 | eol_seen = 0; | |
266 | ||
267 | c = configBuf[configPos]; | |
268 | ||
269 | /* | |
270 | * Get start of next Token. EOF is handled, | |
271 | * whitespaces are skipped. | |
272 | */ | |
273 | ||
274 | again: | |
275 | if (!c) { | |
276 | char *ret; | |
277 | ||
278 | if (numFiles > 0) | |
279 | ret = xf86getNextLine(); | |
280 | else { | |
281 | if (builtinConfig[builtinIndex] == NULL) | |
282 | ret = NULL; | |
283 | else { | |
284 | strlcpy(configBuf, | |
285 | builtinConfig[builtinIndex], CONFIG_BUF_LEN); | |
286 | ret = configBuf; | |
287 | builtinIndex++; | |
288 | } | |
289 | } | |
290 | if (ret == NULL) { | |
291 | /* | |
292 | * if necessary, move to the next file and | |
293 | * read the first line | |
294 | */ | |
295 | if (curFileIndex + 1 < numFiles) { | |
296 | curFileIndex++; | |
297 | configLineNo = 0; | |
298 | goto again; | |
299 | } | |
300 | else | |
301 | return pushToken = EOF_TOKEN; | |
302 | } | |
303 | configLineNo++; | |
304 | configPos = 0; | |
305 | eol_seen = 1; | |
306 | } | |
307 | ||
308 | i = 0; | |
309 | for (;;) { | |
310 | c = configBuf[configPos++]; | |
311 | configRBuf[i++] = c; | |
312 | switch (c) { | |
313 | case ' ': | |
314 | case '\t': | |
315 | case '\r': | |
316 | continue; | |
317 | case '\n': | |
318 | i = 0; | |
319 | continue; | |
320 | } | |
321 | break; | |
322 | } | |
323 | if (c == '\0') | |
324 | goto again; | |
325 | ||
326 | if (c == '#') { | |
327 | do { | |
328 | configRBuf[i++] = (c = configBuf[configPos++]); | |
329 | } | |
330 | while ((c != '\n') && (c != '\r') && (c != '\0')); | |
331 | configRBuf[i] = '\0'; | |
332 | /* XXX no private copy. | |
333 | * Use xf86addComment when setting a comment. | |
334 | */ | |
335 | val.str = configRBuf; | |
336 | return COMMENT; | |
337 | } | |
338 | ||
339 | /* GJA -- handle '-' and ',' * Be careful: "-hsync" is a keyword. */ | |
340 | else if ((c == ',') && !isalpha(configBuf[configPos])) { | |
341 | return COMMA; | |
342 | } | |
343 | else if ((c == '-') && !isalpha(configBuf[configPos])) { | |
344 | return DASH; | |
345 | } | |
346 | ||
347 | /* | |
348 | * Numbers are returned immediately ... | |
349 | */ | |
350 | if (isdigit(c)) { | |
351 | int base; | |
352 | ||
353 | if (c == '0') | |
354 | if ((configBuf[configPos] == 'x') || | |
355 | (configBuf[configPos] == 'X')) { | |
356 | base = 16; | |
357 | val.numType = PARSE_HEX; | |
358 | } | |
359 | else { | |
360 | base = 8; | |
361 | val.numType = PARSE_OCTAL; | |
362 | } | |
363 | else { | |
364 | base = 10; | |
365 | val.numType = PARSE_DECIMAL; | |
366 | } | |
367 | ||
368 | configRBuf[0] = c; | |
369 | i = 1; | |
370 | while (isdigit(c = configBuf[configPos++]) || | |
371 | (c == '.') || (c == 'x') || (c == 'X') || | |
372 | ((base == 16) && (((c >= 'a') && (c <= 'f')) || | |
373 | ((c >= 'A') && (c <= 'F'))))) | |
374 | configRBuf[i++] = c; | |
375 | configPos--; /* GJA -- one too far */ | |
376 | configRBuf[i] = '\0'; | |
377 | val.num = strtoul(configRBuf, NULL, 0); | |
378 | val.realnum = atof(configRBuf); | |
379 | return NUMBER; | |
380 | } | |
381 | ||
382 | /* | |
383 | * All Strings START with a \" ... | |
384 | */ | |
385 | else if (c == '\"') { | |
386 | i = -1; | |
387 | do { | |
388 | configRBuf[++i] = (c = configBuf[configPos++]); | |
389 | } | |
390 | while ((c != '\"') && (c != '\n') && (c != '\r') && (c != '\0')); | |
391 | configRBuf[i] = '\0'; | |
392 | val.str = malloc(strlen(configRBuf) + 1); | |
393 | strcpy(val.str, configRBuf); /* private copy ! */ | |
394 | return STRING; | |
395 | } | |
396 | ||
397 | /* | |
398 | * ... and now we MUST have a valid token. The search is | |
399 | * handled later along with the pushed tokens. | |
400 | */ | |
401 | else { | |
402 | configRBuf[0] = c; | |
403 | i = 0; | |
404 | do { | |
405 | configRBuf[++i] = (c = configBuf[configPos++]); | |
406 | } | |
407 | while ((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r') && | |
408 | (c != '\0') && (c != '#')); | |
409 | --configPos; | |
410 | configRBuf[i] = '\0'; | |
411 | i = 0; | |
412 | } | |
413 | ||
414 | } | |
415 | else { | |
416 | ||
417 | /* | |
418 | * Here we deal with pushed tokens. Reinitialize pushToken again. If | |
419 | * the pushed token was NUMBER || STRING return them again ... | |
420 | */ | |
421 | int temp = pushToken; | |
422 | ||
423 | pushToken = LOCK_TOKEN; | |
424 | ||
425 | if (temp == COMMA || temp == DASH) | |
426 | return temp; | |
427 | if (temp == NUMBER || temp == STRING) | |
428 | return temp; | |
429 | } | |
430 | ||
431 | /* | |
432 | * Joop, at last we have to lookup the token ... | |
433 | */ | |
434 | if (tab) { | |
435 | i = 0; | |
436 | while (tab[i].token != -1) | |
437 | if (xf86nameCompare(configRBuf, tab[i].name) == 0) | |
438 | return tab[i].token; | |
439 | else | |
440 | i++; | |
441 | } | |
442 | ||
443 | return ERROR_TOKEN; /* Error catcher */ | |
444 | } | |
445 | ||
446 | int | |
447 | xf86getSubToken(char **comment) | |
448 | { | |
449 | int token; | |
450 | ||
451 | for (;;) { | |
452 | token = xf86getToken(NULL); | |
453 | if (token == COMMENT) { | |
454 | if (comment) | |
455 | *comment = xf86addComment(*comment, val.str); | |
456 | } | |
457 | else | |
458 | return token; | |
459 | } | |
460 | /*NOTREACHED*/} | |
461 | ||
462 | int | |
463 | xf86getSubTokenWithTab(char **comment, xf86ConfigSymTabRec * tab) | |
464 | { | |
465 | int token; | |
466 | ||
467 | for (;;) { | |
468 | token = xf86getToken(tab); | |
469 | if (token == COMMENT) { | |
470 | if (comment) | |
471 | *comment = xf86addComment(*comment, val.str); | |
472 | } | |
473 | else | |
474 | return token; | |
475 | } | |
476 | /*NOTREACHED*/} | |
477 | ||
478 | void | |
479 | xf86unGetToken(int token) | |
480 | { | |
481 | pushToken = token; | |
482 | } | |
483 | ||
484 | char * | |
485 | xf86tokenString(void) | |
486 | { | |
487 | return configRBuf; | |
488 | } | |
489 | ||
490 | int | |
491 | xf86pathIsAbsolute(const char *path) | |
492 | { | |
493 | if (path && path[0] == '/') | |
494 | return 1; | |
495 | return 0; | |
496 | } | |
497 | ||
498 | /* A path is "safe" if it is relative and if it contains no ".." elements. */ | |
499 | int | |
500 | xf86pathIsSafe(const char *path) | |
501 | { | |
502 | if (xf86pathIsAbsolute(path)) | |
503 | return 0; | |
504 | ||
505 | /* Compare with ".." */ | |
506 | if (!strcmp(path, "..")) | |
507 | return 0; | |
508 | ||
509 | /* Look for leading "../" */ | |
510 | if (!strncmp(path, "../", 3)) | |
511 | return 0; | |
512 | ||
513 | /* Look for trailing "/.." */ | |
514 | if ((strlen(path) > 3) && !strcmp(path + strlen(path) - 3, "/..")) | |
515 | return 0; | |
516 | ||
517 | /* Look for "/../" */ | |
518 | if (strstr(path, "/../")) | |
519 | return 0; | |
520 | ||
521 | return 1; | |
522 | } | |
523 | ||
524 | /* | |
525 | * This function substitutes the following escape sequences: | |
526 | * | |
527 | * %A cmdline argument as an absolute path (must be absolute to match) | |
528 | * %R cmdline argument as a relative path | |
529 | * %S cmdline argument as a "safe" path (relative, and no ".." elements) | |
530 | * %X default config file name ("xorg.conf") | |
531 | * %H hostname | |
532 | * %E config file environment ($XORGCONFIG) as an absolute path | |
533 | * %F config file environment ($XORGCONFIG) as a relative path | |
534 | * %G config file environment ($XORGCONFIG) as a safe path | |
535 | * %P projroot | |
536 | * %C sysconfdir | |
537 | * %D datadir | |
538 | * %% % | |
539 | */ | |
540 | ||
541 | #ifndef XCONFIGFILE | |
542 | #define XCONFIGFILE "xorg.conf" | |
543 | #endif | |
544 | #ifndef XCONFIGDIR | |
545 | #define XCONFIGDIR "xorg.conf.d" | |
546 | #endif | |
547 | #ifndef XCONFIGSUFFIX | |
548 | #define XCONFIGSUFFIX ".conf" | |
549 | #endif | |
550 | #ifndef PROJECTROOT | |
551 | #define PROJECTROOT "/usr/X11R6" | |
552 | #endif | |
553 | #ifndef SYSCONFDIR | |
554 | #define SYSCONFDIR PROJECTROOT "/etc" | |
555 | #endif | |
556 | #ifndef DATADIR | |
557 | #define DATADIR PROJECTROOT "/share" | |
558 | #endif | |
559 | #ifndef XCONFENV | |
560 | #define XCONFENV "XORGCONFIG" | |
561 | #endif | |
562 | ||
563 | #define BAIL_OUT do { \ | |
564 | free(result); \ | |
565 | return NULL; \ | |
566 | } while (0) | |
567 | ||
568 | #define CHECK_LENGTH do { \ | |
569 | if (l > PATH_MAX) { \ | |
570 | BAIL_OUT; \ | |
571 | } \ | |
572 | } while (0) | |
573 | ||
574 | #define APPEND_STR(s) do { \ | |
575 | if (strlen(s) + l > PATH_MAX) { \ | |
576 | BAIL_OUT; \ | |
577 | } else { \ | |
578 | strcpy(result + l, s); \ | |
579 | l += strlen(s); \ | |
580 | } \ | |
581 | } while (0) | |
582 | ||
583 | static char * | |
584 | DoSubstitution(const char *template, const char *cmdline, const char *projroot, | |
585 | int *cmdlineUsed, int *envUsed, const char *XConfigFile) | |
586 | { | |
587 | char *result; | |
588 | int i, l; | |
589 | static const char *env = NULL; | |
590 | static char *hostname = NULL; | |
591 | ||
592 | if (!template) | |
593 | return NULL; | |
594 | ||
595 | if (cmdlineUsed) | |
596 | *cmdlineUsed = 0; | |
597 | if (envUsed) | |
598 | *envUsed = 0; | |
599 | ||
600 | result = malloc(PATH_MAX + 1); | |
601 | l = 0; | |
602 | for (i = 0; template[i]; i++) { | |
603 | if (template[i] != '%') { | |
604 | result[l++] = template[i]; | |
605 | CHECK_LENGTH; | |
606 | } | |
607 | else { | |
608 | switch (template[++i]) { | |
609 | case 'A': | |
610 | if (cmdline && xf86pathIsAbsolute(cmdline)) { | |
611 | APPEND_STR(cmdline); | |
612 | if (cmdlineUsed) | |
613 | *cmdlineUsed = 1; | |
614 | } | |
615 | else | |
616 | BAIL_OUT; | |
617 | break; | |
618 | case 'R': | |
619 | if (cmdline && !xf86pathIsAbsolute(cmdline)) { | |
620 | APPEND_STR(cmdline); | |
621 | if (cmdlineUsed) | |
622 | *cmdlineUsed = 1; | |
623 | } | |
624 | else | |
625 | BAIL_OUT; | |
626 | break; | |
627 | case 'S': | |
628 | if (cmdline && xf86pathIsSafe(cmdline)) { | |
629 | APPEND_STR(cmdline); | |
630 | if (cmdlineUsed) | |
631 | *cmdlineUsed = 1; | |
632 | } | |
633 | else | |
634 | BAIL_OUT; | |
635 | break; | |
636 | case 'X': | |
637 | APPEND_STR(XConfigFile); | |
638 | break; | |
639 | case 'H': | |
640 | if (!hostname) { | |
641 | if ((hostname = malloc(MAXHOSTNAMELEN + 1))) { | |
642 | if (gethostname(hostname, MAXHOSTNAMELEN) == 0) { | |
643 | hostname[MAXHOSTNAMELEN] = '\0'; | |
644 | } | |
645 | else { | |
646 | free(hostname); | |
647 | hostname = NULL; | |
648 | } | |
649 | } | |
650 | } | |
651 | if (hostname) | |
652 | APPEND_STR(hostname); | |
653 | break; | |
654 | case 'E': | |
655 | if (!env) | |
656 | env = getenv(XCONFENV); | |
657 | if (env && xf86pathIsAbsolute(env)) { | |
658 | APPEND_STR(env); | |
659 | if (envUsed) | |
660 | *envUsed = 1; | |
661 | } | |
662 | else | |
663 | BAIL_OUT; | |
664 | break; | |
665 | case 'F': | |
666 | if (!env) | |
667 | env = getenv(XCONFENV); | |
668 | if (env && !xf86pathIsAbsolute(env)) { | |
669 | APPEND_STR(env); | |
670 | if (envUsed) | |
671 | *envUsed = 1; | |
672 | } | |
673 | else | |
674 | BAIL_OUT; | |
675 | break; | |
676 | case 'G': | |
677 | if (!env) | |
678 | env = getenv(XCONFENV); | |
679 | if (env && xf86pathIsSafe(env)) { | |
680 | APPEND_STR(env); | |
681 | if (envUsed) | |
682 | *envUsed = 1; | |
683 | } | |
684 | else | |
685 | BAIL_OUT; | |
686 | break; | |
687 | case 'P': | |
688 | if (projroot && xf86pathIsAbsolute(projroot)) | |
689 | APPEND_STR(projroot); | |
690 | else | |
691 | BAIL_OUT; | |
692 | break; | |
693 | case 'C': | |
694 | APPEND_STR(SYSCONFDIR); | |
695 | break; | |
696 | case 'D': | |
697 | APPEND_STR(DATADIR); | |
698 | break; | |
699 | case '%': | |
700 | result[l++] = '%'; | |
701 | CHECK_LENGTH; | |
702 | break; | |
703 | default: | |
704 | fprintf(stderr, "invalid escape %%%c found in path template\n", | |
705 | template[i]); | |
706 | BAIL_OUT; | |
707 | break; | |
708 | } | |
709 | } | |
710 | } | |
711 | #ifdef DEBUG | |
712 | fprintf(stderr, "Converted `%s' to `%s'\n", template, result); | |
713 | #endif | |
714 | return result; | |
715 | } | |
716 | ||
717 | /* | |
718 | * Given some searching parameters, locate and open the xorg config file. | |
719 | */ | |
720 | static char * | |
721 | OpenConfigFile(const char *path, const char *cmdline, const char *projroot, | |
722 | const char *confname) | |
723 | { | |
724 | char *filepath = NULL; | |
725 | char *pathcopy; | |
726 | const char *template; | |
727 | int cmdlineUsed = 0; | |
728 | FILE *file = NULL; | |
729 | ||
730 | pathcopy = strdup(path); | |
731 | for (template = strtok(pathcopy, ","); template && !file; | |
732 | template = strtok(NULL, ",")) { | |
733 | filepath = DoSubstitution(template, cmdline, projroot, | |
734 | &cmdlineUsed, NULL, confname); | |
735 | if (!filepath) | |
736 | continue; | |
737 | if (cmdline && !cmdlineUsed) { | |
738 | free(filepath); | |
739 | filepath = NULL; | |
740 | continue; | |
741 | } | |
742 | file = fopen(filepath, "r"); | |
743 | if (!file) { | |
744 | free(filepath); | |
745 | filepath = NULL; | |
746 | } | |
747 | } | |
748 | ||
749 | free(pathcopy); | |
750 | if (file) { | |
751 | configFiles[numFiles].file = file; | |
752 | configFiles[numFiles].path = strdup(filepath); | |
753 | numFiles++; | |
754 | } | |
755 | return filepath; | |
756 | } | |
757 | ||
758 | /* | |
759 | * Match non-hidden files in the xorg config directory with a .conf | |
760 | * suffix. This filter is passed to scandir(3). | |
761 | */ | |
762 | static int | |
763 | ConfigFilter(const struct dirent *de) | |
764 | { | |
765 | const char *name = de->d_name; | |
766 | size_t len; | |
767 | size_t suflen = strlen(XCONFIGSUFFIX); | |
768 | ||
769 | if (!name || name[0] == '.') | |
770 | return 0; | |
771 | len = strlen(name); | |
772 | if (len <= suflen) | |
773 | return 0; | |
774 | if (strcmp(&name[len - suflen], XCONFIGSUFFIX) != 0) | |
775 | return 0; | |
776 | return 1; | |
777 | } | |
778 | ||
779 | static Bool | |
780 | AddConfigDirFiles(const char *dirpath, struct dirent **list, int num) | |
781 | { | |
782 | int i; | |
783 | Bool openedFile = FALSE; | |
784 | Bool warnOnce = FALSE; | |
785 | ||
786 | for (i = 0; i < num; i++) { | |
787 | char *path; | |
788 | FILE *file; | |
789 | ||
790 | if (numFiles >= CONFIG_MAX_FILES) { | |
791 | if (!warnOnce) { | |
792 | ErrorF("Maximum number of configuration " "files opened\n"); | |
793 | warnOnce = TRUE; | |
794 | } | |
795 | continue; | |
796 | } | |
797 | ||
798 | path = malloc(PATH_MAX + 1); | |
799 | snprintf(path, PATH_MAX + 1, "%s/%s", dirpath, list[i]->d_name); | |
800 | file = fopen(path, "r"); | |
801 | if (!file) { | |
802 | free(path); | |
803 | continue; | |
804 | } | |
805 | openedFile = TRUE; | |
806 | ||
807 | configFiles[numFiles].file = file; | |
808 | configFiles[numFiles].path = path; | |
809 | numFiles++; | |
810 | } | |
811 | ||
812 | return openedFile; | |
813 | } | |
814 | ||
815 | /* | |
816 | * Given some searching parameters, locate and open the xorg config | |
817 | * directory. The directory does not need to contain config files. | |
818 | */ | |
819 | static char * | |
820 | OpenConfigDir(const char *path, const char *cmdline, const char *projroot, | |
821 | const char *confname) | |
822 | { | |
823 | char *dirpath, *pathcopy; | |
824 | const char *template; | |
825 | Bool found = FALSE; | |
826 | int cmdlineUsed = 0; | |
827 | ||
828 | pathcopy = strdup(path); | |
829 | for (template = strtok(pathcopy, ","); template && !found; | |
830 | template = strtok(NULL, ",")) { | |
831 | struct dirent **list = NULL; | |
832 | int num; | |
833 | ||
834 | dirpath = DoSubstitution(template, cmdline, projroot, | |
835 | &cmdlineUsed, NULL, confname); | |
836 | if (!dirpath) | |
837 | continue; | |
838 | if (cmdline && !cmdlineUsed) { | |
839 | free(dirpath); | |
840 | dirpath = NULL; | |
841 | continue; | |
842 | } | |
843 | ||
844 | /* match files named *.conf */ | |
845 | num = scandir(dirpath, &list, ConfigFilter, alphasort); | |
846 | if (num < 0) { | |
847 | list = NULL; | |
848 | num = 0; | |
849 | } | |
850 | found = AddConfigDirFiles(dirpath, list, num); | |
851 | if (!found) { | |
852 | free(dirpath); | |
853 | dirpath = NULL; | |
854 | } | |
855 | while (num--) | |
856 | free(list[num]); | |
857 | free(list); | |
858 | } | |
859 | ||
860 | free(pathcopy); | |
861 | return dirpath; | |
862 | } | |
863 | ||
864 | /* | |
865 | * xf86initConfigFiles -- Setup global variables and buffers. | |
866 | */ | |
867 | void | |
868 | xf86initConfigFiles(void) | |
869 | { | |
870 | curFileIndex = 0; | |
871 | configPos = 0; | |
872 | configLineNo = 0; | |
873 | pushToken = LOCK_TOKEN; | |
874 | ||
875 | configBuf = malloc(CONFIG_BUF_LEN); | |
876 | configRBuf = malloc(CONFIG_BUF_LEN); | |
877 | configBuf[0] = '\0'; /* sanity ... */ | |
878 | } | |
879 | ||
880 | /* | |
881 | * xf86openConfigFile -- | |
882 | * | |
883 | * This function take a config file search path (optional), a command-line | |
884 | * specified file name (optional) and the ProjectRoot path (optional) and | |
885 | * locates and opens a config file based on that information. If a | |
886 | * command-line file name is specified, then this function fails if none | |
887 | * of the located files. | |
888 | * | |
889 | * The return value is a pointer to the actual name of the file that was | |
890 | * opened. When no file is found, the return value is NULL. The caller should | |
891 | * free() the returned value. | |
892 | * | |
893 | * The escape sequences allowed in the search path are defined above. | |
894 | * | |
895 | */ | |
896 | ||
897 | #ifndef DEFAULT_CONF_PATH | |
898 | #define DEFAULT_CONF_PATH "/etc/X11/%S," \ | |
899 | "%P/etc/X11/%S," \ | |
900 | "/etc/X11/%G," \ | |
901 | "%P/etc/X11/%G," \ | |
902 | "/etc/X11/%X-%M," \ | |
903 | "/etc/X11/%X," \ | |
904 | "/etc/%X," \ | |
905 | "%P/etc/X11/%X.%H," \ | |
906 | "%P/etc/X11/%X-%M," \ | |
907 | "%P/etc/X11/%X," \ | |
908 | "%P/lib/X11/%X.%H," \ | |
909 | "%P/lib/X11/%X-%M," \ | |
910 | "%P/lib/X11/%X" | |
911 | #endif | |
912 | ||
913 | char * | |
914 | xf86openConfigFile(const char *path, const char *cmdline, const char *projroot) | |
915 | { | |
916 | if (!path || !path[0]) | |
917 | path = DEFAULT_CONF_PATH; | |
918 | if (!projroot || !projroot[0]) | |
919 | projroot = PROJECTROOT; | |
920 | ||
921 | /* Search for a config file */ | |
922 | return OpenConfigFile(path, cmdline, projroot, XCONFIGFILE); | |
923 | } | |
924 | ||
925 | /* | |
926 | * xf86openConfigDirFiles -- | |
927 | * | |
928 | * This function take a config directory search path (optional), a | |
929 | * command-line specified directory name (optional) and the ProjectRoot path | |
930 | * (optional) and locates and opens a config directory based on that | |
931 | * information. If a command-line name is specified, then this function | |
932 | * fails if it is not found. | |
933 | * | |
934 | * The return value is a pointer to the actual name of the direcoty that was | |
935 | * opened. When no directory is found, the return value is NULL. The caller | |
936 | * should free() the returned value. | |
937 | * | |
938 | * The escape sequences allowed in the search path are defined above. | |
939 | * | |
940 | */ | |
941 | char * | |
942 | xf86openConfigDirFiles(const char *path, const char *cmdline, | |
943 | const char *projroot) | |
944 | { | |
945 | if (!path || !path[0]) | |
946 | path = DEFAULT_CONF_PATH; | |
947 | if (!projroot || !projroot[0]) | |
948 | projroot = PROJECTROOT; | |
949 | ||
950 | /* Search for the multiconf directory */ | |
951 | return OpenConfigDir(path, cmdline, projroot, XCONFIGDIR); | |
952 | } | |
953 | ||
954 | void | |
955 | xf86closeConfigFile(void) | |
956 | { | |
957 | int i; | |
958 | ||
959 | free(configRBuf); | |
960 | configRBuf = NULL; | |
961 | free(configBuf); | |
962 | configBuf = NULL; | |
963 | ||
964 | if (numFiles == 0) { | |
965 | builtinConfig = NULL; | |
966 | builtinIndex = 0; | |
967 | } | |
968 | for (i = 0; i < numFiles; i++) { | |
969 | fclose(configFiles[i].file); | |
970 | configFiles[i].file = NULL; | |
971 | free(configFiles[i].path); | |
972 | configFiles[i].path = NULL; | |
973 | } | |
974 | numFiles = 0; | |
975 | } | |
976 | ||
977 | void | |
978 | xf86setBuiltinConfig(const char *config[]) | |
979 | { | |
980 | builtinConfig = config; | |
981 | } | |
982 | ||
983 | void | |
984 | xf86parseError(const char *format, ...) | |
985 | { | |
986 | va_list ap; | |
987 | const char *filename = numFiles ? configFiles[curFileIndex].path | |
988 | : "<builtin configuration>"; | |
989 | ||
990 | ErrorF("Parse error on line %d of section %s in file %s\n\t", | |
991 | configLineNo, configSection, filename); | |
992 | va_start(ap, format); | |
993 | VErrorF(format, ap); | |
994 | va_end(ap); | |
995 | ||
996 | ErrorF("\n"); | |
997 | } | |
998 | ||
999 | void | |
1000 | xf86validationError(const char *format, ...) | |
1001 | { | |
1002 | va_list ap; | |
1003 | const char *filename = numFiles ? configFiles[curFileIndex].path | |
1004 | : "<builtin configuration>"; | |
1005 | ||
1006 | ErrorF("Data incomplete in file %s\n\t", filename); | |
1007 | va_start(ap, format); | |
1008 | VErrorF(format, ap); | |
1009 | va_end(ap); | |
1010 | ||
1011 | ErrorF("\n"); | |
1012 | } | |
1013 | ||
1014 | void | |
1015 | xf86setSection(const char *section) | |
1016 | { | |
1017 | free(configSection); | |
1018 | configSection = strdup(section); | |
1019 | } | |
1020 | ||
1021 | /* | |
1022 | * xf86getToken -- | |
1023 | * Lookup a string if it is actually a token in disguise. | |
1024 | */ | |
1025 | int | |
1026 | xf86getStringToken(xf86ConfigSymTabRec * tab) | |
1027 | { | |
1028 | return StringToToken(val.str, tab); | |
1029 | } | |
1030 | ||
1031 | static int | |
1032 | StringToToken(const char *str, xf86ConfigSymTabRec * tab) | |
1033 | { | |
1034 | int i; | |
1035 | ||
1036 | for (i = 0; tab[i].token != -1; i++) { | |
1037 | if (!xf86nameCompare(tab[i].name, str)) | |
1038 | return tab[i].token; | |
1039 | } | |
1040 | return ERROR_TOKEN; | |
1041 | } | |
1042 | ||
1043 | /* | |
1044 | * Compare two names. The characters '_', ' ', and '\t' are ignored | |
1045 | * in the comparison. | |
1046 | */ | |
1047 | int | |
1048 | xf86nameCompare(const char *s1, const char *s2) | |
1049 | { | |
1050 | char c1, c2; | |
1051 | ||
1052 | if (!s1 || *s1 == 0) { | |
1053 | if (!s2 || *s2 == 0) | |
1054 | return 0; | |
1055 | else | |
1056 | return 1; | |
1057 | } | |
1058 | ||
1059 | while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') | |
1060 | s1++; | |
1061 | while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') | |
1062 | s2++; | |
1063 | c1 = (isupper(*s1) ? tolower(*s1) : *s1); | |
1064 | c2 = (isupper(*s2) ? tolower(*s2) : *s2); | |
1065 | while (c1 == c2) { | |
1066 | if (c1 == '\0') | |
1067 | return 0; | |
1068 | s1++; | |
1069 | s2++; | |
1070 | while (*s1 == '_' || *s1 == ' ' || *s1 == '\t') | |
1071 | s1++; | |
1072 | while (*s2 == '_' || *s2 == ' ' || *s2 == '\t') | |
1073 | s2++; | |
1074 | c1 = (isupper(*s1) ? tolower(*s1) : *s1); | |
1075 | c2 = (isupper(*s2) ? tolower(*s2) : *s2); | |
1076 | } | |
1077 | return c1 - c2; | |
1078 | } | |
1079 | ||
1080 | char * | |
1081 | xf86addComment(char *cur, char *add) | |
1082 | { | |
1083 | char *str; | |
1084 | int len, curlen, iscomment, hasnewline = 0, insnewline, endnewline; | |
1085 | ||
1086 | if (add == NULL || add[0] == '\0') | |
1087 | return cur; | |
1088 | ||
1089 | if (cur) { | |
1090 | curlen = strlen(cur); | |
1091 | if (curlen) | |
1092 | hasnewline = cur[curlen - 1] == '\n'; | |
1093 | eol_seen = 0; | |
1094 | } | |
1095 | else | |
1096 | curlen = 0; | |
1097 | ||
1098 | str = add; | |
1099 | iscomment = 0; | |
1100 | while (*str) { | |
1101 | if (*str != ' ' && *str != '\t') | |
1102 | break; | |
1103 | ++str; | |
1104 | } | |
1105 | iscomment = (*str == '#'); | |
1106 | ||
1107 | len = strlen(add); | |
1108 | endnewline = add[len - 1] == '\n'; | |
1109 | ||
1110 | insnewline = eol_seen || (curlen && !hasnewline); | |
1111 | if (insnewline) | |
1112 | len++; | |
1113 | if (!iscomment) | |
1114 | len++; | |
1115 | if (!endnewline) | |
1116 | len++; | |
1117 | ||
1118 | /* Allocate + 1 char for '\0' terminator. */ | |
1119 | str = realloc(cur, curlen + len + 1); | |
1120 | if (!str) | |
1121 | return cur; | |
1122 | ||
1123 | cur = str; | |
1124 | ||
1125 | if (insnewline) | |
1126 | cur[curlen++] = '\n'; | |
1127 | if (!iscomment) | |
1128 | cur[curlen++] = '#'; | |
1129 | strcpy(cur + curlen, add); | |
1130 | if (!endnewline) | |
1131 | strcat(cur, "\n"); | |
1132 | ||
1133 | return cur; | |
1134 | } | |
1135 | ||
1136 | Bool | |
1137 | xf86getBoolValue(Bool *val, const char *str) | |
1138 | { | |
1139 | if (!val || !str) | |
1140 | return FALSE; | |
1141 | if (*str == '\0') { | |
1142 | *val = TRUE; | |
1143 | } | |
1144 | else { | |
1145 | if (xf86nameCompare(str, "1") == 0) | |
1146 | *val = TRUE; | |
1147 | else if (xf86nameCompare(str, "on") == 0) | |
1148 | *val = TRUE; | |
1149 | else if (xf86nameCompare(str, "true") == 0) | |
1150 | *val = TRUE; | |
1151 | else if (xf86nameCompare(str, "yes") == 0) | |
1152 | *val = TRUE; | |
1153 | else if (xf86nameCompare(str, "0") == 0) | |
1154 | *val = FALSE; | |
1155 | else if (xf86nameCompare(str, "off") == 0) | |
1156 | *val = FALSE; | |
1157 | else if (xf86nameCompare(str, "false") == 0) | |
1158 | *val = FALSE; | |
1159 | else if (xf86nameCompare(str, "no") == 0) | |
1160 | *val = FALSE; | |
1161 | else | |
1162 | return FALSE; | |
1163 | } | |
1164 | return TRUE; | |
1165 | } |