diff options
Diffstat (limited to 'lib/names-parse.c')
-rw-r--r-- | lib/names-parse.c | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/lib/names-parse.c b/lib/names-parse.c new file mode 100644 index 0000000..1f8925a --- /dev/null +++ b/lib/names-parse.c @@ -0,0 +1,253 @@ +/* + * The PCI Library -- Parsing of the ID list + * + * Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL v2+. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "internal.h" +#include "names.h" + +#ifdef PCI_COMPRESSED_IDS +#include <zlib.h> +typedef gzFile pci_file; +#define pci_gets(f, l, s) gzgets(f, l, s) +#define pci_eof(f) gzeof(f) + +static pci_file pci_open(struct pci_access *a) +{ + pci_file result; + size_t len; + char *new_name; + + result = gzopen(a->id_file_name, "rb"); + if (result) + return result; + len = strlen(a->id_file_name); + if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0) + return result; + new_name = malloc(len - 2); + memcpy(new_name, a->id_file_name, len - 3); + new_name[len - 3] = 0; + pci_set_name_list_path(a, new_name, 1); + return gzopen(a->id_file_name, "rb"); +} + +#define pci_close(f) gzclose(f) +#define PCI_ERROR(f, err) \ + if (!err) { \ + int errnum = 0; \ + gzerror(f, &errnum); \ + if (errnum >= 0) err = NULL; \ + else if (errnum == Z_ERRNO) err = "I/O error"; \ + else err = zError(errnum); \ + } +#else +typedef FILE * pci_file; +#define pci_gets(f, l, s) fgets(l, s, f) +#define pci_eof(f) feof(f) +#define pci_open(a) fopen(a->id_file_name, "r") +#define pci_close(f) fclose(f) +#define PCI_ERROR(f, err) if (!err && ferror(f)) err = "I/O error"; +#endif + +static int id_hex(char *p, int cnt) +{ + int x = 0; + while (cnt--) + { + x <<= 4; + if (*p >= '0' && *p <= '9') + x += (*p - '0'); + else if (*p >= 'a' && *p <= 'f') + x += (*p - 'a' + 10); + else if (*p >= 'A' && *p <= 'F') + x += (*p - 'A' + 10); + else + return -1; + p++; + } + return x; +} + +static inline int id_white_p(int c) +{ + return (c == ' ') || (c == '\t'); +} + + +static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino) +{ + char line[MAX_LINE]; + char *p; + int id1=0, id2=0, id3=0, id4=0; + int cat = -1; + int nest; + static const char parse_error[] = "Parse error"; + + *lino = 0; + while (pci_gets(f, line, sizeof(line))) + { + (*lino)++; + p = line; + while (*p && *p != '\n' && *p != '\r') + p++; + if (!*p && !pci_eof(f)) + return "Line too long"; + *p = 0; + if (p > line && (p[-1] == ' ' || p[-1] == '\t')) + *--p = 0; + + p = line; + while (id_white_p(*p)) + p++; + if (!*p || *p == '#') + continue; + + p = line; + while (*p == '\t') + p++; + nest = p - line; + + if (!nest) /* Top-level entries */ + { + if (p[0] == 'C' && p[1] == ' ') /* Class block */ + { + if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4])) + return parse_error; + cat = ID_CLASS; + p += 5; + } + else if (p[0] == 'S' && p[1] == ' ') + { /* Generic subsystem block */ + if ((id1 = id_hex(p+2, 4)) < 0 || p[6]) + return parse_error; + if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0)) + return "Vendor does not exist"; + cat = ID_GEN_SUBSYSTEM; + continue; + } + else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ') + { /* Unrecognized block (RFU) */ + cat = ID_UNKNOWN; + continue; + } + else /* Vendor ID */ + { + if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + cat = ID_VENDOR; + p += 5; + } + id2 = id3 = id4 = 0; + } + else if (cat == ID_UNKNOWN) /* Nested entries in RFU blocks are skipped */ + continue; + else if (nest == 1) /* Nesting level 1 */ + switch (cat) + { + case ID_VENDOR: + case ID_DEVICE: + case ID_SUBSYSTEM: + if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + p += 5; + cat = ID_DEVICE; + id3 = id4 = 0; + break; + case ID_GEN_SUBSYSTEM: + if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4])) + return parse_error; + p += 5; + id3 = id4 = 0; + break; + case ID_CLASS: + case ID_SUBCLASS: + case ID_PROGIF: + if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) + return parse_error; + p += 3; + cat = ID_SUBCLASS; + id3 = id4 = 0; + break; + default: + return parse_error; + } + else if (nest == 2) /* Nesting level 2 */ + switch (cat) + { + case ID_DEVICE: + case ID_SUBSYSTEM: + if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9])) + return parse_error; + p += 10; + cat = ID_SUBSYSTEM; + break; + case ID_CLASS: + case ID_SUBCLASS: + case ID_PROGIF: + if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2])) + return parse_error; + p += 3; + cat = ID_PROGIF; + id4 = 0; + break; + default: + return parse_error; + } + else /* Nesting level 3 or more */ + return parse_error; + while (id_white_p(*p)) + p++; + if (!*p) + return parse_error; + if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL)) + return "Duplicate entry"; + } + return NULL; +} + +int +pci_load_name_list(struct pci_access *a) +{ + pci_file f; + int lino; + const char *err; + + pci_free_name_list(a); + a->id_load_attempted = 1; + if (!(f = pci_open(a))) + return 0; + err = id_parse_list(a, f, &lino); + PCI_ERROR(f, err); + pci_close(f); + if (err) + a->error("%s at %s, line %d\n", err, a->id_file_name, lino); + return 1; +} + +void +pci_free_name_list(struct pci_access *a) +{ + pci_id_cache_flush(a); + pci_id_hash_free(a); + pci_id_hwdb_free(a); + a->id_load_attempted = 0; +} + +void +pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed) +{ + if (a->free_id_name) + free(a->id_file_name); + a->id_file_name = name; + a->free_id_name = to_be_freed; +} |