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