VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/USBIdDatabaseGenerator.cpp@ 58005

Last change on this file since 58005 was 58001, checked in by vboxsync, 10 years ago

USBIdDatabase*: The product and vendor arrays should be const, just like the counts. If you're using hungarian for the element counts, use it for the arrays too. RT_ELEMENTS is a handy macro.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.8 KB
Line 
1/* $Id: USBIdDatabaseGenerator.cpp 58001 2015-10-02 10:15:58Z vboxsync $ */
2/** @file
3 * USB device vendor and product ID database - generator.
4 */
5
6/*
7 * Copyright (C) 2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <stdio.h>
19#include <fstream>
20#include <iostream>
21#include <iomanip>
22#include <algorithm>
23#include <vector>
24#include <string>
25
26#include <iprt/initterm.h>
27#include <iprt/message.h>
28#include <iprt/string.h>
29#include <iprt/stream.h>
30
31using namespace std;
32
33static const char * const header =
34 "/** @file\n"
35 " * USB device vendor and product ID database - Autogenerated from <stupid C++ cannot do %s>\n"
36 " */\n"
37 "\n"
38 "/*\n"
39 " * Copyright (C) 2015 Oracle Corporation\n"
40 " *\n"
41 " * This file is part of VirtualBox Open Source Edition(OSE), as\n"
42 " * available from http ://www.virtualbox.org. This file is free software;\n"
43 " * you can redistribute it and / or modify it under the terms of the GNU\n"
44 " * General Public License(GPL) as published by the Free Software\n"
45 " * Foundation, in version 2 as it comes in the \"COPYING\" file of the\n"
46 " * VirtualBox OSE distribution.VirtualBox OSE is distributed in the\n"
47 " * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.\n"
48 " */"
49 "\n"
50 "\n"
51 "#include \"USBIdDatabase.h\"\n"
52 "\n"
53 "/**\n"
54 " * USB devices aliases array.\n"
55 " * Format: VendorId, ProductId, Vendor Name, Product Name\n"
56 " * The source of the list is http://www.linux-usb.org/usb.ids\n"
57 " */\n"
58 "Product const AliasDictionary::aProducts[] =\n"
59 "{\n";
60
61const char *footer =
62 "};\n"
63 "\n"
64 "const size_t AliasDictionary::cProducts = RT_ELEMENTS(AliasDictionary::aProducts);\n";
65
66const char *vendor_header =
67 "\nVendor const AliasDictionary::aVendors[] =\n"
68 "{\n";
69const char *vendor_footer =
70 "};\n"
71 "\n"
72 "const size_t AliasDictionary::cVendors = RT_ELEMENTS(AliasDictionary::aVendors);\n";
73
74const char *start_block = "# Vendors, devices and interfaces. Please keep sorted.";
75const char *end_block = "# List of known device classes, subclasses and protocols";
76
77#define USBKEY(vendorId, productId) (((vendorId) << 16) | (productId))
78
79// error codes
80#define ERROR_INVALID_ARGUMENTS (1)
81#define ERROR_OPEN_FILE (2)
82#define ERROR_IN_PARSE_LINE (3)
83#define ERROR_DUPLICATE_ENTRY (4)
84#define ERROR_WRONG_FILE_FORMAT (5)
85
86struct VendorRecord
87{
88 size_t vendorID;
89 string vendor;
90};
91
92struct ProductRecord
93{
94 size_t key;
95 size_t vendorID;
96 size_t productID;
97 string product;
98};
99
100bool operator < (const ProductRecord& lh, const ProductRecord& rh)
101{
102 return lh.key < rh.key;
103}
104
105bool operator < (const VendorRecord& lh, const VendorRecord& rh)
106{
107 return lh.vendorID < rh.vendorID;
108}
109
110bool operator == (const ProductRecord& lh, const ProductRecord& rh)
111{
112 return lh.key == rh.key;
113}
114
115bool operator == (const VendorRecord& lh, const VendorRecord& rh)
116{
117 return lh.vendorID == rh.vendorID;
118}
119
120string conv(const string& src)
121{
122 string res = src;
123 for (size_t i = 0; i < res.length(); ++i)
124 {
125 switch (res[i])
126 {
127 case '"':
128 case '\\': res.insert(i++, "\\"); break;
129 default:
130 {
131 // encode multibyte UTF-8 symbols to be sure that they
132 // will be safely read by compiler
133 if ((unsigned char)res[i] >= 127)
134 {
135 size_t start = i;
136 string temp = "\" \"";
137 char buffer[8] = { 0 };
138 do
139 {
140 RTStrPrintf(buffer, sizeof(buffer), "\\x%x", (unsigned char)res[i]);
141 temp.append(buffer);
142 } while ((unsigned char)res[++i] & 0x80);
143 // splitting string after escape sequence to finish number sequence
144 // otherwise it could lead to situation when "\x88a" will threathened as
145 // multibyte symbol '\x88a' instead of two symbols '\x88' and 'a'
146 temp.append("\" \"");
147 res.replace(start, i - start, temp);
148 i += temp.length();
149 }
150 }
151 }
152 }
153 return res;
154}
155
156ostream& operator <<(ostream& stream, const ProductRecord product)
157{
158 stream << " { USBKEY(0x" << setfill('0') << setw(4) << hex << product.vendorID
159 << ", 0x" << setfill('0') << setw(4) << product.productID << "), "
160 << "\"" << conv(product.product).c_str() << "\" }," << endl;
161 return stream;
162}
163
164ostream& operator <<(ostream& stream, const VendorRecord vendor)
165{
166 stream << " { 0x" << setfill('0') << setw(4) << hex << vendor.vendorID
167 << ", \"" << conv(vendor.vendor).c_str() << "\" }," << endl;
168 return stream;
169}
170
171namespace State
172{
173 typedef int Value;
174 enum
175 {
176 lookForStartBlock,
177 lookForEndBlock,
178 finished
179 };
180}
181
182typedef vector<ProductRecord> ProductsSet;
183typedef vector<VendorRecord> VendorsSet;
184ProductsSet g_products;
185VendorsSet g_vendors;
186
187
188int ParseAlias(const string& src, size_t& id, string& desc)
189{
190 unsigned int i = 0;
191 int idx = 0;
192 string sin;
193
194 if (sscanf(src.c_str(), "%x", &i) != 1)
195 return ERROR_IN_PARSE_LINE;
196
197 size_t index = src.find_first_of(" \t", 1);
198 index = src.find_first_not_of(" \t", index);
199
200 if (index == string::npos)
201 return ERROR_IN_PARSE_LINE;
202
203 sin = src.substr(index);
204 id = i;
205 desc = sin;
206
207 return 0;
208}
209
210bool IsCommentOrEmptyLine(const string& str)
211{
212 size_t index = str.find_first_not_of(" \t");// skip left spaces
213 return index == string::npos || str[index] == '#';
214}
215
216bool getline(PRTSTREAM instream, string& resString)
217{
218 const size_t szBuf = 4096;
219 char buf[szBuf] = { 0 };
220
221 int rc = RTStrmGetLine(instream, buf, szBuf);
222 if (RT_SUCCESS(rc))
223 {
224 resString = buf;
225 return true;
226 }
227 else if (rc != VERR_EOF)
228 {
229 cerr << "Warning: Invalid line in file. Error: " << hex << rc << endl;
230 }
231 return false;
232}
233
234int ParseUsbIds(PRTSTREAM instream)
235{
236 State::Value state = State::lookForStartBlock;
237 string line;
238 int res = 0;
239 VendorRecord vendor = { 0, "" };
240
241 while (state != State::finished && getline(instream, line))
242 {
243 switch (state)
244 {
245 case State::lookForStartBlock:
246 {
247 if (line.find(start_block) != string::npos)
248 state = State::lookForEndBlock;
249 break;
250 }
251 case State::lookForEndBlock:
252 {
253 if (line.find(end_block) != string::npos)
254 state = State::finished;
255 else
256 {
257 if (!IsCommentOrEmptyLine(line))
258 {
259 if (line[0] == '\t')
260 {
261 // Parse Product line
262 // first line should be vendor
263 if (vendor.vendorID == 0)
264 {
265 cerr << "Wrong file format. Product before vendor: "
266 << line.c_str() << "'" << endl;
267 return ERROR_WRONG_FILE_FORMAT;
268 }
269 ProductRecord product = { 0, vendor.vendorID, 0, "" };
270 if (ParseAlias(line.substr(1), product.productID, product.product) != 0)
271 {
272 cerr << "Error in parsing product line: '"
273 << line.c_str() << "'" << endl;
274 return ERROR_IN_PARSE_LINE;
275 }
276 product.key = USBKEY(product.vendorID, product.productID);
277 Assert(product.vendorID != 0);
278 g_products.push_back(product);
279 }
280 else
281 {
282 // Parse vendor line
283 if (ParseAlias(line, vendor.vendorID, vendor.vendor) != 0)
284 {
285 cerr << "Error in parsing vendor line: '"
286 << line.c_str() << "'" << endl;
287 return ERROR_IN_PARSE_LINE;
288 }
289 g_vendors.push_back(vendor);
290 }
291 }
292 }
293 break;
294 }
295 }
296 }
297 if (state == State::lookForStartBlock)
298 {
299 cerr << "Error: wrong format of input file. Start line is not found." << endl;
300 return ERROR_WRONG_FILE_FORMAT;
301 }
302 return 0;
303}
304
305int main(int argc, char *argv[])
306{
307 int rc = RTR3InitExe(argc, &argv, 0);
308 if (RT_FAILURE(rc))
309 return RTMsgInitFailure(rc);
310
311 if (argc < 4)
312 {
313 cerr << "Format: " << argv[0] <<
314 " [linux.org usb list file] [custom usb list file] [-o output file]" << endl;
315 cerr << "Error: Invalid arguments." << endl;
316 return ERROR_INVALID_ARGUMENTS;
317 }
318 ofstream fout;
319 PRTSTREAM fin;
320 g_products.reserve(20000);
321 g_vendors.reserve(3500);
322
323 const char *outName = NULL;
324 for (int i = 1; i < argc; i++)
325 {
326 if (strcmp(argv[i], "-o") == 0)
327 {
328 outName = argv[++i];
329 continue;
330 }
331
332 rc = RTStrmOpen(argv[i], "r", &fin);
333 if (RT_FAILURE(rc))
334 {
335 cerr << "Format: " << argv[0] <<
336 " [linux.org usb list file] [custom usb list file] [-o output file]" << endl;
337 cerr << "Error: Can not open file '" << argv[i] << "'. Error: " << hex << rc << endl;
338 return ERROR_OPEN_FILE;
339 }
340
341 int res = ParseUsbIds(fin);
342 if (res != 0)
343 {
344 cerr << "Error in parsing USB devices file '" <<
345 argv[i] << "'" << endl;
346 RTStrmClose(fin);
347 return res;
348 }
349 RTStrmClose(fin);
350 }
351
352 sort(g_products.begin(), g_products.end());
353 sort(g_vendors.begin(), g_vendors.end());
354
355 // validate that all records are unique
356 ProductsSet::iterator ita = adjacent_find(g_products.begin(), g_products.end());
357 if (ita != g_products.end())
358 {
359 cerr << "Warning: Duplicate alias detected. " << *ita << endl;
360 /** @todo r=bird: Why return success (0) when we didn't generate the
361 * file?!?!? */
362 return 0;
363 }
364
365 if (!outName)
366 {
367 cerr << "Format: " << argv[0] <<
368 " [linux.org usb list file] [custom usb list file] [-o output file]" << endl;
369 cerr << "Error: Output file is not defined." << endl;
370 return ERROR_OPEN_FILE;
371 }
372
373/** @todo String compression. */
374
375 fout.open(outName);
376 if (!fout.is_open())
377 {
378 cerr << "Format: " << argv[0] <<
379 " [linux.org usb list file] [custom usb list file] [-o output file]" << endl;
380 cerr << "Error: Can not open file to write '" << argv[1] << "'." << endl;
381 return ERROR_OPEN_FILE;
382 }
383
384 fout << header;
385 for (ProductsSet::iterator itp = g_products.begin(); itp != g_products.end(); ++itp)
386 {
387 fout << *itp;
388 }
389 fout << footer;
390
391 fout << vendor_header;
392 for (VendorsSet::iterator itv = g_vendors.begin(); itv != g_vendors.end(); ++itv)
393 {
394 fout << *itv;
395 }
396 fout << vendor_footer;
397
398 fout.close();
399
400
401 return 0;
402}
403
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette