xref: /pciutils/lib/names-parse.c (revision 06f9ecf3)
1752d4d9aSMartin Mares /*
2752d4d9aSMartin Mares  *	The PCI Library -- Parsing of the ID list
3752d4d9aSMartin Mares  *
4752d4d9aSMartin Mares  *	Copyright (c) 1997--2008 Martin Mares <[email protected]>
5752d4d9aSMartin Mares  *
661829219SMartin Mares  *	Can be freely distributed and used under the terms of the GNU GPL v2+.
761829219SMartin Mares  *
861829219SMartin Mares  *	SPDX-License-Identifier: GPL-2.0-or-later
9752d4d9aSMartin Mares  */
10752d4d9aSMartin Mares 
11752d4d9aSMartin Mares #include <stdio.h>
12752d4d9aSMartin Mares #include <stdlib.h>
13752d4d9aSMartin Mares #include <string.h>
14752d4d9aSMartin Mares #include <errno.h>
15752d4d9aSMartin Mares 
16752d4d9aSMartin Mares #include "internal.h"
17752d4d9aSMartin Mares #include "names.h"
18752d4d9aSMartin Mares 
19752d4d9aSMartin Mares #ifdef PCI_COMPRESSED_IDS
20752d4d9aSMartin Mares #include <zlib.h>
21752d4d9aSMartin Mares typedef gzFile pci_file;
22752d4d9aSMartin Mares #define pci_gets(f, l, s)	gzgets(f, l, s)
23752d4d9aSMartin Mares #define pci_eof(f)		gzeof(f)
24752d4d9aSMartin Mares 
pci_open(struct pci_access * a)25752d4d9aSMartin Mares static pci_file pci_open(struct pci_access *a)
26752d4d9aSMartin Mares {
27752d4d9aSMartin Mares   pci_file result;
28752d4d9aSMartin Mares   size_t len;
29752d4d9aSMartin Mares   char *new_name;
30752d4d9aSMartin Mares 
31752d4d9aSMartin Mares   result = gzopen(a->id_file_name, "rb");
32752d4d9aSMartin Mares   if (result)
33752d4d9aSMartin Mares     return result;
34752d4d9aSMartin Mares   len = strlen(a->id_file_name);
35bbd5d563SMartin Mares   if (len < 3 || memcmp(a->id_file_name + len - 3, ".gz", 3) != 0)
36752d4d9aSMartin Mares     return result;
37752d4d9aSMartin Mares   new_name = malloc(len - 2);
38752d4d9aSMartin Mares   memcpy(new_name, a->id_file_name, len - 3);
39752d4d9aSMartin Mares   new_name[len - 3] = 0;
40752d4d9aSMartin Mares   pci_set_name_list_path(a, new_name, 1);
41752d4d9aSMartin Mares   return gzopen(a->id_file_name, "rb");
42752d4d9aSMartin Mares }
43752d4d9aSMartin Mares 
44752d4d9aSMartin Mares #define pci_close(f)		gzclose(f)
45752d4d9aSMartin Mares #define PCI_ERROR(f, err)						\
46752d4d9aSMartin Mares 	if (!err) {							\
4759d840bfSNathan Moinvaziri 		int errnum = 0;						\
48752d4d9aSMartin Mares 		gzerror(f, &errnum);					\
49752d4d9aSMartin Mares 		if (errnum >= 0) err = NULL;				\
50752d4d9aSMartin Mares 		else if (errnum == Z_ERRNO) err = "I/O error";		\
51752d4d9aSMartin Mares 		else err = zError(errnum);				\
52752d4d9aSMartin Mares 	}
53752d4d9aSMartin Mares #else
54752d4d9aSMartin Mares typedef FILE * pci_file;
55752d4d9aSMartin Mares #define pci_gets(f, l, s)	fgets(l, s, f)
56752d4d9aSMartin Mares #define pci_eof(f)		feof(f)
57752d4d9aSMartin Mares #define pci_open(a)		fopen(a->id_file_name, "r")
58752d4d9aSMartin Mares #define pci_close(f)		fclose(f)
59752d4d9aSMartin Mares #define PCI_ERROR(f, err)	if (!err && ferror(f))	err = "I/O error";
60752d4d9aSMartin Mares #endif
61752d4d9aSMartin Mares 
id_hex(char * p,int cnt)62752d4d9aSMartin Mares static int id_hex(char *p, int cnt)
63752d4d9aSMartin Mares {
64752d4d9aSMartin Mares   int x = 0;
65752d4d9aSMartin Mares   while (cnt--)
66752d4d9aSMartin Mares     {
67752d4d9aSMartin Mares       x <<= 4;
68752d4d9aSMartin Mares       if (*p >= '0' && *p <= '9')
69752d4d9aSMartin Mares 	x += (*p - '0');
70752d4d9aSMartin Mares       else if (*p >= 'a' && *p <= 'f')
71752d4d9aSMartin Mares 	x += (*p - 'a' + 10);
72752d4d9aSMartin Mares       else if (*p >= 'A' && *p <= 'F')
73752d4d9aSMartin Mares 	x += (*p - 'A' + 10);
74752d4d9aSMartin Mares       else
75752d4d9aSMartin Mares 	return -1;
76752d4d9aSMartin Mares       p++;
77752d4d9aSMartin Mares     }
78752d4d9aSMartin Mares   return x;
79752d4d9aSMartin Mares }
80752d4d9aSMartin Mares 
id_white_p(int c)81752d4d9aSMartin Mares static inline int id_white_p(int c)
82752d4d9aSMartin Mares {
83752d4d9aSMartin Mares   return (c == ' ') || (c == '\t');
84752d4d9aSMartin Mares }
85752d4d9aSMartin Mares 
86752d4d9aSMartin Mares 
id_parse_list(struct pci_access * a,pci_file f,int * lino)87752d4d9aSMartin Mares static const char *id_parse_list(struct pci_access *a, pci_file f, int *lino)
88752d4d9aSMartin Mares {
89752d4d9aSMartin Mares   char line[MAX_LINE];
90752d4d9aSMartin Mares   char *p;
91752d4d9aSMartin Mares   int id1=0, id2=0, id3=0, id4=0;
92752d4d9aSMartin Mares   int cat = -1;
93752d4d9aSMartin Mares   int nest;
94752d4d9aSMartin Mares   static const char parse_error[] = "Parse error";
95752d4d9aSMartin Mares 
96752d4d9aSMartin Mares   *lino = 0;
97752d4d9aSMartin Mares   while (pci_gets(f, line, sizeof(line)))
98752d4d9aSMartin Mares     {
99752d4d9aSMartin Mares       (*lino)++;
100752d4d9aSMartin Mares       p = line;
101752d4d9aSMartin Mares       while (*p && *p != '\n' && *p != '\r')
102752d4d9aSMartin Mares 	p++;
103752d4d9aSMartin Mares       if (!*p && !pci_eof(f))
104752d4d9aSMartin Mares 	return "Line too long";
105752d4d9aSMartin Mares       *p = 0;
106752d4d9aSMartin Mares       if (p > line && (p[-1] == ' ' || p[-1] == '\t'))
107752d4d9aSMartin Mares 	*--p = 0;
108752d4d9aSMartin Mares 
109752d4d9aSMartin Mares       p = line;
110752d4d9aSMartin Mares       while (id_white_p(*p))
111752d4d9aSMartin Mares 	p++;
112752d4d9aSMartin Mares       if (!*p || *p == '#')
113752d4d9aSMartin Mares 	continue;
114752d4d9aSMartin Mares 
115752d4d9aSMartin Mares       p = line;
116752d4d9aSMartin Mares       while (*p == '\t')
117752d4d9aSMartin Mares 	p++;
118752d4d9aSMartin Mares       nest = p - line;
119752d4d9aSMartin Mares 
120752d4d9aSMartin Mares       if (!nest)					/* Top-level entries */
121752d4d9aSMartin Mares 	{
122752d4d9aSMartin Mares 	  if (p[0] == 'C' && p[1] == ' ')		/* Class block */
123752d4d9aSMartin Mares 	    {
124752d4d9aSMartin Mares 	      if ((id1 = id_hex(p+2, 2)) < 0 || !id_white_p(p[4]))
125752d4d9aSMartin Mares 		return parse_error;
126752d4d9aSMartin Mares 	      cat = ID_CLASS;
127752d4d9aSMartin Mares 	      p += 5;
128752d4d9aSMartin Mares 	    }
129752d4d9aSMartin Mares 	  else if (p[0] == 'S' && p[1] == ' ')
130752d4d9aSMartin Mares 	    {						/* Generic subsystem block */
131752d4d9aSMartin Mares 	      if ((id1 = id_hex(p+2, 4)) < 0 || p[6])
132752d4d9aSMartin Mares 		return parse_error;
133752d4d9aSMartin Mares 	      if (!pci_id_lookup(a, 0, ID_VENDOR, id1, 0, 0, 0))
134752d4d9aSMartin Mares 		return "Vendor does not exist";
135752d4d9aSMartin Mares 	      cat = ID_GEN_SUBSYSTEM;
136752d4d9aSMartin Mares 	      continue;
137752d4d9aSMartin Mares 	    }
138752d4d9aSMartin Mares 	  else if (p[0] >= 'A' && p[0] <= 'Z' && p[1] == ' ')
139752d4d9aSMartin Mares 	    {						/* Unrecognized block (RFU) */
140752d4d9aSMartin Mares 	      cat = ID_UNKNOWN;
141752d4d9aSMartin Mares 	      continue;
142752d4d9aSMartin Mares 	    }
143752d4d9aSMartin Mares 	  else						/* Vendor ID */
144752d4d9aSMartin Mares 	    {
145752d4d9aSMartin Mares 	      if ((id1 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
146752d4d9aSMartin Mares 		return parse_error;
147752d4d9aSMartin Mares 	      cat = ID_VENDOR;
148752d4d9aSMartin Mares 	      p += 5;
149752d4d9aSMartin Mares 	    }
150752d4d9aSMartin Mares 	  id2 = id3 = id4 = 0;
151752d4d9aSMartin Mares 	}
152752d4d9aSMartin Mares       else if (cat == ID_UNKNOWN)			/* Nested entries in RFU blocks are skipped */
153752d4d9aSMartin Mares 	continue;
154752d4d9aSMartin Mares       else if (nest == 1)				/* Nesting level 1 */
155752d4d9aSMartin Mares 	switch (cat)
156752d4d9aSMartin Mares 	  {
157752d4d9aSMartin Mares 	  case ID_VENDOR:
158752d4d9aSMartin Mares 	  case ID_DEVICE:
159752d4d9aSMartin Mares 	  case ID_SUBSYSTEM:
160752d4d9aSMartin Mares 	    if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
161752d4d9aSMartin Mares 	      return parse_error;
162752d4d9aSMartin Mares 	    p += 5;
163752d4d9aSMartin Mares 	    cat = ID_DEVICE;
164752d4d9aSMartin Mares 	    id3 = id4 = 0;
165752d4d9aSMartin Mares 	    break;
166752d4d9aSMartin Mares 	  case ID_GEN_SUBSYSTEM:
167752d4d9aSMartin Mares 	    if ((id2 = id_hex(p, 4)) < 0 || !id_white_p(p[4]))
168752d4d9aSMartin Mares 	      return parse_error;
169752d4d9aSMartin Mares 	    p += 5;
170752d4d9aSMartin Mares 	    id3 = id4 = 0;
171752d4d9aSMartin Mares 	    break;
172752d4d9aSMartin Mares 	  case ID_CLASS:
173752d4d9aSMartin Mares 	  case ID_SUBCLASS:
174752d4d9aSMartin Mares 	  case ID_PROGIF:
175752d4d9aSMartin Mares 	    if ((id2 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
176752d4d9aSMartin Mares 	      return parse_error;
177752d4d9aSMartin Mares 	    p += 3;
178752d4d9aSMartin Mares 	    cat = ID_SUBCLASS;
179752d4d9aSMartin Mares 	    id3 = id4 = 0;
180752d4d9aSMartin Mares 	    break;
181752d4d9aSMartin Mares 	  default:
182752d4d9aSMartin Mares 	    return parse_error;
183752d4d9aSMartin Mares 	  }
184752d4d9aSMartin Mares       else if (nest == 2)				/* Nesting level 2 */
185752d4d9aSMartin Mares 	switch (cat)
186752d4d9aSMartin Mares 	  {
187752d4d9aSMartin Mares 	  case ID_DEVICE:
188752d4d9aSMartin Mares 	  case ID_SUBSYSTEM:
189752d4d9aSMartin Mares 	    if ((id3 = id_hex(p, 4)) < 0 || !id_white_p(p[4]) || (id4 = id_hex(p+5, 4)) < 0 || !id_white_p(p[9]))
190752d4d9aSMartin Mares 	      return parse_error;
191752d4d9aSMartin Mares 	    p += 10;
192752d4d9aSMartin Mares 	    cat = ID_SUBSYSTEM;
193752d4d9aSMartin Mares 	    break;
194752d4d9aSMartin Mares 	  case ID_CLASS:
195752d4d9aSMartin Mares 	  case ID_SUBCLASS:
196752d4d9aSMartin Mares 	  case ID_PROGIF:
197752d4d9aSMartin Mares 	    if ((id3 = id_hex(p, 2)) < 0 || !id_white_p(p[2]))
198752d4d9aSMartin Mares 	      return parse_error;
199752d4d9aSMartin Mares 	    p += 3;
200752d4d9aSMartin Mares 	    cat = ID_PROGIF;
201752d4d9aSMartin Mares 	    id4 = 0;
202752d4d9aSMartin Mares 	    break;
203752d4d9aSMartin Mares 	  default:
204752d4d9aSMartin Mares 	    return parse_error;
205752d4d9aSMartin Mares 	  }
206752d4d9aSMartin Mares       else						/* Nesting level 3 or more */
207752d4d9aSMartin Mares 	return parse_error;
208752d4d9aSMartin Mares       while (id_white_p(*p))
209752d4d9aSMartin Mares 	p++;
210752d4d9aSMartin Mares       if (!*p)
211752d4d9aSMartin Mares 	return parse_error;
212752d4d9aSMartin Mares       if (pci_id_insert(a, cat, id1, id2, id3, id4, p, SRC_LOCAL))
213752d4d9aSMartin Mares 	return "Duplicate entry";
214752d4d9aSMartin Mares     }
215752d4d9aSMartin Mares   return NULL;
216752d4d9aSMartin Mares }
217752d4d9aSMartin Mares 
218752d4d9aSMartin Mares int
pci_load_name_list(struct pci_access * a)219752d4d9aSMartin Mares pci_load_name_list(struct pci_access *a)
220752d4d9aSMartin Mares {
221752d4d9aSMartin Mares   pci_file f;
222752d4d9aSMartin Mares   int lino;
223752d4d9aSMartin Mares   const char *err;
224752d4d9aSMartin Mares 
225752d4d9aSMartin Mares   pci_free_name_list(a);
226*06f9ecf3SMartin Mares   a->id_load_attempted = 1;
227752d4d9aSMartin Mares   if (!(f = pci_open(a)))
228752d4d9aSMartin Mares     return 0;
229752d4d9aSMartin Mares   err = id_parse_list(a, f, &lino);
230752d4d9aSMartin Mares   PCI_ERROR(f, err);
231752d4d9aSMartin Mares   pci_close(f);
232752d4d9aSMartin Mares   if (err)
233752d4d9aSMartin Mares     a->error("%s at %s, line %d\n", err, a->id_file_name, lino);
234752d4d9aSMartin Mares   return 1;
235752d4d9aSMartin Mares }
236752d4d9aSMartin Mares 
237c953c309SMartin Mares void
pci_free_name_list(struct pci_access * a)238c953c309SMartin Mares pci_free_name_list(struct pci_access *a)
239c953c309SMartin Mares {
240c953c309SMartin Mares   pci_id_cache_flush(a);
241c953c309SMartin Mares   pci_id_hash_free(a);
242ac357d3bSMartin Mares   pci_id_hwdb_free(a);
243*06f9ecf3SMartin Mares   a->id_load_attempted = 0;
244c953c309SMartin Mares }
245c953c309SMartin Mares 
246c953c309SMartin Mares void
pci_set_name_list_path(struct pci_access * a,char * name,int to_be_freed)247c953c309SMartin Mares pci_set_name_list_path(struct pci_access *a, char *name, int to_be_freed)
248752d4d9aSMartin Mares {
249752d4d9aSMartin Mares   if (a->free_id_name)
250752d4d9aSMartin Mares     free(a->id_file_name);
251752d4d9aSMartin Mares   a->id_file_name = name;
252752d4d9aSMartin Mares   a->free_id_name = to_be_freed;
253752d4d9aSMartin Mares }
254