1142 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			1142 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * Copyright (c) 1997  Metro Link Incorporated
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 | |
|  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 | |
|  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 | |
|  * SOFTWARE.
 | |
|  *
 | |
|  * Except as contained in this notice, the name of the Metro Link shall not be
 | |
|  * used in advertising or otherwise to promote the sale, use or other dealings
 | |
|  * in this Software without prior written authorization from Metro Link.
 | |
|  *
 | |
|  */
 | |
| /*
 | |
|  * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a
 | |
|  * copy of this software and associated documentation files (the "Software"),
 | |
|  * to deal in the Software without restriction, including without limitation
 | |
|  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | |
|  * and/or sell copies of the Software, and to permit persons to whom the
 | |
|  * Software is furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | |
|  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
 | |
|  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 | |
|  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 | |
|  * OTHER DEALINGS IN THE SOFTWARE.
 | |
|  *
 | |
|  * Except as contained in this notice, the name of the copyright holder(s)
 | |
|  * and author(s) shall not be used in advertising or otherwise to promote
 | |
|  * the sale, use or other dealings in this Software without prior written
 | |
|  * authorization from the copyright holder(s) and author(s).
 | |
|  */
 | |
| 
 | |
| #ifdef HAVE_XORG_CONFIG_H
 | |
| #include <xorg-config.h>
 | |
| #endif
 | |
| 
 | |
| #include <ctype.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/types.h>
 | |
| #include <dirent.h>
 | |
| #include <unistd.h>
 | |
| #include <stdarg.h>
 | |
| #include <X11/Xdefs.h>
 | |
| #include <X11/Xfuncproto.h>
 | |
| 
 | |
| #if defined(_POSIX_SOURCE)
 | |
| #include <limits.h>
 | |
| #else
 | |
| #define _POSIX_SOURCE
 | |
| #include <limits.h>
 | |
| #undef _POSIX_SOURCE
 | |
| #endif                          /* _POSIX_SOURCE */
 | |
| 
 | |
| #if !defined(MAXHOSTNAMELEN)
 | |
| #define MAXHOSTNAMELEN 32
 | |
| #endif                          /* !MAXHOSTNAMELEN */
 | |
| 
 | |
| /* For PATH_MAX */
 | |
| #include "misc.h"
 | |
| 
 | |
| #include "Configint.h"
 | |
| #include "xf86tokens.h"
 | |
| 
 | |
| #define CONFIG_BUF_LEN     1024
 | |
| #define CONFIG_MAX_FILES   64
 | |
| 
 | |
| static struct {
 | |
|     FILE *file;
 | |
|     char *path;
 | |
| } configFiles[CONFIG_MAX_FILES];
 | |
| static const char **builtinConfig = NULL;
 | |
| static int builtinIndex = 0;
 | |
| static int configPos = 0;       /* current readers position */
 | |
| static int configLineNo = 0;    /* linenumber */
 | |
| static char *configBuf, *configRBuf;    /* buffer for lines */
 | |
| static char *configSection = NULL;      /* name of current section being parsed */
 | |
| static int numFiles = 0;        /* number of config files */
 | |
| static int curFileIndex = 0;    /* index of current config file */
 | |
| static int pushToken = LOCK_TOKEN;
 | |
| static int eol_seen = 0;        /* private state to handle comments */
 | |
| LexRec xf86_lex_val;
 | |
| 
 | |
| /*
 | |
|  * xf86getNextLine --
 | |
|  *
 | |
|  *  read from the configFiles FILE stream until we encounter a new
 | |
|  *  line; this is effectively just a big wrapper for fgets(3).
 | |
|  *
 | |
|  *  xf86getToken() assumes that we will read up to the next
 | |
|  *  newline; we need to grow configBuf and configRBuf as needed to
 | |
|  *  support that.
 | |
|  */
 | |
| 
 | |
| static char *
 | |
| xf86getNextLine(void)
 | |
