]> mj.ucw.cz Git - pciutils.git/commitdiff
Split handling of the ID list to several files.
authorMartin Mares <mj@ucw.cz>
Mon, 11 Feb 2008 19:59:53 +0000 (20:59 +0100)
committerMartin Mares <mj@ucw.cz>
Mon, 11 Feb 2008 19:59:53 +0000 (20:59 +0100)
TODO
lib/Makefile
lib/names-cache.c [new file with mode: 0644]
lib/names-hash.c [new file with mode: 0644]
lib/names-net.c [new file with mode: 0644]
lib/names-parse.c [new file with mode: 0644]
lib/names.c
lib/names.h [new file with mode: 0644]

diff --git a/TODO b/TODO
index f9370ae99c49b1617eef54ade403c75722cd1bec..a7a73e3628499e702dac328cdf49d6e98253228c 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,5 +1,10 @@
 - review class names
 
+DNS mode:
+- races when writing cache
+- options in lspci
+- check negative caching
+
 Capabilities with partial decoding:
 - PCIe 2nd set of control/status registers (have spec)
 - HyperTransport caps (have spec)
index 2afd886b8f716d30a2f8a261af37fbcb2682fc72..1a0678c229599d24f4c586148b0568b9be987376 100644 (file)
@@ -1,9 +1,9 @@
 # Makefile for The PCI Library
-# (c) 1999--2007 Martin Mares <mj@ucw.cz>
+# (c) 1999--2008 Martin Mares <mj@ucw.cz>
 
 include config.mk
 
-OBJS=access.o generic.o dump.o names.o filter.o
+OBJS=access.o generic.o dump.o names.o filter.o names-hash.o names-parse.o
 INCL=internal.h pci.h config.h header.h sysdep.h types.h
 
 PCILIB=libpci.a
@@ -46,6 +46,9 @@ OBJS += nbsd-libpci.o
 PCILIB=libpciutils.a
 endif
 
+# FIXME: Conditions
+OBJS += names-net.o names-cache.o
+
 all: $(PCILIB) $(PCILIBPC)
 
 $(PCILIB): $(OBJS)
@@ -71,7 +74,11 @@ obsd-device.o: obsd-device.c $(INCL)
 fbsd-device.o: fbsd-device.c $(INCL)
 aix-device.o: aix-device.c $(INCL)
 dump.o: dump.c $(INCL)
-names.o: names.c $(INCL)
+names.o: names.c $(INCL) names.h
+names-cache.o: names-cache.c $(INCL) names.h
+names-hash.o: names-hash.c $(INCL) names.h
+names-net.o: names-net.c $(INCL) names.h
+names-parse.o: names-parse.c $(INCL) names.h
 filter.o: filter.c $(INCL)
 nbsd-libpci.o: nbsd-libpci.c $(INCL)
 
diff --git a/lib/names-cache.c b/lib/names-cache.c
new file mode 100644 (file)
index 0000000..2811fe4
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ *     The PCI Library -- ID to Name Cache
+ *
+ *     Copyright (c) 2008 Martin Mares <mj@ucw.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <pwd.h>
+#include <unistd.h>
+
+#include "internal.h"
+#include "names.h"
+
+static const char cache_version[] = "#PCI-CACHE-1.0";
+
+int
+pci_id_cache_load(struct pci_access *a, int flags)
+{
+  char *name;
+  char line[MAX_LINE];
+  const char default_name[] = "/.pciids-cache";
+  FILE *f;
+  int lino;
+
+  a->id_cache_status = 1;
+  if (!a->id_cache_file)
+    {
+      /* Construct the default ID cache name */
+      uid_t uid = getuid();
+      struct passwd *pw = getpwuid(uid);
+      if (!pw)
+        return 0;
+      name = pci_malloc(a, strlen(pw->pw_dir) + sizeof(default_name));
+      sprintf(name, "%s%s", pw->pw_dir, default_name);
+      pci_set_id_cache(a, name, 1);
+    }
+  a->debug("Using cache %s\n", a->id_cache_file);
+  if (flags & PCI_LOOKUP_REFRESH_CACHE)
+    {
+      a->debug("Not loading cache, will refresh everything\n");
+      a->id_cache_status = 2;
+      return 0;
+    }
+
+  f = fopen(a->id_cache_file, "rb");
+  if (!f)
+    {
+      a->debug("Cache file does not exist\n");
+      return 0;
+    }
+  /* FIXME: Compare timestamp with the pci.ids file? */
+
+  lino = 0;
+  while (fgets(line, sizeof(line), f))
+    {
+      char *p = strchr(line, '\n');
+      lino++;
+      if (p)
+        {
+         *p = 0;
+         if (lino == 1)
+           {
+             if (strcmp(line, cache_version))
+               {
+                 a->debug("Unrecognized cache version %s, ignoring\n", line);
+                 break;
+               }
+             continue;
+           }
+         else
+           {
+             int cat, id1, id2, id3, id4, cnt;
+             if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5)
+               {
+                 p = line + cnt;
+                 while (*p && *p == ' ')
+                   p++;
+                 pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE);
+                 continue;
+               }
+           }
+       }
+      a->warning("Malformed cache file %s (line %d), ignoring", a->id_cache_file, lino);
+      break;
+    }
+
+  if (ferror(f))
+    a->warning("Error while reading %s", a->id_cache_file);
+  fclose(f);
+  return 1;
+}
+
+void
+pci_id_cache_dirty(struct pci_access *a)
+{
+  if (a->id_cache_status >= 1)
+    a->id_cache_status = 2;
+}
+
+void
+pci_id_cache_flush(struct pci_access *a)
+{
+  int orig_status = a->id_cache_status;
+  FILE *f;
+  unsigned int h;
+  struct id_entry *e, *e2;
+
+  a->id_cache_status = 0;
+  if (orig_status < 2)
+    return;
+  if (!a->id_cache_file)
+    return;
+  f = fopen(a->id_cache_file, "wb");
+  if (!f)
+    {
+      a->warning("Cannot write %s: %s", a->id_cache_file, strerror(errno));
+      return;
+    }
+  a->debug("Writing cache to %s\n", a->id_cache_file);
+  fprintf(f, "%s\n", cache_version);
+
+  for (h=0; h<HASH_SIZE; h++)
+    for (e=a->id_hash[h]; e; e=e->next)
+      if (e->src == SRC_CACHE || e->src == SRC_NET)
+       {
+         /* Verify that every entry is written at most once */
+         for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
+           if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
+               e2->cat == e->cat &&
+               e2->id12 == e->id12 && e2->id34 == e->id34)
+           break;
+         if (e2 == e)
+           fprintf(f, "%d %x %x %x %x %s\n",
+                   e->cat,
+                   pair_first(e->id12), pair_second(e->id12),
+                   pair_first(e->id34), pair_second(e->id34),
+                   e->name);
+       }
+
+  fflush(f);
+  if (ferror(f))
+    a->warning("Error writing %s", a->id_cache_file);
+  fclose(f);
+}
+
+void
+pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed)
+{
+  if (a->free_id_cache_file)
+    free(a->id_cache_file);
+  a->id_cache_file = name;
+  a->free_id_cache_file = to_be_freed;
+}
diff --git a/lib/names-hash.c b/lib/names-hash.c
new file mode 100644 (file)
index 0000000..d0748c7
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ *     The PCI Library -- ID to Name Hash
+ *
+ *     Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <string.h>
+
+#include "internal.h"
+#include "names.h"
+
+struct id_bucket {
+  struct id_bucket *next;
+  unsigned int full;
+};
+
+#ifdef __GNUC__
+#define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
+#else
+union id_align {
+  struct id_bucket *next;
+  unsigned int full;
+};
+#define BUCKET_ALIGNMENT sizeof(union id_align)
+#endif
+#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
+
+static void *id_alloc(struct pci_access *a, unsigned int size)
+{
+  struct id_bucket *buck = a->current_id_bucket;
+  unsigned int pos;
+
+  if (!a->id_hash)
+    {
+      a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
+      memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE);
+    }
+
+  if (!buck || buck->full + size > BUCKET_SIZE)
+    {
+      buck = pci_malloc(a, BUCKET_SIZE);
+      buck->next = a->current_id_bucket;
+      a->current_id_bucket = buck;
+      buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
+    }
+  pos = buck->full;
+  buck->full = BUCKET_ALIGN(buck->full + size);
+  return (byte *)buck + pos;
+}
+
+static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
+{
+  unsigned int h;
+
+  h = id12 ^ (id34 << 3) ^ (cat << 5);
+  return h % HASH_SIZE;
+}
+
+int
+pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src)
+{
+  u32 id12 = id_pair(id1, id2);
+  u32 id34 = id_pair(id3, id4);
+  unsigned int h = id_hash(cat, id12, id34);
+  struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL;
+  int len = strlen(text);
+
+  while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
+    n = n->next;
+  if (n)
+    return 1;
+  n = id_alloc(a, sizeof(struct id_entry) + len);
+  n->id12 = id12;
+  n->id34 = id34;
+  n->cat = cat;
+  n->src = src;
+  memcpy(n->name, text, len+1);
+  n->next = a->id_hash[h];
+  a->id_hash[h] = n;
+  return 0;
+}
+
+char
+*pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
+{
+  struct id_entry *n, *best;
+  u32 id12 = id_pair(id1, id2);
+  u32 id34 = id_pair(id3, id4);
+
+  if (a->id_hash)
+    {
+      n = a->id_hash[id_hash(cat, id12, id34)];
+      best = NULL;
+      for (; n; n=n->next)
+        {
+         if (n->id12 != id12 || n->id34 != id34 || n->cat != cat)
+           continue;
+         if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL))
+           continue;
+         if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK))
+           continue;
+         if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE))
+           continue;
+         if (!best || best->src < n->src)
+           best = n;
+       }
+      if (best)
+       return best->name;
+    }
+  return NULL;
+}
+
+static void
+pci_id_hash_free(struct pci_access *a)
+{
+  pci_mfree(a->id_hash);
+  a->id_hash = NULL;
+  a->id_cache_status = 0;
+  while (a->current_id_bucket)
+    {
+      struct id_bucket *buck = a->current_id_bucket;
+      a->current_id_bucket = buck->next;
+      pci_mfree(buck);
+    }
+}
+
+void
+pci_free_name_list(struct pci_access *a)
+{
+  pci_id_cache_flush(a);
+  pci_id_hash_free(a);
+}
diff --git a/lib/names-net.c b/lib/names-net.c
new file mode 100644 (file)
index 0000000..167cd1f
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ *     The PCI Library -- Resolving ID's via DNS
+ *
+ *     Copyright (c) 2007--2008 Martin Mares <mj@ucw.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+#include "internal.h"
+#include "names.h"
+
+char
+*pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
+{
+  char name[256], dnsname[256], txt[256];
+  byte answer[4096];
+  const byte *data;
+  int res, i, j, dlen;
+  ns_msg m;
+  ns_rr rr;
+
+  switch (cat)
+    {
+    case ID_VENDOR:
+      sprintf(name, "%04x", id1);
+      break;
+    case ID_DEVICE:
+      sprintf(name, "%04x.%04x", id2, id1);
+      break;
+    case ID_SUBSYSTEM:
+      sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1);
+      break;
+    case ID_GEN_SUBSYSTEM:
+      sprintf(name, "%04x.%04x.s", id2, id1);
+      break;
+    case ID_CLASS:
+      sprintf(name, "%02x.c", id1);
+      break;
+    case ID_SUBCLASS:
+      sprintf(name, "%02x.%02x.c", id2, id1);
+      break;
+    case ID_PROGIF:
+      sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1);
+      break;
+    default:
+      return NULL;
+    }
+  sprintf(dnsname, "%s.%s", name, a->id_domain);
+
+  a->debug("Resolving %s\n", dnsname);
+  res_init();
+  res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer));
+  if (res < 0)
+    {
+      a->debug("\tfailed, h_errno=%d\n", _res.res_h_errno);
+      return NULL;
+    }
+  if (ns_initparse(answer, res, &m) < 0)
+    {
+      a->debug("\tinitparse failed\n");
+      return NULL;
+    }
+  for (i=0; ns_parserr(&m, ns_s_an, i, &rr) >= 0; i++)
+    {
+      a->debug("\tanswer %d (class %d, type %d)\n", i, ns_rr_class(rr), ns_rr_type(rr));
+      if (ns_rr_class(rr) != ns_c_in || ns_rr_type(rr) != ns_t_txt)
+       continue;
+      data = ns_rr_rdata(rr);
+      dlen = ns_rr_rdlen(rr);
+      j = 0;
+      while (j < dlen && j+1+data[j] <= dlen)
+       {
+         memcpy(txt, &data[j+1], data[j]);
+         txt[data[j]] = 0;
+         j += 1+data[j];
+         a->debug("\t\t%s\n", txt);
+         if (txt[0] == 'i' && txt[1] == '=')
+           return strdup(txt+2);
+       }
+    }
+
+  return NULL;
+}
+
+void
+pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed)
+{
+  if (a->free_id_domain)
+    free(a->id_domain);
+  a->id_domain = name;
+  a->free_id_domain = to_be_freed;
+}
diff --git a/lib/names-parse.c b/lib/names-parse.c
new file mode 100644 (file)
index 0000000..71b622f
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ *     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.
+ */
+
+#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;                                             \
+               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_failed = 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);
+  a->id_load_failed = 0;
+  return 1;
+}
+
+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;
+}
index 30b6b6eb5f182c7c9cae4f5e57cd27436b13f6ed..f3fdf17e594dfbe6a50c7bcf742c1e0a9376661f 100644 (file)
  */
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
-#include <errno.h>
-#include <netinet/in.h>
-#include <arpa/nameser.h>
-#include <resolv.h>
-#include <sys/types.h>
-#include <pwd.h>
-#include <unistd.h>
 
 #include "internal.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;                                             \
