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