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