-               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
-
-struct id_entry {
-  struct id_entry *next;
-  u32 id12, id34;
-  byte cat;
-  byte src;
-  char name[1];
-};
-
-enum id_entry_type {
-  ID_UNKNOWN,
-  ID_VENDOR,
-  ID_DEVICE,
-  ID_SUBSYSTEM,
-  ID_GEN_SUBSYSTEM,
-  ID_CLASS,
-  ID_SUBCLASS,
-  ID_PROGIF
-};
-
-enum id_entry_src {
-  SRC_UNKNOWN,
-  SRC_CACHE,
-  SRC_NET,
-  SRC_LOCAL,
-};
-
-struct id_bucket {
-  struct id_bucket *next;
-  unsigned int full;
-};
-
-#define MAX_LINE 1024
-#define BUCKET_SIZE 8192
-#define HASH_SIZE 4099
-
-#ifdef __GNUC__
-#define BUCKET_ALIGNMENT __alignof__(struct id_bucket)
-#else
-union id_align {
-  struct id_bucket *next;
-  unsigned int full;
-};
-#define BUCKET_ALIGNMENT sizeof(union id_align)
-#endif
-#define BUCKET_ALIGN(n) ((n)+BUCKET_ALIGNMENT-(n)%BUCKET_ALIGNMENT)
-
-static void *id_alloc(struct pci_access *a, unsigned int size)
-{
-  struct id_bucket *buck = a->current_id_bucket;
-  unsigned int pos;
-
-  if (!a->id_hash)
-    {
-      a->id_hash = pci_malloc(a, sizeof(struct id_entry *) * HASH_SIZE);
-      memset(a->id_hash, 0, sizeof(struct id_entry *) * HASH_SIZE);
-    }
-
-  if (!buck || buck->full + size > BUCKET_SIZE)
-    {
-      buck = pci_malloc(a, BUCKET_SIZE);
-      buck->next = a->current_id_bucket;
-      a->current_id_bucket = buck;
-      buck->full = BUCKET_ALIGN(sizeof(struct id_bucket));
-    }
-  pos = buck->full;
-  buck->full = BUCKET_ALIGN(buck->full + size);
-  return (byte *)buck + pos;
-}
-
-static inline u32 id_pair(unsigned int x, unsigned int y)
-{
-  return ((x << 16) | y);
-}
-
-static inline unsigned int pair_first(unsigned int x)
-{
-  return (x >> 16) & 0xffff;
-}
-
-static inline unsigned int pair_second(unsigned int x)
-{
-  return x & 0xffff;
-}
-
-static inline unsigned int id_hash(int cat, u32 id12, u32 id34)
-{
-  unsigned int h;
-
-  h = id12 ^ (id34 << 3) ^ (cat << 5);
-  return h % HASH_SIZE;
-}
-
-static int id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src)
-{
-  u32 id12 = id_pair(id1, id2);
-  u32 id34 = id_pair(id3, id4);
-  unsigned int h = id_hash(cat, id12, id34);
-  struct id_entry *n = a->id_hash ? a->id_hash[h] : NULL;
-  int len = strlen(text);
-
-  while (n && (n->id12 != id12 || n->id34 != id34 || n->cat != cat))
-    n = n->next;
-  if (n)
-    return 1;
-  n = id_alloc(a, sizeof(struct id_entry) + len);
-  n->id12 = id12;
-  n->id34 = id34;
-  n->cat = cat;
-  n->src = src;
-  memcpy(n->name, text, len+1);
-  n->next = a->id_hash[h];
-  a->id_hash[h] = n;
-  return 0;
-}
-
-static char *id_lookup_raw(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
-{
-  struct id_entry *n, *best;
-  u32 id12 = id_pair(id1, id2);
-  u32 id34 = id_pair(id3, id4);
-
-  if (a->id_hash)
-    {
-      n = a->id_hash[id_hash(cat, id12, id34)];
-      best = NULL;
-      for (; n; n=n->next)
-        {
-         if (n->id12 != id12 || n->id34 != id34 || n->cat != cat)
-           continue;
-         if (n->src == SRC_LOCAL && (flags & PCI_LOOKUP_SKIP_LOCAL))
-           continue;
-         if (n->src == SRC_NET && !(flags & PCI_LOOKUP_NETWORK))
-           continue;
-         if (n->src == SRC_CACHE && !(flags & PCI_LOOKUP_CACHE))
-           continue;
-         if (!best || best->src < n->src)
-           best = n;
-       }
-      if (best)
-       return best->name;
-    }
-  return NULL;
-}
-
-static const char cache_version[] = "#PCI-CACHE-1.0";
-
-static int
-pci_id_cache_load(struct pci_access *a, int flags)
-{
-  char *name;
-  char line[MAX_LINE];
-  const char default_name[] = "/.pciids-cache";
-  FILE *f;
-  int lino;
-
-  a->id_cache_status = 1;
-  if (!a->id_cache_file)
-    {
-      /* Construct the default ID cache name */
-      uid_t uid = getuid();
-      struct passwd *pw = getpwuid(uid);
-      if (!pw)
-        return 0;
-      name = pci_malloc(a, strlen(pw->pw_dir) + sizeof(default_name));
-      sprintf(name, "%s%s", pw->pw_dir, default_name);
-      pci_set_id_cache(a, name, 1);
-    }
-  a->debug("Using cache %s\n", a->id_cache_file);
-  if (flags & PCI_LOOKUP_REFRESH_CACHE)
-    {
-      a->debug("Not loading cache, will refresh everything\n");
-      a->id_cache_status = 2;
-      return 0;
-    }
-
-  f = fopen(a->id_cache_file, "rb");
-  if (!f)
-    {
-      a->debug("Cache file does not exist\n");
-      return 0;
-    }
-  /* FIXME: Compare timestamp with the pci.ids file? */
-
-  lino = 0;
-  while (fgets(line, sizeof(line), f))
-    {
-      char *p = strchr(line, '\n');
-      lino++;
-      if (p)
-        {
-         *p = 0;
-         if (lino == 1)
-           {
-             if (strcmp(line, cache_version))
-               {
-                 a->debug("Unrecognized cache version %s, ignoring\n", line);
-                 break;
-               }
-             continue;
-           }
-         else
-           {
-             int cat, id1, id2, id3, id4, cnt;
-             if (sscanf(line, "%d%x%x%x%x%n", &cat, &id1, &id2, &id3, &id4, &cnt) >= 5)
-               {
-                 p = line + cnt;
-                 while (*p && *p == ' ')
-                   p++;
-                 id_insert(a, cat, id1, id2, id3, id4, p, SRC_CACHE);
-                 continue;
-               }
-           }
-       }
-      a->warning("Malformed cache file %s (line %d), ignoring", a->id_cache_file, lino);
-      break;
-    }
-
-  if (ferror(f))
-    a->warning("Error while reading %s", a->id_cache_file);
-  fclose(f);
-  return 1;
-}
-
-static void
-pci_id_cache_dirty(struct pci_access *a)
-{
-  if (a->id_cache_status >= 1)
-    a->id_cache_status = 2;
-}
-
-void
-pci_id_cache_flush(struct pci_access *a)
-{
-  int orig_status = a->id_cache_status;
-  FILE *f;
-  unsigned int h;
-  struct id_entry *e, *e2;
-
-  a->id_cache_status = 0;
-  if (orig_status < 2)
-    return;
-  if (!a->id_cache_file)
-    return;
-  f = fopen(a->id_cache_file, "wb");
-  if (!f)
-    {
-      a->warning("Cannot write %s: %s", a->id_cache_file, strerror(errno));
-      return;
-    }
-  a->debug("Writing cache to %s\n", a->id_cache_file);
-  fprintf(f, "%s\n", cache_version);
-
-  for (h=0; h<HASH_SIZE; h++)
-    for (e=a->id_hash[h]; e; e=e->next)
-      if (e->src == SRC_CACHE || e->src == SRC_NET)
-       {
-         /* Verify that every entry is written at most once */
-         for (e2=a->id_hash[h]; e2 != e; e2=e2->next)
-           if ((e2->src == SRC_CACHE || e2->src == SRC_NET) &&
-               e2->cat == e->cat &&
-               e2->id12 == e->id12 && e2->id34 == e->id34)
-           break;
-         if (e2 == e)
-           fprintf(f, "%d %x %x %x %x %s\n",
-                   e->cat,
-                   pair_first(e->id12), pair_second(e->id12),
-                   pair_first(e->id34), pair_second(e->id34),
-                   e->name);
-       }
-
-  fflush(f);
-  if (ferror(f))
-    a->warning("Error writing %s", a->id_cache_file);
-  fclose(f);
-}
-
-static char *id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4)
-{
-  char name[256], dnsname[256], txt[256];
-  byte answer[4096];
-  const byte *data;
-  int res, i, j, dlen;
-  ns_msg m;
-  ns_rr rr;
-
-  switch (cat)
-    {
-    case ID_VENDOR:
-      sprintf(name, "%04x", id1);
-      break;
-    case ID_DEVICE:
-      sprintf(name, "%04x.%04x", id2, id1);
-      break;
-    case ID_SUBSYSTEM:
-      sprintf(name, "%04x.%04x.%04x.%04x", id4, id3, id2, id1);
-      break;
-    case ID_GEN_SUBSYSTEM:
-      sprintf(name, "%04x.%04x.s", id2, id1);
-      break;
-    case ID_CLASS:
-      sprintf(name, "%02x.c", id1);
-      break;
-    case ID_SUBCLASS:
-      sprintf(name, "%02x.%02x.c", id2, id1);
-      break;
-    case ID_PROGIF:
-      sprintf(name, "%02x.%02x.%02x.c", id3, id2, id1);
-      break;
-    default:
-      return NULL;
-    }
-  sprintf(dnsname, "%s.%s", name, a->id_domain);
-
-  a->debug("Resolving %s\n", dnsname);
-  res_init();
-  res = res_query(dnsname, ns_c_in, ns_t_txt, answer, sizeof(answer));
-  if (res < 0)
-    {
-      a->debug("\tfailed, h_errno=%d\n", _res.res_h_errno);
-      return NULL;
-    }
-  if (ns_initparse(answer, res, &m) < 0)
-    {
-      a->debug("\tinitparse failed\n");
-      return NULL;
-    }
-  for (i=0; ns_parserr(&m, ns_s_an, i, &rr) >= 0; i++)
-    {
-      a->debug("\tanswer %d (class %d, type %d)\n", i, ns_rr_class(rr), ns_rr_type(rr));
-      if (ns_rr_class(rr) != ns_c_in || ns_rr_type(rr) != ns_t_txt)
-       continue;
-      data = ns_rr_rdata(rr);
-      dlen = ns_rr_rdlen(rr);
-      j = 0;
-      while (j < dlen && j+1+data[j] <= dlen)
-       {
-         memcpy(txt, &data[j+1], data[j]);
-         txt[data[j]] = 0;
-         j += 1+data[j];
-         a->debug("\t\t%s\n", txt);
-         if (txt[0] == 'i' && txt[1] == '=')
-           return strdup(txt+2);
-       }
-    }
-
-  return NULL;
-}
+#include "names.h"
 
 static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4)
 {
   char *name;
 
-  while (!(name = id_lookup_raw(a, flags, cat, id1, id2, id3, id4)))
+  while (!(name = pci_id_lookup(a, flags, cat, id1, id2, id3, id4)))
     {
       if ((flags & PCI_LOOKUP_CACHE) && !a->id_cache_status)
        {
@@ -424,14 +26,14 @@ static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id
        }
       if (flags & PCI_LOOKUP_NETWORK)
         {
-         if (name = id_net_lookup(a, cat, id1, id2, id3, id4))
+         if (name = pci_id_net_lookup(a, cat, id1, id2, id3, id4))
            {
-             id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET);
-             free(name);
+             pci_id_insert(a, cat, id1, id2, id3, id4, name, SRC_NET);
+             pci_mfree(name);
              pci_id_cache_dirty(a);
            }
          else
-           id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET); /* FIXME: Check that negative caching works */
+           pci_id_insert(a, cat, id1, id2, id3, id4, "", SRC_NET);
          /* We want to iterate the lookup to get the allocated ID entry from the hash */
          continue;
        }
