VirtualBox

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

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

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