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
47 typedef int regex_t;
48 #define REG_EXTENDED 0
49 #define REG_NOSUB 0
50 #define REG_NOMATCH 1
regcomp(regex_t * r,const char * s,int f)51 static inline int regcomp(regex_t *r, const char *s, int f) { return 0; }
regexec(regex_t * r,const char * s,int n,void * p,int f)52 static inline int regexec(regex_t *r, const char *s, int n, void *p, int f) { return REG_NOMATCH; }
regfree(regex_t * r)53 static 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 
68 static bool
be_verbose(void)69 be_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. */
87 static int
strToI(const char * string,const char ** tail,int base)88 strToI(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. */
149 static float
strToF(const char * string,const char ** tail)150 strToF(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. */
213 static unsigned char
parseValue(driOptionValue * v,driOptionType type,const char * string)214 parseValue(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 */
258 static uint32_t
findOption(const driOptionCache * cache,const char * name)259 findOption(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. */
295 UNUSED static bool
checkValue(const driOptionValue * v,const driOptionInfo * info)296 checkValue(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 
315 void
driParseOptionInfo(driOptionCache * info,const driOptionDescription * configOptions,unsigned numOptions)316 driParseOptionInfo(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 
405 char *
driGetOptionsXml(const driOptionDescription * configOptions,unsigned numOptions)406 driGetOptionsXml(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  */
541 static void
__driUtilMessage(const char * f,...)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. */
589 struct 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. */
612 static unsigned char
parseRange(driOptionInfo * info,const char * string)613 parseRange(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. */
648 static void
parseDeviceAttr(struct OptConfData * data,const char ** attr)649 parseDeviceAttr(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. */
678 static void
parseAppAttr(struct OptConfData * data,const char ** attr)679 parseAppAttr(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. */
749 static void
parseEngineAttr(struct OptConfData * data,const char ** attr)750 parseEngineAttr(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. */
786 static void
parseOptConfAttr(struct OptConfData * data,const char ** attr)787 parseOptConfAttr(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. */
820 enum OptConfElem {
821    OC_APPLICATION = 0, OC_DEVICE, OC_DRICONF, OC_ENGINE, OC_OPTION, OC_COUNT
822 };
823 static 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 
compare(const void * a,const void * b)831 static 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. */
835 static uint32_t
bsearchStr(const char * name,const char * elems[],uint32_t count)836 bsearchStr(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. */
847 static void
optConfStartElem(void * userData,const char * name,const char ** attr)848 optConfStartElem(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. */
903 static void
optConfEndElem(void * userData,const char * name)904 optConfEndElem(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 
929 static void
_parseOneConfigFile(XML_Parser p)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 */
970 static void
parseOneConfigFile(struct OptConfData * data,const char * filename)971 parseOneConfigFile(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 
991 static int
scandir_filter(const struct dirent * ent)992 scandir_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 */
1013 static void
parseConfigDir(struct OptConfData * data,const char * dirname)1014 parseConfigDir(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 
1037 static void
parseStaticOptions(struct OptConfData * data,const struct driconf_option * options,unsigned num_options)1038 parseStaticOptions(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 
1053 static void
parseStaticConfig(struct OptConfData * data)1054 parseStaticConfig(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 */
1114 static void
initOptionCache(driOptionCache * cache,const driOptionCache * info)1115 initOptionCache(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 "/usr/local/etc"
1135 #endif
1136 
1137 #ifndef DATADIR
1138 #define DATADIR "/usr/local/share"
1139 #endif
1140 
1141 static const char *datadir = DATADIR "/drirc.d";
1142 static const char *execname;
1143 
1144 void
driInjectDataDir(const char * dir)1145 driInjectDataDir(const char *dir)
1146 {
1147    datadir = dir;
1148 }
1149 
1150 void
driInjectExecName(const char * exec)1151 driInjectExecName(const char *exec)
1152 {
1153    execname = exec;
1154 }
1155 
1156 void
driParseConfigFiles(driOptionCache * cache,const driOptionCache * info,int screenNum,const char * driverName,const char * kernelDriverName,const char * deviceName,const char * applicationName,uint32_t applicationVersion,const char * engineName,uint32_t engineVersion)1157 driParseConfigFiles(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 
1195 void
driDestroyOptionInfo(driOptionCache * info)1196 driDestroyOptionInfo(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 
1210 void
driDestroyOptionCache(driOptionCache * cache)1211 driDestroyOptionCache(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 
1223 unsigned char
driCheckOption(const driOptionCache * cache,const char * name,driOptionType type)1224 driCheckOption(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 
1231 unsigned char
driQueryOptionb(const driOptionCache * cache,const char * name)1232 driQueryOptionb(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 
1241 int
driQueryOptioni(const driOptionCache * cache,const char * name)1242 driQueryOptioni(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 
1251 float
driQueryOptionf(const driOptionCache * cache,const char * name)1252 driQueryOptionf(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 
1261 char *
driQueryOptionstr(const driOptionCache * cache,const char * name)1262 driQueryOptionstr(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