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