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