• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * XML DRI client-side driver configuration
3  * Copyright (C) 2003 Felix Kuehling
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * FELIX KUEHLING, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
21  * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  *
23  */
24 /**
25  * \file xmlconfig.c
26  * \brief Driver-independent client-side part of the XML configuration
27  * \author Felix Kuehling
28  */
29 
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <expat.h>
35 #include <fcntl.h>
36 #include <math.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include "xmlconfig.h"
40 
41 #undef GET_PROGRAM_NAME
42 
43 #if (defined(__GNU_LIBRARY__) || defined(__GLIBC__)) && !defined(__UCLIBC__)
44 #    if !defined(__GLIBC__) || (__GLIBC__ < 2)
45 /* These aren't declared in any libc5 header */
46 extern char *program_invocation_name, *program_invocation_short_name;
47 #    endif
48 #    define GET_PROGRAM_NAME() program_invocation_short_name
49 #elif defined(__CYGWIN__)
50 #    define GET_PROGRAM_NAME() program_invocation_short_name
51 #elif defined(__FreeBSD__) && (__FreeBSD__ >= 2)
52 #    include <osreldate.h>
53 #    if (__FreeBSD_version >= 440000)
54 #        include <stdlib.h>
55 #        define GET_PROGRAM_NAME() getprogname()
56 #    endif
57 #elif defined(__NetBSD__) && defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 106000100)
58 #    include <stdlib.h>
59 #    define GET_PROGRAM_NAME() getprogname()
60 #elif defined(__DragonFly__)
61 #    include <stdlib.h>
62 #    define GET_PROGRAM_NAME() getprogname()
63 #elif defined(__APPLE__)
64 #    include <stdlib.h>
65 #    define GET_PROGRAM_NAME() getprogname()
66 #elif defined(__sun)
67 /* Solaris has getexecname() which returns the full path - return just
68    the basename to match BSD getprogname() */
69 #    include <stdlib.h>
70 #    include <libgen.h>
71 
72 static const char *
__getProgramName()73 __getProgramName()
74 {
75     static const char *progname;
76 
77     if (progname == NULL) {
78         const char *e = getexecname();
79         if (e != NULL) {
80             /* Have to make a copy since getexecname can return a readonly
81                string, but basename expects to be able to modify its arg. */
82             char *n = strdup(e);
83             if (n != NULL) {
84                 progname = basename(n);
85             }
86         }
87     }
88     return progname;
89 }
90 
91 #    define GET_PROGRAM_NAME() __getProgramName()
92 #endif
93 
94 #if !defined(GET_PROGRAM_NAME)
95 #    if defined(__OpenBSD__) || defined(NetBSD) || defined(__UCLIBC__) || defined(ANDROID)
96 /* This is a hack. It's said to work on OpenBSD, NetBSD and GNU.
97  * Rogelio M.Serrano Jr. reported it's also working with UCLIBC. It's
98  * used as a last resort, if there is no documented facility available. */
99 static const char *
__getProgramName()100 __getProgramName()
101 {
102     extern const char *__progname;
103     char * arg = strrchr(__progname, '/');
104     if (arg)
105         return arg+1;
106     else
107         return __progname;
108 }
109 #        define GET_PROGRAM_NAME() __getProgramName()
110 #    else
111 #        define GET_PROGRAM_NAME() ""
112 #        warning "Per application configuration won't work with your OS version."
113 #    endif
114 #endif
115 
116 /** \brief Find an option in an option cache with the name as key */
117 static uint32_t
findOption(const driOptionCache * cache,const char * name)118 findOption(const driOptionCache *cache, const char *name)
119 {
120     uint32_t len = strlen (name);
121     uint32_t size = 1 << cache->tableSize, mask = size - 1;
122     uint32_t hash = 0;
123     uint32_t i, shift;
124 
125   /* compute a hash from the variable length name */
126     for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
127         hash += (uint32_t)name[i] << shift;
128     hash *= hash;
129     hash = (hash >> (16-cache->tableSize/2)) & mask;
130 
131   /* this is just the starting point of the linear search for the option */
132     for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
133       /* if we hit an empty entry then the option is not defined (yet) */
134         if (cache->info[hash].name == 0)
135             break;
136         else if (!strcmp (name, cache->info[hash].name))
137             break;
138     }
139   /* this assertion fails if the hash table is full */
140     assert (i < size);
141 
142     return hash;
143 }
144 
145 /** \brief Like strdup but using malloc and with error checking. */
146 #define XSTRDUP(dest,source) do { \
147     uint32_t len = strlen (source); \
148     if (!(dest = malloc(len+1))) { \
149         fprintf (stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
150         abort(); \
151     } \
152     memcpy (dest, source, len+1); \
153 } while (0)
154 
compare(const void * a,const void * b)155 static int compare (const void *a, const void *b) {
156     return strcmp (*(char *const*)a, *(char *const*)b);
157 }
158 /** \brief Binary search in a string array. */
159 static uint32_t
bsearchStr(const XML_Char * name,const XML_Char * elems[],uint32_t count)160 bsearchStr (const XML_Char *name, const XML_Char *elems[], uint32_t count)
161 {
162     const XML_Char **found;
163     found = bsearch (&name, elems, count, sizeof (XML_Char *), compare);
164     if (found)
165         return found - elems;
166     else
167         return count;
168 }
169 
170 /** \brief Locale-independent integer parser.
171  *
172  * Works similar to strtol. Leading space is NOT skipped. The input
173  * number may have an optional sign. Radix is specified by base. If
174  * base is 0 then decimal is assumed unless the input number is
175  * prefixed by 0x or 0X for hexadecimal or 0 for octal. After
176  * returning tail points to the first character that is not part of
177  * the integer number. If no number was found then tail points to the
178  * start of the input string. */
179 static int
strToI(const XML_Char * string,const XML_Char ** tail,int base)180 strToI(const XML_Char *string, const XML_Char **tail, int base)
181 {
182     int radix = base == 0 ? 10 : base;
183     int result = 0;
184     int sign = 1;
185     bool numberFound = false;
186     const XML_Char *start = string;
187 
188     assert (radix >= 2 && radix <= 36);
189 
190     if (*string == '-') {
191         sign = -1;
192         string++;
193     } else if (*string == '+')
194         string++;
195     if (base == 0 && *string == '0') {
196         numberFound = true;
197         if (*(string+1) == 'x' || *(string+1) == 'X') {
198             radix = 16;
199             string += 2;
200         } else {
201             radix = 8;
202             string++;
203         }
204     }
205     do {
206         int digit = -1;
207         if (radix <= 10) {
208             if (*string >= '0' && *string < '0' + radix)
209                 digit = *string - '0';
210         } else {
211             if (*string >= '0' && *string <= '9')
212                 digit = *string - '0';
213             else if (*string >= 'a' && *string < 'a' + radix - 10)
214                 digit = *string - 'a' + 10;
215             else if (*string >= 'A' && *string < 'A' + radix - 10)
216                 digit = *string - 'A' + 10;
217         }
218         if (digit != -1) {
219             numberFound = true;
220             result = radix*result + digit;
221             string++;
222         } else
223             break;
224     } while (true);
225     *tail = numberFound ? string : start;
226     return sign * result;
227 }
228 
229 /** \brief Locale-independent floating-point parser.
230  *
231  * Works similar to strtod. Leading space is NOT skipped. The input
232  * number may have an optional sign. '.' is interpreted as decimal
233  * point and may occur at most once. Optionally the number may end in
234  * [eE]<exponent>, where <exponent> is an integer as recognized by
235  * strToI. In that case the result is number * 10^exponent. After
236  * returning tail points to the first character that is not part of
237  * the floating point number. If no number was found then tail points
238  * to the start of the input string.
239  *
240  * Uses two passes for maximum accuracy. */
241 static float
strToF(const XML_Char * string,const XML_Char ** tail)242 strToF(const XML_Char *string, const XML_Char **tail)
243 {
244     int nDigits = 0, pointPos, exponent;
245     float sign = 1.0f, result = 0.0f, scale;
246     const XML_Char *start = string, *numStart;
247 
248     /* sign */
249     if (*string == '-') {
250         sign = -1.0f;
251         string++;
252     } else if (*string == '+')
253         string++;
254 
255     /* first pass: determine position of decimal point, number of
256      * digits, exponent and the end of the number. */
257     numStart = string;
258     while (*string >= '0' && *string <= '9') {
259         string++;
260         nDigits++;
261     }
262     pointPos = nDigits;
263     if (*string == '.') {
264         string++;
265         while (*string >= '0' && *string <= '9') {
266             string++;
267             nDigits++;
268         }
269     }
270     if (nDigits == 0) {
271         /* no digits, no number */
272         *tail = start;
273         return 0.0f;
274     }
275     *tail = string;
276     if (*string == 'e' || *string == 'E') {
277         const XML_Char *expTail;
278         exponent = strToI (string+1, &expTail, 10);
279         if (expTail == string+1)
280             exponent = 0;
281         else
282             *tail = expTail;
283     } else
284         exponent = 0;
285     string = numStart;
286 
287     /* scale of the first digit */
288     scale = sign * (float)pow (10.0, (double)(pointPos-1 + exponent));
289 
290     /* second pass: parse digits */
291     do {
292         if (*string != '.') {
293             assert (*string >= '0' && *string <= '9');
294             result += scale * (float)(*string - '0');
295             scale *= 0.1f;
296             nDigits--;
297         }
298         string++;
299     } while (nDigits > 0);
300 
301     return result;
302 }
303 
304 /** \brief Parse a value of a given type. */
305 static unsigned char
parseValue(driOptionValue * v,driOptionType type,const XML_Char * string)306 parseValue(driOptionValue *v, driOptionType type, const XML_Char *string)
307 {
308     const XML_Char *tail = NULL;
309   /* skip leading white-space */
310     string += strspn (string, " \f\n\r\t\v");
311     switch (type) {
312       case DRI_BOOL:
313         if (!strcmp (string, "false")) {
314             v->_bool = false;
315             tail = string + 5;
316         } else if (!strcmp (string, "true")) {
317             v->_bool = true;
318             tail = string + 4;
319         }
320         else
321             return false;
322         break;
323       case DRI_ENUM: /* enum is just a special integer */
324       case DRI_INT:
325         v->_int = strToI (string, &tail, 0);
326         break;
327       case DRI_FLOAT:
328         v->_float = strToF (string, &tail);
329         break;
330       case DRI_STRING:
331         free (v->_string);
332         v->_string = strndup(string, STRING_CONF_MAXLEN);
333         return true;
334     }
335 
336     if (tail == string)
337         return false; /* empty string (or containing only white-space) */
338   /* skip trailing white space */
339     if (*tail)
340         tail += strspn (tail, " \f\n\r\t\v");
341     if (*tail)
342         return false; /* something left over that is not part of value */
343 
344     return true;
345 }
346 
347 /** \brief Parse a list of ranges of type info->type. */
348 static unsigned char
parseRanges(driOptionInfo * info,const XML_Char * string)349 parseRanges(driOptionInfo *info, const XML_Char *string)
350 {
351     XML_Char *cp, *range;
352     uint32_t nRanges, i;
353     driOptionRange *ranges;
354 
355     XSTRDUP (cp, string);
356   /* pass 1: determine the number of ranges (number of commas + 1) */
357     range = cp;
358     for (nRanges = 1; *range; ++range)
359         if (*range == ',')
360             ++nRanges;
361 
362     if ((ranges = malloc(nRanges*sizeof(driOptionRange))) == NULL) {
363         fprintf (stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
364         abort();
365     }
366 
367   /* pass 2: parse all ranges into preallocated array */
368     range = cp;
369     for (i = 0; i < nRanges; ++i) {
370         XML_Char *end, *sep;
371         assert (range);
372         end = strchr (range, ',');
373         if (end)
374             *end = '\0';
375         sep = strchr (range, ':');
376         if (sep) { /* non-empty interval */
377             *sep = '\0';
378             if (!parseValue (&ranges[i].start, info->type, range) ||
379                 !parseValue (&ranges[i].end, info->type, sep+1))
380                 break;
381             if (info->type == DRI_INT &&
382                 ranges[i].start._int > ranges[i].end._int)
383                 break;
384             if (info->type == DRI_FLOAT &&
385                 ranges[i].start._float > ranges[i].end._float)
386                 break;
387         } else { /* empty interval */
388             if (!parseValue (&ranges[i].start, info->type, range))
389                 break;
390             ranges[i].end = ranges[i].start;
391         }
392         if (end)
393             range = end+1;
394         else
395             range = NULL;
396     }
397     free(cp);
398     if (i < nRanges) {
399         free(ranges);
400         return false;
401     } else
402         assert (range == NULL);
403 
404     info->nRanges = nRanges;
405     info->ranges = ranges;
406     return true;
407 }
408 
409 /** \brief Check if a value is in one of info->ranges. */
410 static bool
checkValue(const driOptionValue * v,const driOptionInfo * info)411 checkValue(const driOptionValue *v, const driOptionInfo *info)
412 {
413     uint32_t i;
414     assert (info->type != DRI_BOOL); /* should be caught by the parser */
415     if (info->nRanges == 0)
416         return true;
417     switch (info->type) {
418       case DRI_ENUM: /* enum is just a special integer */
419       case DRI_INT:
420         for (i = 0; i < info->nRanges; ++i)
421             if (v->_int >= info->ranges[i].start._int &&
422                 v->_int <= info->ranges[i].end._int)
423                 return true;
424         break;
425       case DRI_FLOAT:
426         for (i = 0; i < info->nRanges; ++i)
427             if (v->_float >= info->ranges[i].start._float &&
428                 v->_float <= info->ranges[i].end._float)
429                 return true;
430         break;
431       case DRI_STRING:
432         break;
433       default:
434         assert (0); /* should never happen */
435     }
436     return false;
437 }
438 
439 /**
440  * Print message to \c stderr if the \c LIBGL_DEBUG environment variable
441  * is set.
442  *
443  * Is called from the drivers.
444  *
445  * \param f \c printf like format string.
446  */
447 static void
__driUtilMessage(const char * f,...)448 __driUtilMessage(const char *f, ...)
449 {
450     va_list args;
451     const char *libgl_debug;
452 
453     libgl_debug=getenv("LIBGL_DEBUG");
454     if (libgl_debug && !strstr(libgl_debug, "quiet")) {
455         fprintf(stderr, "libGL: ");
456         va_start(args, f);
457         vfprintf(stderr, f, args);
458         va_end(args);
459         fprintf(stderr, "\n");
460     }
461 }
462 
463 /** \brief Output a warning message. */
464 #define XML_WARNING1(msg) do {\
465     __driUtilMessage ("Warning in %s line %d, column %d: "msg, data->name, \
466                       (int) XML_GetCurrentLineNumber(data->parser), \
467                       (int) XML_GetCurrentColumnNumber(data->parser)); \
468 } while (0)
469 #define XML_WARNING(msg, ...) do { \
470     __driUtilMessage ("Warning in %s line %d, column %d: "msg, data->name, \
471                       (int) XML_GetCurrentLineNumber(data->parser), \
472                       (int) XML_GetCurrentColumnNumber(data->parser), \
473                       ##__VA_ARGS__); \
474 } while (0)
475 /** \brief Output an error message. */
476 #define XML_ERROR1(msg) do { \
477     __driUtilMessage ("Error in %s line %d, column %d: "msg, data->name, \
478                       (int) XML_GetCurrentLineNumber(data->parser), \
479                       (int) XML_GetCurrentColumnNumber(data->parser)); \
480 } while (0)
481 #define XML_ERROR(msg, ...) do { \
482     __driUtilMessage ("Error in %s line %d, column %d: "msg, data->name, \
483                       (int) XML_GetCurrentLineNumber(data->parser), \
484                       (int) XML_GetCurrentColumnNumber(data->parser), \
485                       ##__VA_ARGS__); \
486 } while (0)
487 /** \brief Output a fatal error message and abort. */
488 #define XML_FATAL1(msg) do { \
489     fprintf (stderr, "Fatal error in %s line %d, column %d: "msg"\n", \
490              data->name, \
491              (int) XML_GetCurrentLineNumber(data->parser),        \
492              (int) XML_GetCurrentColumnNumber(data->parser)); \
493     abort();\
494 } while (0)
495 #define XML_FATAL(msg, ...) do { \
496     fprintf (stderr, "Fatal error in %s line %d, column %d: "msg"\n", \
497              data->name, \
498              (int) XML_GetCurrentLineNumber(data->parser), \
499              (int) XML_GetCurrentColumnNumber(data->parser), \
500              ##__VA_ARGS__); \
501     abort();\
502 } while (0)
503 
504 /** \brief Parser context for __driConfigOptions. */
505 struct OptInfoData {
506     const char *name;
507     XML_Parser parser;
508     driOptionCache *cache;
509     bool inDriInfo;
510     bool inSection;
511     bool inDesc;
512     bool inOption;
513     bool inEnum;
514     int curOption;
515 };
516 
517 /** \brief Elements in __driConfigOptions. */
518 enum OptInfoElem {
519     OI_DESCRIPTION = 0, OI_DRIINFO, OI_ENUM, OI_OPTION, OI_SECTION, OI_COUNT
520 };
521 static const XML_Char *OptInfoElems[] = {
522     "description", "driinfo", "enum", "option", "section"
523 };
524 
525 /** \brief Parse attributes of an enum element.
526  *
527  * We're not actually interested in the data. Just make sure this is ok
528  * for external configuration tools.
529  */
530 static void
parseEnumAttr(struct OptInfoData * data,const XML_Char ** attr)531 parseEnumAttr(struct OptInfoData *data, const XML_Char **attr)
532 {
533     uint32_t i;
534     const XML_Char *value = NULL, *text = NULL;
535     driOptionValue v;
536     uint32_t opt = data->curOption;
537     for (i = 0; attr[i]; i += 2) {
538         if (!strcmp (attr[i], "value")) value = attr[i+1];
539         else if (!strcmp (attr[i], "text")) text = attr[i+1];
540         else XML_FATAL("illegal enum attribute: %s.", attr[i]);
541     }
542     if (!value) XML_FATAL1 ("value attribute missing in enum.");
543     if (!text) XML_FATAL1 ("text attribute missing in enum.");
544      if (!parseValue (&v, data->cache->info[opt].type, value))
545         XML_FATAL ("illegal enum value: %s.", value);
546     if (!checkValue (&v, &data->cache->info[opt]))
547         XML_FATAL ("enum value out of valid range: %s.", value);
548 }
549 
550 /** \brief Parse attributes of a description element.
551  *
552  * We're not actually interested in the data. Just make sure this is ok
553  * for external configuration tools.
554  */
555 static void
parseDescAttr(struct OptInfoData * data,const XML_Char ** attr)556 parseDescAttr(struct OptInfoData *data, const XML_Char **attr)
557 {
558     uint32_t i;
559     const XML_Char *lang = NULL, *text = NULL;
560     for (i = 0; attr[i]; i += 2) {
561         if (!strcmp (attr[i], "lang")) lang = attr[i+1];
562         else if (!strcmp (attr[i], "text")) text = attr[i+1];
563         else XML_FATAL("illegal description attribute: %s.", attr[i]);
564     }
565     if (!lang) XML_FATAL1 ("lang attribute missing in description.");
566     if (!text) XML_FATAL1 ("text attribute missing in description.");
567 }
568 
569 /** \brief Parse attributes of an option element. */
570 static void
parseOptInfoAttr(struct OptInfoData * data,const XML_Char ** attr)571 parseOptInfoAttr(struct OptInfoData *data, const XML_Char **attr)
572 {
573     enum OptAttr {OA_DEFAULT = 0, OA_NAME, OA_TYPE, OA_VALID, OA_COUNT};
574     static const XML_Char *optAttr[] = {"default", "name", "type", "valid"};
575     const XML_Char *attrVal[OA_COUNT] = {NULL, NULL, NULL, NULL};
576     const char *defaultVal;
577     driOptionCache *cache = data->cache;
578     uint32_t opt, i;
579     for (i = 0; attr[i]; i += 2) {
580         uint32_t attrName = bsearchStr (attr[i], optAttr, OA_COUNT);
581         if (attrName >= OA_COUNT)
582             XML_FATAL ("illegal option attribute: %s", attr[i]);
583         attrVal[attrName] = attr[i+1];
584     }
585     if (!attrVal[OA_NAME]) XML_FATAL1 ("name attribute missing in option.");
586     if (!attrVal[OA_TYPE]) XML_FATAL1 ("type attribute missing in option.");
587     if (!attrVal[OA_DEFAULT]) XML_FATAL1 ("default attribute missing in option.");
588 
589     opt = findOption (cache, attrVal[OA_NAME]);
590     if (cache->info[opt].name)
591         XML_FATAL ("option %s redefined.", attrVal[OA_NAME]);
592     data->curOption = opt;
593 
594     XSTRDUP (cache->info[opt].name, attrVal[OA_NAME]);
595 
596     if (!strcmp (attrVal[OA_TYPE], "bool"))
597         cache->info[opt].type = DRI_BOOL;
598     else if (!strcmp (attrVal[OA_TYPE], "enum"))
599         cache->info[opt].type = DRI_ENUM;
600     else if (!strcmp (attrVal[OA_TYPE], "int"))
601         cache->info[opt].type = DRI_INT;
602     else if (!strcmp (attrVal[OA_TYPE], "float"))
603         cache->info[opt].type = DRI_FLOAT;
604     else if (!strcmp (attrVal[OA_TYPE], "string"))
605         cache->info[opt].type = DRI_STRING;
606     else
607         XML_FATAL ("illegal type in option: %s.", attrVal[OA_TYPE]);
608 
609     defaultVal = getenv (cache->info[opt].name);
610     if (defaultVal != NULL) {
611       /* don't use XML_WARNING, we want the user to see this! */
612         fprintf (stderr,
613                  "ATTENTION: default value of option %s overridden by environment.\n",
614                  cache->info[opt].name);
615     } else
616         defaultVal = attrVal[OA_DEFAULT];
617     if (!parseValue (&cache->values[opt], cache->info[opt].type, defaultVal))
618         XML_FATAL ("illegal default value for %s: %s.", cache->info[opt].name, defaultVal);
619 
620     if (attrVal[OA_VALID]) {
621         if (cache->info[opt].type == DRI_BOOL)
622             XML_FATAL1 ("boolean option with valid attribute.");
623         if (!parseRanges (&cache->info[opt], attrVal[OA_VALID]))
624             XML_FATAL ("illegal valid attribute: %s.", attrVal[OA_VALID]);
625         if (!checkValue (&cache->values[opt], &cache->info[opt]))
626             XML_FATAL ("default value out of valid range '%s': %s.",
627                        attrVal[OA_VALID], defaultVal);
628     } else if (cache->info[opt].type == DRI_ENUM) {
629         XML_FATAL1 ("valid attribute missing in option (mandatory for enums).");
630     } else {
631         cache->info[opt].nRanges = 0;
632         cache->info[opt].ranges = NULL;
633     }
634 }
635 
636 /** \brief Handler for start element events. */
637 static void
optInfoStartElem(void * userData,const XML_Char * name,const XML_Char ** attr)638 optInfoStartElem(void *userData, const XML_Char *name, const XML_Char **attr)
639 {
640     struct OptInfoData *data = (struct OptInfoData *)userData;
641     enum OptInfoElem elem = bsearchStr (name, OptInfoElems, OI_COUNT);
642     switch (elem) {
643       case OI_DRIINFO:
644         if (data->inDriInfo)
645             XML_FATAL1 ("nested <driinfo> elements.");
646         if (attr[0])
647             XML_FATAL1 ("attributes specified on <driinfo> element.");
648         data->inDriInfo = true;
649         break;
650       case OI_SECTION:
651         if (!data->inDriInfo)
652             XML_FATAL1 ("<section> must be inside <driinfo>.");
653         if (data->inSection)
654             XML_FATAL1 ("nested <section> elements.");
655         if (attr[0])
656             XML_FATAL1 ("attributes specified on <section> element.");
657         data->inSection = true;
658         break;
659       case OI_DESCRIPTION:
660         if (!data->inSection && !data->inOption)
661             XML_FATAL1 ("<description> must be inside <description> or <option.");
662         if (data->inDesc)
663             XML_FATAL1 ("nested <description> elements.");
664         data->inDesc = true;
665         parseDescAttr (data, attr);
666         break;
667       case OI_OPTION:
668         if (!data->inSection)
669             XML_FATAL1 ("<option> must be inside <section>.");
670         if (data->inDesc)
671             XML_FATAL1 ("<option> nested in <description> element.");
672         if (data->inOption)
673             XML_FATAL1 ("nested <option> elements.");
674         data->inOption = true;
675         parseOptInfoAttr (data, attr);
676         break;
677       case OI_ENUM:
678         if (!(data->inOption && data->inDesc))
679             XML_FATAL1 ("<enum> must be inside <option> and <description>.");
680         if (data->inEnum)
681             XML_FATAL1 ("nested <enum> elements.");
682         data->inEnum = true;
683         parseEnumAttr (data, attr);
684         break;
685       default:
686         XML_FATAL ("unknown element: %s.", name);
687     }
688 }
689 
690 /** \brief Handler for end element events. */
691 static void
optInfoEndElem(void * userData,const XML_Char * name)692 optInfoEndElem(void *userData, const XML_Char *name)
693 {
694     struct OptInfoData *data = (struct OptInfoData *)userData;
695     enum OptInfoElem elem = bsearchStr (name, OptInfoElems, OI_COUNT);
696     switch (elem) {
697       case OI_DRIINFO:
698         data->inDriInfo = false;
699         break;
700       case OI_SECTION:
701         data->inSection = false;
702         break;
703       case OI_DESCRIPTION:
704         data->inDesc = false;
705         break;
706       case OI_OPTION:
707         data->inOption = false;
708         break;
709       case OI_ENUM:
710         data->inEnum = false;
711         break;
712       default:
713         assert (0); /* should have been caught by StartElem */
714     }
715 }
716 
717 void
driParseOptionInfo(driOptionCache * info,const char * configOptions)718 driParseOptionInfo(driOptionCache *info, const char *configOptions)
719 {
720     XML_Parser p;
721     int status;
722     struct OptInfoData userData;
723     struct OptInfoData *data = &userData;
724 
725     /* Make the hash table big enough to fit more than the maximum number of
726      * config options we've ever seen in a driver.
727      */
728     info->tableSize = 6;
729     info->info = calloc(1 << info->tableSize, sizeof (driOptionInfo));
730     info->values = calloc(1 << info->tableSize, sizeof (driOptionValue));
731     if (info->info == NULL || info->values == NULL) {
732         fprintf (stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
733         abort();
734     }
735 
736     p = XML_ParserCreate ("UTF-8"); /* always UTF-8 */
737     XML_SetElementHandler (p, optInfoStartElem, optInfoEndElem);
738     XML_SetUserData (p, data);
739 
740     userData.name = "__driConfigOptions";
741     userData.parser = p;
742     userData.cache = info;
743     userData.inDriInfo = false;
744     userData.inSection = false;
745     userData.inDesc = false;
746     userData.inOption = false;
747     userData.inEnum = false;
748     userData.curOption = -1;
749 
750     status = XML_Parse (p, configOptions, strlen (configOptions), 1);
751     if (!status)
752         XML_FATAL ("%s.", XML_ErrorString(XML_GetErrorCode(p)));
753 
754     XML_ParserFree (p);
755 }
756 
757 /** \brief Parser context for configuration files. */
758 struct OptConfData {
759     const char *name;
760     XML_Parser parser;
761     driOptionCache *cache;
762     int screenNum;
763     const char *driverName, *execName;
764     uint32_t ignoringDevice;
765     uint32_t ignoringApp;
766     uint32_t inDriConf;
767     uint32_t inDevice;
768     uint32_t inApp;
769     uint32_t inOption;
770 };
771 
772 /** \brief Elements in configuration files. */
773 enum OptConfElem {
774     OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_OPTION, OC_COUNT
775 };
776 static const XML_Char *OptConfElems[] = {
777     [OC_APPLICATION]  = "application",
778     [OC_DEVICE] = "device",
779     [OC_DRICONF] = "driconf",
780     [OC_OPTION] = "option",
781 };
782 
783 /** \brief Parse attributes of a device element. */
784 static void
parseDeviceAttr(struct OptConfData * data,const XML_Char ** attr)785 parseDeviceAttr(struct OptConfData *data, const XML_Char **attr)
786 {
787     uint32_t i;
788     const XML_Char *driver = NULL, *screen = NULL;
789     for (i = 0; attr[i]; i += 2) {
790         if (!strcmp (attr[i], "driver")) driver = attr[i+1];
791         else if (!strcmp (attr[i], "screen")) screen = attr[i+1];
792         else XML_WARNING("unknown device attribute: %s.", attr[i]);
793     }
794     if (driver && strcmp (driver, data->driverName))
795         data->ignoringDevice = data->inDevice;
796     else if (screen) {
797         driOptionValue screenNum;
798         if (!parseValue (&screenNum, DRI_INT, screen))
799             XML_WARNING("illegal screen number: %s.", screen);
800         else if (screenNum._int != data->screenNum)
801             data->ignoringDevice = data->inDevice;
802     }
803 }
804 
805 /** \brief Parse attributes of an application element. */
806 static void
parseAppAttr(struct OptConfData * data,const XML_Char ** attr)807 parseAppAttr(struct OptConfData *data, const XML_Char **attr)
808 {
809     uint32_t i;
810     const XML_Char *exec = NULL;
811     for (i = 0; attr[i]; i += 2) {
812         if (!strcmp (attr[i], "name")) /* not needed here */;
813         else if (!strcmp (attr[i], "executable")) exec = attr[i+1];
814         else XML_WARNING("unknown application attribute: %s.", attr[i]);
815     }
816     if (exec && strcmp (exec, data->execName))
817         data->ignoringApp = data->inApp;
818 }
819 
820 /** \brief Parse attributes of an option element. */
821 static void
parseOptConfAttr(struct OptConfData * data,const XML_Char ** attr)822 parseOptConfAttr(struct OptConfData *data, const XML_Char **attr)
823 {
824     uint32_t i;
825     const XML_Char *name = NULL, *value = NULL;
826     for (i = 0; attr[i]; i += 2) {
827         if (!strcmp (attr[i], "name")) name = attr[i+1];
828         else if (!strcmp (attr[i], "value")) value = attr[i+1];
829         else XML_WARNING("unknown option attribute: %s.", attr[i]);
830     }
831     if (!name) XML_WARNING1 ("name attribute missing in option.");
832     if (!value) XML_WARNING1 ("value attribute missing in option.");
833     if (name && value) {
834         driOptionCache *cache = data->cache;
835         uint32_t opt = findOption (cache, name);
836         if (cache->info[opt].name == NULL)
837             /* don't use XML_WARNING, drirc defines options for all drivers,
838              * but not all drivers support them */
839             return;
840         else if (getenv (cache->info[opt].name))
841           /* don't use XML_WARNING, we want the user to see this! */
842             fprintf (stderr, "ATTENTION: option value of option %s ignored.\n",
843                      cache->info[opt].name);
844         else if (!parseValue (&cache->values[opt], cache->info[opt].type, value))
845             XML_WARNING ("illegal option value: %s.", value);
846     }
847 }
848 
849 /** \brief Handler for start element events. */
850 static void
optConfStartElem(void * userData,const XML_Char * name,const XML_Char ** attr)851 optConfStartElem(void *userData, const XML_Char *name,
852                  const XML_Char **attr)
853 {
854     struct OptConfData *data = (struct OptConfData *)userData;
855     enum OptConfElem elem = bsearchStr (name, OptConfElems, OC_COUNT);
856     switch (elem) {
857       case OC_DRICONF:
858         if (data->inDriConf)
859             XML_WARNING1 ("nested <driconf> elements.");
860         if (attr[0])
861             XML_WARNING1 ("attributes specified on <driconf> element.");
862         data->inDriConf++;
863         break;
864       case OC_DEVICE:
865         if (!data->inDriConf)
866             XML_WARNING1 ("<device> should be inside <driconf>.");
867         if (data->inDevice)
868             XML_WARNING1 ("nested <device> elements.");
869         data->inDevice++;
870         if (!data->ignoringDevice && !data->ignoringApp)
871             parseDeviceAttr (data, attr);
872         break;
873       case OC_APPLICATION:
874         if (!data->inDevice)
875             XML_WARNING1 ("<application> should be inside <device>.");
876         if (data->inApp)
877             XML_WARNING1 ("nested <application> elements.");
878         data->inApp++;
879         if (!data->ignoringDevice && !data->ignoringApp)
880             parseAppAttr (data, attr);
881         break;
882       case OC_OPTION:
883         if (!data->inApp)
884             XML_WARNING1 ("<option> should be inside <application>.");
885         if (data->inOption)
886             XML_WARNING1 ("nested <option> elements.");
887         data->inOption++;
888         if (!data->ignoringDevice && !data->ignoringApp)
889             parseOptConfAttr (data, attr);
890         break;
891       default:
892         XML_WARNING ("unknown element: %s.", name);
893     }
894 }
895 
896 /** \brief Handler for end element events. */
897 static void
optConfEndElem(void * userData,const XML_Char * name)898 optConfEndElem(void *userData, const XML_Char *name)
899 {
900     struct OptConfData *data = (struct OptConfData *)userData;
901     enum OptConfElem elem = bsearchStr (name, OptConfElems, OC_COUNT);
902     switch (elem) {
903       case OC_DRICONF:
904         data->inDriConf--;
905         break;
906       case OC_DEVICE:
907         if (data->inDevice-- == data->ignoringDevice)
908             data->ignoringDevice = 0;
909         break;
910       case OC_APPLICATION:
911         if (data->inApp-- == data->ignoringApp)
912             data->ignoringApp = 0;
913         break;
914       case OC_OPTION:
915         data->inOption--;
916         break;
917       default:
918         /* unknown element, warning was produced on start tag */;
919     }
920 }
921 
922 /** \brief Initialize an option cache based on info */
923 static void
initOptionCache(driOptionCache * cache,const driOptionCache * info)924 initOptionCache(driOptionCache *cache, const driOptionCache *info)
925 {
926     unsigned i, size = 1 << info->tableSize;
927     cache->info = info->info;
928     cache->tableSize = info->tableSize;
929     cache->values = malloc((1<<info->tableSize) * sizeof (driOptionValue));
930     if (cache->values == NULL) {
931         fprintf (stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
932         abort();
933     }
934     memcpy (cache->values, info->values,
935             (1<<info->tableSize) * sizeof (driOptionValue));
936     for (i = 0; i < size; ++i) {
937         if (cache->info[i].type == DRI_STRING)
938             XSTRDUP(cache->values[i]._string, info->values[i]._string);
939     }
940 }
941 
942 /** \brief Parse the named configuration file */
943 static void
parseOneConfigFile(XML_Parser p)944 parseOneConfigFile(XML_Parser p)
945 {
946 #define BUF_SIZE 0x1000
947     struct OptConfData *data = (struct OptConfData *)XML_GetUserData (p);
948     int status;
949     int fd;
950 
951     if ((fd = open (data->name, O_RDONLY)) == -1) {
952         __driUtilMessage ("Can't open configuration file %s: %s.",
953                           data->name, strerror (errno));
954         return;
955     }
956 
957     while (1) {
958         int bytesRead;
959         void *buffer = XML_GetBuffer (p, BUF_SIZE);
960         if (!buffer) {
961             __driUtilMessage ("Can't allocate parser buffer.");
962             break;
963         }
964         bytesRead = read (fd, buffer, BUF_SIZE);
965         if (bytesRead == -1) {
966             __driUtilMessage ("Error reading from configuration file %s: %s.",
967                               data->name, strerror (errno));
968             break;
969         }
970         status = XML_ParseBuffer (p, bytesRead, bytesRead == 0);
971         if (!status) {
972             XML_ERROR ("%s.", XML_ErrorString(XML_GetErrorCode(p)));
973             break;
974         }
975         if (bytesRead == 0)
976             break;
977     }
978 
979     close (fd);
980 #undef BUF_SIZE
981 }
982 
983 #ifndef SYSCONFDIR
984 #define SYSCONFDIR "/etc"
985 #endif
986 
987 void
driParseConfigFiles(driOptionCache * cache,const driOptionCache * info,int screenNum,const char * driverName)988 driParseConfigFiles(driOptionCache *cache, const driOptionCache *info,
989                     int screenNum, const char *driverName)
990 {
991     char *filenames[2] = { SYSCONFDIR "/drirc", NULL};
992     char *home;
993     uint32_t i;
994     struct OptConfData userData;
995 
996     initOptionCache (cache, info);
997 
998     userData.cache = cache;
999     userData.screenNum = screenNum;
1000     userData.driverName = driverName;
1001     userData.execName = GET_PROGRAM_NAME();
1002 
1003     if ((home = getenv ("HOME"))) {
1004         uint32_t len = strlen (home);
1005         filenames[1] = malloc(len + 7+1);
1006         if (filenames[1] == NULL)
1007             __driUtilMessage ("Can't allocate memory for %s/.drirc.", home);
1008         else {
1009             memcpy (filenames[1], home, len);
1010             memcpy (filenames[1] + len, "/.drirc", 7+1);
1011         }
1012     }
1013 
1014     for (i = 0; i < 2; ++i) {
1015         XML_Parser p;
1016         if (filenames[i] == NULL)
1017             continue;
1018 
1019         p = XML_ParserCreate (NULL); /* use encoding specified by file */
1020         XML_SetElementHandler (p, optConfStartElem, optConfEndElem);
1021         XML_SetUserData (p, &userData);
1022         userData.parser = p;
1023         userData.name = filenames[i];
1024         userData.ignoringDevice = 0;
1025         userData.ignoringApp = 0;
1026         userData.inDriConf = 0;
1027         userData.inDevice = 0;
1028         userData.inApp = 0;
1029         userData.inOption = 0;
1030 
1031         parseOneConfigFile (p);
1032         XML_ParserFree (p);
1033     }
1034 
1035     free(filenames[1]);
1036 }
1037 
1038 void
driDestroyOptionInfo(driOptionCache * info)1039 driDestroyOptionInfo(driOptionCache *info)
1040 {
1041     driDestroyOptionCache(info);
1042     if (info->info) {
1043         uint32_t i, size = 1 << info->tableSize;
1044         for (i = 0; i < size; ++i) {
1045             if (info->info[i].name) {
1046                 free(info->info[i].name);
1047                 free(info->info[i].ranges);
1048             }
1049         }
1050         free(info->info);
1051     }
1052 }
1053 
1054 void
driDestroyOptionCache(driOptionCache * cache)1055 driDestroyOptionCache(driOptionCache *cache)
1056 {
1057     if (cache->info) {
1058         unsigned i, size = 1 << cache->tableSize;
1059         for (i = 0; i < size; ++i) {
1060             if (cache->info[i].type == DRI_STRING)
1061                 free(cache->values[i]._string);
1062         }
1063     }
1064     free(cache->values);
1065 }
1066 
1067 unsigned char
driCheckOption(const driOptionCache * cache,const char * name,driOptionType type)1068 driCheckOption(const driOptionCache *cache, const char *name,
1069                driOptionType type)
1070 {
1071     uint32_t i = findOption (cache, name);
1072     return cache->info[i].name != NULL && cache->info[i].type == type;
1073 }
1074 
1075 unsigned char
driQueryOptionb(const driOptionCache * cache,const char * name)1076 driQueryOptionb(const driOptionCache *cache, const char *name)
1077 {
1078     uint32_t i = findOption (cache, name);
1079   /* make sure the option is defined and has the correct type */
1080     assert (cache->info[i].name != NULL);
1081     assert (cache->info[i].type == DRI_BOOL);
1082     return cache->values[i]._bool;
1083 }
1084 
1085 int
driQueryOptioni(const driOptionCache * cache,const char * name)1086 driQueryOptioni(const driOptionCache *cache, const char *name)
1087 {
1088     uint32_t i = findOption (cache, name);
1089   /* make sure the option is defined and has the correct type */
1090     assert (cache->info[i].name != NULL);
1091     assert (cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM);
1092     return cache->values[i]._int;
1093 }
1094 
1095 float
driQueryOptionf(const driOptionCache * cache,const char * name)1096 driQueryOptionf(const driOptionCache *cache, const char *name)
1097 {
1098     uint32_t i = findOption (cache, name);
1099   /* make sure the option is defined and has the correct type */
1100     assert (cache->info[i].name != NULL);
1101     assert (cache->info[i].type == DRI_FLOAT);
1102     return cache->values[i]._float;
1103 }
1104 
1105 char *
driQueryOptionstr(const driOptionCache * cache,const char * name)1106 driQueryOptionstr(const driOptionCache *cache, const char *name)
1107 {
1108     uint32_t i = findOption (cache, name);
1109   /* make sure the option is defined and has the correct type */
1110     assert (cache->info[i].name != NULL);
1111     assert (cache->info[i].type == DRI_STRING);
1112     return cache->values[i]._string;
1113 }
1114