xref: /third_party/mesa3d/src/util/xmlconfig.c (revision bf215546)
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 == NULL)
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 = 7;
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 *exec_regexp = NULL;
685   const char *application_name_match = NULL;
686   const char *application_versions = NULL;
687   driOptionInfo version_range = {
688      .type = DRI_INT,
689   };
690
691   for (i = 0; attr[i]; i += 2) {
692      if (!strcmp(attr[i], "name")) /* not needed here */;
693      else if (!strcmp(attr[i], "executable")) exec = attr[i+1];
694      else if (!strcmp(attr[i], "executable_regexp")) exec_regexp = attr[i+1];
695      else if (!strcmp(attr[i], "sha1")) sha1 = attr[i+1];
696      else if (!strcmp(attr[i], "application_name_match"))
697         application_name_match = attr[i+1];
698      else if (!strcmp(attr[i], "application_versions"))
699         application_versions = attr[i+1];
700      else XML_WARNING("unknown application attribute: %s.", attr[i]);
701   }
702   if (exec && strcmp(exec, data->execName)) {
703      data->ignoringApp = data->inApp;
704   } else if (exec_regexp) {
705      regex_t re;
706
707      if (regcomp(&re, exec_regexp, REG_EXTENDED|REG_NOSUB) == 0) {
708         if (regexec(&re, data->execName, 0, NULL, 0) == REG_NOMATCH)
709            data->ignoringApp = data->inApp;
710         regfree(&re);
711      } else
712         XML_WARNING("Invalid executable_regexp=\"%s\".", exec_regexp);
713   } else if (sha1) {
714      /* SHA1_DIGEST_STRING_LENGTH includes terminating null byte */
715      if (strlen(sha1) != (SHA1_DIGEST_STRING_LENGTH - 1)) {
716         XML_WARNING("Incorrect sha1 application attribute");
717         data->ignoringApp = data->inApp;
718      } else {
719         size_t len;
720         char* content;
721         char path[PATH_MAX];
722         if (util_get_process_exec_path(path, ARRAY_SIZE(path)) > 0 &&
723             (content = os_read_file(path, &len))) {
724            uint8_t sha1x[SHA1_DIGEST_LENGTH];
725            char sha1s[SHA1_DIGEST_STRING_LENGTH];
726            _mesa_sha1_compute(content, len, sha1x);
727            _mesa_sha1_format((char*) sha1s, sha1x);
728            free(content);
729
730            if (strcmp(sha1, sha1s)) {
731               data->ignoringApp = data->inApp;
732            }
733         } else {
734            data->ignoringApp = data->inApp;
735         }
736      }
737   } else if (application_name_match) {
738      regex_t re;
739
740      if (regcomp(&re, application_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
741         if (regexec(&re, data->applicationName, 0, NULL, 0) == REG_NOMATCH)
742            data->ignoringApp = data->inApp;
743         regfree(&re);
744      } else
745         XML_WARNING("Invalid application_name_match=\"%s\".", application_name_match);
746   }
747   if (application_versions) {
748      driOptionValue v = { ._int = data->applicationVersion };
749      if (parseRange(&version_range, application_versions)) {
750         if (!checkValue(&v, &version_range))
751            data->ignoringApp = data->inApp;
752      } else {
753         XML_WARNING("Failed to parse application_versions range=\"%s\".",
754                     application_versions);
755      }
756   }
757}
758
759/** \brief Parse attributes of an application element. */
760static void
761parseEngineAttr(struct OptConfData *data, const char **attr)
762{
763   uint32_t i;
764   const char *engine_name_match = NULL, *engine_versions = NULL;
765   driOptionInfo version_range = {
766      .type = DRI_INT,
767   };
768   for (i = 0; attr[i]; i += 2) {
769      if (!strcmp(attr[i], "name")) /* not needed here */;
770      else if (!strcmp(attr[i], "engine_name_match")) engine_name_match = attr[i+1];
771      else if (!strcmp(attr[i], "engine_versions")) engine_versions = attr[i+1];
772      else XML_WARNING("unknown application attribute: %s.", attr[i]);
773   }
774   if (engine_name_match) {
775      regex_t re;
776
777      if (regcomp(&re, engine_name_match, REG_EXTENDED|REG_NOSUB) == 0) {
778         if (regexec(&re, data->engineName, 0, NULL, 0) == REG_NOMATCH)
779            data->ignoringApp = data->inApp;
780         regfree(&re);
781      } else
782         XML_WARNING("Invalid engine_name_match=\"%s\".", engine_name_match);
783   }
784   if (engine_versions) {
785      driOptionValue v = { ._int = data->engineVersion };
786      if (parseRange(&version_range, engine_versions)) {
787         if (!checkValue(&v, &version_range))
788            data->ignoringApp = data->inApp;
789      } else {
790         XML_WARNING("Failed to parse engine_versions range=\"%s\".",
791                     engine_versions);
792      }
793   }
794}
795
796/** \brief Parse attributes of an option element. */
797static void
798parseOptConfAttr(struct OptConfData *data, const char **attr)
799{
800   uint32_t i;
801   const char *name = NULL, *value = NULL;
802   for (i = 0; attr[i]; i += 2) {
803      if (!strcmp(attr[i], "name")) name = attr[i+1];
804      else if (!strcmp(attr[i], "value")) value = attr[i+1];
805      else XML_WARNING("unknown option attribute: %s.", attr[i]);
806   }
807   if (!name) XML_WARNING1("name attribute missing in option.");
808   if (!value) XML_WARNING1("value attribute missing in option.");
809   if (name && value) {
810      driOptionCache *cache = data->cache;
811      uint32_t opt = findOption(cache, name);
812      if (cache->info[opt].name == NULL)
813         /* don't use XML_WARNING, drirc defines options for all drivers,
814          * but not all drivers support them */
815         return;
816      else if (getenv(cache->info[opt].name)) {
817         /* don't use XML_WARNING, we want the user to see this! */
818         if (be_verbose()) {
819            fprintf(stderr,
820                    "ATTENTION: option value of option %s ignored.\n",
821                    cache->info[opt].name);
822         }
823      } else if (!parseValue(&cache->values[opt], cache->info[opt].type, value))
824         XML_WARNING("illegal option value: %s.", value);
825   }
826}
827
828#if WITH_XMLCONFIG
829
830/** \brief Elements in configuration files. */
831enum OptConfElem {
832   OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
833};
834static const char *OptConfElems[] = {
835   [OC_APPLICATION]  = "application",
836   [OC_DEVICE] = "device",
837   [OC_DRICONF] = "driconf",
838   [OC_ENGINE]  = "engine",
839   [OC_OPTION] = "option",
840};
841
842static int compare(const void *a, const void *b) {
843   return strcmp(*(char *const*)a, *(char *const*)b);
844}
845/** \brief Binary search in a string array. */
846static uint32_t
847bsearchStr(const char *name, const char *elems[], uint32_t count)
848{
849   const char **found;
850   found = bsearch(&name, elems, count, sizeof(char *), compare);
851   if (found)
852      return found - elems;
853   else
854      return count;
855}
856
857/** \brief Handler for start element events. */
858static void
859optConfStartElem(void *userData, const char *name,
860                 const char **attr)
861{
862   struct OptConfData *data = (struct OptConfData *)userData;
863   enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
864   switch (elem) {
865   case OC_DRICONF:
866      if (data->inDriConf)
867         XML_WARNING1("nested <driconf> elements.");
868      if (attr[0])
869         XML_WARNING1("attributes specified on <driconf> element.");
870      data->inDriConf++;
871      break;
872   case OC_DEVICE:
873      if (!data->inDriConf)
874         XML_WARNING1("<device> should be inside <driconf>.");
875      if (data->inDevice)
876         XML_WARNING1("nested <device> elements.");
877      data->inDevice++;
878      if (!data->ignoringDevice && !data->ignoringApp)
879         parseDeviceAttr(data, attr);
880      break;
881   case OC_APPLICATION:
882      if (!data->inDevice)
883         XML_WARNING1("<application> should be inside <device>.");
884      if (data->inApp)
885         XML_WARNING1("nested <application> or <engine> elements.");
886      data->inApp++;
887      if (!data->ignoringDevice && !data->ignoringApp)
888         parseAppAttr(data, attr);
889      break;
890   case OC_ENGINE:
891      if (!data->inDevice)
892         XML_WARNING1("<engine> should be inside <device>.");
893      if (data->inApp)
894         XML_WARNING1("nested <application> or <engine> elements.");
895      data->inApp++;
896      if (!data->ignoringDevice && !data->ignoringApp)
897         parseEngineAttr(data, attr);
898      break;
899   case OC_OPTION:
900      if (!data->inApp)
901         XML_WARNING1("<option> should be inside <application>.");
902      if (data->inOption)
903         XML_WARNING1("nested <option> elements.");
904      data->inOption++;
905      if (!data->ignoringDevice && !data->ignoringApp)
906         parseOptConfAttr(data, attr);
907      break;
908   default:
909      XML_WARNING("unknown element: %s.", name);
910   }
911}
912
913/** \brief Handler for end element events. */
914static void
915optConfEndElem(void *userData, const char *name)
916{
917   struct OptConfData *data = (struct OptConfData *)userData;
918   enum OptConfElem elem = bsearchStr(name, OptConfElems, OC_COUNT);
919   switch (elem) {
920   case OC_DRICONF:
921      data->inDriConf--;
922      break;
923   case OC_DEVICE:
924      if (data->inDevice-- == data->ignoringDevice)
925         data->ignoringDevice = 0;
926      break;
927   case OC_APPLICATION:
928   case OC_ENGINE:
929      if (data->inApp-- == data->ignoringApp)
930         data->ignoringApp = 0;
931      break;
932   case OC_OPTION:
933      data->inOption--;
934      break;
935   default:
936      /* unknown element, warning was produced on start tag */;
937   }
938}
939
940static void
941_parseOneConfigFile(XML_Parser p)
942{
943#define BUF_SIZE 0x1000
944   struct OptConfData *data = (struct OptConfData *)XML_GetUserData(p);
945   int status;
946   int fd;
947
948   if ((fd = open(data->name, O_RDONLY)) == -1) {
949      __driUtilMessage("Can't open configuration file %s: %s.",
950                       data->name, strerror(errno));
951      return;
952   }
953
954   while (1) {
955      int bytesRead;
956      void *buffer = XML_GetBuffer(p, BUF_SIZE);
957      if (!buffer) {
958         __driUtilMessage("Can't allocate parser buffer.");
959         break;
960      }
961      bytesRead = read(fd, buffer, BUF_SIZE);
962      if (bytesRead == -1) {
963         __driUtilMessage("Error reading from configuration file %s: %s.",
964                          data->name, strerror(errno));
965         break;
966      }
967      status = XML_ParseBuffer(p, bytesRead, bytesRead == 0);
968      if (!status) {
969         XML_ERROR("%s.", XML_ErrorString(XML_GetErrorCode(p)));
970         break;
971      }
972      if (bytesRead == 0)
973         break;
974   }
975
976   close(fd);
977#undef BUF_SIZE
978}
979
980/** \brief Parse the named configuration file */
981static void
982parseOneConfigFile(struct OptConfData *data, const char *filename)
983{
984   XML_Parser p;
985
986   p = XML_ParserCreate(NULL); /* use encoding specified by file */
987   XML_SetElementHandler(p, optConfStartElem, optConfEndElem);
988   XML_SetUserData(p, data);
989   data->parser = p;
990   data->name = filename;
991   data->ignoringDevice = 0;
992   data->ignoringApp = 0;
993   data->inDriConf = 0;
994   data->inDevice = 0;
995   data->inApp = 0;
996   data->inOption = 0;
997
998   _parseOneConfigFile(p);
999   XML_ParserFree(p);
1000}
1001
1002static int
1003scandir_filter(const struct dirent *ent)
1004{
1005#ifndef DT_REG /* systems without d_type in dirent results */
1006   struct stat st;
1007
1008   if ((lstat(ent->d_name, &st) != 0) ||
1009       (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)))
1010      return 0;
1011#else
1012   /* Allow through unknown file types for filesystems that don't support d_type
1013    * The full filepath isn't available here to stat the file
1014    */
1015   if (ent->d_type != DT_REG && ent->d_type != DT_LNK && ent->d_type != DT_UNKNOWN)
1016      return 0;
1017#endif
1018
1019   int len = strlen(ent->d_name);
1020   if (len <= 5 || strcmp(ent->d_name + len - 5, ".conf"))
1021      return 0;
1022
1023   return 1;
1024}
1025
1026/** \brief Parse configuration files in a directory */
1027static void
1028parseConfigDir(struct OptConfData *data, const char *dirname)
1029{
1030   int i, count;
1031   struct dirent **entries = NULL;
1032
1033   count = scandir(dirname, &entries, scandir_filter, alphasort);
1034   if (count < 0)
1035      return;
1036
1037   for (i = 0; i < count; i++) {
1038      char filename[PATH_MAX];
1039#ifdef DT_REG
1040      unsigned char d_type = entries[i]->d_type;
1041#endif
1042
1043      snprintf(filename, PATH_MAX, "%s/%s", dirname, entries[i]->d_name);
1044      free(entries[i]);
1045
1046#ifdef DT_REG
1047      /* In the case of unknown d_type, ensure it is a regular file
1048       * This can be accomplished with stat on the full filepath
1049       */
1050      if (d_type == DT_UNKNOWN) {
1051         struct stat st;
1052         if (stat(filename, &st) != 0 ||
1053             !S_ISREG(st.st_mode)) {
1054            continue;
1055         }
1056      }
1057#endif
1058
1059      parseOneConfigFile(data, filename);
1060   }
1061
1062   free(entries);
1063}
1064#else
1065#  include "driconf_static.h"
1066
1067static void
1068parseStaticOptions(struct OptConfData *data, const struct driconf_option *options,
1069                   unsigned num_options)
1070{
1071   if (data->ignoringDevice || data->ignoringApp)
1072      return;
1073   for (unsigned i = 0; i < num_options; i++) {
1074      const char *optattr[] = {
1075         "name", options[i].name,
1076         "value", options[i].value,
1077         NULL
1078      };
1079      parseOptConfAttr(data, optattr);
1080   }
1081}
1082
1083static void
1084parseStaticConfig(struct OptConfData *data)
1085{
1086   data->ignoringDevice = 0;
1087   data->ignoringApp = 0;
1088   data->inDriConf = 0;
1089   data->inDevice = 0;
1090   data->inApp = 0;
1091   data->inOption = 0;
1092
1093   for (unsigned i = 0; i < ARRAY_SIZE(driconf); i++) {
1094      const struct driconf_device *d = driconf[i];
1095      const char *devattr[] = {
1096         "driver", d->driver,
1097         "device", d->device,
1098         NULL
1099      };
1100
1101      data->ignoringDevice = 0;
1102      data->inDevice++;
1103      parseDeviceAttr(data, devattr);
1104      data->inDevice--;
1105
1106      data->inApp++;
1107
1108      for (unsigned j = 0; j < d->num_engines; j++) {
1109         const struct driconf_engine *e = &d->engines[j];
1110         const char *engattr[] = {
1111            "engine_name_match", e->engine_name_match,
1112            "engine_versions", e->engine_versions,
1113            NULL
1114         };
1115
1116         data->ignoringApp = 0;
1117         parseEngineAttr(data, engattr);
1118         parseStaticOptions(data, e->options, e->num_options);
1119      }
1120
1121      for (unsigned j = 0; j < d->num_applications; j++) {
1122         const struct driconf_application *a = &d->applications[j];
1123         const char *appattr[] = {
1124            "name", a->name,
1125            "executable", a->executable,
1126            "executable_regexp", a->executable_regexp,
1127            "sha1", a->sha1,
1128            "application_name_match", a->application_name_match,
1129            "application_versions", a->application_versions,
1130            NULL
1131         };
1132
1133         data->ignoringApp = 0;
1134         parseAppAttr(data, appattr);
1135         parseStaticOptions(data, a->options, a->num_options);
1136      }
1137
1138      data->inApp--;
1139   }
1140}
1141#endif /* WITH_XMLCONFIG */
1142
1143/** \brief Initialize an option cache based on info */
1144static void
1145initOptionCache(driOptionCache *cache, const driOptionCache *info)
1146{
1147   unsigned i, size = 1 << info->tableSize;
1148   cache->info = info->info;
1149   cache->tableSize = info->tableSize;
1150   cache->values = malloc(((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1151   if (cache->values == NULL) {
1152      fprintf(stderr, "%s: %d: out of memory.\n", __FILE__, __LINE__);
1153      abort();
1154   }
1155   memcpy(cache->values, info->values,
1156           ((size_t)1 << info->tableSize) * sizeof(driOptionValue));
1157   for (i = 0; i < size; ++i) {
1158      if (cache->info[i].type == DRI_STRING)
1159         XSTRDUP(cache->values[i]._string, info->values[i]._string);
1160   }
1161}
1162
1163#ifndef SYSCONFDIR
1164#define SYSCONFDIR "/etc"
1165#endif
1166
1167#ifndef DATADIR
1168#define DATADIR "/usr/share"
1169#endif
1170
1171static const char *datadir = DATADIR "/drirc.d";
1172static const char *execname;
1173
1174void
1175driInjectDataDir(const char *dir)
1176{
1177   datadir = dir;
1178}
1179
1180void
1181driInjectExecName(const char *exec)
1182{
1183   execname = exec;
1184}
1185
1186void
1187driParseConfigFiles(driOptionCache *cache, const driOptionCache *info,
1188                    int screenNum, const char *driverName,
1189                    const char *kernelDriverName,
1190                    const char *deviceName,
1191                    const char *applicationName, uint32_t applicationVersion,
1192                    const char *engineName, uint32_t engineVersion)
1193{
1194   initOptionCache(cache, info);
1195   struct OptConfData userData = {0};
1196
1197   userData.cache = cache;
1198   userData.screenNum = screenNum;
1199   userData.driverName = driverName;
1200   userData.kernelDriverName = kernelDriverName;
1201   userData.deviceName = deviceName;
1202   userData.applicationName = applicationName ? applicationName : "";
1203   userData.applicationVersion = applicationVersion;
1204   userData.engineName = engineName ? engineName : "";
1205   userData.engineVersion = engineVersion;
1206   userData.execName = execname ? execname : util_get_process_name();
1207
1208#if WITH_XMLCONFIG
1209   char *home;
1210
1211   parseConfigDir(&userData, datadir);
1212   parseOneConfigFile(&userData, SYSCONFDIR "/drirc");
1213
1214   if ((home = getenv("HOME"))) {
1215      char filename[PATH_MAX];
1216
1217      snprintf(filename, PATH_MAX, "%s/.drirc", home);
1218      parseOneConfigFile(&userData, filename);
1219   }
1220#else
1221   parseStaticConfig(&userData);
1222#endif /* WITH_XMLCONFIG */
1223}
1224
1225void
1226driDestroyOptionInfo(driOptionCache *info)
1227{
1228   driDestroyOptionCache(info);
1229   if (info->info) {
1230      uint32_t i, size = 1 << info->tableSize;
1231      for (i = 0; i < size; ++i) {
1232         if (info->info[i].name) {
1233            free(info->info[i].name);
1234         }
1235      }
1236      free(info->info);
1237   }
1238}
1239
1240void
1241driDestroyOptionCache(driOptionCache *cache)
1242{
1243   if (cache->info) {
1244      unsigned i, size = 1 << cache->tableSize;
1245      for (i = 0; i < size; ++i) {
1246         if (cache->info[i].type == DRI_STRING)
1247            free(cache->values[i]._string);
1248      }
1249   }
1250   free(cache->values);
1251}
1252
1253unsigned char
1254driCheckOption(const driOptionCache *cache, const char *name,
1255               driOptionType type)
1256{
1257   uint32_t i = findOption(cache, name);
1258   return cache->info[i].name != NULL && cache->info[i].type == type;
1259}
1260
1261unsigned char
1262driQueryOptionb(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_BOOL);
1268   return cache->values[i]._bool;
1269}
1270
1271int
1272driQueryOptioni(const driOptionCache *cache, const char *name)
1273{
1274   uint32_t i = findOption(cache, name);
1275   /* make sure the option is defined and has the correct type */
1276   assert(cache->info[i].name != NULL);
1277   assert(cache->info[i].type == DRI_INT || cache->info[i].type == DRI_ENUM);
1278   return cache->values[i]._int;
1279}
1280
1281float
1282driQueryOptionf(const driOptionCache *cache, const char *name)
1283{
1284   uint32_t i = findOption(cache, name);
1285   /* make sure the option is defined and has the correct type */
1286   assert(cache->info[i].name != NULL);
1287   assert(cache->info[i].type == DRI_FLOAT);
1288   return cache->values[i]._float;
1289}
1290
1291char *
1292driQueryOptionstr(const driOptionCache *cache, const char *name)
1293{
1294   uint32_t i = findOption(cache, name);
1295   /* make sure the option is defined and has the correct type */
1296   assert(cache->info[i].name != NULL);
1297   assert(cache->info[i].type == DRI_STRING);
1298   return cache->values[i]._string;
1299}
1300