| {
 | |
|     static int configBufLen = CONFIG_BUF_LEN;
 | |
|     char *tmpConfigBuf, *tmpConfigRBuf;
 | |
|     int c, i, pos = 0, eolFound = 0;
 | |
|     char *ret = NULL;
 | |
| 
 | |
|     /*
 | |
|      * reallocate the string if it was grown last time (i.e., is no
 | |
|      * longer CONFIG_BUF_LEN); we malloc the new strings first, so
 | |
|      * that if either of the mallocs fail, we can fall back on the
 | |
|      * existing buffer allocations
 | |
|      */
 | |
| 
 | |
|     if (configBufLen != CONFIG_BUF_LEN) {
 | |
| 
 | |
|         tmpConfigBuf = malloc(CONFIG_BUF_LEN);
 | |
|         tmpConfigRBuf = malloc(CONFIG_BUF_LEN);
 | |
| 
 | |
|         if (!tmpConfigBuf || !tmpConfigRBuf) {
 | |
| 
 | |
|             /*
 | |
|              * at least one of the mallocs failed; keep the old buffers
 | |
|              * and free any partial allocations
 | |
|              */
 | |
| 
 | |
|             free(tmpConfigBuf);
 | |
|             free(tmpConfigRBuf);
 | |
| 
 | |
|         }
 | |
|         else {
 | |
| 
 | |
|             /*
 | |
|              * malloc succeeded; free the old buffers and use the new
 | |
|              * buffers
 | |
|              */
 | |
| 
 | |
|             configBufLen = CONFIG_BUF_LEN;
 | |
| 
 | |
|             free(configBuf);
 | |
|             free(configRBuf);
 | |
| 
 | |
|             configBuf = tmpConfigBuf;
 | |
|             configRBuf = tmpConfigRBuf;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* read in another block of chars */
 | |
| 
 | |
|     do {
 | |
|         ret = fgets(configBuf + pos, configBufLen - pos - 1,
 | |
|                     configFiles[curFileIndex].file);
 | |
| 
 | |
|         if (!ret) {
 | |
|             /*
 | |
|              * if the file doesn't end in a newline, add one
 | |
|              * and trigger another read
 | |
|              */
 | |
|             if (pos != 0) {
 | |
|                 strcpy(&configBuf[pos], "\n");
 | |
|                 ret = configBuf;
 | |
|             }
 | |
|             else
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         /* search for EOL in the new block of chars */
 | |
| 
 | |
|         for (i = pos; i < (configBufLen - 1); i++) {
 | |
|             c = configBuf[i];
 | |
| 
 | |
|             if (c == '\0')
 | |
|                 break;
 | |
| 
 | |
|             if ((c == '\n') || (c == '\r')) {
 | |
|                 eolFound = 1;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          * if we didn't find EOL, then grow the string and
 | |
|          * read in more
 | |
|          */
 | |
| 
 | |
|         if (!eolFound) {
 | |
| 
 | |
|             tmpConfigBuf = realloc(configBuf, configBufLen + CONFIG_BUF_LEN);
 | |
|             tmpConfigRBuf = realloc(configRBuf, configBufLen + CONFIG_BUF_LEN);
 | |
| 
 | |
|             if (!tmpConfigBuf || !tmpConfigRBuf) {
 | |
| 
 | |
|                 /*
 | |
|                  * at least one of the reallocations failed; use the
 | |
|                  * new allocation that succeeded, but we have to
 | |
|                  * fallback to the previous configBufLen size and use
 | |
|                  * the string we have, even though we don't have an
 | |
|                  * EOL
 | |
|                  */
 | |
| 
 | |
|                 if (tmpConfigBuf)
 | |
|                     configBuf = tmpConfigBuf;
 | |
|                 if (tmpConfigRBuf)
 | |
|                     configRBuf = tmpConfigRBuf;
 | |
| 
 | |
|                 break;
 | |
| 
 | |
|             }
 | |
|             else {
 | |
| 
 | |
|                 /* reallocation succeeded */
 | |
| 
 | |
|                 configBuf = tmpConfigBuf;
 | |
|                 configRBuf = tmpConfigRBuf;
 | |
|                 pos = i;
 | |
|                 configBufLen += CONFIG_BUF_LEN;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|     } while (!eolFound);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static int
 | |
| StringToToken(const char *str, const xf86ConfigSymTabRec * tab)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     for (i = 0; tab[i].token != -1; i++) {
 | |
|         if (!xf86nameCompare(tab[i].name, str))
 | |
|             return tab[i].token;
 | |
|     }
 | |
|     return ERROR_TOKEN;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86getToken --
 | |
|  *      Read next Token from the config file. Handle the global variable
 | |
|  *      pushToken.
 | |
|  */
 | |
| int
 | |
| xf86getToken(const xf86ConfigSymTabRec * tab)
 | |
| {
 | |
|     int c, i;
 | |
| 
 | |
|     /*
 | |
|      * First check whether pushToken has a different value than LOCK_TOKEN.
 | |
|      * In this case rBuf[] contains a valid STRING/TOKEN/NUMBER. But in the
 | |
|      * oth * case the next token must be read from the input.
 | |
|      */
 | |
|     if (pushToken == EOF_TOKEN)
 | |
|         return EOF_TOKEN;
 | |
|     else if (pushToken == LOCK_TOKEN) {
 | |
|         /*
 | |
|          * eol_seen is only set for the first token after a newline.
 | |
|          */
 | |
|         eol_seen = 0;
 | |
| 
 | |
|         c = configBuf[configPos];
 | |
| 
 | |
|         /*
 | |
|          * Get start of next Token. EOF is handled,
 | |
|          * whitespaces are skipped.
 | |
|          */
 | |
| 
 | |
|  again:
 | |
|         if (!c) {
 | |
|             char *ret;
 | |
| 
 | |
|             if (numFiles > 0)
 | |
|                 ret = xf86getNextLine();
 | |
|             else {
 | |
|                 if (builtinConfig[builtinIndex] == NULL)
 | |
|                     ret = NULL;
 | |
|                 else {
 | |
|                     strlcpy(configBuf,
 | |
|                             builtinConfig[builtinIndex], CONFIG_BUF_LEN);
 | |
|                     ret = configBuf;
 | |
|                     builtinIndex++;
 | |
|                 }
 | |
|             }
 | |
|             if (ret == NULL) {
 | |
|                 /*
 | |
|                  * if necessary, move to the next file and
 | |
|                  * read the first line
 | |
|                  */
 | |
|                 if (curFileIndex + 1 < numFiles) {
 | |
|                     curFileIndex++;
 | |
|                     configLineNo = 0;
 | |
|                     goto again;
 | |
|                 }
 | |
|                 else
 | |
|                     return pushToken = EOF_TOKEN;
 | |
|             }
 | |
|             configLineNo++;
 | |
|             configPos = 0;
 | |
|             eol_seen = 1;
 | |
|         }
 | |
| 
 | |
|         i = 0;
 | |
|         for (;;) {
 | |
|             c = configBuf[configPos++];
 | |
|             configRBuf[i++] = c;
 | |
|             switch (c) {
 | |
|             case ' ':
 | |
|             case '\t':
 | |
|             case '\r':
 | |
|                 continue;
 | |
|             case '\n':
 | |
|                 i = 0;
 | |
|                 continue;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|         if (c == '\0')
 | |
|             goto again;
 | |
| 
 | |
|         if (c == '#') {
 | |
|             do {
 | |
|                 configRBuf[i++] = (c = configBuf[configPos++]);
 | |
|             }
 | |
|             while ((c != '\n') && (c != '\r') && (c != '\0'));
 | |
|             configRBuf[i] = '\0';
 | |
|             /* XXX no private copy.
 | |
|              * Use xf86addComment when setting a comment.
 | |
|              */
 | |
|             xf86_lex_val.str = configRBuf;
 | |
|             return COMMENT;
 | |
|         }
 | |
| 
 | |
|         /* GJA -- handle '-' and ','  * Be careful: "-hsync" is a keyword. */
 | |
|         else if ((c == ',') && !isalpha(configBuf[configPos])) {
 | |
|             return COMMA;
 | |
|         }
 | |
|         else if ((c == '-') && !isalpha(configBuf[configPos])) {
 | |
|             return DASH;
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          * Numbers are returned immediately ...
 | |
|          */
 | |
|         if (isdigit(c)) {
 | |
|             int base;
 | |
| 
 | |
|             if (c == '0')
 | |
|                 if ((configBuf[configPos] == 'x') ||
 | |
|                     (configBuf[configPos] == 'X')) {
 | |
|                     base = 16;
 | |
|                     xf86_lex_val.numType = PARSE_HEX;
 | |
|                 }
 | |
|                 else {
 | |
|                     base = 8;
 | |
|                     xf86_lex_val.numType = PARSE_OCTAL;
 | |
|                 }
 | |
|             else {
 | |
|                 base = 10;
 | |
|                 xf86_lex_val.numType = PARSE_DECIMAL;
 | |
|             }
 | |
| 
 | |
|             configRBuf[0] = c;
 | |
|             i = 1;
 | |
|             while (isdigit(c = configBuf[configPos++]) ||
 | |
|                    (c == '.') || (c == 'x') || (c == 'X') ||
 | |
|                    ((base == 16) && (((c >= 'a') && (c <= 'f')) ||
 | |
|                                      ((c >= 'A') && (c <= 'F')))))
 | |
|                 configRBuf[i++] = c;
 | |
|             configPos--;        /* GJA -- one too far */
 | |
|             configRBuf[i] = '\0';
 | |
|             xf86_lex_val.num = strtoul(configRBuf, NULL, 0);
 | |
|             xf86_lex_val.realnum = atof(configRBuf);
 | |
|             return NUMBER;
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          * All Strings START with a \" ...
 | |
|          */
 | |
|         else if (c == '\"') {
 | |
|             i = -1;
 | |
|             do {
 | |
|                 configRBuf[++i] = (c = configBuf[configPos++]);
 | |
|             }
 | |
|             while ((c != '\"') && (c != '\n') && (c != '\r') && (c != '\0'));
 | |
|             configRBuf[i] = '\0';
 | |
|             xf86_lex_val.str = malloc(strlen(configRBuf) + 1);
 | |
|             strcpy(xf86_lex_val.str, configRBuf);        /* private copy ! */
 | |
|             return STRING;
 | |
|         }
 | |
| 
 | |
|         /*
 | |
|          * ... and now we MUST have a valid token.  The search is
 | |
|          * handled later along with the pushed tokens.
 | |
|          */
 | |
|         else {
 | |
|             configRBuf[0] = c;
 | |
|             i = 0;
 | |
|             do {
 | |
|                 configRBuf[++i] = (c = configBuf[configPos++]);
 | |
|             }
 | |
|             while ((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r') &&
 | |
|                    (c != '\0') && (c != '#'));
 | |
|             --configPos;
 | |
|             configRBuf[i] = '\0';
 | |
|             i = 0;
 | |
|         }
 | |
| 
 | |
|     }
 | |
|     else {
 | |
| 
 | |
|         /*
 | |
|          * Here we deal with pushed tokens. Reinitialize pushToken again. If
 | |
|          * the pushed token was NUMBER || STRING return them again ...
 | |
|          */
 | |
|         int temp = pushToken;
 | |
| 
 | |
|         pushToken = LOCK_TOKEN;
 | |
| 
 | |
|         if (temp == COMMA || temp == DASH)
 | |
|             return temp;
 | |
|         if (temp == NUMBER || temp == STRING)
 | |
|             return temp;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * Joop, at last we have to lookup the token ...
 | |
|      */
 | |
|     if (tab)
 | |
|         return StringToToken(configRBuf, tab);
 | |
| 
 | |
|     return ERROR_TOKEN;         /* Error catcher */
 | |
| }
 | |
| 
 | |
| int
 | |
| xf86getSubToken(char **comment)
 | |
| {
 | |
|     int token;
 | |
| 
 | |
|     for (;;) {
 | |
|         token = xf86getToken(NULL);
 | |
|         if (token == COMMENT) {
 | |
|             if (comment)
 | |
|                 *comment = xf86addComment(*comment, xf86_lex_val.str);
 | |
|         }
 | |
|         else
 | |
|             return token;
 | |
|     }
 | |
|  /*NOTREACHED*/}
 | |
| 
 | |
| int
 | |
| xf86getSubTokenWithTab(char **comment, const xf86ConfigSymTabRec * tab)
 | |
| {
 | |
|     int token;
 | |
| 
 | |
|     for (;;) {
 | |
|         token = xf86getToken(tab);
 | |
|         if (token == COMMENT) {
 | |
|             if (comment)
 | |
|                 *comment = xf86addComment(*comment, xf86_lex_val.str);
 | |
|         }
 | |
|         else
 | |
|             return token;
 | |
|     }
 | |
|  /*NOTREACHED*/}
 | |
| 
 | |
| void
 | |
| xf86unGetToken(int token)
 | |
| {
 | |
|     pushToken = token;
 | |
| }
 | |
| 
 | |
| char *
 | |
| xf86tokenString(void)
 | |
| {
 | |
|     return configRBuf;
 | |
| }
 | |
| 
 | |
| int
 | |
| xf86pathIsAbsolute(const char *path)
 | |
| {
 | |
|     if (path && path[0] == '/')
 | |
|         return 1;
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* A path is "safe" if it is relative and if it contains no ".." elements. */
 | |
| int
 | |
| xf86pathIsSafe(const char *path)
 | |
| {
 | |
|     if (xf86pathIsAbsolute(path))
 | |
|         return 0;
 | |
| 
 | |
|     /* Compare with ".." */
 | |
|     if (!strcmp(path, ".."))
 | |
|         return 0;
 | |
| 
 | |
|     /* Look for leading "../" */
 | |
|     if (!strncmp(path, "../", 3))
 | |
|         return 0;
 | |
| 
 | |
|     /* Look for trailing "/.." */
 | |
|     if ((strlen(path) > 3) && !strcmp(path + strlen(path) - 3, "/.."))
 | |
|         return 0;
 | |
| 
 | |
|     /* Look for "/../" */
 | |
|     if (strstr(path, "/../"))
 | |
|         return 0;
 | |
| 
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This function substitutes the following escape sequences:
 | |
|  *
 | |
|  *    %A    cmdline argument as an absolute path (must be absolute to match)
 | |
|  *    %R    cmdline argument as a relative path
 | |
|  *    %S    cmdline argument as a "safe" path (relative, and no ".." elements)
 | |
|  *    %X    default config file name ("xorg.conf")
 | |
|  *    %H    hostname
 | |
|  *    %E    config file environment ($XORGCONFIG) as an absolute path
 | |
|  *    %F    config file environment ($XORGCONFIG) as a relative path
 | |
|  *    %G    config file environment ($XORGCONFIG) as a safe path
 | |
|  *    %P    projroot
 | |
|  *    %C    sysconfdir
 | |
|  *    %D    datadir
 | |
|  *    %%    %
 | |
|  */
 | |
| 
 | |
| #define XCONFIGSUFFIX	".conf"
 | |
| #define XCONFENV	"XORGCONFIG"
 | |
| 
 | |
| #define BAIL_OUT		do {									\
 | |
| 							free(result);				\
 | |
| 							return NULL;						\
 | |
| 						} while (0)
 | |
| 
 | |
| #define CHECK_LENGTH	do {									\
 | |
| 							if (l > PATH_MAX) {					\
 | |
| 								BAIL_OUT;						\
 | |
| 							}									\
 | |
| 						} while (0)
 | |
| 
 | |
| #define APPEND_STR(s)	do {									\
 | |
| 							if (strlen(s) + l > PATH_MAX) {		\
 | |
| 								BAIL_OUT;						\
 | |
| 							} else {							\
 | |
| 								strcpy(result + l, s);			\
 | |
| 								l += strlen(s);					\
 | |
| 							}									\
 | |
| 						} while (0)
 | |
| 
 | |
| static char *
 | |
| DoSubstitution(const char *template, const char *cmdline, const char *projroot,
 | |
|                int *cmdlineUsed, int *envUsed, const char *XConfigFile)
 | |
| {
 | |
|     char *result;
 | |
|     int i, l;
 | |
|     static const char *env = NULL;
 | |
|     static char *hostname = NULL;
 | |
| 
 | |
|     if (!template)
 | |
|         return NULL;
 | |
| 
 | |
|     if (cmdlineUsed)
 | |
|         *cmdlineUsed = 0;
 | |
|     if (envUsed)
 | |
|         *envUsed = 0;
 | |
| 
 | |
|     result = malloc(PATH_MAX + 1);
 | |
|     l = 0;
 | |
|     for (i = 0; template[i]; i++) {
 | |
|         if (template[i] != '%') {
 | |
|             result[l++] = template[i];
 | |
|             CHECK_LENGTH;
 | |
|         }
 | |
|         else {
 | |
|             switch (template[++i]) {
 | |
|             case 'A':
 | |
|                 if (cmdline && xf86pathIsAbsolute(cmdline)) {
 | |
|                     APPEND_STR(cmdline);
 | |
|                     if (cmdlineUsed)
 | |
|                         *cmdlineUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'R':
 | |
|                 if (cmdline && !xf86pathIsAbsolute(cmdline)) {
 | |
|                     APPEND_STR(cmdline);
 | |
|                     if (cmdlineUsed)
 | |
|                         *cmdlineUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'S':
 | |
|                 if (cmdline && xf86pathIsSafe(cmdline)) {
 | |
|                     APPEND_STR(cmdline);
 | |
|                     if (cmdlineUsed)
 | |
|                         *cmdlineUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'X':
 | |
|                 APPEND_STR(XConfigFile);
 | |
|                 break;
 | |
|             case 'H':
 | |
|                 if (!hostname) {
 | |
|                     if ((hostname = malloc(MAXHOSTNAMELEN + 1))) {
 | |
|                         if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
 | |
|                             hostname[MAXHOSTNAMELEN] = '\0';
 | |
|                         }
 | |
|                         else {
 | |
|                             free(hostname);
 | |
|                             hostname = NULL;
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 if (hostname)
 | |
|                     APPEND_STR(hostname);
 | |
|                 break;
 | |
|             case 'E':
 | |
|                 if (!env)
 | |
|                     env = getenv(XCONFENV);
 | |
|                 if (env && xf86pathIsAbsolute(env)) {
 | |
|                     APPEND_STR(env);
 | |
|                     if (envUsed)
 | |
|                         *envUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'F':
 | |
|                 if (!env)
 | |
|                     env = getenv(XCONFENV);
 | |
|                 if (env && !xf86pathIsAbsolute(env)) {
 | |
|                     APPEND_STR(env);
 | |
|                     if (envUsed)
 | |
|                         *envUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'G':
 | |
|                 if (!env)
 | |
|                     env = getenv(XCONFENV);
 | |
|                 if (env && xf86pathIsSafe(env)) {
 | |
|                     APPEND_STR(env);
 | |
|                     if (envUsed)
 | |
|                         *envUsed = 1;
 | |
|                 }
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'P':
 | |
|                 if (projroot && xf86pathIsAbsolute(projroot))
 | |
|                     APPEND_STR(projroot);
 | |
|                 else
 | |
|                     BAIL_OUT;
 | |
|                 break;
 | |
|             case 'C':
 | |
|                 APPEND_STR(SYSCONFDIR);
 | |
|                 break;
 | |
|             case 'D':
 | |
|                 APPEND_STR(DATADIR);
 | |
|                 break;
 | |
|             case '%':
 | |
|                 result[l++] = '%';
 | |
|                 CHECK_LENGTH;
 | |
|                 break;
 | |
|             default:
 | |
|                 fprintf(stderr, "invalid escape %%%c found in path template\n",
 | |
|                         template[i]);
 | |
|                 BAIL_OUT;
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| #ifdef DEBUG
 | |
|     fprintf(stderr, "Converted `%s' to `%s'\n", template, result);
 | |
| #endif
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given some searching parameters, locate and open the xorg config file.
 | |
|  */
 | |
| static char *
 | |
| OpenConfigFile(const char *path, const char *cmdline, const char *projroot,
 | |
|                const char *confname)
 | |
| {
 | |
|     char *filepath = NULL;
 | |
|     char *pathcopy;
 | |
|     const char *template;
 | |
|     int cmdlineUsed = 0;
 | |
|     FILE *file = NULL;
 | |
| 
 | |
|     pathcopy = strdup(path);
 | |
|     for (template = strtok(pathcopy, ","); template && !file;
 | |
|          template = strtok(NULL, ",")) {
 | |
|         filepath = DoSubstitution(template, cmdline, projroot,
 | |
|                                   &cmdlineUsed, NULL, confname);
 | |
|         if (!filepath)
 | |
|             continue;
 | |
|         if (cmdline && !cmdlineUsed) {
 | |
|             free(filepath);
 | |
|             filepath = NULL;
 | |
|             continue;
 | |
|         }
 | |
|         file = fopen(filepath, "r");
 | |
|         if (!file) {
 | |
|             free(filepath);
 | |
|             filepath = NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     free(pathcopy);
 | |
|     if (file) {
 | |
|         configFiles[numFiles].file = file;
 | |
|         configFiles[numFiles].path = strdup(filepath);
 | |
|         numFiles++;
 | |
|     }
 | |
|     return filepath;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Match non-hidden files in the xorg config directory with a .conf
 | |
|  * suffix. This filter is passed to scandir(3).
 | |
|  */
 | |
| static int
 | |
| ConfigFilter(const struct dirent *de)
 | |
| {
 | |
|     const char *name = de->d_name;
 | |
|     size_t len;
 | |
|     size_t suflen = strlen(XCONFIGSUFFIX);
 | |
| 
 | |
|     if (!name || name[0] == '.')
 | |
|         return 0;
 | |
|     len = strlen(name);
 | |
|     if (len <= suflen)
 | |
|         return 0;
 | |
|     if (strcmp(&name[len - suflen], XCONFIGSUFFIX) != 0)
 | |
|         return 0;
 | |
|     return 1;
 | |
| }
 | |
| 
 | |
| static Bool
 | |
| AddConfigDirFiles(const char *dirpath, struct dirent **list, int num)
 | |
| {
 | |
|     int i;
 | |
|     Bool openedFile = FALSE;
 | |
|     Bool warnOnce = FALSE;
 | |
| 
 | |
|     for (i = 0; i < num; i++) {
 | |
|         char *path;
 | |
|         FILE *file;
 | |
| 
 | |
|         if (numFiles >= CONFIG_MAX_FILES) {
 | |
|             if (!warnOnce) {
 | |
|                 ErrorF("Maximum number of configuration " "files opened\n");
 | |
|                 warnOnce = TRUE;
 | |
|             }
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         path = malloc(PATH_MAX + 1);
 | |
|         snprintf(path, PATH_MAX + 1, "%s/%s", dirpath, list[i]->d_name);
 | |
|         file = fopen(path, "r");
 | |
|         if (!file) {
 | |
|             free(path);
 | |
|             continue;
 | |
|         }
 | |
|         openedFile = TRUE;
 | |
| 
 | |
|         configFiles[numFiles].file = file;
 | |
|         configFiles[numFiles].path = path;
 | |
|         numFiles++;
 | |
|     }
 | |
| 
 | |
|     return openedFile;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Given some searching parameters, locate and open the xorg config
 | |
|  * directory. The directory does not need to contain config files.
 | |
|  */
 | |
| static char *
 | |
| OpenConfigDir(const char *path, const char *cmdline, const char *projroot,
 | |
|               const char *confname)
 | |
| {
 | |
|     char *dirpath = NULL, *pathcopy;
 | |
|     const char *template;
 | |
|     Bool found = FALSE;
 | |
|     int cmdlineUsed = 0;
 | |
| 
 | |
|     pathcopy = strdup(path);
 | |
|     for (template = strtok(pathcopy, ","); template && !found;
 | |
|          template = strtok(NULL, ",")) {
 | |
|         struct dirent **list = NULL;
 | |
|         int num;
 | |
| 
 | |
|         dirpath = DoSubstitution(template, cmdline, projroot,
 | |
|                                  &cmdlineUsed, NULL, confname);
 | |
|         if (!dirpath)
 | |
|             continue;
 | |
|         if (cmdline && !cmdlineUsed) {
 | |
|             free(dirpath);
 | |
|             dirpath = NULL;
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         /* match files named *.conf */
 | |
|         num = scandir(dirpath, &list, ConfigFilter, alphasort);
 | |
|         if (num < 0) {
 | |
|             list = NULL;
 | |
|             num = 0;
 | |
|         }
 | |
|         found = AddConfigDirFiles(dirpath, list, num);
 | |
|         if (!found) {
 | |
|             free(dirpath);
 | |
|             dirpath = NULL;
 | |
|         }
 | |
|         while (num--)
 | |
|             free(list[num]);
 | |
|         free(list);
 | |
|     }
 | |
| 
 | |
|     free(pathcopy);
 | |
|     return dirpath;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86initConfigFiles -- Setup global variables and buffers.
 | |
|  */
 | |
| void
 | |
| xf86initConfigFiles(void)
 | |
| {
 | |
|     curFileIndex = 0;
 | |
|     configPos = 0;
 | |
|     configLineNo = 0;
 | |
|     pushToken = LOCK_TOKEN;
 | |
| 
 | |
|     configBuf = malloc(CONFIG_BUF_LEN);
 | |
|     configRBuf = malloc(CONFIG_BUF_LEN);
 | |
|     configBuf[0] = '\0';        /* sanity ... */
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86openConfigFile --
 | |
|  *
 | |
|  * This function take a config file search path (optional), a command-line
 | |
|  * specified file name (optional) and the ProjectRoot path (optional) and
 | |
|  * locates and opens a config file based on that information.  If a
 | |
|  * command-line file name is specified, then this function fails if none
 | |
|  * of the located files.
 | |
|  *
 | |
|  * The return value is a pointer to the actual name of the file that was
 | |
|  * opened.  When no file is found, the return value is NULL. The caller should
 | |
|  * free() the returned value.
 | |
|  *
 | |
|  * The escape sequences allowed in the search path are defined above.
 | |
|  *
 | |
|  */
 | |
| 
 | |
| #ifndef DEFAULT_CONF_PATH
 | |
| #define DEFAULT_CONF_PATH	"/etc/X11/%S," \
 | |
| 							"%P/etc/X11/%S," \
 | |
| 							"/etc/X11/%G," \
 | |
| 							"%P/etc/X11/%G," \
 | |
| 							"/etc/X11/%X-%M," \
 | |
| 							"/etc/X11/%X," \
 | |
| 							"/etc/%X," \
 | |
| 							"%P/etc/X11/%X.%H," \
 | |
| 							"%P/etc/X11/%X-%M," \
 | |
| 							"%P/etc/X11/%X," \
 | |
| 							"%P/lib/X11/%X.%H," \
 | |
| 							"%P/lib/X11/%X-%M," \
 | |
| 							"%P/lib/X11/%X"
 | |
| #endif
 | |
| 
 | |
| char *
 | |
| xf86openConfigFile(const char *path, const char *cmdline, const char *projroot)
 | |
| {
 | |
|     if (!path || !path[0])
 | |
|         path = DEFAULT_CONF_PATH;
 | |
|     if (!projroot || !projroot[0])
 | |
|         projroot = PROJECTROOT;
 | |
| 
 | |
|     /* Search for a config file */
 | |
|     return OpenConfigFile(path, cmdline, projroot, XCONFIGFILE);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86openConfigDirFiles --
 | |
|  *
 | |
|  * This function take a config directory search path (optional), a
 | |
|  * command-line specified directory name (optional) and the ProjectRoot path
 | |
|  * (optional) and locates and opens a config directory based on that
 | |
|  * information.  If a command-line name is specified, then this function
 | |
|  * fails if it is not found.
 | |
|  *
 | |
|  * The return value is a pointer to the actual name of the direcoty that was
 | |
|  * opened.  When no directory is found, the return value is NULL. The caller
 | |
|  * should free() the returned value.
 | |
|  *
 | |
|  * The escape sequences allowed in the search path are defined above.
 | |
|  *
 | |
|  */
 | |
| char *
 | |
| xf86openConfigDirFiles(const char *path, const char *cmdline,
 | |
|                        const char *projroot)
 | |
| {
 | |
|     if (!path || !path[0])
 | |
|         path = DEFAULT_CONF_PATH;
 | |
|     if (!projroot || !projroot[0])
 | |
|         projroot = PROJECTROOT;
 | |
| 
 | |
|     /* Search for the multiconf directory */
 | |
|     return OpenConfigDir(path, cmdline, projroot, XCONFIGDIR);
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86closeConfigFile(void)
 | |
| {
 | |
|     int i;
 | |
| 
 | |
|     free(configRBuf);
 | |
|     configRBuf = NULL;
 | |
|     free(configBuf);
 | |
|     configBuf = NULL;
 | |
| 
 | |
|     if (numFiles == 0) {
 | |
|         builtinConfig = NULL;
 | |
|         builtinIndex = 0;
 | |
|     }
 | |
|     for (i = 0; i < numFiles; i++) {
 | |
|         fclose(configFiles[i].file);
 | |
|         configFiles[i].file = NULL;
 | |
|         free(configFiles[i].path);
 | |
|         configFiles[i].path = NULL;
 | |
|     }
 | |
|     numFiles = 0;
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86setBuiltinConfig(const char *config[])
 | |
| {
 | |
|     builtinConfig = config;
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86parseError(const char *format, ...)
 | |
| {
 | |
|     va_list ap;
 | |
|     const char *filename = numFiles ? configFiles[curFileIndex].path
 | |
|         : "<builtin configuration>";
 | |
| 
 | |
|     ErrorF("Parse error on line %d of section %s in file %s\n\t",
 | |
|            configLineNo, configSection, filename);
 | |
|     va_start(ap, format);
 | |
|     VErrorF(format, ap);
 | |
|     va_end(ap);
 | |
| 
 | |
|     ErrorF("\n");
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86validationError(const char *format, ...)
 | |
| {
 | |
|     va_list ap;
 | |
|     const char *filename = numFiles ? configFiles[curFileIndex].path
 | |
|         : "<builtin configuration>";
 | |
| 
 | |
|     ErrorF("Data incomplete in file %s\n\t", filename);
 | |
|     va_start(ap, format);
 | |
|     VErrorF(format, ap);
 | |
|     va_end(ap);
 | |
| 
 | |
|     ErrorF("\n");
 | |
| }
 | |
| 
 | |
| void
 | |
| xf86setSection(const char *section)
 | |
| {
 | |
|     free(configSection);
 | |
|     configSection = strdup(section);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xf86getToken --
 | |
|  *  Lookup a string if it is actually a token in disguise.
 | |
|  */
 | |
| int
 | |
| xf86getStringToken(const xf86ConfigSymTabRec * tab)
 | |
| {
 | |
|     return StringToToken(xf86_lex_val.str, tab);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Compare two names.  The characters '_', ' ', and '\t' are ignored
 | |
|  * in the comparison.
 | |
|  */
 | |
| int
 | |
| xf86nameCompare(const char *s1, const char *s2)
 | |
| {
 | |
|     char c1, c2;
 | |
| 
 | |
|     if (!s1 || *s1 == 0) {
 | |
|         if (!s2 || *s2 == 0)
 | |
|             return 0;
 | |
|         else
 | |
|             return 1;
 | |
|     } else if (!s2 || *s2 == 0) {
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
 | |
|         s1++;
 | |
|     while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
 | |
|         s2++;
 | |
|     c1 = (isupper(*s1) ? tolower(*s1) : *s1);
 | |
|     c2 = (isupper(*s2) ? tolower(*s2) : *s2);
 | |
|     while (c1 == c2) {
 | |
|         if (c1 == '\0')
 | |
|             return 0;
 | |
|         s1++;
 | |
|         s2++;
 | |
|         while (*s1 == '_' || *s1 == ' ' || *s1 == '\t')
 | |
|             s1++;
 | |
|         while (*s2 == '_' || *s2 == ' ' || *s2 == '\t')
 | |
|             s2++;
 | |
|         c1 = (isupper(*s1) ? tolower(*s1) : *s1);
 | |
|         c2 = (isupper(*s2) ? tolower(*s2) : *s2);
 | |
|     }
 | |
|     return c1 - c2;
 | |
| }
 | |
| 
 | |
| char *
 | |
| xf86addComment(char *cur, const char *add)
 | |
| {
 | |
|     char *str;
 | |
|     const char *cstr;
 | |
|     int len, curlen, iscomment, hasnewline = 0, insnewline, endnewline;
 | |
| 
 | |
|     if (add == NULL || add[0] == '\0')
 | |
|         return cur;
 | |
| 
 | |
|     if (cur) {
 | |
|         curlen = strlen(cur);
 | |
|         if (curlen)
 | |
|             hasnewline = cur[curlen - 1] == '\n';
 | |
|         eol_seen = 0;
 | |
|     }
 | |
|     else
 | |
|         curlen = 0;
 | |
| 
 | |
|     cstr = add;
 | |
|     iscomment = 0;
 | |
|     while (*cstr) {
 | |
|         if (*cstr != ' ' && *cstr != '\t')
 | |
|             break;
 | |
|         ++cstr;
 | |
|     }
 | |
|     iscomment = (*cstr == '#');
 | |
| 
 | |
|     len = strlen(add);
 | |
|     endnewline = add[len - 1] == '\n';
 | |
| 
 | |
|     insnewline = eol_seen || (curlen && !hasnewline);
 | |
|     if (insnewline)
 | |
|         len++;
 | |
|     if (!iscomment)
 | |
|         len++;
 | |
|     if (!endnewline)
 | |
|         len++;
 | |
| 
 | |
|     /* Allocate + 1 char for '\0' terminator. */
 | |
|     str = realloc(cur, curlen + len + 1);
 | |
|     if (!str)
 | |
|         return cur;
 | |
| 
 | |
|     cur = str;
 | |
| 
 | |
|     if (insnewline)
 | |
|         cur[curlen++] = '\n';
 | |
|     if (!iscomment)
 | |
|         cur[curlen++] = '#';
 | |
|     strcpy(cur + curlen, add);
 | |
|     if (!endnewline)
 | |
|         strcat(cur, "\n");
 | |
| 
 | |
|     return cur;
 | |
| }
 | |
| 
 | |
| Bool
 | |
| xf86getBoolValue(Bool *val, const char *str)
 | |
| {
 | |
|     if (!val || !str)
 | |
|         return FALSE;
 | |
|     if (*str == '\0') {
 | |
|         *val = TRUE;
 | |
|     }
 | |
|     else {
 | |
|         if (xf86nameCompare(str, "1") == 0)
 | |
|             *val = TRUE;
 | |
|         else if (xf86nameCompare(str, "on") == 0)
 | |
|             *val = TRUE;
 | |
|         else if (xf86nameCompare(str, "true") == 0)
 | |
|             *val = TRUE;
 | |
|         else if (xf86nameCompare(str, "yes") == 0)
 | |
|             *val = TRUE;
 | |
|         else if (xf86nameCompare(str, "0") == 0)
 | |
|             *val = FALSE;
 | |
|         else if (xf86nameCompare(str, "off") == 0)
 | |
|             *val = FALSE;
 | |
|         else if (xf86nameCompare(str, "false") == 0)
 | |
|             *val = FALSE;
 | |
|         else if (xf86nameCompare(str, "no") == 0)
 | |
|             *val = FALSE;
 | |
|         else
 | |
|             return FALSE;
 | |
|     }
 | |
|     return TRUE;
 | |
| }
 |