@@ -440,196 +42,6 @@ static char *id_lookup(struct pci_access *a, int flags, int cat, int id1, int id
   return (name[0] ? name : NULL);
 }
 
-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 (!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 (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_failed = 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);
-  a->id_load_failed = 0;
-  return 1;
-}
-
-void
-pci_free_name_list(struct pci_access *a)
-{
-  pci_id_cache_flush(a);
-  pci_mfree(a->id_hash);
-  a->id_hash = NULL;
-  a->id_cache_status = 0;
-  while (a->current_id_bucket)
-    {
-      struct id_bucket *buck = a->current_id_bucket;
-      a->current_id_bucket = buck->next;
-      pci_mfree(buck);
-    }
-}
-
 static char *
 id_lookup_subsys(struct pci_access *a, int flags, int iv, int id, int isv, int isd)
 {
@@ -791,27 +203,3 @@ pci_lookup_name(struct pci_access *a, char *buf, int size, int flags, ...)
       return "<pci_lookup_name: invalid request>";
     }
 }
-
-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;
-}
-
-void pci_set_net_domain(struct pci_access *a, char *name, int to_be_freed)
-{
-  if (a->free_id_domain)
-    free(a->id_domain);
-  a->id_domain = name;
-  a->free_id_domain = to_be_freed;
-}
-
-void pci_set_id_cache(struct pci_access *a, char *name, int to_be_freed)
-{
-  if (a->free_id_cache_file)
-    free(a->id_cache_file);
-  a->id_cache_file = name;
-  a->free_id_cache_file = to_be_freed;
-}
diff --git a/lib/names.h b/lib/names.h
new file mode 100644 (file)
index 0000000..1ecea8b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ *     The PCI Library -- ID to Name Translation
+ *
+ *     Copyright (c) 1997--2008 Martin Mares <mj@ucw.cz>
+ *
+ *     Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#define MAX_LINE 1024
+
+/* names-hash.c */
+
+struct id_entry {
+  struct id_entry *next;
+  u32 id12, id34;
+  byte cat;
+  byte src;
+  char name[1];
+};
+
+enum id_entry_type {
+  ID_UNKNOWN,
+  ID_VENDOR,
+  ID_DEVICE,
+  ID_SUBSYSTEM,
+  ID_GEN_SUBSYSTEM,
+  ID_CLASS,
+  ID_SUBCLASS,
+  ID_PROGIF
+};
+
+enum id_entry_src {
+  SRC_UNKNOWN,
+  SRC_CACHE,
+  SRC_NET,
+  SRC_LOCAL,
+};
+
+#define BUCKET_SIZE 8192
+#define HASH_SIZE 4099
+
+static inline u32 id_pair(unsigned int x, unsigned int y)
+{
+  return ((x << 16) | y);
+}
+
+static inline unsigned int pair_first(unsigned int x)
+{
+  return (x >> 16) & 0xffff;
+}
+
+static inline unsigned int pair_second(unsigned int x)
+{
+  return x & 0xffff;
+}
+
+int pci_id_insert(struct pci_access *a, int cat, int id1, int id2, int id3, int id4, char *text, enum id_entry_src src);
+char *pci_id_lookup(struct pci_access *a, int flags, int cat, int id1, int id2, int id3, int id4);
+
+/* names-cache.c */
+
+int pci_id_cache_load(struct pci_access *a, int flags);
+void pci_id_cache_dirty(struct pci_access *a);
+void pci_id_cache_flush(struct pci_access *a);
+
+/* names-dns.c */
+
+char *pci_id_net_lookup(struct pci_access *a, int cat, int id1, int id2, int id3, int id4);