101e04c3fSmrg/* 201e04c3fSmrg * XML DRI client-side driver configuration 301e04c3fSmrg * Copyright (C) 2003 Felix Kuehling 401e04c3fSmrg * 501e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 601e04c3fSmrg * copy of this software and associated documentation files (the "Software"), 701e04c3fSmrg * to deal in the Software without restriction, including without limitation 801e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 901e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 1001e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1101e04c3fSmrg * 1201e04c3fSmrg * The above copyright notice and this permission notice shall be included 1301e04c3fSmrg * in all copies or substantial portions of the Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1601e04c3fSmrg * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * FELIX KUEHLING, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 1901e04c3fSmrg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2001e04c3fSmrg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 2101e04c3fSmrg * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 2201e04c3fSmrg * 2301e04c3fSmrg */ 2401e04c3fSmrg/** 2501e04c3fSmrg * \file xmlconfig.c 2601e04c3fSmrg * \brief Driver-independent client-side part of the XML configuration 2701e04c3fSmrg * \author Felix Kuehling 2801e04c3fSmrg */ 2901e04c3fSmrg 307ec681f3Smrg#include "xmlconfig.h" 3101e04c3fSmrg#include <limits.h> 3201e04c3fSmrg#include <stdarg.h> 337ec681f3Smrg#include <stdbool.h> 347ec681f3Smrg#include <stdint.h> 3501e04c3fSmrg#include <stdio.h> 367ec681f3Smrg#include <stdlib.h> 3701e04c3fSmrg#include <string.h> 3801e04c3fSmrg#include <assert.h> 397ec681f3Smrg#if WITH_XMLCONFIG 4001e04c3fSmrg#include <expat.h> 4101e04c3fSmrg#include <unistd.h> 4201e04c3fSmrg#include <errno.h> 4301e04c3fSmrg#include <dirent.h> 447ec681f3Smrg#include <sys/stat.h> 457ec681f3Smrg#endif 467ec681f3Smrg#ifdef NO_REGEX 477ec681f3Smrgtypedef int regex_t; 487ec681f3Smrg#define REG_EXTENDED 0 497ec681f3Smrg#define REG_NOSUB 0 507ec681f3Smrg#define REG_NOMATCH 1 517ec681f3Smrgstatic inline int regcomp(regex_t *r, const char *s, int f) { return 0; } 527ec681f3Smrgstatic inline int regexec(regex_t *r, const char *s, int n, void *p, int f) { return REG_NOMATCH; } 537ec681f3Smrgstatic inline void regfree(regex_t* r) {} 547ec681f3Smrg#else 557ec681f3Smrg#include <regex.h> 567ec681f3Smrg#endif 577ec681f3Smrg#include <fcntl.h> 587ec681f3Smrg#include <math.h> 597ec681f3Smrg#include "strndup.h" 6001e04c3fSmrg#include "u_process.h" 617ec681f3Smrg#include "os_file.h" 6201e04c3fSmrg 638a1362adSmaya/* For systems like Hurd */ 648a1362adSmaya#ifndef PATH_MAX 658a1362adSmaya#define PATH_MAX 4096 668a1362adSmaya#endif 6701e04c3fSmrg 687ec681f3Smrgstatic bool 697ec681f3Smrgbe_verbose(void) 7001e04c3fSmrg{ 717ec681f3Smrg const char *s = getenv("MESA_DEBUG"); 727ec681f3Smrg if (!s) 737ec681f3Smrg return true; 7401e04c3fSmrg 757ec681f3Smrg return strstr(s, "silent") == NULL; 7601e04c3fSmrg} 7701e04c3fSmrg 7801e04c3fSmrg/** \brief Locale-independent integer parser. 7901e04c3fSmrg * 8001e04c3fSmrg * Works similar to strtol. Leading space is NOT skipped. The input 8101e04c3fSmrg * number may have an optional sign. Radix is specified by base. If 8201e04c3fSmrg * base is 0 then decimal is assumed unless the input number is 8301e04c3fSmrg * prefixed by 0x or 0X for hexadecimal or 0 for octal. After 8401e04c3fSmrg * returning tail points to the first character that is not part of 8501e04c3fSmrg * the integer number. If no number was found then tail points to the 8601e04c3fSmrg * start of the input string. */ 8701e04c3fSmrgstatic int 887ec681f3SmrgstrToI(const char *string, const char **tail, int base) 8901e04c3fSmrg{ 907ec681f3Smrg int radix = base == 0 ? 10 : base; 917ec681f3Smrg int result = 0; 927ec681f3Smrg int sign = 1; 937ec681f3Smrg bool numberFound = false; 947ec681f3Smrg const char *start = string; 957ec681f3Smrg 967ec681f3Smrg assert(radix >= 2 && radix <= 36); 977ec681f3Smrg 987ec681f3Smrg if (*string == '-') { 997ec681f3Smrg sign = -1; 1007ec681f3Smrg string++; 1017ec681f3Smrg } else if (*string == '+') 1027ec681f3Smrg string++; 1037ec681f3Smrg if (base == 0 && *string == '0') { 1047ec681f3Smrg numberFound = true; 1057ec681f3Smrg if (*(string+1) == 'x' || *(string+1) == 'X') { 1067ec681f3Smrg radix = 16; 1077ec681f3Smrg string += 2; 1087ec681f3Smrg } else { 1097ec681f3Smrg radix = 8; 1107ec681f3Smrg string++; 1117ec681f3Smrg } 1127ec681f3Smrg } 1137ec681f3Smrg do { 1147ec681f3Smrg int digit = -1; 1157ec681f3Smrg if (radix <= 10) { 1167ec681f3Smrg if (*string >= '0' && *string < '0' + radix) 1177ec681f3Smrg digit = *string - '0'; 1187ec681f3Smrg } else { 1197ec681f3Smrg if (*string >= '0' && *string <= '9') 1207ec681f3Smrg digit = *string - '0'; 1217ec681f3Smrg else if (*string >= 'a' && *string < 'a' + radix - 10) 1227ec681f3Smrg digit = *string - 'a' + 10; 1237ec681f3Smrg else if (*string >= 'A' && *string < 'A' + radix - 10) 1247ec681f3Smrg digit = *string - 'A' + 10; 1257ec681f3Smrg } 1267ec681f3Smrg if (digit != -1) { 1277ec681f3Smrg numberFound = true; 1287ec681f3Smrg result = radix*result + digit; 1297ec681f3Smrg string++; 1307ec681f3Smrg } else 1317ec681f3Smrg break; 1327ec681f3Smrg } while (true); 1337ec681f3Smrg *tail = numberFound ? string : start; 1347ec681f3Smrg return sign * result; 13501e04c3fSmrg} 13601e04c3fSmrg 13701e04c3fSmrg/** \brief Locale-independent floating-point parser. 13801e04c3fSmrg * 13901e04c3fSmrg * Works similar to strtod. Leading space is NOT skipped. The input 14001e04c3fSmrg * number may have an optional sign. '.' is interpreted as decimal 14101e04c3fSmrg * point and may occur at most once. Optionally the number may end in 14201e04c3fSmrg * [eE]<exponent>, where <exponent> is an integer as recognized by 14301e04c3fSmrg * strToI. In that case the result is number * 10^exponent. After 14401e04c3fSmrg * returning tail points to the first character that is not part of 14501e04c3fSmrg * the floating point number. If no number was found then tail points 14601e04c3fSmrg * to the start of the input string. 14701e04c3fSmrg * 14801e04c3fSmrg * Uses two passes for maximum accuracy. */ 14901e04c3fSmrgstatic float 1507ec681f3SmrgstrToF(const char *string, const char **tail) 15101e04c3fSmrg{ 1527ec681f3Smrg int nDigits = 0, pointPos, exponent; 1537ec681f3Smrg float sign = 1.0f, result = 0.0f, scale; 1547ec681f3Smrg const char *start = string, *numStart; 1557ec681f3Smrg 1567ec681f3Smrg /* sign */ 1577ec681f3Smrg if (*string == '-') { 1587ec681f3Smrg sign = -1.0f; 1597ec681f3Smrg string++; 1607ec681f3Smrg } else if (*string == '+') 1617ec681f3Smrg string++; 1627ec681f3Smrg 1637ec681f3Smrg /* first pass: determine position of decimal point, number of 1647ec681f3Smrg * digits, exponent and the end of the number. */ 1657ec681f3Smrg numStart = string; 1667ec681f3Smrg while (*string >= '0' && *string <= '9') { 1677ec681f3Smrg string++; 1687ec681f3Smrg nDigits++; 1697ec681f3Smrg } 1707ec681f3Smrg pointPos = nDigits; 1717ec681f3Smrg if (*string == '.') { 1727ec681f3Smrg string++; 1737ec681f3Smrg while (*string >= '0' && *string <= '9') { 1747ec681f3Smrg string++; 1757ec681f3Smrg nDigits++; 1767ec681f3Smrg } 1777ec681f3Smrg } 1787ec681f3Smrg if (nDigits == 0) { 1797ec681f3Smrg /* no digits, no number */ 1807ec681f3Smrg *tail = start; 1817ec681f3Smrg return 0.0f; 1827ec681f3Smrg } 1837ec681f3Smrg *tail = string; 1847ec681f3Smrg if (*string == 'e' || *string == 'E') { 1857ec681f3Smrg const char *expTail; 1867ec681f3Smrg exponent = strToI(string+1, &expTail, 10); 1877ec681f3Smrg if (expTail == string+1) 1887ec681f3Smrg exponent = 0; 1897ec681f3Smrg else 1907ec681f3Smrg *tail = expTail; 1917ec681f3Smrg } else 1927ec681f3Smrg exponent = 0; 1937ec681f3Smrg string = numStart; 1947ec681f3Smrg 1957ec681f3Smrg /* scale of the first digit */ 1967ec681f3Smrg scale = sign * (float)pow(10.0, (double)(pointPos-1 + exponent)); 1977ec681f3Smrg 1987ec681f3Smrg /* second pass: parse digits */ 1997ec681f3Smrg do { 2007ec681f3Smrg if (*string != '.') { 2017ec681f3Smrg assert(*string >= '0' && *string <= '9'); 2027ec681f3Smrg result += scale * (float)(*string - '0'); 2037ec681f3Smrg scale *= 0.1f; 2047ec681f3Smrg nDigits--; 2057ec681f3Smrg } 2067ec681f3Smrg string++; 2077ec681f3Smrg } while (nDigits > 0); 2087ec681f3Smrg 2097ec681f3Smrg return result; 21001e04c3fSmrg} 21101e04c3fSmrg 21201e04c3fSmrg/** \brief Parse a value of a given type. */ 21301e04c3fSmrgstatic unsigned char 2147ec681f3SmrgparseValue(driOptionValue *v, driOptionType type, const char *string) 21501e04c3fSmrg{ 2167ec681f3Smrg const char *tail = NULL; 2177ec681f3Smrg /* skip leading white-space */ 2187ec681f3Smrg string += strspn(string, " \f\n\r\t\v"); 2197ec681f3Smrg switch (type) { 2207ec681f3Smrg case DRI_BOOL: 2217ec681f3Smrg if (!strcmp(string, "false")) { 2227ec681f3Smrg v->_bool = false; 2237ec681f3Smrg tail = string + 5; 2247ec681f3Smrg } else if (!strcmp(string, "true")) { 2257ec681f3Smrg v->_bool = true; 2267ec681f3Smrg tail = string + 4; 2277ec681f3Smrg } 2287ec681f3Smrg else 2297ec681f3Smrg return false; 2307ec681f3Smrg break; 2317ec681f3Smrg case DRI_ENUM: /* enum is just a special integer */ 2327ec681f3Smrg case DRI_INT: 2337ec681f3Smrg v->_int = strToI(string, &tail, 0); 2347ec681f3Smrg break; 2357ec681f3Smrg case DRI_FLOAT: 2367ec681f3Smrg v->_float = strToF(string, &tail); 2377ec681f3Smrg break; 2387ec681f3Smrg case DRI_STRING: 2397ec681f3Smrg free(v->_string); 2407ec681f3Smrg v->_string = strndup(string, STRING_CONF_MAXLEN); 2417ec681f3Smrg return true; 2427ec681f3Smrg case DRI_SECTION: 2437ec681f3Smrg unreachable("shouldn't be parsing values in section declarations"); 2447ec681f3Smrg } 2457ec681f3Smrg 2467ec681f3Smrg if (tail == string) 2477ec681f3Smrg return false; /* empty string (or containing only white-space) */ 2487ec681f3Smrg /* skip trailing white space */ 2497ec681f3Smrg if (*tail) 2507ec681f3Smrg tail += strspn(tail, " \f\n\r\t\v"); 2517ec681f3Smrg if (*tail) 2527ec681f3Smrg return false; /* something left over that is not part of value */ 2537ec681f3Smrg 2547ec681f3Smrg return true; 2557ec681f3Smrg} 2567ec681f3Smrg 2577ec681f3Smrg/** \brief Find an option in an option cache with the name as key */ 2587ec681f3Smrgstatic uint32_t 2597ec681f3SmrgfindOption(const driOptionCache *cache, const char *name) 2607ec681f3Smrg{ 2617ec681f3Smrg uint32_t len = strlen(name); 2627ec681f3Smrg uint32_t size = 1 << cache->tableSize, mask = size - 1; 2637ec681f3Smrg uint32_t hash = 0; 2647ec681f3Smrg uint32_t i, shift; 2657ec681f3Smrg 2667ec681f3Smrg /* compute a hash from the variable length name */ 2677ec681f3Smrg for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31) 2687ec681f3Smrg hash += (uint32_t)name[i] << shift; 2697ec681f3Smrg hash *= hash; 2707ec681f3Smrg hash = (hash >> (16-cache->tableSize/2)) & mask; 2717ec681f3Smrg 2727ec681f3Smrg /* this is just the starting point of the linear search for the option */ 2737ec681f3Smrg for (i = 0; i < size; ++i, hash = (hash+1) & mask) { 2747ec681f3Smrg /* if we hit an empty entry then the option is not defined (yet) */ 2757ec681f3Smrg if (cache->info[hash].name == 0) 2767ec681f3Smrg break; 2777ec681f3Smrg else if (!strcmp(name, cache->info[hash].name)) 2787ec681f3Smrg break; 2797ec681f3Smrg } 2807ec681f3Smrg /* this assertion fails if the hash table is full */ 2817ec681f3Smrg assert (i < size); 2827ec681f3Smrg 2837ec681f3Smrg return hash; 2847ec681f3Smrg} 2857ec681f3Smrg 2867ec681f3Smrg/** \brief Like strdup with error checking. */ 2877ec681f3Smrg#define XSTRDUP(dest,source) do { \ 2887ec681f3Smrg if (!(dest = strdup(source))) { \ 2897ec681f3Smrg fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \ 2907ec681f3Smrg abort(); \ 2917ec681f3Smrg } \ 2927ec681f3Smrg } while (0) 2937ec681f3Smrg 2947ec681f3Smrg/** \brief Check if a value is in info->range. */ 2957ec681f3SmrgUNUSED static bool 2967ec681f3SmrgcheckValue(const driOptionValue *v, const driOptionInfo *info) 2977ec681f3Smrg{ 2987ec681f3Smrg switch (info->type) { 2997ec681f3Smrg case DRI_ENUM: /* enum is just a special integer */ 3007ec681f3Smrg case DRI_INT: 3017ec681f3Smrg return (info->range.start._int == info->range.end._int || 3027ec681f3Smrg (v->_int >= info->range.start._int && 3037ec681f3Smrg v->_int <= info->range.end._int)); 3047ec681f3Smrg 3057ec681f3Smrg case DRI_FLOAT: 3067ec681f3Smrg return (info->range.start._float == info->range.end._float || 3077ec681f3Smrg (v->_float >= info->range.start._float && 3087ec681f3Smrg v->_float <= info->range.end._float)); 3097ec681f3Smrg 3107ec681f3Smrg default: 3117ec681f3Smrg return true; 3127ec681f3Smrg } 3137ec681f3Smrg} 3147ec681f3Smrg 3157ec681f3Smrgvoid 3167ec681f3SmrgdriParseOptionInfo(driOptionCache *info, 3177ec681f3Smrg const driOptionDescription *configOptions, 3187ec681f3Smrg unsigned numOptions) 3197ec681f3Smrg{ 3207ec681f3Smrg /* Make the hash table big enough to fit more than the maximum number of 3217ec681f3Smrg * config options we've ever seen in a driver. 3227ec681f3Smrg */ 3237ec681f3Smrg info->tableSize = 6; 3247ec681f3Smrg info->info = calloc((size_t)1 << info->tableSize, sizeof(driOptionInfo)); 3257ec681f3Smrg info->values = calloc((size_t)1 << info->tableSize, sizeof(driOptionValue)); 3267ec681f3Smrg if (info->info == NULL || info->values == NULL) { 3277ec681f3Smrg fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); 3287ec681f3Smrg abort(); 3297ec681f3Smrg } 3307ec681f3Smrg 3317ec681f3Smrg UNUSED bool in_section = false; 3327ec681f3Smrg for (int o = 0; o < numOptions; o++) { 3337ec681f3Smrg const driOptionDescription *opt = &configOptions[o]; 3347ec681f3Smrg 3357ec681f3Smrg if (opt->info.type == DRI_SECTION) { 3367ec681f3Smrg in_section = true; 3377ec681f3Smrg continue; 3387ec681f3Smrg } 3397ec681f3Smrg 3407ec681f3Smrg /* for driconf xml generation, options must always be preceded by a 3417ec681f3Smrg * DRI_CONF_SECTION 3427ec681f3Smrg */ 3437ec681f3Smrg assert(in_section); 3447ec681f3Smrg 3457ec681f3Smrg const char *name = opt->info.name; 3467ec681f3Smrg int i = findOption(info, name); 3477ec681f3Smrg driOptionInfo *optinfo = &info->info[i]; 3487ec681f3Smrg driOptionValue *optval = &info->values[i]; 3497ec681f3Smrg 3507ec681f3Smrg assert(!optinfo->name); /* No duplicate options in your list. */ 3517ec681f3Smrg 3527ec681f3Smrg optinfo->type = opt->info.type; 3537ec681f3Smrg optinfo->range = opt->info.range; 3547ec681f3Smrg XSTRDUP(optinfo->name, name); 3557ec681f3Smrg 3567ec681f3Smrg switch (opt->info.type) { 35701e04c3fSmrg case DRI_BOOL: 3587ec681f3Smrg optval->_bool = opt->value._bool; 3597ec681f3Smrg break; 3607ec681f3Smrg 36101e04c3fSmrg case DRI_INT: 3627ec681f3Smrg case DRI_ENUM: 3637ec681f3Smrg optval->_int = opt->value._int; 3647ec681f3Smrg break; 3657ec681f3Smrg 36601e04c3fSmrg case DRI_FLOAT: 3677ec681f3Smrg optval->_float = opt->value._float; 3687ec681f3Smrg break; 3697ec681f3Smrg 37001e04c3fSmrg case DRI_STRING: 3717ec681f3Smrg XSTRDUP(optval->_string, opt->value._string); 3727ec681f3Smrg break; 3737ec681f3Smrg 3747ec681f3Smrg case DRI_SECTION: 3757ec681f3Smrg unreachable("handled above"); 3767ec681f3Smrg } 3777ec681f3Smrg 3787ec681f3Smrg /* Built-in default values should always be valid. */ 3797ec681f3Smrg assert(checkValue(optval, optinfo)); 3807ec681f3Smrg 3817ec681f3Smrg char *envVal = getenv(name); 3827ec681f3Smrg if (envVal != NULL) { 3837ec681f3Smrg driOptionValue v; 3847ec681f3Smrg 3857ec681f3Smrg /* make sure the value is initialized to something sensible */ 3867ec681f3Smrg v._string = NULL; 3877ec681f3Smrg 3887ec681f3Smrg if (parseValue(&v, opt->info.type, envVal) && 3897ec681f3Smrg checkValue(&v, optinfo)) { 3907ec681f3Smrg /* don't use XML_WARNING, we want the user to see this! */ 3917ec681f3Smrg if (be_verbose()) { 3927ec681f3Smrg fprintf(stderr, 3937ec681f3Smrg "ATTENTION: default value of option %s overridden by environment.\n", 3947ec681f3Smrg name); 3957ec681f3Smrg } 3967ec681f3Smrg *optval = v; 3977ec681f3Smrg } else { 3987ec681f3Smrg fprintf(stderr, "illegal environment value for %s: \"%s\". Ignoring.\n", 3997ec681f3Smrg name, envVal); 4007ec681f3Smrg } 4017ec681f3Smrg } 4027ec681f3Smrg } 40301e04c3fSmrg} 40401e04c3fSmrg 4057ec681f3Smrgchar * 4067ec681f3SmrgdriGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions) 40701e04c3fSmrg{ 4087ec681f3Smrg char *str = ralloc_strdup(NULL, 4097ec681f3Smrg "<?xml version=\"1.0\" standalone=\"yes\"?>\n" \ 4107ec681f3Smrg "<!DOCTYPE driinfo [\n" \ 4117ec681f3Smrg " <!ELEMENT driinfo (section*)>\n" \ 4127ec681f3Smrg " <!ELEMENT section (description+, option+)>\n" \ 4137ec681f3Smrg " <!ELEMENT description (enum*)>\n" \ 4147ec681f3Smrg " <!ATTLIST description lang CDATA #FIXED \"en\"\n" \ 4157ec681f3Smrg " text CDATA #REQUIRED>\n" \ 4167ec681f3Smrg " <!ELEMENT option (description+)>\n" \ 4177ec681f3Smrg " <!ATTLIST option name CDATA #REQUIRED\n" \ 4187ec681f3Smrg " type (bool|enum|int|float) #REQUIRED\n" \ 4197ec681f3Smrg " default CDATA #REQUIRED\n" \ 4207ec681f3Smrg " valid CDATA #IMPLIED>\n" \ 4217ec681f3Smrg " <!ELEMENT enum EMPTY>\n" \ 4227ec681f3Smrg " <!ATTLIST enum value CDATA #REQUIRED\n" \ 4237ec681f3Smrg " text CDATA #REQUIRED>\n" \ 4247ec681f3Smrg "]>" \ 4257ec681f3Smrg "<driinfo>\n"); 4267ec681f3Smrg 4277ec681f3Smrg bool in_section = false; 4287ec681f3Smrg for (int o = 0; o < numOptions; o++) { 4297ec681f3Smrg const driOptionDescription *opt = &configOptions[o]; 4307ec681f3Smrg 4317ec681f3Smrg const char *name = opt->info.name; 4327ec681f3Smrg const char *types[] = { 4337ec681f3Smrg [DRI_BOOL] = "bool", 4347ec681f3Smrg [DRI_INT] = "int", 4357ec681f3Smrg [DRI_FLOAT] = "float", 4367ec681f3Smrg [DRI_ENUM] = "enum", 4377ec681f3Smrg [DRI_STRING] = "string", 4387ec681f3Smrg }; 4397ec681f3Smrg 4407ec681f3Smrg if (opt->info.type == DRI_SECTION) { 4417ec681f3Smrg if (in_section) 4427ec681f3Smrg ralloc_asprintf_append(&str, " </section>\n"); 4437ec681f3Smrg 4447ec681f3Smrg ralloc_asprintf_append(&str, 4457ec681f3Smrg " <section>\n" 4467ec681f3Smrg " <description lang=\"en\" text=\"%s\"/>\n", 4477ec681f3Smrg opt->desc); 4487ec681f3Smrg 4497ec681f3Smrg in_section = true; 4507ec681f3Smrg continue; 4517ec681f3Smrg } 4527ec681f3Smrg 4537ec681f3Smrg ralloc_asprintf_append(&str, 4547ec681f3Smrg " <option name=\"%s\" type=\"%s\" default=\"", 4557ec681f3Smrg name, 4567ec681f3Smrg types[opt->info.type]); 4577ec681f3Smrg 4587ec681f3Smrg switch (opt->info.type) { 4597ec681f3Smrg case DRI_BOOL: 4607ec681f3Smrg ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false"); 4617ec681f3Smrg break; 46201e04c3fSmrg 46301e04c3fSmrg case DRI_INT: 4647ec681f3Smrg case DRI_ENUM: 4657ec681f3Smrg ralloc_asprintf_append(&str, "%d", opt->value._int); 4667ec681f3Smrg break; 4677ec681f3Smrg 46801e04c3fSmrg case DRI_FLOAT: 4697ec681f3Smrg ralloc_asprintf_append(&str, "%f", opt->value._float); 4707ec681f3Smrg break; 4717ec681f3Smrg 47201e04c3fSmrg case DRI_STRING: 4737ec681f3Smrg ralloc_asprintf_append(&str, "%s", opt->value._string); 4747ec681f3Smrg break; 4757ec681f3Smrg 4767ec681f3Smrg case DRI_SECTION: 4777ec681f3Smrg unreachable("handled above"); 4787ec681f3Smrg break; 4797ec681f3Smrg } 4807ec681f3Smrg ralloc_asprintf_append(&str, "\""); 4817ec681f3Smrg 4827ec681f3Smrg 4837ec681f3Smrg switch (opt->info.type) { 4847ec681f3Smrg case DRI_INT: 4857ec681f3Smrg case DRI_ENUM: 4867ec681f3Smrg if (opt->info.range.start._int < opt->info.range.end._int) { 4877ec681f3Smrg ralloc_asprintf_append(&str, " valid=\"%d:%d\"", 4887ec681f3Smrg opt->info.range.start._int, 4897ec681f3Smrg opt->info.range.end._int); 4907ec681f3Smrg } 4917ec681f3Smrg break; 4927ec681f3Smrg 4937ec681f3Smrg case DRI_FLOAT: 4947ec681f3Smrg if (opt->info.range.start._float < opt->info.range.end._float) { 4957ec681f3Smrg ralloc_asprintf_append(&str, " valid=\"%f:%f\"", 4967ec681f3Smrg opt->info.range.start._float, 4977ec681f3Smrg opt->info.range.end._float); 4987ec681f3Smrg } 4997ec681f3Smrg break; 5007ec681f3Smrg 50101e04c3fSmrg default: 5027ec681f3Smrg break; 5037ec681f3Smrg } 5047ec681f3Smrg 5057ec681f3Smrg ralloc_asprintf_append(&str, ">\n"); /* end of <option> */ 5067ec681f3Smrg 5077ec681f3Smrg 5087ec681f3Smrg ralloc_asprintf_append(&str, " <description lang=\"en\" text=\"%s\"%s>\n", 5097ec681f3Smrg opt->desc, opt->info.type != DRI_ENUM ? "/" : ""); 5107ec681f3Smrg 5117ec681f3Smrg if (opt->info.type == DRI_ENUM) { 5127ec681f3Smrg for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) { 5137ec681f3Smrg ralloc_asprintf_append(&str, " <enum value=\"%d\" text=\"%s\"/>\n", 5147ec681f3Smrg opt->enums[i].value, opt->enums[i].desc); 5157ec681f3Smrg } 5167ec681f3Smrg ralloc_asprintf_append(&str, " </description>\n"); 5177ec681f3Smrg } 5187ec681f3Smrg 5197ec681f3Smrg ralloc_asprintf_append(&str, " </option>\n"); 5207ec681f3Smrg } 5217ec681f3Smrg 5227ec681f3Smrg assert(in_section); 5237ec681f3Smrg ralloc_asprintf_append(&str, " </section>\n"); 5247ec681f3Smrg 5257ec681f3Smrg ralloc_asprintf_append(&str, "</driinfo>\n"); 5267ec681f3Smrg 5277ec681f3Smrg char *output = strdup(str); 5287ec681f3Smrg ralloc_free(str); 5297ec681f3Smrg 5307ec681f3Smrg return output; 53101e04c3fSmrg} 53201e04c3fSmrg 53301e04c3fSmrg/** 53401e04c3fSmrg * Print message to \c stderr if the \c LIBGL_DEBUG environment variable 5357ec681f3Smrg * is set. 5367ec681f3Smrg * 53701e04c3fSmrg * Is called from the drivers. 5387ec681f3Smrg * 53901e04c3fSmrg * \param f \c printf like format string. 54001e04c3fSmrg */ 54101e04c3fSmrgstatic void 54201e04c3fSmrg__driUtilMessage(const char *f, ...) 54301e04c3fSmrg{ 5447ec681f3Smrg va_list args; 5457ec681f3Smrg const char *libgl_debug; 5467ec681f3Smrg 5477ec681f3Smrg libgl_debug=getenv("LIBGL_DEBUG"); 5487ec681f3Smrg if (libgl_debug && !strstr(libgl_debug, "quiet")) { 5497ec681f3Smrg fprintf(stderr, "libGL: "); 5507ec681f3Smrg va_start(args, f); 5517ec681f3Smrg vfprintf(stderr, f, args); 5527ec681f3Smrg va_end(args); 5537ec681f3Smrg fprintf(stderr, "\n"); 5547ec681f3Smrg } 55501e04c3fSmrg} 55601e04c3fSmrg 5577ec681f3Smrg/* We don't have real line/column # info in static-config case: */ 5587ec681f3Smrg#if !WITH_XML_CONFIG 5597ec681f3Smrg# define XML_GetCurrentLineNumber(p) -1 5607ec681f3Smrg# define XML_GetCurrentColumnNumber(p) -1 5617ec681f3Smrg#endif 5627ec681f3Smrg 56301e04c3fSmrg/** \brief Output a warning message. */ 5647ec681f3Smrg#define XML_WARNING1(msg) do { \ 5657ec681f3Smrg __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \ 5667ec681f3Smrg (int) XML_GetCurrentLineNumber(data->parser), \ 5677ec681f3Smrg (int) XML_GetCurrentColumnNumber(data->parser)); \ 5687ec681f3Smrg } while (0) 5697ec681f3Smrg#define XML_WARNING(msg, ...) do { \ 5707ec681f3Smrg __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \ 5717ec681f3Smrg (int) XML_GetCurrentLineNumber(data->parser), \ 5727ec681f3Smrg (int) XML_GetCurrentColumnNumber(data->parser), \ 5737ec681f3Smrg ##__VA_ARGS__); \ 5747ec681f3Smrg } while (0) 57501e04c3fSmrg/** \brief Output an error message. */ 5767ec681f3Smrg#define XML_ERROR1(msg) do { \ 5777ec681f3Smrg __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \ 5787ec681f3Smrg (int) XML_GetCurrentLineNumber(data->parser), \ 5797ec681f3Smrg (int) XML_GetCurrentColumnNumber(data->parser)); \ 5807ec681f3Smrg } while (0) 5817ec681f3Smrg#define XML_ERROR(msg, ...) do { \ 5827ec681f3Smrg __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \ 5837ec681f3Smrg (int) XML_GetCurrentLineNumber(data->parser), \ 5847ec681f3Smrg (int) XML_GetCurrentColumnNumber(data->parser), \ 5857ec681f3Smrg ##__VA_ARGS__); \ 5867ec681f3Smrg } while (0) 58701e04c3fSmrg 5887ec681f3Smrg/** \brief Parser context for configuration files. */ 5897ec681f3Smrgstruct OptConfData { 5907ec681f3Smrg const char *name; 5917ec681f3Smrg#if WITH_XMLCONFIG 5927ec681f3Smrg XML_Parser parser; 5937ec681f3Smrg#endif 5947ec681f3Smrg driOptionCache *cache; 5957ec681f3Smrg int screenNum; 5967ec681f3Smrg const char *driverName, *execName; 5977ec681f3Smrg const char *kernelDriverName; 5987ec681f3Smrg const char *deviceName; 5997ec681f3Smrg const char *engineName; 6007ec681f3Smrg const char *applicationName; 6017ec681f3Smrg uint32_t engineVersion; 6027ec681f3Smrg uint32_t applicationVersion; 6037ec681f3Smrg uint32_t ignoringDevice; 6047ec681f3Smrg uint32_t ignoringApp; 6057ec681f3Smrg uint32_t inDriConf; 6067ec681f3Smrg uint32_t inDevice; 6077ec681f3Smrg uint32_t inApp; 6087ec681f3Smrg uint32_t inOption; 60901e04c3fSmrg}; 61001e04c3fSmrg 6117ec681f3Smrg/** \brief Parse a list of ranges of type info->type. */ 6127ec681f3Smrgstatic unsigned char 6137ec681f3SmrgparseRange(driOptionInfo *info, const char *string) 61401e04c3fSmrg{ 6157ec681f3Smrg char *cp; 6167ec681f3Smrg 6177ec681f3Smrg XSTRDUP(cp, string); 6187ec681f3Smrg 6197ec681f3Smrg char *sep; 6207ec681f3Smrg sep = strchr(cp, ':'); 6217ec681f3Smrg if (!sep) { 6227ec681f3Smrg free(cp); 6237ec681f3Smrg return false; 6247ec681f3Smrg } 6257ec681f3Smrg 6267ec681f3Smrg *sep = '\0'; 6277ec681f3Smrg if (!parseValue(&info->range.start, info->type, cp) || 6287ec681f3Smrg !parseValue(&info->range.end, info->type, sep+1)) { 6297ec681f3Smrg free(cp); 6307ec681f3Smrg return false; 6317ec681f3Smrg } 6327ec681f3Smrg if (info->type == DRI_INT && 6337ec681f3Smrg info->range.start._int >= info->range.end._int) { 6347ec681f3Smrg free(cp); 6357ec681f3Smrg return false; 6367ec681f3Smrg } 6377ec681f3Smrg if (info->type == DRI_FLOAT && 6387ec681f3Smrg info->range.start._float >= info->range.end._float) { 6397ec681f3Smrg free(cp); 6407ec681f3Smrg return false; 6417ec681f3Smrg } 6427ec681f3Smrg 6437ec681f3Smrg free(cp); 6447ec681f3Smrg return true; 64501e04c3fSmrg} 64601e04c3fSmrg 6477ec681f3Smrg/** \brief Parse attributes of a device element. */ 64801e04c3fSmrgstatic void 6497ec681f3SmrgparseDeviceAttr(struct OptConfData *data, const char **attr) 65001e04c3fSmrg{ 6517ec681f3Smrg uint32_t i; 6527ec681f3Smrg const char *driver = NULL, *screen = NULL, *kernel = NULL, *device = NULL; 6537ec681f3Smrg for (i = 0; attr[i]; i += 2) { 6547ec681f3Smrg if (!strcmp(attr[i], "driver")) driver = attr[i+1]; 6557ec681f3Smrg else if (!strcmp(attr[i], "screen")) screen = attr[i+1]; 6567ec681f3Smrg else if (!strcmp(attr[i], "kernel_driver")) kernel = attr[i+1]; 6577ec681f3Smrg else if (!strcmp(attr[i], "device")) device = attr[i+1]; 6587ec681f3Smrg else XML_WARNING("unknown device attribute: %s.", attr[i]); 6597ec681f3Smrg } 6607ec681f3Smrg if (driver && strcmp(driver, data->driverName)) 6617ec681f3Smrg data->ignoringDevice = data->inDevice; 6627ec681f3Smrg else if (kernel && (!data->kernelDriverName || 6637ec681f3Smrg strcmp(kernel, data->kernelDriverName))) 6647ec681f3Smrg data->ignoringDevice = data->inDevice; 6657ec681f3Smrg else if (device && (!data->deviceName || 6667ec681f3Smrg strcmp(device, data->deviceName))) 6677ec681f3Smrg data->ignoringDevice = data->inDevice; 6687ec681f3Smrg else if (screen) { 6697ec681f3Smrg driOptionValue screenNum; 6707ec681f3Smrg if (!parseValue(&screenNum, DRI_INT, screen)) 6717ec681f3Smrg XML_WARNING("illegal screen number: %s.", screen); 6727ec681f3Smrg else if (screenNum._int != data->screenNum) 6737ec681f3Smrg data->ignoringDevice = data->inDevice; 6747ec681f3Smrg } 67501e04c3fSmrg} 67601e04c3fSmrg 6777ec681f3Smrg/** \brief Parse attributes of an application element. */ 67801e04c3fSmrgstatic void 6797ec681f3SmrgparseAppAttr(struct OptConfData *data, const char **attr) 68001e04c3fSmrg{ 6817ec681f3Smrg uint32_t i; 6827ec681f3Smrg const char *exec = NULL; 6837ec681f3Smrg const char *sha1 = NULL; 6847ec681f3Smrg const char *application_name_match = NULL; 6857ec681f3Smrg const char *application_versions = NULL; 6867ec681f3Smrg driOptionInfo version_range = { 6877ec681f3Smrg .type = DRI_INT, 6887ec681f3Smrg }; 6897ec681f3Smrg 6907ec681f3Smrg for (i = 0; attr[i]; i += 2) { 6917ec681f3Smrg if (!strcmp(attr[i], "name")) /* not needed here */; 6927ec681f3Smrg else if (!strcmp(attr[i], "executable")) exec = attr[i+1]; 6937ec681f3Smrg else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1]; 6947ec681f3Smrg else if (!strcmp(attr[i], "application_name_match")) 6957ec681f3Smrg application_name_match = attr[i+1]; 6967ec681f3Smrg else if (!strcmp(attr[i], "application_versions")) 6977ec681f3Smrg application_versions = attr[i+1]; 6987ec681f3Smrg else XML_WARNING("unknown application attribute: %s.", attr[i]); 6997ec681f3Smrg } 7007ec681f3Smrg if (exec && strcmp(exec, data->execName)) { 7017ec681f3Smrg data->ignoringApp = data->inApp; 7027ec681f3Smrg } else if (sha1) { 7037ec681f3Smrg /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */ 7047ec681f3Smrg if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) { 7057ec681f3Smrg XML_WARNING("Incorrect sha1 application attribute"); 7067ec681f3Smrg data->ignoringApp = data->inApp; 7077ec681f3Smrg } else { 7087ec681f3Smrg size_t len; 7097ec681f3Smrg char* content; 7107ec681f3Smrg char path[PATH_MAX]; 7117ec681f3Smrg if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 && 7127ec681f3Smrg (content = os_read_file(path, &len))) { 7137ec681f3Smrg uint8_t sha1x[SHA1_DIGEST_LENGTH]; 7147ec681f3Smrg char sha1s[SHA1_DIGEST_STRING_LENGTH]; 7157ec681f3Smrg _mesa_sha1_compute(content, len, sha1x); 7167ec681f3Smrg _mesa_sha1_format((char*) sha1s, sha1x); 7177ec681f3Smrg free(content); 7187ec681f3Smrg 7197ec681f3Smrg if (strcmp(sha1, sha1s)) { 7207ec681f3Smrg data->ignoringApp = data->inApp; 7217ec681f3Smrg } 7227ec681f3Smrg } else { 7237ec681f3Smrg data->ignoringApp = data->inApp; 7247ec681f3Smrg } 7257ec681f3Smrg } 7267ec681f3Smrg } else if (application_name_match) { 7277ec681f3Smrg regex_t re; 7287ec681f3Smrg 7297ec681f3Smrg if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 7307ec681f3Smrg if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH) 7317ec681f3Smrg data->ignoringApp = data->inApp; 7327ec681f3Smrg regfree(&re); 7337ec681f3Smrg } else 7347ec681f3Smrg XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match); 7357ec681f3Smrg } 7367ec681f3Smrg if (application_versions) { 7377ec681f3Smrg driOptionValue v = { ._int = data->applicationVersion }; 7387ec681f3Smrg if (parseRange(&version_range, application_versions)) { 7397ec681f3Smrg if (!checkValue(&v, &version_range)) 7407ec681f3Smrg data->ignoringApp = data->inApp; 7417ec681f3Smrg } else { 7427ec681f3Smrg XML_WARNING("Failed to parse application_versions range=\"%s\".", 7437ec681f3Smrg application_versions); 7447ec681f3Smrg } 7457ec681f3Smrg } 74601e04c3fSmrg} 74701e04c3fSmrg 7487ec681f3Smrg/** \brief Parse attributes of an application element. */ 74901e04c3fSmrgstatic void 7507ec681f3SmrgparseEngineAttr(struct OptConfData *data, const char **attr) 75101e04c3fSmrg{ 7527ec681f3Smrg uint32_t i; 7537ec681f3Smrg const char *engine_name_match = NULL, *engine_versions = NULL; 7547ec681f3Smrg driOptionInfo version_range = { 7557ec681f3Smrg .type = DRI_INT, 7567ec681f3Smrg }; 7577ec681f3Smrg for (i = 0; attr[i]; i += 2) { 7587ec681f3Smrg if (!strcmp(attr[i], "name")) /* not needed here */; 7597ec681f3Smrg else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1]; 7607ec681f3Smrg else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1]; 7617ec681f3Smrg else XML_WARNING("unknown application attribute: %s.", attr[i]); 7627ec681f3Smrg } 7637ec681f3Smrg if (engine_name_match) { 7647ec681f3Smrg regex_t re; 7657ec681f3Smrg 7667ec681f3Smrg if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) { 7677ec681f3Smrg if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH) 7687ec681f3Smrg data->ignoringApp = data->inApp; 7697ec681f3Smrg regfree(&re); 7707ec681f3Smrg } else 7717ec681f3Smrg XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match); 7727ec681f3Smrg } 7737ec681f3Smrg if (engine_versions) { 7747ec681f3Smrg driOptionValue v = { ._int = data->engineVersion }; 7757ec681f3Smrg if (parseRange(&version_range, engine_versions)) { 7767ec681f3Smrg if (!checkValue(&v, &version_range)) 7777ec681f3Smrg data->ignoringApp = data->inApp; 7787ec681f3Smrg } else { 7797ec681f3Smrg XML_WARNING("Failed to parse engine_versions range=\"%s\".", 7807ec681f3Smrg engine_versions); 7817ec681f3Smrg } 7827ec681f3Smrg } 78301e04c3fSmrg} 78401e04c3fSmrg 7857ec681f3Smrg/** \brief Parse attributes of an option element. */ 78601e04c3fSmrgstatic void 7877ec681f3SmrgparseOptConfAttr(struct OptConfData *data, const char **attr) 78801e04c3fSmrg{ 7897ec681f3Smrg uint32_t i; 7907ec681f3Smrg const char *name = NULL, *value = NULL; 7917ec681f3Smrg for (i = 0; attr[i]; i += 2) { 7927ec681f3Smrg if (!strcmp(attr[i], "name")) name = attr[i+1]; 7937ec681f3Smrg else if (!strcmp(attr[i], "value")) value = attr[i+1]; 7947ec681f3Smrg else XML_WARNING("unknown option attribute: %s.", attr[i]); 7957ec681f3Smrg } 7967ec681f3Smrg if (!name) XML_WARNING1("name attribute missing in option."); 7977ec681f3Smrg if (!value) XML_WARNING1("value attribute missing in option."); 7987ec681f3Smrg if (name && value) { 7997ec681f3Smrg driOptionCache *cache = data->cache; 8007ec681f3Smrg uint32_t opt = findOption(cache, name); 8017ec681f3Smrg if (cache->info[opt].name == NULL) 8027ec681f3Smrg /* don't use XML_WARNING, drirc defines options for all drivers, 8037ec681f3Smrg * but not all drivers support them */ 8047ec681f3Smrg return; 8057ec681f3Smrg else if (getenv(cache->info[opt].name)) { 8067ec681f3Smrg /* don't use XML_WARNING, we want the user to see this! */ 8077ec681f3Smrg if (be_verbose()) { 8087ec681f3Smrg fprintf(stderr, 8097ec681f3Smrg "ATTENTION: option value of option %s ignored.\n", 8107ec681f3Smrg cache->info[opt].name); 8117ec681f3Smrg } 8127ec681f3Smrg } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value)) 8137ec681f3Smrg XML_WARNING("illegal option value: %s.", value); 8147ec681f3Smrg } 81501e04c3fSmrg} 81601e04c3fSmrg 8177ec681f3Smrg#if WITH_XMLCONFIG 81801e04c3fSmrg 81901e04c3fSmrg/** \brief Elements in configuration files. */ 82001e04c3fSmrgenum OptConfElem { 8217ec681f3Smrg OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT 82201e04c3fSmrg}; 8237ec681f3Smrgstatic const char *OptConfElems[] = { 8247ec681f3Smrg [OC_APPLICATION] = "application", 8257ec681f3Smrg [OC_DEVICE] = "device", 8267ec681f3Smrg [OC_DRICONF] = "driconf", 8277ec681f3Smrg [OC_ENGINE] = "engine", 8287ec681f3Smrg [OC_OPTION] = "option", 82901e04c3fSmrg}; 83001e04c3fSmrg 8317ec681f3Smrgstatic int compare(const void *a, const void *b) { 8327ec681f3Smrg return strcmp(*(char *const*)a, *(char *const*)b); 83301e04c3fSmrg} 8347ec681f3Smrg/** \brief Binary search in a string array. */ 8357ec681f3Smrgstatic uint32_t 8367ec681f3SmrgbsearchStr(const char *name, const char *elems[], uint32_t count) 83701e04c3fSmrg{ 8387ec681f3Smrg const char **found; 8397ec681f3Smrg found = bsearch(&name, elems, count, sizeof(char *), compare); 8407ec681f3Smrg if (found) 8417ec681f3Smrg return found - elems; 8427ec681f3Smrg else 8437ec681f3Smrg return count; 84401e04c3fSmrg} 84501e04c3fSmrg 84601e04c3fSmrg/** \brief Handler for start element events. */ 84701e04c3fSmrgstatic void 8487ec681f3SmrgoptConfStartElem(void *userData, const char *name, 8497ec681f3Smrg const char **attr) 85001e04c3fSmrg{ 8517ec681f3Smrg struct OptConfData *data = (struct OptConfData *)userData; 8527ec681f3Smrg enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 8537ec681f3Smrg switch (elem) { 8547ec681f3Smrg case OC_DRICONF: 8557ec681f3Smrg if (data->inDriConf) 8567ec681f3Smrg XML_WARNING1("nested <driconf> elements."); 8577ec681f3Smrg if (attr[0]) 8587ec681f3Smrg XML_WARNING1("attributes specified on <driconf> element."); 8597ec681f3Smrg data->inDriConf++; 8607ec681f3Smrg break; 8617ec681f3Smrg case OC_DEVICE: 8627ec681f3Smrg if (!data->inDriConf) 8637ec681f3Smrg XML_WARNING1("<device> should be inside <driconf>."); 8647ec681f3Smrg if (data->inDevice) 8657ec681f3Smrg XML_WARNING1("nested <device> elements."); 8667ec681f3Smrg data->inDevice++; 8677ec681f3Smrg if (!data->ignoringDevice && !data->ignoringApp) 8687ec681f3Smrg parseDeviceAttr(data, attr); 8697ec681f3Smrg break; 8707ec681f3Smrg case OC_APPLICATION: 8717ec681f3Smrg if (!data->inDevice) 8727ec681f3Smrg XML_WARNING1("<application> should be inside <device>."); 8737ec681f3Smrg if (data->inApp) 8747ec681f3Smrg XML_WARNING1("nested <application> or <engine> elements."); 8757ec681f3Smrg data->inApp++; 8767ec681f3Smrg if (!data->ignoringDevice && !data->ignoringApp) 8777ec681f3Smrg parseAppAttr(data, attr); 8787ec681f3Smrg break; 8797ec681f3Smrg case OC_ENGINE: 8807ec681f3Smrg if (!data->inDevice) 8817ec681f3Smrg XML_WARNING1("<engine> should be inside <device>."); 8827ec681f3Smrg if (data->inApp) 8837ec681f3Smrg XML_WARNING1("nested <application> or <engine> elements."); 8847ec681f3Smrg data->inApp++; 8857ec681f3Smrg if (!data->ignoringDevice && !data->ignoringApp) 8867ec681f3Smrg parseEngineAttr(data, attr); 8877ec681f3Smrg break; 8887ec681f3Smrg case OC_OPTION: 8897ec681f3Smrg if (!data->inApp) 8907ec681f3Smrg XML_WARNING1("<option> should be inside <application>."); 8917ec681f3Smrg if (data->inOption) 8927ec681f3Smrg XML_WARNING1("nested <option> elements."); 8937ec681f3Smrg data->inOption++; 8947ec681f3Smrg if (!data->ignoringDevice && !data->ignoringApp) 8957ec681f3Smrg parseOptConfAttr(data, attr); 8967ec681f3Smrg break; 8977ec681f3Smrg default: 8987ec681f3Smrg XML_WARNING("unknown element: %s.", name); 8997ec681f3Smrg } 90001e04c3fSmrg} 90101e04c3fSmrg 90201e04c3fSmrg/** \brief Handler for end element events. */ 90301e04c3fSmrgstatic void 9047ec681f3SmrgoptConfEndElem(void *userData, const char *name) 90501e04c3fSmrg{ 9067ec681f3Smrg struct OptConfData *data = (struct OptConfData *)userData; 9077ec681f3Smrg enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT); 9087ec681f3Smrg switch (elem) { 9097ec681f3Smrg case OC_DRICONF: 9107ec681f3Smrg data->inDriConf--; 9117ec681f3Smrg break; 9127ec681f3Smrg case OC_DEVICE: 9137ec681f3Smrg if (data->inDevice-- == data->ignoringDevice) 9147ec681f3Smrg data->ignoringDevice = 0; 9157ec681f3Smrg break; 9167ec681f3Smrg case OC_APPLICATION: 9177ec681f3Smrg case OC_ENGINE: 9187ec681f3Smrg if (data->inApp-- == data->ignoringApp) 9197ec681f3Smrg data->ignoringApp = 0; 9207ec681f3Smrg break; 9217ec681f3Smrg case OC_OPTION: 9227ec681f3Smrg data->inOption--; 9237ec681f3Smrg break; 9247ec681f3Smrg default: 9257ec681f3Smrg /* unknown element, warning was produced on start tag */; 9267ec681f3Smrg } 92701e04c3fSmrg} 92801e04c3fSmrg 92901e04c3fSmrgstatic void 93001e04c3fSmrg_parseOneConfigFile(XML_Parser p) 93101e04c3fSmrg{ 93201e04c3fSmrg#define BUF_SIZE 0x1000 9337ec681f3Smrg struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p); 9347ec681f3Smrg int status; 9357ec681f3Smrg int fd; 9367ec681f3Smrg 9377ec681f3Smrg if ((fd = open(data->name, O_RDONLY)) == -1) { 9387ec681f3Smrg __driUtilMessage("Can't open configuration file %s: %s.", 9397ec681f3Smrg data->name, strerror(errno)); 9407ec681f3Smrg return; 9417ec681f3Smrg } 9427ec681f3Smrg 9437ec681f3Smrg while (1) { 9447ec681f3Smrg int bytesRead; 9457ec681f3Smrg void *buffer = XML_GetBuffer(p, BUF_SIZE); 9467ec681f3Smrg if (!buffer) { 9477ec681f3Smrg __driUtilMessage("Can't allocate parser buffer."); 9487ec681f3Smrg break; 9497ec681f3Smrg } 9507ec681f3Smrg bytesRead = read(fd, buffer, BUF_SIZE); 9517ec681f3Smrg if (bytesRead == -1) { 9527ec681f3Smrg __driUtilMessage("Error reading from configuration file %s: %s.", 9537ec681f3Smrg data->name, strerror(errno)); 9547ec681f3Smrg break; 9557ec681f3Smrg } 9567ec681f3Smrg status = XML_ParseBuffer(p, bytesRead, bytesRead == 0); 9577ec681f3Smrg if (!status) { 9587ec681f3Smrg XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p))); 9597ec681f3Smrg break; 9607ec681f3Smrg } 9617ec681f3Smrg if (bytesRead == 0) 9627ec681f3Smrg break; 9637ec681f3Smrg } 9647ec681f3Smrg 9657ec681f3Smrg close(fd); 96601e04c3fSmrg#undef BUF_SIZE 96701e04c3fSmrg} 96801e04c3fSmrg 96901e04c3fSmrg/** \brief Parse the named configuration file */ 97001e04c3fSmrgstatic void 97101e04c3fSmrgparseOneConfigFile(struct OptConfData *data, const char *filename) 97201e04c3fSmrg{ 9737ec681f3Smrg XML_Parser p; 9747ec681f3Smrg 9757ec681f3Smrg p = XML_ParserCreate(NULL); /* use encoding specified by file */ 9767ec681f3Smrg XML_SetElementHandler(p, optConfStartElem, optConfEndElem); 9777ec681f3Smrg XML_SetUserData(p, data); 9787ec681f3Smrg data->parser = p; 9797ec681f3Smrg data->name = filename; 9807ec681f3Smrg data->ignoringDevice = 0; 9817ec681f3Smrg data->ignoringApp = 0; 9827ec681f3Smrg data->inDriConf = 0; 9837ec681f3Smrg data->inDevice = 0; 9847ec681f3Smrg data->inApp = 0; 9857ec681f3Smrg data->inOption = 0; 9867ec681f3Smrg 9877ec681f3Smrg _parseOneConfigFile(p); 9887ec681f3Smrg XML_ParserFree(p); 98901e04c3fSmrg} 99001e04c3fSmrg 99101e04c3fSmrgstatic int 99201e04c3fSmrgscandir_filter(const struct dirent *ent) 99301e04c3fSmrg{ 99401e04c3fSmrg#ifndef DT_REG /* systems without d_type in dirent results */ 9957ec681f3Smrg struct stat st; 99601e04c3fSmrg 9977ec681f3Smrg if ((lstat(ent->d_name, &st) != 0) || 9987ec681f3Smrg (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode))) 9997ec681f3Smrg return 0; 100001e04c3fSmrg#else 10017ec681f3Smrg if (ent->d_type != DT_REG && ent->d_type != DT_LNK) 10027ec681f3Smrg return 0; 100301e04c3fSmrg#endif 100401e04c3fSmrg 10057ec681f3Smrg int len = strlen(ent->d_name); 10067ec681f3Smrg if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf")) 10077ec681f3Smrg return 0; 100801e04c3fSmrg 10097ec681f3Smrg return 1; 101001e04c3fSmrg} 101101e04c3fSmrg 101201e04c3fSmrg/** \brief Parse configuration files in a directory */ 101301e04c3fSmrgstatic void 101401e04c3fSmrgparseConfigDir(struct OptConfData *data, const char *dirname) 101501e04c3fSmrg{ 10167ec681f3Smrg int i, count; 10177ec681f3Smrg struct dirent **entries = NULL; 10187ec681f3Smrg 10197ec681f3Smrg count = scandir(dirname, &entries, scandir_filter, alphasort); 10207ec681f3Smrg if (count < 0) 10217ec681f3Smrg return; 102201e04c3fSmrg 10237ec681f3Smrg for (i = 0; i < count; i++) { 10247ec681f3Smrg char filename[PATH_MAX]; 102501e04c3fSmrg 10267ec681f3Smrg snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name); 10277ec681f3Smrg free(entries[i]); 102801e04c3fSmrg 10297ec681f3Smrg parseOneConfigFile(data, filename); 10307ec681f3Smrg } 103101e04c3fSmrg 10327ec681f3Smrg free(entries); 10337ec681f3Smrg} 10347ec681f3Smrg#else 10357ec681f3Smrg# include "driconf_static.h" 103601e04c3fSmrg 10377ec681f3Smrgstatic void 10387ec681f3SmrgparseStaticOptions(struct OptConfData *data, const struct driconf_option *options, 10397ec681f3Smrg unsigned num_options) 10407ec681f3Smrg{ 10417ec681f3Smrg if (data->ignoringDevice || data->ignoringApp) 10427ec681f3Smrg return; 10437ec681f3Smrg for (unsigned i = 0; i < num_options; i++) { 10447ec681f3Smrg const char *optattr[] = { 10457ec681f3Smrg "name", options[i].name, 10467ec681f3Smrg "value", options[i].value, 10477ec681f3Smrg NULL 10487ec681f3Smrg }; 10497ec681f3Smrg parseOptConfAttr(data, optattr); 10507ec681f3Smrg } 10517ec681f3Smrg} 10527ec681f3Smrg 10537ec681f3Smrgstatic void 10547ec681f3SmrgparseStaticConfig(struct OptConfData *data) 10557ec681f3Smrg{ 10567ec681f3Smrg data->ignoringDevice = 0; 10577ec681f3Smrg data->ignoringApp = 0; 10587ec681f3Smrg data->inDriConf = 0; 10597ec681f3Smrg data->inDevice = 0; 10607ec681f3Smrg data->inApp = 0; 10617ec681f3Smrg data->inOption = 0; 10627ec681f3Smrg 10637ec681f3Smrg for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) { 10647ec681f3Smrg const struct driconf_device *d = driconf[i]; 10657ec681f3Smrg const char *devattr[] = { 10667ec681f3Smrg "driver", d->driver, 10677ec681f3Smrg "device", d->device, 10687ec681f3Smrg NULL 10697ec681f3Smrg }; 10707ec681f3Smrg 10717ec681f3Smrg data->ignoringDevice = 0; 10727ec681f3Smrg data->inDevice++; 10737ec681f3Smrg parseDeviceAttr(data, devattr); 10747ec681f3Smrg data->inDevice--; 10757ec681f3Smrg 10767ec681f3Smrg data->inApp++; 10777ec681f3Smrg 10787ec681f3Smrg for (unsigned j = 0; j < d->num_engines; j++) { 10797ec681f3Smrg const struct driconf_engine *e = &d->engines[j]; 10807ec681f3Smrg const char *engattr[] = { 10817ec681f3Smrg "engine_name_match", e->engine_name_match, 10827ec681f3Smrg "engine_versions", e->engine_versions, 10837ec681f3Smrg NULL 10847ec681f3Smrg }; 10857ec681f3Smrg 10867ec681f3Smrg data->ignoringApp = 0; 10877ec681f3Smrg parseEngineAttr(data, engattr); 10887ec681f3Smrg parseStaticOptions(data, e->options, e->num_options); 10897ec681f3Smrg } 10907ec681f3Smrg 10917ec681f3Smrg for (unsigned j = 0; j < d->num_applications; j++) { 10927ec681f3Smrg const struct driconf_application *a = &d->applications[j]; 10937ec681f3Smrg const char *appattr[] = { 10947ec681f3Smrg "name", a->name, 10957ec681f3Smrg "executable", a->executable, 10967ec681f3Smrg "executable_regexp", a->executable_regexp, 10977ec681f3Smrg "sha1", a->sha1, 10987ec681f3Smrg "application_name_match", a->application_name_match, 10997ec681f3Smrg "application_versions", a->application_versions, 11007ec681f3Smrg NULL 11017ec681f3Smrg }; 11027ec681f3Smrg 11037ec681f3Smrg data->ignoringApp = 0; 11047ec681f3Smrg parseAppAttr(data, appattr); 11057ec681f3Smrg parseStaticOptions(data, a->options, a->num_options); 11067ec681f3Smrg } 11077ec681f3Smrg 11087ec681f3Smrg data->inApp--; 11097ec681f3Smrg } 11107ec681f3Smrg} 11117ec681f3Smrg#endif /* WITH_XMLCONFIG */ 11127ec681f3Smrg 11137ec681f3Smrg/** \brief Initialize an option cache based on info */ 11147ec681f3Smrgstatic void 11157ec681f3SmrginitOptionCache(driOptionCache *cache, const driOptionCache *info) 11167ec681f3Smrg{ 11177ec681f3Smrg unsigned i, size = 1 << info->tableSize; 11187ec681f3Smrg cache->info = info->info; 11197ec681f3Smrg cache->tableSize = info->tableSize; 11207ec681f3Smrg cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 11217ec681f3Smrg if (cache->values == NULL) { 11227ec681f3Smrg fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); 11237ec681f3Smrg abort(); 11247ec681f3Smrg } 11257ec681f3Smrg memcpy(cache->values, info->values, 11267ec681f3Smrg ((size_t)1 << info->tableSize) * sizeof(driOptionValue)); 11277ec681f3Smrg for (i = 0; i < size; ++i) { 11287ec681f3Smrg if (cache->info[i].type == DRI_STRING) 11297ec681f3Smrg XSTRDUP(cache->values[i]._string, info->values[i]._string); 11307ec681f3Smrg } 113101e04c3fSmrg} 113201e04c3fSmrg 113301e04c3fSmrg#ifndef SYSCONFDIR 113401e04c3fSmrg#define SYSCONFDIR "/etc" 113501e04c3fSmrg#endif 113601e04c3fSmrg 113701e04c3fSmrg#ifndef DATADIR 113801e04c3fSmrg#define DATADIR "/usr/share" 113901e04c3fSmrg#endif 114001e04c3fSmrg 11417ec681f3Smrgstatic const char *datadir = DATADIR "/drirc.d"; 11427ec681f3Smrgstatic const char *execname; 11437ec681f3Smrg 114401e04c3fSmrgvoid 11457ec681f3SmrgdriInjectDataDir(const char *dir) 114601e04c3fSmrg{ 11477ec681f3Smrg datadir = dir; 11487ec681f3Smrg} 114901e04c3fSmrg 11507ec681f3Smrgvoid 11517ec681f3SmrgdriInjectExecName(const char *exec) 11527ec681f3Smrg{ 11537ec681f3Smrg execname = exec; 11547ec681f3Smrg} 115501e04c3fSmrg 11567ec681f3Smrgvoid 11577ec681f3SmrgdriParseConfigFiles(driOptionCache *cache, const driOptionCache *info, 11587ec681f3Smrg int screenNum, const char *driverName, 11597ec681f3Smrg const char *kernelDriverName, 11607ec681f3Smrg const char *deviceName, 11617ec681f3Smrg const char *applicationName, uint32_t applicationVersion, 11627ec681f3Smrg const char *engineName, uint32_t engineVersion) 11637ec681f3Smrg{ 11647ec681f3Smrg initOptionCache(cache, info); 11657ec681f3Smrg struct OptConfData userData; 11667ec681f3Smrg 11677ec681f3Smrg userData.cache = cache; 11687ec681f3Smrg userData.screenNum = screenNum; 11697ec681f3Smrg userData.driverName = driverName; 11707ec681f3Smrg userData.kernelDriverName = kernelDriverName; 11717ec681f3Smrg userData.deviceName = deviceName; 11727ec681f3Smrg userData.applicationName = applicationName ? applicationName : ""; 11737ec681f3Smrg userData.applicationVersion = applicationVersion; 11747ec681f3Smrg userData.engineName = engineName ? engineName : ""; 11757ec681f3Smrg userData.engineVersion = engineVersion; 11767ec681f3Smrg userData.execName = execname ? execname : util_get_process_name(); 11777ec681f3Smrg 11787ec681f3Smrg#if WITH_XMLCONFIG 11797ec681f3Smrg char *home; 11807ec681f3Smrg 11817ec681f3Smrg parseConfigDir(&userData, datadir); 11827ec681f3Smrg parseOneConfigFile(&userData, SYSCONFDIR "/drirc"); 11837ec681f3Smrg 11847ec681f3Smrg if ((home = getenv("HOME"))) { 11857ec681f3Smrg char filename[PATH_MAX]; 11867ec681f3Smrg 11877ec681f3Smrg snprintf(filename, PATH_MAX, "%s/.drirc", home); 11887ec681f3Smrg parseOneConfigFile(&userData, filename); 11897ec681f3Smrg } 11907ec681f3Smrg#else 11917ec681f3Smrg parseStaticConfig(&userData); 11927ec681f3Smrg#endif /* WITH_XMLCONFIG */ 119301e04c3fSmrg} 119401e04c3fSmrg 119501e04c3fSmrgvoid 119601e04c3fSmrgdriDestroyOptionInfo(driOptionCache *info) 119701e04c3fSmrg{ 11987ec681f3Smrg driDestroyOptionCache(info); 11997ec681f3Smrg if (info->info) { 12007ec681f3Smrg uint32_t i, size = 1 << info->tableSize; 12017ec681f3Smrg for (i = 0; i < size; ++i) { 12027ec681f3Smrg if (info->info[i].name) { 12037ec681f3Smrg free(info->info[i].name); 12047ec681f3Smrg } 12057ec681f3Smrg } 12067ec681f3Smrg free(info->info); 12077ec681f3Smrg } 120801e04c3fSmrg} 120901e04c3fSmrg 121001e04c3fSmrgvoid 121101e04c3fSmrgdriDestroyOptionCache(driOptionCache *cache) 121201e04c3fSmrg{ 12137ec681f3Smrg if (cache->info) { 12147ec681f3Smrg unsigned i, size = 1 << cache->tableSize; 12157ec681f3Smrg for (i = 0; i < size; ++i) { 12167ec681f3Smrg if (cache->info[i].type == DRI_STRING) 12177ec681f3Smrg free(cache->values[i]._string); 12187ec681f3Smrg } 12197ec681f3Smrg } 12207ec681f3Smrg free(cache->values); 122101e04c3fSmrg} 122201e04c3fSmrg 122301e04c3fSmrgunsigned char 122401e04c3fSmrgdriCheckOption(const driOptionCache *cache, const char *name, 122501e04c3fSmrg driOptionType type) 122601e04c3fSmrg{ 12277ec681f3Smrg uint32_t i = findOption(cache, name); 12287ec681f3Smrg return cache->info[i].name != NULL && cache->info[i].type == type; 122901e04c3fSmrg} 123001e04c3fSmrg 123101e04c3fSmrgunsigned char 123201e04c3fSmrgdriQueryOptionb(const driOptionCache *cache, const char *name) 123301e04c3fSmrg{ 12347ec681f3Smrg uint32_t i = findOption(cache, name); 12357ec681f3Smrg /* make sure the option is defined and has the correct type */ 12367ec681f3Smrg assert(cache->info[i].name != NULL); 12377ec681f3Smrg assert(cache->info[i].type == DRI_BOOL); 12387ec681f3Smrg return cache->values[i]._bool; 123901e04c3fSmrg} 124001e04c3fSmrg 124101e04c3fSmrgint 124201e04c3fSmrgdriQueryOptioni(const driOptionCache *cache, const char *name) 124301e04c3fSmrg{ 12447ec681f3Smrg uint32_t i = findOption(cache, name); 12457ec681f3Smrg /* make sure the option is defined and has the correct type */ 12467ec681f3Smrg assert(cache->info[i].name != NULL); 12477ec681f3Smrg assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM); 12487ec681f3Smrg return cache->values[i]._int; 124901e04c3fSmrg} 125001e04c3fSmrg 125101e04c3fSmrgfloat 125201e04c3fSmrgdriQueryOptionf(const driOptionCache *cache, const char *name) 125301e04c3fSmrg{ 12547ec681f3Smrg uint32_t i = findOption(cache, name); 12557ec681f3Smrg /* make sure the option is defined and has the correct type */ 12567ec681f3Smrg assert(cache->info[i].name != NULL); 12577ec681f3Smrg assert(cache->info[i].type == DRI_FLOAT); 12587ec681f3Smrg return cache->values[i]._float; 125901e04c3fSmrg} 126001e04c3fSmrg 126101e04c3fSmrgchar * 126201e04c3fSmrgdriQueryOptionstr(const driOptionCache *cache, const char *name) 126301e04c3fSmrg{ 12647ec681f3Smrg uint32_t i = findOption(cache, name); 12657ec681f3Smrg /* make sure the option is defined and has the correct type */ 12667ec681f3Smrg assert(cache->info[i].name != NULL); 12677ec681f3Smrg assert(cache->info[i].type == DRI_STRING); 12687ec681f3Smrg return cache->values[i]._string; 126901e04c3fSmrg} 1270