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