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 "xmlconfig.h"
31#include <limits.h>
32#include <stdarg.h>
33#include <stdbool.h>
34#include <stdint.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <assert.h>
39#if WITH_XMLCONFIG
40#include <expat.h>
41#include <unistd.h>
42#include <errno.h>
43#include <dirent.h>
44#include <sys/stat.h>
45#endif
46#ifdef NO_REGEX
47typedef int regex_t;
48#define REG_EXTENDED 0
49#define REG_NOSUB 0
50#define REG_NOMATCH 1
51static inline int regcomp(regex_t *r, const char *s, int f) { return 0; }
52static inline int regexec(regex_t *r, const char *s, int n, void *p, int f) { return REG_NOMATCH; }
53static inline void regfree(regex_t* r) {}
54#else
55#include <regex.h>
56#endif
57#include <fcntl.h>
58#include <math.h>
59#include "strndup.h"
60#include "u_process.h"
61#include "os_file.h"
62
63/* For systems like Hurd */
64#ifndef PATH_MAX
65#define PATH_MAX 4096
66#endif
67
68static bool
69be_verbose(void)
70{
71   const char *s = getenv("MESA_DEBUG");
72   if (!s)
73      return true;
74
75   return strstr(s, "silent") == NULL;
76}
77
78/** \brief Locale-independent integer parser.
79 *
80 * Works similar to strtol. Leading space is NOT skipped. The input
81 * number may have an optional sign. Radix is specified by base. If
82 * base is 0 then decimal is assumed unless the input number is
83 * prefixed by 0x or 0X for hexadecimal or 0 for octal. After
84 * returning tail points to the first character that is not part of
85 * the integer number. If no number was found then tail points to the
86 * start of the input string. */
87static int
88strToI(const char *string, const char **tail, int base)
89{
90   int radix = base == 0 ? 10 : base;
91   int result = 0;
92   int sign = 1;
93   bool numberFound = false;
94   const char *start = string;
95
96   assert(radix >= 2 && radix <= 36);
97
98   if (*string == '-') {
99      sign = -1;
100      string++;
101   } else if (*string == '+')
102      string++;
103   if (base == 0 && *string == '0') {
104      numberFound = true;
105      if (*(string+1) == 'x' || *(string+1) == 'X') {
106         radix = 16;
107         string += 2;
108      } else {
109         radix = 8;
110         string++;
111      }
112   }
113   do {
114      int digit = -1;
115      if (radix <= 10) {
116         if (*string >= '0' && *string < '0' + radix)
117            digit = *string - '0';
118      } else {
119         if (*string >= '0' && *string <= '9')
120            digit = *string - '0';
121         else if (*string >= 'a' && *string < 'a' + radix - 10)
122            digit = *string - 'a' + 10;
123         else if (*string >= 'A' && *string < 'A' + radix - 10)
124            digit = *string - 'A' + 10;
125      }
126      if (digit != -1) {
127         numberFound = true;
128         result = radix*result + digit;
129         string++;
130      } else
131         break;
132   } while (true);
133   *tail = numberFound ? string : start;
134   return sign * result;
135}
136
137/** \brief Locale-independent floating-point parser.
138 *
139 * Works similar to strtod. Leading space is NOT skipped. The input
140 * number may have an optional sign. '.' is interpreted as decimal
141 * point and may occur at most once. Optionally the number may end in
142 * [eE]<exponent>, where <exponent> is an integer as recognized by
143 * strToI. In that case the result is number * 10^exponent. After
144 * returning tail points to the first character that is not part of
145 * the floating point number. If no number was found then tail points
146 * to the start of the input string.
147 *
148 * Uses two passes for maximum accuracy. */
149static float
150strToF(const char *string, const char **tail)
151{
152   int nDigits = 0, pointPos, exponent;
153   float sign = 1.0f, result = 0.0f, scale;
154   const char *start = string, *numStart;
155
156   /* sign */
157   if (*string == '-') {
158      sign = -1.0f;
159      string++;
160   } else if (*string == '+')
161      string++;
162
163   /* first pass: determine position of decimal point, number of
164    * digits, exponent and the end of the number. */
165   numStart = string;
166   while (*string >= '0' && *string <= '9') {
167      string++;
168      nDigits++;
169   }
170   pointPos = nDigits;
171   if (*string == '.') {
172      string++;
173      while (*string >= '0' && *string <= '9') {
174         string++;
175         nDigits++;
176      }
177   }
178   if (nDigits == 0) {
179      /* no digits, no number */
180      *tail = start;
181      return 0.0f;
182   }
183   *tail = string;
184   if (*string == 'e' || *string == 'E') {
185      const char *expTail;
186      exponent = strToI(string+1, &expTail, 10);
187      if (expTail == string+1)
188         exponent = 0;
189      else
190         *tail = expTail;
191   } else
192      exponent = 0;
193   string = numStart;
194
195   /* scale of the first digit */
196   scale = sign * (float)pow(10.0, (double)(pointPos-1 + exponent));
197
198   /* second pass: parse digits */
199   do {
200      if (*string != '.') {
201         assert(*string >= '0' && *string <= '9');
202         result += scale * (float)(*string - '0');
203         scale *= 0.1f;
204         nDigits--;
205      }
206      string++;
207   } while (nDigits > 0);
208
209   return result;
210}
211
212/** \brief Parse a value of a given type. */
213static unsigned char
214parseValue(driOptionValue *v, driOptionType type, const char *string)
215{
216   const char *tail = NULL;
217   /* skip leading white-space */
218   string += strspn(string, " \f\n\r\t\v");
219   switch (type) {
220   case DRI_BOOL:
221      if (!strcmp(string, "false")) {
222         v->_bool = false;
223         tail = string + 5;
224      } else if (!strcmp(string, "true")) {
225         v->_bool = true;
226         tail = string + 4;
227      }
228      else
229         return false;
230      break;
231   case DRI_ENUM: /* enum is just a special integer */
232   case DRI_INT:
233      v->_int = strToI(string, &tail, 0);
234      break;
235   case DRI_FLOAT:
236      v->_float = strToF(string, &tail);
237      break;
238   case DRI_STRING:
239      free(v->_string);
240      v->_string = strndup(string, STRING_CONF_MAXLEN);
241      return true;
242   case DRI_SECTION:
243      unreachable("shouldn't be parsing values in section declarations");
244   }
245
246   if (tail == string)
247      return false; /* empty string (or containing only white-space) */
248   /* skip trailing white space */
249   if (*tail)
250      tail += strspn(tail, " \f\n\r\t\v");
251   if (*tail)
252      return false; /* something left over that is not part of value */
253
254   return true;
255}
256
257/** \brief Find an option in an option cache with the name as key */
258static uint32_t
259findOption(const driOptionCache *cache, const char *name)
260{
261   uint32_t len = strlen(name);
262   uint32_t size = 1 << cache->tableSize, mask = size - 1;
263   uint32_t hash = 0;
264   uint32_t i, shift;
265
266   /* compute a hash from the variable length name */
267   for (i = 0, shift = 0; i < len; ++i, shift = (shift+8) & 31)
268      hash += (uint32_t)name[i] << shift;
269   hash *= hash;
270   hash = (hash >> (16-cache->tableSize/2)) & mask;
271
272   /* this is just the starting point of the linear search for the option */
273   for (i = 0; i < size; ++i, hash = (hash+1) & mask) {
274      /* if we hit an empty entry then the option is not defined (yet) */
275      if (cache->info[hash].name == 0)
276         break;
277      else if (!strcmp(name, cache->info[hash].name))
278         break;
279   }
280   /* this assertion fails if the hash table is full */
281   assert (i < size);
282
283   return hash;
284}
285
286/** \brief Like strdup with error checking. */
287#define XSTRDUP(dest,source) do {                                       \
288      if (!(dest = strdup(source))) {                                   \
289         fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__); \
290         abort();                                                       \
291      }                                                                 \
292   } while (0)
293
294/** \brief Check if a value is in info->range. */
295UNUSED static bool
296checkValue(const driOptionValue *v, const driOptionInfo *info)
297{
298   switch (info->type) {
299   case DRI_ENUM: /* enum is just a special integer */
300   case DRI_INT:
301      return (info->range.start._int == info->range.end._int ||
302              (v->_int >= info->range.start._int &&
303               v->_int <= info->range.end._int));
304
305   case DRI_FLOAT:
306      return (info->range.start._float == info->range.end._float ||
307              (v->_float >= info->range.start._float &&
308               v->_float <= info->range.end._float));
309
310   default:
311      return true;
312   }
313}
314
315void
316driParseOptionInfo(driOptionCache *info,
317                   const driOptionDescription *configOptions,
318                   unsigned numOptions)
319{
320   /* Make the hash table big enough to fit more than the maximum number of
321    * config options we've ever seen in a driver.
322    */
323   info->tableSize = 6;
324   info->info = calloc((size_t)1 << info->tableSize, sizeof(driOptionInfo));
325   info->values = calloc((size_t)1 << info->tableSize, sizeof(driOptionValue));
326   if (info->info == NULL || info->values == NULL) {
327      fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
328      abort();
329   }
330
331   UNUSED bool in_section = false;
332   for (int o = 0; o < numOptions; o++) {
333      const driOptionDescription *opt = &configOptions[o];
334
335      if (opt->info.type == DRI_SECTION) {
336         in_section = true;
337         continue;
338      }
339
340      /* for driconf xml generation, options must always be preceded by a
341       * DRI_CONF_SECTION
342       */
343      assert(in_section);
344
345      const char *name = opt->info.name;
346      int i = findOption(info, name);
347      driOptionInfo *optinfo = &info->info[i];
348      driOptionValue *optval = &info->values[i];
349
350      assert(!optinfo->name); /* No duplicate options in your list. */
351
352      optinfo->type = opt->info.type;
353      optinfo->range = opt->info.range;
354      XSTRDUP(optinfo->name, name);
355
356      switch (opt->info.type) {
357      case DRI_BOOL:
358         optval->_bool = opt->value._bool;
359         break;
360
361      case DRI_INT:
362      case DRI_ENUM:
363         optval->_int = opt->value._int;
364         break;
365
366      case DRI_FLOAT:
367         optval->_float = opt->value._float;
368         break;
369
370      case DRI_STRING:
371         XSTRDUP(optval->_string, opt->value._string);
372         break;
373
374      case DRI_SECTION:
375         unreachable("handled above");
376      }
377
378      /* Built-in default values should always be valid. */
379      assert(checkValue(optval, optinfo));
380
381      char *envVal = getenv(name);
382      if (envVal != NULL) {
383         driOptionValue v;
384
385         /* make sure the value is initialized to something sensible */
386         v._string = NULL;
387
388         if (parseValue(&v, opt->info.type, envVal) &&
389             checkValue(&v, optinfo)) {
390            /* don't use XML_WARNING, we want the user to see this! */
391            if (be_verbose()) {
392               fprintf(stderr,
393                       "ATTENTION: default value of option %s overridden by environment.\n",
394                       name);
395            }
396            *optval = v;
397         } else {
398            fprintf(stderr, "illegal environment value for %s: \"%s\".  Ignoring.\n",
399                    name, envVal);
400         }
401      }
402   }
403}
404
405char *
406driGetOptionsXml(const driOptionDescription *configOptions, unsigned numOptions)
407{
408   char *str = ralloc_strdup(NULL,
409      "<?xml version=\"1.0\" standalone=\"yes\"?>\n" \
410      "<!DOCTYPE driinfo [\n" \
411      "   <!ELEMENT driinfo      (section*)>\n" \
412      "   <!ELEMENT section      (description+, option+)>\n" \
413      "   <!ELEMENT description  (enum*)>\n" \
414      "   <!ATTLIST description  lang CDATA #FIXED \"en\"\n" \
415      "                          text CDATA #REQUIRED>\n" \
416      "   <!ELEMENT option       (description+)>\n" \
417      "   <!ATTLIST option       name CDATA #REQUIRED\n" \
418      "                          type (bool|enum|int|float) #REQUIRED\n" \
419      "                          default CDATA #REQUIRED\n" \
420      "                          valid CDATA #IMPLIED>\n" \
421      "   <!ELEMENT enum         EMPTY>\n" \
422      "   <!ATTLIST enum         value CDATA #REQUIRED\n" \
423      "                          text CDATA #REQUIRED>\n" \
424      "]>" \
425      "<driinfo>\n");
426
427   bool in_section = false;
428   for (int o = 0; o < numOptions; o++) {
429      const driOptionDescription *opt = &configOptions[o];
430
431      const char *name = opt->info.name;
432      const char *types[] = {
433         [DRI_BOOL] = "bool",
434         [DRI_INT] = "int",
435         [DRI_FLOAT] = "float",
436         [DRI_ENUM] = "enum",
437         [DRI_STRING] = "string",
438      };
439
440      if (opt->info.type == DRI_SECTION) {
441         if (in_section)
442            ralloc_asprintf_append(&str, "  </section>\n");
443
444         ralloc_asprintf_append(&str,
445                                "  <section>\n"
446                                "    <description lang=\"en\" text=\"%s\"/>\n",
447                                opt->desc);
448
449         in_section = true;
450         continue;
451      }
452
453      ralloc_asprintf_append(&str,
454                             "      <option name=\"%s\" type=\"%s\" default=\"",
455                             name,
456                             types[opt->info.type]);
457
458      switch (opt->info.type) {
459      case DRI_BOOL:
460         ralloc_asprintf_append(&str, opt->value._bool ? "true" : "false");
461         break;
462
463      case DRI_INT:
464      case DRI_ENUM:
465         ralloc_asprintf_append(&str, "%d", opt->value._int);
466         break;
467
468      case DRI_FLOAT:
469         ralloc_asprintf_append(&str, "%f", opt->value._float);
470         break;
471
472      case DRI_STRING:
473         ralloc_asprintf_append(&str, "%s", opt->value._string);
474         break;
475
476      case DRI_SECTION:
477         unreachable("handled above");
478         break;
479      }
480      ralloc_asprintf_append(&str, "\"");
481
482
483      switch (opt->info.type) {
484      case DRI_INT:
485      case DRI_ENUM:
486         if (opt->info.range.start._int < opt->info.range.end._int) {
487            ralloc_asprintf_append(&str, " valid=\"%d:%d\"",
488                                   opt->info.range.start._int,
489                                   opt->info.range.end._int);
490         }
491         break;
492
493      case DRI_FLOAT:
494         if (opt->info.range.start._float < opt->info.range.end._float) {
495            ralloc_asprintf_append(&str, " valid=\"%f:%f\"",
496                                   opt->info.range.start._float,
497                                   opt->info.range.end._float);
498         }
499         break;
500
501      default:
502         break;
503      }
504
505      ralloc_asprintf_append(&str, ">\n"); /* end of <option> */
506
507
508      ralloc_asprintf_append(&str, "        <description lang=\"en\" text=\"%s\"%s>\n",
509                             opt->desc, opt->info.type != DRI_ENUM ? "/" : "");
510
511      if (opt->info.type == DRI_ENUM) {
512         for (int i = 0; i < ARRAY_SIZE(opt->enums) && opt->enums[i].desc; i++) {
513            ralloc_asprintf_append(&str, "          <enum value=\"%d\" text=\"%s\"/>\n",
514                                   opt->enums[i].value, opt->enums[i].desc);
515         }
516         ralloc_asprintf_append(&str, "        </description>\n");
517      }
518
519      ralloc_asprintf_append(&str, "      </option>\n");
520   }
521
522   assert(in_section);
523   ralloc_asprintf_append(&str, "  </section>\n");
524
525   ralloc_asprintf_append(&str, "</driinfo>\n");
526
527   char *output = strdup(str);
528   ralloc_free(str);
529
530   return output;
531}
532
533/**
534 * Print message to \c stderr if the \c LIBGL_DEBUG environment variable
535 * is set.
536 *
537 * Is called from the drivers.
538 *
539 * \param f \c printf like format string.
540 */
541static void
542__driUtilMessage(const char *f, ...)
543{
544   va_list args;
545   const char *libgl_debug;
546
547   libgl_debug=getenv("LIBGL_DEBUG");
548   if (libgl_debug && !strstr(libgl_debug, "quiet")) {
549      fprintf(stderr, "libGL: ");
550      va_start(args, f);
551      vfprintf(stderr, f, args);
552      va_end(args);
553      fprintf(stderr, "\n");
554   }
555}
556
557/* We don't have real line/column # info in static-config case: */
558#if !WITH_XML_CONFIG
559#  define XML_GetCurrentLineNumber(p) -1
560#  define XML_GetCurrentColumnNumber(p) -1
561#endif
562
563/** \brief Output a warning message. */
564#define XML_WARNING1(msg) do {                                          \
565      __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
566                        (int) XML_GetCurrentLineNumber(data->parser),   \
567                        (int) XML_GetCurrentColumnNumber(data->parser)); \
568   } while (0)
569#define XML_WARNING(msg, ...) do {                                      \
570      __driUtilMessage("Warning in %s line %d, column %d: "msg, data->name, \
571                        (int) XML_GetCurrentLineNumber(data->parser),   \
572                        (int) XML_GetCurrentColumnNumber(data->parser), \
573                        ##__VA_ARGS__);                                 \
574   } while (0)
575/** \brief Output an error message. */
576#define XML_ERROR1(msg) do {                                            \
577      __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
578                        (int) XML_GetCurrentLineNumber(data->parser),   \
579                        (int) XML_GetCurrentColumnNumber(data->parser)); \
580   } while (0)
581#define XML_ERROR(msg, ...) do {                                        \
582      __driUtilMessage("Error in %s line %d, column %d: "msg, data->name, \
583                        (int) XML_GetCurrentLineNumber(data->parser),   \
584                        (int) XML_GetCurrentColumnNumber(data->parser), \
585                        ##__VA_ARGS__);                                 \
586   } while (0)
587
588/** \brief Parser context for configuration files. */
589struct OptConfData {
590   const char *name;
591#if WITH_XMLCONFIG
592   XML_Parser parser;
593#endif
594   driOptionCache *cache;
595   int screenNum;
596   const char *driverName, *execName;
597   const char *kernelDriverName;
598   const char *deviceName;
599   const char *engineName;
600   const char *applicationName;
601   uint32_t engineVersion;
602   uint32_t applicationVersion;
603   uint32_t ignoringDevice;
604   uint32_t ignoringApp;
605   uint32_t inDriConf;
606   uint32_t inDevice;
607   uint32_t inApp;
608   uint32_t inOption;
609};
610
611/** \brief Parse a list of ranges of type info->type. */
612static unsigned char
613parseRange(driOptionInfo *info, const char *string)
614{
615   char *cp;
616
617   XSTRDUP(cp, string);
618
619   char *sep;
620   sep = strchr(cp, ':');
621   if (!sep) {
622      free(cp);
623      return false;
624   }
625
626   *sep = '\0';
627   if (!parseValue(&info->range.start, info->type, cp) ||
628       !parseValue(&info->range.end, info->type, sep+1)) {
629      free(cp);
630      return false;
631   }
632   if (info->type == DRI_INT &&
633       info->range.start._int >= info->range.end._int) {
634      free(cp);
635      return false;
636   }
637   if (info->type == DRI_FLOAT &&
638       info->range.start._float >= info->range.end._float) {
639      free(cp);
640      return false;
641   }
642
643   free(cp);
644   return true;
645}
646
647/** \brief Parse attributes of a device element. */
648static void
649parseDeviceAttr(struct OptConfData *data, const char **attr)
650{
651   uint32_t i;
652   const char *driver = NULL, *screen = NULL, *kernel = NULL, *device = NULL;
653   for (i = 0; attr[i]; i += 2) {
654      if (!strcmp(attr[i], "driver")) driver = attr[i+1];
655      else if (!strcmp(attr[i], "screen")) screen = attr[i+1];
656      else if (!strcmp(attr[i], "kernel_driver")) kernel = attr[i+1];
657      else if (!strcmp(attr[i], "device")) device = attr[i+1];
658      else XML_WARNING("unknown device attribute: %s.", attr[i]);
659   }
660   if (driver && strcmp(driver, data->driverName))
661      data->ignoringDevice = data->inDevice;
662   else if (kernel && (!data->kernelDriverName ||
663                       strcmp(kernel, data->kernelDriverName)))
664      data->ignoringDevice = data->inDevice;
665   else if (device && (!data->deviceName ||
666                       strcmp(device, data->deviceName)))
667      data->ignoringDevice = data->inDevice;
668   else if (screen) {
669      driOptionValue screenNum;
670      if (!parseValue(&screenNum, DRI_INT, screen))
671         XML_WARNING("illegal screen number: %s.", screen);
672      else if (screenNum._int != data->screenNum)
673         data->ignoringDevice = data->inDevice;
674   }
675}
676
677/** \brief Parse attributes of an application element. */
678static void
679parseAppAttr(struct OptConfData *data, const char **attr)
680{
681   uint32_t i;
682   const char *exec = NULL;
683   const char *sha1 = NULL;
684   const char *application_name_match = NULL;
685   const char *application_versions = NULL;
686   driOptionInfo version_range = {
687      .type = DRI_INT,
688   };
689
690   for (i = 0; attr[i]; i += 2) {
691      if (!strcmp(attr[i], "name")) /* not needed here */;
692      else if (!strcmp(attr[i], "executable")) exec = attr[i+1];
693      else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1];
694      else if (!strcmp(attr[i], "application_name_match"))
695         application_name_match = attr[i+1];
696      else if (!strcmp(attr[i], "application_versions"))
697         application_versions = attr[i+1];
698      else XML_WARNING("unknown application attribute: %s.", attr[i]);
699   }
700   if (exec && strcmp(exec, data->execName)) {
701      data->ignoringApp = data->inApp;
702   } else if (sha1) {
703      /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */
704      if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) {
705         XML_WARNING("Incorrect sha1 application attribute");
706         data->ignoringApp = data->inApp;
707      } else {
708         size_t len;
709         char* content;
710         char path[PATH_MAX];
711         if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 &&
712             (content = os_read_file(path, &len))) {
713            uint8_t sha1x[SHA1_DIGEST_LENGTH];
714            char sha1s[SHA1_DIGEST_STRING_LENGTH];
715            _mesa_sha1_compute(content, len, sha1x);
716            _mesa_sha1_format((char*) sha1s, sha1x);
717            free(content);
718
719            if (strcmp(sha1, sha1s)) {
720               data->ignoringApp = data->inApp;
721            }
722         } else {
723            data->ignoringApp = data->inApp;
724         }
725      }
726   } else if (application_name_match) {
727      regex_t re;
728
729      if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
730         if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH)
731            data->ignoringApp = data->inApp;
732         regfree(&re);
733      } else
734         XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match);
735   }
736   if (application_versions) {
737      driOptionValue v = { ._int = data->applicationVersion };
738      if (parseRange(&version_range, application_versions)) {
739         if (!checkValue(&v, &version_range))
740            data->ignoringApp = data->inApp;
741      } else {
742         XML_WARNING("Failed to parse application_versions range=\"%s\".",
743                     application_versions);
744      }
745   }
746}
747
748/** \brief Parse attributes of an application element. */
749static void
750parseEngineAttr(struct OptConfData *data, const char **attr)
751{
752   uint32_t i;
753   const char *engine_name_match = NULL, *engine_versions = NULL;
754   driOptionInfo version_range = {
755      .type = DRI_INT,
756   };
757   for (i = 0; attr[i]; i += 2) {
758      if (!strcmp(attr[i], "name")) /* not needed here */;
759      else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1];
760      else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1];
761      else XML_WARNING("unknown application attribute: %s.", attr[i]);
762   }
763   if (engine_name_match) {
764      regex_t re;
765
766      if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
767         if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH)
768            data->ignoringApp = data->inApp;
769         regfree(&re);
770      } else
771         XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match);
772   }
773   if (engine_versions) {
774      driOptionValue v = { ._int = data->engineVersion };
775      if (parseRange(&version_range, engine_versions)) {
776         if (!checkValue(&v, &version_range))
777            data->ignoringApp = data->inApp;
778      } else {
779         XML_WARNING("Failed to parse engine_versions range=\"%s\".",
780                     engine_versions);
781      }
782   }
783}
784
785/** \brief Parse attributes of an option element. */
786static void
787parseOptConfAttr(struct OptConfData *data, const char **attr)
788{
789   uint32_t i;
790   const char *name = NULL, *value = NULL;
791   for (i = 0; attr[i]; i += 2) {
792      if (!strcmp(attr[i], "name")) name = attr[i+1];
793      else if (!strcmp(attr[i], "value")) value = attr[i+1];
794      else XML_WARNING("unknown option attribute: %s.", attr[i]);
795   }
796   if (!name) XML_WARNING1("name attribute missing in option.");
797   if (!value) XML_WARNING1("value attribute missing in option.");
798   if (name && value) {
799      driOptionCache *cache = data->cache;
800      uint32_t opt = findOption(cache, name);
801      if (cache->info[opt].name == NULL)
802         /* don't use XML_WARNING, drirc defines options for all drivers,
803          * but not all drivers support them */
804         return;
805      else if (getenv(cache->info[opt].name)) {
806         /* don't use XML_WARNING, we want the user to see this! */
807         if (be_verbose()) {
808            fprintf(stderr,
809                    "ATTENTION: option value of option %s ignored.\n",
810                    cache->info[opt].name);
811         }
812      } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value))
813         XML_WARNING("illegal option value: %s.", value);
814   }
815}
816
817#if WITH_XMLCONFIG
818
819/** \brief Elements in configuration files. */
820enum OptConfElem {
821   OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
822};
823static const char *OptConfElems[] = {
824   [OC_APPLICATION]  = "application",
825   [OC_DEVICE] = "device",
826   [OC_DRICONF] = "driconf",
827   [OC_ENGINE]  = "engine",
828   [OC_OPTION] = "option",
829};
830
831static int compare(const void *a, const void *b) {
832   return strcmp(*(char *const*)a, *(char *const*)b);
833}
834/** \brief Binary search in a string array. */
835static uint32_t
836bsearchStr(const char *name, const char *elems[], uint32_t count)
837{
838   const char **found;
839   found = bsearch(&name, elems, count, sizeof(char *), compare);
840   if (found)
841      return found - elems;
842   else
843      return count;
844}
845
846/** \brief Handler for start element events. */
847static void
848optConfStartElem(void *userData, const char *name,
849                 const char **attr)
850{
851   struct OptConfData *data = (struct OptConfData *)userData;
852   enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
853   switch (elem) {
854   case OC_DRICONF:
855      if (data->inDriConf)
856         XML_WARNING1("nested <driconf> elements.");
857      if (attr[0])
858         XML_WARNING1("attributes specified on <driconf> element.");
859      data->inDriConf++;
860      break;
861   case OC_DEVICE:
862      if (!data->inDriConf)
863         XML_WARNING1("<device> should be inside <driconf>.");
864      if (data->inDevice)
865         XML_WARNING1("nested <device> elements.");
866      data->inDevice++;
867      if (!data->ignoringDevice && !data->ignoringApp)
868         parseDeviceAttr(data, attr);
869      break;
870   case OC_APPLICATION:
871      if (!data->inDevice)
872         XML_WARNING1("<application> should be inside <device>.");
873      if (data->inApp)
874         XML_WARNING1("nested <application> or <engine> elements.");
875      data->inApp++;
876      if (!data->ignoringDevice && !data->ignoringApp)
877         parseAppAttr(data, attr);
878      break;
879   case OC_ENGINE:
880      if (!data->inDevice)
881         XML_WARNING1("<engine> should be inside <device>.");
882      if (data->inApp)
883         XML_WARNING1("nested <application> or <engine> elements.");
884      data->inApp++;
885      if (!data->ignoringDevice && !data->ignoringApp)
886         parseEngineAttr(data, attr);
887      break;
888   case OC_OPTION:
889      if (!data->inApp)
890         XML_WARNING1("<option> should be inside <application>.");
891      if (data->inOption)
892         XML_WARNING1("nested <option> elements.");
893      data->inOption++;
894      if (!data->ignoringDevice && !data->ignoringApp)
895         parseOptConfAttr(data, attr);
896      break;
897   default:
898      XML_WARNING("unknown element: %s.", name);
899   }
900}
901
902/** \brief Handler for end element events. */
903static void
904optConfEndElem(void *userData, const char *name)
905{
906   struct OptConfData *data = (struct OptConfData *)userData;
907   enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
908   switch (elem) {
909   case OC_DRICONF:
910      data->inDriConf--;
911      break;
912   case OC_DEVICE:
913      if (data->inDevice-- == data->ignoringDevice)
914         data->ignoringDevice = 0;
915      break;
916   case OC_APPLICATION:
917   case OC_ENGINE:
918      if (data->inApp-- == data->ignoringApp)
919         data->ignoringApp = 0;
920      break;
921   case OC_OPTION:
922      data->inOption--;
923      break;
924   default:
925      /* unknown element, warning was produced on start tag */;
926   }
927}
928
929static void
930_parseOneConfigFile(XML_Parser p)
931{
932#define BUF_SIZE 0x1000
933   struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p);
934   int status;
935   int fd;
936
937   if ((fd = open(data->name, O_RDONLY)) == -1) {
938      __driUtilMessage("Can't open configuration file %s: %s.",
939                       data->name, strerror(errno));
940      return;
941   }
942
943   while (1) {
944      int bytesRead;
945      void *buffer = XML_GetBuffer(p, BUF_SIZE);
946      if (!buffer) {
947         __driUtilMessage("Can't allocate parser buffer.");
948         break;
949      }
950      bytesRead = read(fd, buffer, BUF_SIZE);
951      if (bytesRead == -1) {
952         __driUtilMessage("Error reading from configuration file %s: %s.",
953                          data->name, strerror(errno));
954         break;
955      }
956      status = XML_ParseBuffer(p, bytesRead, bytesRead == 0);
957      if (!status) {
958         XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p)));
959         break;
960      }
961      if (bytesRead == 0)
962         break;
963   }
964
965   close(fd);
966#undef BUF_SIZE
967}
968
969/** \brief Parse the named configuration file */
970static void
971parseOneConfigFile(struct OptConfData *data, const char *filename)
972{
973   XML_Parser p;
974
975   p = XML_ParserCreate(NULL); /* use encoding specified by file */
976   XML_SetElementHandler(p, optConfStartElem, optConfEndElem);
977   XML_SetUserData(p, data);
978   data->parser = p;
979   data->name = filename;
980   data->ignoringDevice = 0;
981   data->ignoringApp = 0;
982   data->inDriConf = 0;
983   data->inDevice = 0;
984   data->inApp = 0;
985   data->inOption = 0;
986
987   _parseOneConfigFile(p);
988   XML_ParserFree(p);
989}
990
991static int
992scandir_filter(const struct dirent *ent)
993{
994#ifndef DT_REG /* systems without d_type in dirent results */
995   struct stat st;
996
997   if ((lstat(ent->d_name, &st) != 0) ||
998       (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)))
999      return 0;
1000#else
1001   if (ent->d_type != DT_REG && ent->d_type != DT_LNK)
1002      return 0;
1003#endif
1004
1005   int len = strlen(ent->d_name);
1006   if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf"))
1007      return 0;
1008
1009   return 1;
1010}
1011
1012/** \brief Parse configuration files in a directory */
1013static void
1014parseConfigDir(struct OptConfData *data, const char *dirname)
1015{
1016   int i, count;
1017   struct dirent **entries = NULL;
1018
1019   count = scandir(dirname, &entries, scandir_filter, alphasort);
1020   if (count < 0)
1021      return;
1022
1023   for (i = 0; i < count; i++) {
1024      char filename[PATH_MAX];
1025
1026      snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name);
1027      free(entries[i]);
1028
1029      parseOneConfigFile(data, filename);
1030   }
1031
1032   free(entries);
1033}
1034#else
1035#  include "driconf_static.h"
1036
1037static void
1038parseStaticOptions(struct OptConfData *data, const struct driconf_option *options,
1039                   unsigned num_options)
1040{
1041   if (data->ignoringDevice || data->ignoringApp)
1042      return;
1043   for (unsigned i = 0; i < num_options; i++) {
1044      const char *optattr[] = {
1045         "name", options[i].name,
1046         "value", options[i].value,
1047         NULL
1048      };
1049      parseOptConfAttr(data, optattr);
1050   }
1051}
1052
1053static void
1054parseStaticConfig(struct OptConfData *data)
1055{
1056   data->ignoringDevice = 0;
1057   data->ignoringApp = 0;
1058   data->inDriConf = 0;
1059   data->inDevice = 0;
1060   data->inApp = 0;
1061   data->inOption = 0;
1062
1063   for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) {
1064      const struct driconf_device *d = driconf[i];
1065      const char *devattr[] = {
1066         "driver", d->driver,
1067         "device", d->device,
1068         NULL
1069      };
1070
1071      data->ignoringDevice = 0;
1072      data->inDevice++;
1073      parseDeviceAttr(data, devattr);
1074      data->inDevice--;
1075
1076      data->inApp++;
1077
1078      for (unsigned j = 0; j < d->num_engines; j++) {
1079         const struct driconf_engine *e = &d->engines[j];
1080         const char *engattr[] = {
1081            "engine_name_match", e->engine_name_match,
1082            "engine_versions", e->engine_versions,
1083            NULL
1084         };
1085
1086         data->ignoringApp = 0;
1087         parseEngineAttr(data, engattr);
1088         parseStaticOptions(data, e->options, e->num_options);
1089      }
1090
1091      for (unsigned j = 0; j < d->num_applications; j++) {
1092         const struct driconf_application *a = &d->applications[j];
1093         const char *appattr[] = {
1094            "name", a->name,
1095            "executable", a->executable,
1096            "executable_regexp", a->executable_regexp,
1097            "sha1", a->sha1,
1098            "application_name_match", a->application_name_match,
1099            "application_versions", a->application_versions,
1100            NULL
1101         };
1102
1103         data->ignoringApp = 0;
1104         parseAppAttr(data, appattr);
1105         parseStaticOptions(data, a->options, a->num_options);
1106      }
1107
1108      data->inApp--;
1109   }
1110}
1111#endif /* WITH_XMLCONFIG */
1112
1113/** \brief Initialize an option cache based on info */
1114static void
1115initOptionCache(driOptionCache *cache, const driOptionCache *info)
1116{
1117   unsigned i, size = 1 << info->tableSize;
1118   cache->info = info->info;
1119   cache->tableSize = info->tableSize;
1120   cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1121   if (cache->values == NULL) {
1122      fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
1123      abort();
1124   }
1125   memcpy(cache->values, info->values,
1126           ((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1127   for (i = 0; i < size; ++i) {
1128      if (cache->info[i].type == DRI_STRING)
1129         XSTRDUP(cache->values[i]._string, info->values[i]._string);
1130   }
1131}
1132
1133#ifndef SYSCONFDIR
1134#define SYSCONFDIR "/etc"
1135#endif
1136
1137#ifndef DATADIR
1138#define DATADIR "/usr/share"
1139#endif
1140
1141static const char *datadir = DATADIR "/drirc.d";
1142static const char *execname;
1143
1144void
1145driInjectDataDir(const char *dir)
1146{
1147   datadir = dir;
1148}
1149
1150void
1151driInjectExecName(const char *exec)
1152{
1153   execname = exec;
1154}
1155
1156void
1157driParseConfigFiles(driOptionCache *cache, const driOptionCache *info,
1158                    int screenNum, const char *driverName,
1159                    const char *kernelDriverName,
1160                    const char *deviceName,
1161                    const char *applicationName, uint32_t applicationVersion,
1162                    const char *engineName, uint32_t engineVersion)
1163{
1164   initOptionCache(cache, info);
1165   struct OptConfData userData;
1166
1167   userData.cache = cache;
1168   userData.screenNum = screenNum;
1169   userData.driverName = driverName;
1170   userData.kernelDriverName = kernelDriverName;
1171   userData.deviceName = deviceName;
1172   userData.applicationName = applicationName ? applicationName : "";
1173   userData.applicationVersion = applicationVersion;
1174   userData.engineName = engineName ? engineName : "";
1175   userData.engineVersion = engineVersion;
1176   userData.execName = execname ? execname : util_get_process_name();
1177
1178#if WITH_XMLCONFIG
1179   char *home;
1180
1181   parseConfigDir(&userData, datadir);
1182   parseOneConfigFile(&userData, SYSCONFDIR "/drirc");
1183
1184   if ((home = getenv("HOME"))) {
1185      char filename[PATH_MAX];
1186
1187      snprintf(filename, PATH_MAX, "%s/.drirc", home);
1188      parseOneConfigFile(&userData, filename);
1189   }
1190#else
1191   parseStaticConfig(&userData);
1192#endif /* WITH_XMLCONFIG */
1193}
1194
1195void
1196driDestroyOptionInfo(driOptionCache *info)
1197{
1198   driDestroyOptionCache(info);
1199   if (info->info) {
1200      uint32_t i, size = 1 << info->tableSize;
1201      for (i = 0; i < size; ++i) {
1202         if (info->info[i].name) {
1203            free(info->info[i].name);
1204         }
1205      }
1206      free(info->info);
1207   }
1208}
1209
1210void
1211driDestroyOptionCache(driOptionCache *cache)
1212{
1213   if (cache->info) {
1214      unsigned i, size = 1 << cache->tableSize;
1215      for (i = 0; i < size; ++i) {
1216         if (cache->info[i].type == DRI_STRING)
1217            free(cache->values[i]._string);
1218      }
1219   }
1220   free(cache->values);
1221}
1222
1223unsigned char
1224driCheckOption(const driOptionCache *cache, const char *name,
1225               driOptionType type)
1226{
1227   uint32_t i = findOption(cache, name);
1228   return cache->info[i].name != NULL && cache->info[i].type == type;
1229}
1230
1231unsigned char
1232driQueryOptionb(const driOptionCache *cache, const char *name)
1233{
1234   uint32_t i = findOption(cache, name);
1235   /* make sure the option is defined and has the correct type */
1236   assert(cache->info[i].name != NULL);
1237   assert(cache->info[i].type == DRI_BOOL);
1238   return cache->values[i]._bool;
1239}
1240
1241int
1242driQueryOptioni(const driOptionCache *cache, const char *name)
1243{
1244   uint32_t i = findOption(cache, name);
1245   /* make sure the option is defined and has the correct type */
1246   assert(cache->info[i].name != NULL);
1247   assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM);
1248   return cache->values[i]._int;
1249}
1250
1251float
1252driQueryOptionf(const driOptionCache *cache, const char *name)
1253{
1254   uint32_t i = findOption(cache, name);
1255   /* make sure the option is defined and has the correct type */
1256   assert(cache->info[i].name != NULL);
1257   assert(cache->info[i].type == DRI_FLOAT);
1258   return cache->values[i]._float;
1259}
1260
1261char *
1262driQueryOptionstr(const driOptionCache *cache, const char *name)
1263{
1264   uint32_t i = findOption(cache, name);
1265   /* make sure the option is defined and has the correct type */
1266   assert(cache->info[i].name != NULL);
1267   assert(cache->info[i].type == DRI_STRING);
1268   return cache->values[i]._string;
1269}
1270