VirtualBox

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

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

USBIdDatabase*: ERROR_DUPLICATE_ENTRY and duplicate usage statement.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 11.6 KB
Line 
1/* $Id: USBIdDatabaseGenerator.cpp 58006 2015-10-02 11:52:56Z 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.215389.xyz. 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.215389.xyz. 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 (complements RTEXITCODE_XXX).
80#define ERROR_OPEN_FILE (12)
81#define ERROR_IN_PARSE_LINE (13)
82#define ERROR_DUPLICATE_ENTRY (14)
83#define ERROR_WRONG_FILE_FORMAT (15)
84
85struct VendorRecord
86{
87 size_t vendorID;
88 string vendor;
89};
90
91struct ProductRecord
92{
93 size_t key;
94 size_t vendorID;
95 size_t productID;
96 string product;
97};
98
99bool operator < (const ProductRecord& lh, const ProductRecord& rh)
100{
101 return lh.key < rh.key;
102}
103
104bool operator < (const VendorRecord& lh, const VendorRecord& rh)
105{
106 return lh.vendorID < rh.vendorID;
107}
108
109bool operator == (const ProductRecord& lh, const ProductRecord& rh)
110{
111 return lh.key == rh.key;
112}
113
114bool operator == (const VendorRecord& lh, const VendorRecord& rh)
115{
116 return lh.vendorID == rh.vendorID;
117}
118
119string conv(const string& src)
120{
121 string res = src;
122 for (size_t i = 0; i < res.length(); ++i)
123 {
124 switch (res[i])
125 {
126 case '"':
127 case '\\': res.insert(i++, "\\"); break;
128 default:
129 {
130 // encode multibyte UTF-8 symbols to be sure that they
131 // will be safely read by compiler
132 if ((unsigned char)res[i] >= 127)
133 {
134 size_t start = i;
135 string temp = "\" \"";
136 char buffer[8] = { 0 };
137 do
138 {
139 RTStrPrintf(buffer, sizeof(buffer), "\\x%x", (unsigned char)res[i]);
140 temp.append(buffer);
141 } while ((unsigned char)res[++i] & 0x80);
142 // splitting string after escape sequence to finish number sequence
143 // otherwise it could lead to situation when "\x88a" will threathened as
144 // multibyte symbol '\x88a' instead of two symbols '\x88' and 'a'
145 temp.append("\" \"");
146 res.replace(start, i - start, temp);
147 i += temp.length();
148 }
149 }
150 }
151 }
152 return res;
153}
154
155ostream& operator <<(ostream& stream, const ProductRecord product)
156{
157 stream << " { USBKEY(0x" << setfill('0') << setw(4) << hex << product.vendorID
158 << ", 0x" << setfill('0') << setw(4) << product.productID << "), "
159 << "\"" << conv(product.product).c_str() << "\" }," << endl;
160 return stream;
161}
162
163ostream& operator <<(ostream& stream, const VendorRecord vendor)
164{
165 stream << " { 0x" << setfill('0') << setw(4) << hex << vendor.vendorID
166 << ", \"" << conv(vendor.vendor).c_str() << "\" }," << endl;
167 return stream;
168}
169
170namespace State
171{
172 typedef int Value;
173 enum
174 {
175 lookForStartBlock,
176 lookForEndBlock,
177 finished
178 };
179}
180
181typedef vector<ProductRecord> ProductsSet;
182typedef vector<VendorRecord> VendorsSet;
183ProductsSet g_products;
184VendorsSet g_vendors;
185
186
187int ParseAlias(const string& src, size_t& id, string& desc)
188{
189 unsigned int i = 0;
190 int idx = 0;
191 string sin;
192
193 if (sscanf(src.c_str(), "%x", &i) != 1)
194 return ERROR_IN_PARSE_LINE;
195
196 size_t index = src.find_first_of(" \t", 1);
197 index = src.find_first_not_of(" \t", index);
198
199 if (index == string::npos)
200 return ERROR_IN_PARSE_LINE;
201
202 sin = src.substr(index);
203 id = i;
204 desc = sin;
205
206 return 0;
207}
208
209bool IsCommentOrEmptyLine(const string& str)
210{
211 size_t index = str.find_first_not_of(" \t");// skip left spaces
212 return index == string::npos || str[index] == '#';
213}
214
215bool getline(PRTSTREAM instream, string& resString)
216{
217 const size_t szBuf = 4096;
218 char buf[szBuf] = { 0 };
219
220 int rc = RTStrmGetLine(instream, buf, szBuf);
221 if (RT_SUCCESS(rc))
222 {
223 resString = buf;
224 return true;
225 }
226 else if (rc != VERR_EOF)
227 {
228 cerr << "Warning: Invalid line in file. Error: " << hex << rc << endl;
229 }
230 return false;
231}
232
233int ParseUsbIds(PRTSTREAM instream)
234{
235 State::Value state = State::lookForStartBlock;
236 string line;
237 int res = 0;
238 VendorRecord vendor = { 0, "" };
239
240 while (state != State::finished && getline(instream, line))
241 {
242 switch (state)
243 {
244 case State::lookForStartBlock:
245 {
246 if (line.find(start_block) != string::npos)
247 state = State::lookForEndBlock;
248 break;
249 }
250 case State::lookForEndBlock:
251 {
252 if (line.find(end_block) != string::npos)
253 state = State::finished;
254 else
255 {
256 if (!IsCommentOrEmptyLine(line))
257 {
258 if (line[0] == '\t')
259 {
260 // Parse Product line
261 // first line should be vendor
262 if (vendor.vendorID == 0)
263 {
264 cerr << "Wrong file format. Product before vendor: "
265 << line.c_str() << "'" << endl;
266 return ERROR_WRONG_FILE_FORMAT;
267 }
268 ProductRecord product = { 0, vendor.vendorID, 0, "" };
269 if (ParseAlias(line.substr(1), product.productID, product.product) != 0)
270 {
271 cerr << "Error in parsing product line: '"
272 << line.c_str() << "'" << endl;
273 return ERROR_IN_PARSE_LINE;
274 }
275 product.key = USBKEY(product.vendorID, product.productID);
276 Assert(product.vendorID != 0);
277 g_products.push_back(product);
278 }
279 else
280 {
281 // Parse vendor line
282 if (ParseAlias(line, vendor.vendorID, vendor.vendor) != 0)
283 {
284 cerr << "Error in parsing vendor line: '"
285 << line.c_str() << "'" << endl;
286 return ERROR_IN_PARSE_LINE;
287 }
288 g_vendors.push_back(vendor);
289 }
290 }
291 }
292 break;
293 }
294 }
295 }
296 if (state == State::lookForStartBlock)
297 {
298 cerr << "Error: wrong format of input file. Start line is not found." << endl;
299 return ERROR_WRONG_FILE_FORMAT;
300 }
301 return 0;
302}
303
304static int usage(ostream &rOut, const char *argv0)
305{
306 rOut << "Usage: " << argv0
307 << " [linux.org usb list file] [custom usb list file] [-o output file]" << endl;
308 return RTEXITCODE_SYNTAX;
309}
310
311int main(int argc, char *argv[])
312{
313 int rc = RTR3InitExe(argc, &argv, 0);
314 if (RT_FAILURE(rc))
315 return RTMsgInitFailure(rc);
316
317 if (argc < 4)
318 {
319 usage(cerr, argv[0]);
320 cerr << "Error: Not enough arguments." << endl;
321 return RTEXITCODE_SYNTAX;
322 }
323 ofstream fout;
324 PRTSTREAM fin;
325 g_products.reserve(20000);
326 g_vendors.reserve(3500);
327
328 const char *outName = NULL;
329 for (int i = 1; i < argc; i++)
330 {
331 if (strcmp(argv[i], "-o") == 0)
332 {
333 outName = argv[++i];
334 continue;
335 }
336 if ( strcmp(argv[i], "-h") == 0
337 || strcmp(argv[i], "-?") == 0
338 || strcmp(argv[i], "--help") == 0)
339 {
340 usage(cout, argv[0]);
341 return RTEXITCODE_SUCCESS;
342 }
343
344 rc = RTStrmOpen(argv[i], "r", &fin);
345 if (RT_FAILURE(rc))
346 {
347 cerr << "Error: Failed to open file '" << argv[i] << "' for reading. rc=" << rc << endl;
348 return ERROR_OPEN_FILE;
349 }
350
351 rc = ParseUsbIds(fin);
352 if (rc != 0)
353 {
354 cerr << "Error: Failed parsing USB devices file '" << argv[i] << "'" << endl;
355 RTStrmClose(fin);
356 return rc;
357 }
358 RTStrmClose(fin);
359 }
360
361 sort(g_products.begin(), g_products.end());
362 sort(g_vendors.begin(), g_vendors.end());
363
364 // validate that all records are unique
365 ProductsSet::iterator ita = adjacent_find(g_products.begin(), g_products.end());
366 if (ita != g_products.end())
367 {
368 cerr << "Error: Duplicate alias detected. " << *ita << endl;
369 return ERROR_DUPLICATE_ENTRY;
370 }
371
372 if (!outName)
373 {
374 cerr << "Error: Output file is not specified." << endl;
375 return ERROR_OPEN_FILE;
376 }
377
378/** @todo String compression. */
379
380 fout.open(outName);
381 if (!fout.is_open())
382 {
383 cerr << "Error: Can not open file to write '" << outName << "'." << endl;
384 return ERROR_OPEN_FILE;
385 }
386
387 fout << header;
388 for (ProductsSet::iterator itp = g_products.begin(); itp != g_products.end(); ++itp)
389 {
390 fout << *itp;
391 }
392 fout << footer;
393
394 fout << vendor_header;
395 for (VendorsSet::iterator itv = g_vendors.begin(); itv != g_vendors.end(); ++itv)
396 {
397 fout << *itv;
398 }
399 fout << vendor_footer;
400
401 fout.close();
402
403
404 return RTEXITCODE_SUCCESS;
405}
406
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