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