xref: /pciutils/lib/names-parse.c (revision bbd5d563)
1 /*
2  *	The PCI Library -- Parsing of the ID list
3  *
4  *	Copyright (c) 1997--2008 Martin Mares <[email protected]>
5  *
6  *	Can be freely distributed and used under the terms of the GNU GPL.
7  */
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 
14 #include "internal.h"
15 #include "names.h"
16 
17 #ifdef PCI_COMPRESSED_IDS
18 #include <zlib.h>
19 typedef gzFile pci_file;
20 #define pci_gets(f, l, s)	gzgets(f, l, s)
21 #define pci_eof(f)		gzeof(f)
22 
23 static pci_file pci_open(struct pci_access *a)
24 {
25   pci_file result;
26   size_t len;
27   char *new_name;
28 
29   result = gzopen(a->id_file_name, "rb");
30   if (result)
31     return result;
32   len = strlen(a->id_file_name);
33   if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0)
34     return result;
35   new_name = malloc(len - 2);
36   memcpy(new_name, a->id_file_name, len - 3);
37   new_name[len - 3] = 0;
38   pci_set_name_list_path(a, new_name, 1);
39   return gzopen(a->id_file_name, "rb");
40 }
41 
42 #define pci_close(f)		gzclose(f)
43 #define PCI_ERROR(f, err)						\
44 	if (!err) {							\
45 		int errnum;						\
46 		gzerror(f, &errnum);					\
47 		if (errnum >= 0) err = NULL;				\
48 		else if (errnum == Z_ERRNO) err = "I/O error";		\
49 		else err = zError(errnum);				\
50 	}
51 #else
52 typedef FILE * pci_file;
53 #define pci_gets(f, l, s)	fgets(l, s, f)
54 #define pci_eof(f)		feof(f)
55 #define pci_open(a)		fopen(a->id_file_name, "r")
56 #define pci_close(f)		fclose(f)
57 #define PCI_ERROR(f, err)	if (!err && ferror(f))	err = "I/O error";
58 #endif
59 
60 static int id_hex(char *p, int cnt)
61 {
62   int x = 0;
63   while (cnt--)
64     {
65       x <<= 4;
66       if (*p >= '0' && *p <= '9')
67 	x += (*p - '0');
68       else if (*p >= 'a' && *p <= 'f')
69 	x += (*p - 'a' + 10);
70       else if (*p >= 'A' && *p <= 'F')
71 	x += (*p - 'A' + 10);
72       else
73 	return -1;
74       p++;
75     }
76   return x;
77 }
78 
79 static inline int id_white_p(int c)
80 {
81   return (c == ' ') || (c == '\t');
82 }
83 
84 
85 static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino)
86 {
87   char line[MAX_LINE];
88   char *p;
89   int id1=0, id2=0, id3=0, id4=0;
90   int cat = -1;
91   int nest;
92   static const char parse_error[] = "Parse error";
93 
94   *lino = 0;
95   while (pci_gets(f, line, sizeof(line)))
96     {
97       (*lino)++;
98       p = line;
99       while (*p && *p != '\n' && *p != '\r')
100 	p++;
101       if (!*p && !pci_eof(f))
102 	return "Line too long";
103       *p = 0;
104       if (p > line && (p[-1] == ' ' || p[-1] == '\t'))
105 	*--p = 0;
106 
107       p = line;
108       while (id_white_p(*p))
109 	p++;
110       if (!*p || *p == '#')
111 	continue;
112 
113       p = line;
114       while (*p == '\t')
115 	p++;
116       nest = p - line;
117 
118       if (!nest)					/* Top-level entries */
119 	{
120 	  if (p[0] == 'C' && p[1] == ' ')		/* Class block */
121 	    {
122 	      if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4]))
123 		return parse_error;
124 	      cat = ID_CLASS;
125 	      p += 5;
126 	    }
127 	  else if (p[0] == 'S' && p[1] == ' ')
128 	    {						/* Generic subsystem block */
129 	      if ((id1 = id_hex(p+2, 4)) < 0 || p[6])
130 		return parse_error;
131 	      if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0))
132 		return "Vendor does not exist";
133 	      cat = ID_GEN_SUBSYSTEM;
134 	      continue;
135 	    }
136 	  else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ')
137 	    {						/* Unrecognized block (RFU) */
138 	      cat = ID_UNKNOWN;
139 	      continue;
140 	    }
141 	  else						/* Vendor ID */
142 	    {
143 	      if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
144 		return parse_error;
145 	      cat = ID_VENDOR;
146 	      p += 5;
147 	    }
148 	  id2 = id3 = id4 = 0;
149 	}
150       else if (cat == ID_UNKNOWN)			/* Nested entries in RFU blocks are skipped */
151 	continue;
152       else if (nest == 1)				/* Nesting level 1 */
153 	switch (cat)
154 	  {
155 	  case ID_VENDOR:
156 	  case ID_DEVICE:
157 	  case ID_SUBSYSTEM:
158 	    if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
159 	      return parse_error;
160 	    p += 5;
161 	    cat = ID_DEVICE;
162 	    id3 = id4 = 0;
163 	    break;
164 	  case ID_GEN_SUBSYSTEM:
165 	    if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
166 	      return parse_error;
167 	    p += 5;
168 	    id3 = id4 = 0;
169 	    break;
170 	  case ID_CLASS:
171 	  case ID_SUBCLASS:
172 	  case ID_PROGIF:
173 	    if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
174 	      return parse_error;
175 	    p += 3;
176 	    cat = ID_SUBCLASS;
177 	    id3 = id4 = 0;
178 	    break;
179 	  default:
180 	    return parse_error;
181 	  }
182       else if (nest == 2)				/* Nesting level 2 */
183 	switch (cat)
184 	  {
185 	  case ID_DEVICE:
186 	  case ID_SUBSYSTEM:
187 	    if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9]))
188 	      return parse_error;
189 	    p += 10;
190 	    cat = ID_SUBSYSTEM;
191 	    break;
192 	  case ID_CLASS:
193 	  case ID_SUBCLASS:
194 	  case ID_PROGIF:
195 	    if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
196 	      return parse_error;
197 	    p += 3;
198 	    cat = ID_PROGIF;
199 	    id4 = 0;
200 	    break;
201 	  default:
202 	    return parse_error;
203 	  }
204       else						/* Nesting level 3 or more */
205 	return parse_error;
206       while (id_white_p(*p))
207 	p++;
208       if (!*p)
209 	return parse_error;
210       if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL))
211 	return "Duplicate entry";
212     }
213   return NULL;
214 }
215 
216 int
217 pci_load_name_list(struct pci_access *a)
218 {
219   pci_file f;
220   int lino;
221   const char *err;
222 
223   pci_free_name_list(a);
224   a->id_load_failed = 1;
225   if (!(f = pci_open(a)))
226     return 0;
227   err = id_parse_list(a, f, &lino);
228   PCI_ERROR(f, err);
229   pci_close(f);
230   if (err)
231     a->error("%s at %s, line %d\n", err, a->id_file_name, lino);
232   a->id_load_failed = 0;
233   return 1;
234 }
235 
236 void
237 pci_free_name_list(struct pci_access *a)
238 {
239   pci_id_cache_flush(a);
240   pci_id_hash_free(a);
241   a->id_load_failed = 0;
242 }
243 
244 void
245 pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed)
246 {
247   if (a->free_id_name)
248     free(a->id_file_name);
249   a->id_file_name = name;
250   a->free_id_name = to_be_freed;
251 }
252