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