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