2 * Copyright (c) 1997 Metro Link Incorporated
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
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.
28 * Copyright (c) 1997-2003 by The XFree86 Project, Inc.
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:
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
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.
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).
54 #ifdef HAVE_XORG_CONFIG_H
55 #include <xorg-config.h>
62 #include <sys/types.h>
66 #include <X11/Xdefs.h>
67 #include <X11/Xfuncproto.h>
69 #if defined(_POSIX_SOURCE)
75 #endif /* _POSIX_SOURCE */
77 #if !defined(MAXHOSTNAMELEN)
78 #define MAXHOSTNAMELEN 32
79 #endif /* !MAXHOSTNAMELEN */
84 #include "Configint.h"
85 #include "xf86tokens.h"
87 #define CONFIG_BUF_LEN 1024
88 #define CONFIG_MAX_FILES 64
90 static int StringToToken(const char *, xf86ConfigSymTabRec
*);
95 } configFiles
[CONFIG_MAX_FILES
];
96 static const char **builtinConfig
= NULL
;
97 static int builtinIndex
= 0;
98 static int configPos
= 0; /* current readers position */
99 static int configLineNo
= 0; /* linenumber */
100 static char *configBuf
, *configRBuf
; /* buffer for lines */
101 static char *configSection
= NULL
; /* name of current section being parsed */
102 static int numFiles
= 0; /* number of config files */
103 static int curFileIndex
= 0; /* index of current config file */
104 static int pushToken
= LOCK_TOKEN
;
105 static int eol_seen
= 0; /* private state to handle comments */
111 * read from the configFiles FILE stream until we encounter a new
112 * line; this is effectively just a big wrapper for fgets(3).
114 * xf86getToken() assumes that we will read up to the next
115 * newline; we need to grow configBuf and configRBuf as needed to
120 xf86getNextLine(void)
122 static int configBufLen
= CONFIG_BUF_LEN
;
123 char *tmpConfigBuf
, *tmpConfigRBuf
;
124 int c
, i
, pos
= 0, eolFound
= 0;
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
134 if (configBufLen
!= CONFIG_BUF_LEN
) {
136 tmpConfigBuf
= malloc(CONFIG_BUF_LEN
);
137 tmpConfigRBuf
= malloc(CONFIG_BUF_LEN
);
139 if (!tmpConfigBuf
|| !tmpConfigRBuf
) {
142 * at least one of the mallocs failed; keep the old buffers
143 * and free any partial allocations
153 * malloc succeeded; free the old buffers and use the new
157 configBufLen
= CONFIG_BUF_LEN
;
162 configBuf
= tmpConfigBuf
;
163 configRBuf
= tmpConfigRBuf
;
167 /* read in another block of chars */
170 ret
= fgets(configBuf
+ pos
, configBufLen
- pos
- 1,
171 configFiles
[curFileIndex
].file
);
175 * if the file doesn't end in a newline, add one
176 * and trigger another read
179 strcpy(&configBuf
[pos
], "\n");
186 /* search for EOL in the new block of chars */
188 for (i
= pos
; i
< (configBufLen
- 1); i
++) {
194 if ((c
== '\n') || (c
== '\r')) {
201 * if we didn't find EOL, then grow the string and
207 tmpConfigBuf
= realloc(configBuf
, configBufLen
+ CONFIG_BUF_LEN
);
208 tmpConfigRBuf
= realloc(configRBuf
, configBufLen
+ CONFIG_BUF_LEN
);
210 if (!tmpConfigBuf
|| !tmpConfigRBuf
) {
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
221 configBuf
= tmpConfigBuf
;
223 configRBuf
= tmpConfigRBuf
;
230 /* reallocation succeeded */
232 configBuf
= tmpConfigBuf
;
233 configRBuf
= tmpConfigRBuf
;
235 configBufLen
+= CONFIG_BUF_LEN
;
246 * Read next Token from the config file. Handle the global variable
250 xf86getToken(xf86ConfigSymTabRec
* tab
)
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.
259 if (pushToken
== EOF_TOKEN
)
261 else if (pushToken
== LOCK_TOKEN
) {
263 * eol_seen is only set for the first token after a newline.
267 c
= configBuf
[configPos
];
270 * Get start of next Token. EOF is handled,
271 * whitespaces are skipped.
279 ret
= xf86getNextLine();
281 if (builtinConfig
[builtinIndex
] == NULL
)
285 builtinConfig
[builtinIndex
], CONFIG_BUF_LEN
);
292 * if necessary, move to the next file and
293 * read the first line
295 if (curFileIndex
+ 1 < numFiles
) {
301 return pushToken
= EOF_TOKEN
;
310 c
= configBuf
[configPos
++];
328 configRBuf
[i
++] = (c
= configBuf
[configPos
++]);
330 while ((c
!= '\n') && (c
!= '\r') && (c
!= '\0'));
331 configRBuf
[i
] = '\0';
332 /* XXX no private copy.
333 * Use xf86addComment when setting a comment.
335 val
.str
= configRBuf
;
339 /* GJA -- handle '-' and ',' * Be careful: "-hsync" is a keyword. */
340 else if ((c
== ',') && !isalpha(configBuf
[configPos
])) {
343 else if ((c
== '-') && !isalpha(configBuf
[configPos
])) {
348 * Numbers are returned immediately ...
354 if ((configBuf
[configPos
] == 'x') ||
355 (configBuf
[configPos
] == 'X')) {
357 val
.numType
= PARSE_HEX
;
361 val
.numType
= PARSE_OCTAL
;
365 val
.numType
= PARSE_DECIMAL
;
370 while (isdigit(c
= configBuf
[configPos
++]) ||
371 (c
== '.') || (c
== 'x') || (c
== 'X') ||
372 ((base
== 16) && (((c
>= 'a') && (c
<= 'f')) ||
373 ((c
>= 'A') && (c
<= 'F')))))
375 configPos
--; /* GJA -- one too far */
376 configRBuf
[i
] = '\0';
377 val
.num
= strtoul(configRBuf
, NULL
, 0);
378 val
.realnum
= atof(configRBuf
);
383 * All Strings START with a \" ...
385 else if (c
== '\"') {
388 configRBuf
[++i
] = (c
= configBuf
[configPos
++]);
390 while ((c
!= '\"') && (c
!= '\n') && (c
!= '\r') && (c
!= '\0'));
391 configRBuf
[i
] = '\0';
392 val
.str
= malloc(strlen(configRBuf
) + 1);
393 strcpy(val
.str
, configRBuf
); /* private copy ! */
398 * ... and now we MUST have a valid token. The search is
399 * handled later along with the pushed tokens.
405 configRBuf
[++i
] = (c
= configBuf
[configPos
++]);
407 while ((c
!= ' ') && (c
!= '\t') && (c
!= '\n') && (c
!= '\r') &&
408 (c
!= '\0') && (c
!= '#'));
410 configRBuf
[i
] = '\0';
418 * Here we deal with pushed tokens. Reinitialize pushToken again. If
419 * the pushed token was NUMBER || STRING return them again ...
421 int temp
= pushToken
;
423 pushToken
= LOCK_TOKEN
;
425 if (temp
== COMMA
|| temp
== DASH
)
427 if (temp
== NUMBER
|| temp
== STRING
)
432 * Joop, at last we have to lookup the token ...
436 while (tab
[i
].token
!= -1)
437 if (xf86nameCompare(configRBuf
, tab
[i
].name
) == 0)
443 return ERROR_TOKEN
; /* Error catcher */
447 xf86getSubToken(char **comment
)
452 token
= xf86getToken(NULL
);
453 if (token
== COMMENT
) {
455 *comment
= xf86addComment(*comment
, val
.str
);
463 xf86getSubTokenWithTab(char **comment
, xf86ConfigSymTabRec
* tab
)
468 token
= xf86getToken(tab
);
469 if (token
== COMMENT
) {
471 *comment
= xf86addComment(*comment
, val
.str
);
479 xf86unGetToken(int token
)
485 xf86tokenString(void)
491 xf86pathIsAbsolute(const char *path
)
493 if (path
&& path
[0] == '/')
498 /* A path is "safe" if it is relative and if it contains no ".." elements. */
500 xf86pathIsSafe(const char *path
)
502 if (xf86pathIsAbsolute(path
))
505 /* Compare with ".." */
506 if (!strcmp(path
, ".."))
509 /* Look for leading "../" */
510 if (!strncmp(path
, "../", 3))
513 /* Look for trailing "/.." */
514 if ((strlen(path
) > 3) && !strcmp(path
+ strlen(path
) - 3, "/.."))
517 /* Look for "/../" */
518 if (strstr(path
, "/../"))
525 * This function substitutes the following escape sequences:
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")
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
542 #define XCONFIGFILE "xorg.conf"
545 #define XCONFIGDIR "xorg.conf.d"
547 #ifndef XCONFIGSUFFIX
548 #define XCONFIGSUFFIX ".conf"
551 #define PROJECTROOT "/usr/X11R6"
554 #define SYSCONFDIR PROJECTROOT "/etc"
557 #define DATADIR PROJECTROOT "/share"
560 #define XCONFENV "XORGCONFIG"
563 #define BAIL_OUT do { \
568 #define CHECK_LENGTH do { \
569 if (l > PATH_MAX) { \
574 #define APPEND_STR(s) do { \
575 if (strlen(s) + l > PATH_MAX) { \
578 strcpy(result + l, s); \
584 DoSubstitution(const char *template, const char *cmdline
, const char *projroot
,
585 int *cmdlineUsed
, int *envUsed
, const char *XConfigFile
)
589 static const char *env
= NULL
;
590 static char *hostname
= NULL
;
600 result
= malloc(PATH_MAX
+ 1);
602 for (i
= 0; template[i
]; i
++) {
603 if (template[i
] != '%') {
604 result
[l
++] = template[i
];
608 switch (template[++i
]) {
610 if (cmdline
&& xf86pathIsAbsolute(cmdline
)) {
619 if (cmdline
&& !xf86pathIsAbsolute(cmdline
)) {
628 if (cmdline
&& xf86pathIsSafe(cmdline
)) {
637 APPEND_STR(XConfigFile
);
641 if ((hostname
= malloc(MAXHOSTNAMELEN
+ 1))) {
642 if (gethostname(hostname
, MAXHOSTNAMELEN
) == 0) {
643 hostname
[MAXHOSTNAMELEN
] = '\0';
652 APPEND_STR(hostname
);
656 env
= getenv(XCONFENV
);
657 if (env
&& xf86pathIsAbsolute(env
)) {
667 env
= getenv(XCONFENV
);
668 if (env
&& !xf86pathIsAbsolute(env
)) {
678 env
= getenv(XCONFENV
);
679 if (env
&& xf86pathIsSafe(env
)) {
688 if (projroot
&& xf86pathIsAbsolute(projroot
))
689 APPEND_STR(projroot
);
694 APPEND_STR(SYSCONFDIR
);
704 fprintf(stderr
, "invalid escape %%%c found in path template\n",
712 fprintf(stderr
, "Converted `%s' to `%s'\n", template, result
);
718 * Given some searching parameters, locate and open the xorg config file.
721 OpenConfigFile(const char *path
, const char *cmdline
, const char *projroot
,
722 const char *confname
)
724 char *filepath
= NULL
;
726 const char *template;
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
);
737 if (cmdline
&& !cmdlineUsed
) {
742 file
= fopen(filepath
, "r");
751 configFiles
[numFiles
].file
= file
;
752 configFiles
[numFiles
].path
= strdup(filepath
);
759 * Match non-hidden files in the xorg config directory with a .conf
760 * suffix. This filter is passed to scandir(3).
763 ConfigFilter(const struct dirent
*de
)
765 const char *name
= de
->d_name
;
767 size_t suflen
= strlen(XCONFIGSUFFIX
);
769 if (!name
|| name
[0] == '.')
774 if (strcmp(&name
[len
- suflen
], XCONFIGSUFFIX
) != 0)
780 AddConfigDirFiles(const char *dirpath
, struct dirent
**list
, int num
)
783 Bool openedFile
= FALSE
;
784 Bool warnOnce
= FALSE
;
786 for (i
= 0; i
< num
; i
++) {
790 if (numFiles
>= CONFIG_MAX_FILES
) {
792 ErrorF("Maximum number of configuration " "files opened\n");
798 path
= malloc(PATH_MAX
+ 1);
799 snprintf(path
, PATH_MAX
+ 1, "%s/%s", dirpath
, list
[i
]->d_name
);
800 file
= fopen(path
, "r");
807 configFiles
[numFiles
].file
= file
;
808 configFiles
[numFiles
].path
= path
;
816 * Given some searching parameters, locate and open the xorg config
817 * directory. The directory does not need to contain config files.
820 OpenConfigDir(const char *path
, const char *cmdline
, const char *projroot
,
821 const char *confname
)
823 char *dirpath
, *pathcopy
;
824 const char *template;
828 pathcopy
= strdup(path
);
829 for (template = strtok(pathcopy
, ","); template && !found
;
830 template = strtok(NULL
, ",")) {
831 struct dirent
**list
= NULL
;
834 dirpath
= DoSubstitution(template, cmdline
, projroot
,
835 &cmdlineUsed
, NULL
, confname
);
838 if (cmdline
&& !cmdlineUsed
) {
844 /* match files named *.conf */
845 num
= scandir(dirpath
, &list
, ConfigFilter
, alphasort
);
850 found
= AddConfigDirFiles(dirpath
, list
, num
);
865 * xf86initConfigFiles -- Setup global variables and buffers.
868 xf86initConfigFiles(void)
873 pushToken
= LOCK_TOKEN
;
875 configBuf
= malloc(CONFIG_BUF_LEN
);
876 configRBuf
= malloc(CONFIG_BUF_LEN
);
877 configBuf
[0] = '\0'; /* sanity ... */
881 * xf86openConfigFile --
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.
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.
893 * The escape sequences allowed in the search path are defined above.
897 #ifndef DEFAULT_CONF_PATH
898 #define DEFAULT_CONF_PATH "/etc/X11/%S," \
905 "%P/etc/X11/%X.%H," \
906 "%P/etc/X11/%X-%M," \
908 "%P/lib/X11/%X.%H," \
909 "%P/lib/X11/%X-%M," \
914 xf86openConfigFile(const char *path
, const char *cmdline
, const char *projroot
)
916 if (!path
|| !path
[0])
917 path
= DEFAULT_CONF_PATH
;
918 if (!projroot
|| !projroot
[0])
919 projroot
= PROJECTROOT
;
921 /* Search for a config file */
922 return OpenConfigFile(path
, cmdline
, projroot
, XCONFIGFILE
);
926 * xf86openConfigDirFiles --
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.
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.
938 * The escape sequences allowed in the search path are defined above.
942 xf86openConfigDirFiles(const char *path
, const char *cmdline
,
943 const char *projroot
)
945 if (!path
|| !path
[0])
946 path
= DEFAULT_CONF_PATH
;
947 if (!projroot
|| !projroot
[0])
948 projroot
= PROJECTROOT
;
950 /* Search for the multiconf directory */
951 return OpenConfigDir(path
, cmdline
, projroot
, XCONFIGDIR
);
955 xf86closeConfigFile(void)
965 builtinConfig
= NULL
;
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
;
978 xf86setBuiltinConfig(const char *config
[])
980 builtinConfig
= config
;
984 xf86parseError(const char *format
, ...)
987 const char *filename
= numFiles
? configFiles
[curFileIndex
].path
988 : "<builtin configuration>";
990 ErrorF("Parse error on line %d of section %s in file %s\n\t",
991 configLineNo
, configSection
, filename
);
992 va_start(ap
, format
);
1000 xf86validationError(const char *format
, ...)
1003 const char *filename
= numFiles
? configFiles
[curFileIndex
].path
1004 : "<builtin configuration>";
1006 ErrorF("Data incomplete in file %s\n\t", filename
);
1007 va_start(ap
, format
);
1008 VErrorF(format
, ap
);
1015 xf86setSection(const char *section
)
1017 free(configSection
);
1018 configSection
= strdup(section
);
1023 * Lookup a string if it is actually a token in disguise.
1026 xf86getStringToken(xf86ConfigSymTabRec
* tab
)
1028 return StringToToken(val
.str
, tab
);
1032 StringToToken(const char *str
, xf86ConfigSymTabRec
* tab
)
1036 for (i
= 0; tab
[i
].token
!= -1; i
++) {
1037 if (!xf86nameCompare(tab
[i
].name
, str
))
1038 return tab
[i
].token
;
1044 * Compare two names. The characters '_', ' ', and '\t' are ignored
1045 * in the comparison.
1048 xf86nameCompare(const char *s1
, const char *s2
)
1052 if (!s1
|| *s1
== 0) {
1053 if (!s2
|| *s2
== 0)
1059 while (*s1
== '_' || *s1
== ' ' || *s1
== '\t')
1061 while (*s2
== '_' || *s2
== ' ' || *s2
== '\t')
1063 c1
= (isupper(*s1
) ? tolower(*s1
) : *s1
);
1064 c2
= (isupper(*s2
) ? tolower(*s2
) : *s2
);
1070 while (*s1
== '_' || *s1
== ' ' || *s1
== '\t')
1072 while (*s2
== '_' || *s2
== ' ' || *s2
== '\t')
1074 c1
= (isupper(*s1
) ? tolower(*s1
) : *s1
);
1075 c2
= (isupper(*s2
) ? tolower(*s2
) : *s2
);
1081 xf86addComment(char *cur
, char *add
)
1084 int len
, curlen
, iscomment
, hasnewline
= 0, insnewline
, endnewline
;
1086 if (add
== NULL
|| add
[0] == '\0')
1090 curlen
= strlen(cur
);
1092 hasnewline
= cur
[curlen
- 1] == '\n';
1101 if (*str
!= ' ' && *str
!= '\t')
1105 iscomment
= (*str
== '#');
1108 endnewline
= add
[len
- 1] == '\n';
1110 insnewline
= eol_seen
|| (curlen
&& !hasnewline
);
1118 /* Allocate + 1 char for '\0' terminator. */
1119 str
= realloc(cur
, curlen
+ len
+ 1);
1126 cur
[curlen
++] = '\n';
1128 cur
[curlen
++] = '#';
1129 strcpy(cur
+ curlen
, add
);
1137 xf86getBoolValue(Bool
*val
, const char *str
)
1145 if (xf86nameCompare(str
, "1") == 0)
1147 else if (xf86nameCompare(str
, "on") == 0)
1149 else if (xf86nameCompare(str
, "true") == 0)
1151 else if (xf86nameCompare(str
, "yes") == 0)
1153 else if (xf86nameCompare(str
, "0") == 0)
1155 else if (xf86nameCompare(str
, "off") == 0)
1157 else if (xf86nameCompare(str
, "false") == 0)
1159 else if (xf86nameCompare(str
, "no") == 0)