scan.c revision 35c4bbdf
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
90static int StringToToken(const char *, xf86ConfigSymTabRec *);
91
92static struct {
93    FILE *file;
94    char *path;
95} configFiles[CONFIG_MAX_FILES];
96static const char **builtinConfig = NULL;
97static int builtinIndex = 0;
98static int configPos = 0;       /* current readers position */
99static int configLineNo = 0;    /* linenumber */
100static char *configBuf, *configRBuf;    /* buffer for lines */
101static char *configSection = NULL;      /* name of current section being parsed */
102static int numFiles = 0;        /* number of config files */
103static int curFileIndex = 0;    /* index of current config file */
104static int pushToken = LOCK_TOKEN;
105static int eol_seen = 0;        /* private state to handle comments */
106LexRec xf86_lex_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
119static char *
120xf86getNextLine(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 */
249int
250xf86getToken(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            xf86_lex_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                    xf86_lex_val.numType = PARSE_HEX;
358                }
359                else {
360                    base = 8;
361                    xf86_lex_val.numType = PARSE_OCTAL;
362                }
363            else {
364                base = 10;
365                xf86_lex_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            xf86_lex_val.num = strtoul(configRBuf, NULL, 0);
378            xf86_lex_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            xf86_lex_val.str = malloc(strlen(configRBuf) + 1);
393            strcpy(xf86_lex_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
446int
447xf86getSubToken(char **comment)
448{
449    int token;
450
451    for (;;) {
452        token = xf86getToken(NULL);
453        if (token == COMMENT) {
454            if (comment)
455                *comment = xf86addComment(*comment, xf86_lex_val.str);
456        }
457        else
458            return token;
459    }
460 /*NOTREACHED*/}
461
462int
463xf86getSubTokenWithTab(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, xf86_lex_val.str);
472        }
473        else
474            return token;
475    }
476 /*NOTREACHED*/}
477
478void
479xf86unGetToken(int token)
480{
481    pushToken = token;
482}
483
484char *
485xf86tokenString(void)
486{
487    return configRBuf;
488}
489
490int
491xf86pathIsAbsolute(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. */
499int
500xf86pathIsSafe(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
583static char *
584DoSubstitution(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 */
720static char *
721OpenConfigFile(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 */
762static int
763ConfigFilter(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
779static Bool
780AddConfigDirFiles(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 */
819static char *
820OpenConfigDir(const char *path, const char *cmdline, const char *projroot,
821              const char *confname)
822{
823    char *dirpath = NULL, *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 */
867void
868xf86initConfigFiles(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
913char *
914xf86openConfigFile(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 */
941char *
942xf86openConfigDirFiles(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
954void
955xf86closeConfigFile(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
977void
978xf86setBuiltinConfig(const char *config[])
979{
980    builtinConfig = config;
981}
982
983void
984xf86parseError(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
999void
1000xf86validationError(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
1014void
1015xf86setSection(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 */
1025int
1026xf86getStringToken(xf86ConfigSymTabRec * tab)
1027{
1028    return StringToToken(xf86_lex_val.str, tab);
1029}
1030
1031static int
1032StringToToken(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 */
1047int
1048xf86nameCompare(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
1080char *
1081xf86addComment(char *cur, const char *add)
1082{
1083    char *str;
1084    const char *cstr;
1085    int len, curlen, iscomment, hasnewline = 0, insnewline, endnewline;
1086
1087    if (add == NULL || add[0] == '\0')
1088        return cur;
1089
1090    if (cur) {
1091        curlen = strlen(cur);
1092        if (curlen)
1093            hasnewline = cur[curlen - 1] == '\n';
1094        eol_seen = 0;
1095    }
1096    else
1097        curlen = 0;
1098
1099    cstr = add;
1100    iscomment = 0;
1101    while (*cstr) {
1102        if (*cstr != ' ' && *cstr != '\t')
1103            break;
1104        ++cstr;
1105    }
1106    iscomment = (*cstr == '#');
1107
1108    len = strlen(add);
1109    endnewline = add[len - 1] == '\n';
1110
1111    insnewline = eol_seen || (curlen && !hasnewline);
1112    if (insnewline)
1113        len++;
1114    if (!iscomment)
1115        len++;
1116    if (!endnewline)
1117        len++;
1118
1119    /* Allocate + 1 char for '\0' terminator. */
1120    str = realloc(cur, curlen + len + 1);
1121    if (!str)
1122        return cur;
1123
1124    cur = str;
1125
1126    if (insnewline)
1127        cur[curlen++] = '\n';
1128    if (!iscomment)
1129        cur[curlen++] = '#';
1130    strcpy(cur + curlen, add);
1131    if (!endnewline)
1132        strcat(cur, "\n");
1133
1134    return cur;
1135}
1136
1137Bool
1138xf86getBoolValue(Bool *val, const char *str)
1139{
1140    if (!val || !str)
1141        return FALSE;
1142    if (*str == '\0') {
1143        *val = TRUE;
1144    }
1145    else {
1146        if (xf86nameCompare(str, "1") == 0)
1147            *val = TRUE;
1148        else if (xf86nameCompare(str, "on") == 0)
1149            *val = TRUE;
1150        else if (xf86nameCompare(str, "true") == 0)
1151            *val = TRUE;
1152        else if (xf86nameCompare(str, "yes") == 0)
1153            *val = TRUE;
1154        else if (xf86nameCompare(str, "0") == 0)
1155            *val = FALSE;
1156        else if (xf86nameCompare(str, "off") == 0)
1157            *val = FALSE;
1158        else if (xf86nameCompare(str, "false") == 0)
1159            *val = FALSE;
1160        else if (xf86nameCompare(str, "no") == 0)
1161            *val = FALSE;
1162        else
1163            return FALSE;
1164    }
1165    return TRUE;
1166}
1167