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