]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - knowndrives.cpp
Correct maintscript syntax
[mirror_smartmontools-debian.git] / knowndrives.cpp
CommitLineData
832b75ed 1/*
4d59bff9 2 * knowndrives.cpp
832b75ed
GG
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 * Address of support mailing list: smartmontools-support@lists.sourceforge.net
6 *
cfbba5b9 7 * Copyright (C) 2003-11 Philip Williams, Bruce Allen
ee38a438 8 * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
832b75ed
GG
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2, or (at your option)
13 * any later version.
14 *
15 * You should have received a copy of the GNU General Public License
ee38a438 16 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
832b75ed
GG
17 *
18 */
19
20#include "config.h"
21#include "int64.h"
22#include <stdio.h>
23#include "atacmds.h"
832b75ed 24#include "knowndrives.h"
2127e193 25#include "utility.h"
832b75ed 26
2127e193
GI
27#ifdef HAVE_UNISTD_H
28#include <unistd.h>
29#endif
30#ifdef _WIN32
31#include <io.h> // access()
32#endif
33
34#include <stdexcept>
35
ee38a438 36const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $"
a23d5117 37 KNOWNDRIVES_H_CVSID;
832b75ed
GG
38
39#define MODEL_STRING_LENGTH 40
40#define FIRMWARE_STRING_LENGTH 8
41#define TABLEPRINTWIDTH 19
42
832b75ed 43
a23d5117
GI
44// Builtin table of known drives.
45// Used as a default if not read from
46// "/usr/{,/local}share/smartmontools/drivedb.h"
47// or any other file specified by '-B' option,
48// see read_default_drive_databases() below.
49// The drive_settings structure is described in drivedb.h.
50const drive_settings builtin_knowndrives[] = {
51#include "drivedb.h"
832b75ed
GG
52};
53
2127e193
GI
54
55/// Drive database class. Stores custom entries read from file.
56/// Provides transparent access to concatenation of custom and
57/// default table.
58class drive_database
59{
60public:
61 drive_database();
62
63 ~drive_database();
64
65 /// Get total number of entries.
66 unsigned size() const
67 { return m_custom_tab.size() + m_builtin_size; }
68
69 /// Get number of custom entries.
70 unsigned custom_size() const
71 { return m_custom_tab.size(); }
72
73 /// Array access.
74 const drive_settings & operator[](unsigned i);
75
76 /// Append new custom entry.
77 void push_back(const drive_settings & src);
78
79 /// Append builtin table.
80 void append(const drive_settings * builtin_tab, unsigned builtin_size)
81 { m_builtin_tab = builtin_tab; m_builtin_size = builtin_size; }
82
83private:
84 const drive_settings * m_builtin_tab;
85 unsigned m_builtin_size;
86
87 std::vector<drive_settings> m_custom_tab;
88 std::vector<char *> m_custom_strings;
89
90 const char * copy_string(const char * str);
91
92 drive_database(const drive_database &);
93 void operator=(const drive_database &);
94};
95
96drive_database::drive_database()
97: m_builtin_tab(0), m_builtin_size(0)
98{
99}
100
101drive_database::~drive_database()
102{
103 for (unsigned i = 0; i < m_custom_strings.size(); i++)
104 delete [] m_custom_strings[i];
105}
106
107const drive_settings & drive_database::operator[](unsigned i)
108{
109 return (i < m_custom_tab.size() ? m_custom_tab[i]
110 : m_builtin_tab[i - m_custom_tab.size()] );
111}
112
113void drive_database::push_back(const drive_settings & src)
114{
115 drive_settings dest;
116 dest.modelfamily = copy_string(src.modelfamily);
117 dest.modelregexp = copy_string(src.modelregexp);
118 dest.firmwareregexp = copy_string(src.firmwareregexp);
119 dest.warningmsg = copy_string(src.warningmsg);
120 dest.presets = copy_string(src.presets);
121 m_custom_tab.push_back(dest);
122}
123
124const char * drive_database::copy_string(const char * src)
125{
ee38a438
GI
126 size_t len = strlen(src);
127 char * dest = new char[len+1];
128 memcpy(dest, src, len+1);
2127e193
GI
129 try {
130 m_custom_strings.push_back(dest);
131 }
132 catch (...) {
133 delete [] dest; throw;
134 }
ee38a438 135 return dest;
2127e193
GI
136}
137
138
139/// The drive database.
140static drive_database knowndrives;
141
142
e9583e0c
GI
143// Return true if modelfamily string describes entry for USB ID
144static bool is_usb_modelfamily(const char * modelfamily)
145{
146 return !strncmp(modelfamily, "USB:", 4);
147}
148
149// Return true if entry for USB ID
150static inline bool is_usb_entry(const drive_settings * dbentry)
151{
152 return is_usb_modelfamily(dbentry->modelfamily);
153}
154
2127e193
GI
155// Compile regular expression, print message on failure.
156static bool compile(regular_expression & regex, const char *pattern)
157{
158 if (!regex.compile(pattern, REG_EXTENDED)) {
159 pout("Internal error: unable to compile regular expression \"%s\": %s\n"
160 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n",
161 pattern, regex.get_errmsg());
162 return false;
163 }
164 return true;
165}
166
167// Compile & match a regular expression.
168static bool match(const char * pattern, const char * str)
169{
170 regular_expression regex;
171 if (!compile(regex, pattern))
172 return false;
173 return regex.full_match(str);
174}
175
832b75ed
GG
176// Searches knowndrives[] for a drive with the given model number and firmware
177// string. If either the drive's model or firmware strings are not set by the
2127e193
GI
178// manufacturer then values of NULL may be used. Returns the entry of the
179// first match in knowndrives[] or 0 if no match if found.
cfbba5b9 180static const drive_settings * lookup_drive(const char * model, const char * firmware)
832b75ed 181{
2127e193
GI
182 if (!model)
183 model = "";
184 if (!firmware)
185 firmware = "";
832b75ed 186
2127e193 187 for (unsigned i = 0; i < knowndrives.size(); i++) {
e9583e0c
GI
188 // Skip USB entries
189 if (is_usb_entry(&knowndrives[i]))
190 continue;
191
2127e193
GI
192 // Check whether model matches the regular expression in knowndrives[i].
193 if (!match(knowndrives[i].modelregexp, model))
194 continue;
832b75ed 195
2127e193
GI
196 // Model matches, now check firmware. "" matches always.
197 if (!( !*knowndrives[i].firmwareregexp
198 || match(knowndrives[i].firmwareregexp, firmware)))
199 continue;
832b75ed 200
2127e193
GI
201 // Found
202 return &knowndrives[i];
832b75ed
GG
203 }
204
2127e193
GI
205 // Not found
206 return 0;
832b75ed
GG
207}
208
e9583e0c
GI
209
210// Parse drive or USB options in preset string, return false on error.
211static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
ee38a438 212 firmwarebug_defs * firmwarebugs, std::string * type)
2127e193
GI
213{
214 for (int i = 0; ; ) {
215 i += strspn(presets+i, " \t");
216 if (!presets[i])
217 break;
a7e8ffec
GI
218 char opt, arg[80+1+13]; int len = -1;
219 if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
2127e193 220 return false;
e9583e0c 221 if (opt == 'v' && defs) {
bed94269 222 // Parse "-v N,format[,name]"
e9583e0c 223 if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE))
2127e193 224 return false;
2127e193 225 }
ee38a438
GI
226 else if (opt == 'F' && firmwarebugs) {
227 firmwarebug_defs bug;
228 if (!parse_firmwarebug_def(arg, bug))
2127e193 229 return false;
ee38a438
GI
230 // Don't set if user specified '-F none'.
231 if (!firmwarebugs->is_set(BUG_NONE))
232 firmwarebugs->set(bug);
e9583e0c
GI
233 }
234 else if (opt == 'd' && type) {
235 // TODO: Check valid types
236 *type = arg;
2127e193
GI
237 }
238 else
239 return false;
832b75ed 240
2127e193
GI
241 i += len;
242 }
243 return true;
244}
245
e9583e0c
GI
246// Parse '-v' and '-F' options in preset string, return false on error.
247static inline bool parse_presets(const char * presets,
248 ata_vendor_attr_defs & defs,
ee38a438 249 firmwarebug_defs & firmwarebugs)
e9583e0c 250{
ee38a438 251 return parse_db_presets(presets, &defs, &firmwarebugs, 0);
e9583e0c
GI
252}
253
254// Parse '-d' option in preset string, return false on error.
255static inline bool parse_usb_type(const char * presets, std::string & type)
256{
257 return parse_db_presets(presets, 0, 0, &type);
258}
259
260// Parse "USB: [DEVICE] ; [BRIDGE]" string
261static void parse_usb_names(const char * names, usb_dev_info & info)
262{
263 int n1 = -1, n2 = -1, n3 = -1;
264 sscanf(names, "USB: %n%*[^;]%n; %n", &n1, &n2, &n3);
265 if (0 < n1 && n1 < n2)
266 info.usb_device.assign(names+n1, n2-n1);
267 else
268 sscanf(names, "USB: ; %n", &n3);
269 if (0 < n3)
270 info.usb_bridge = names+n3;
271}
272
273// Search drivedb for USB device with vendor:product ID.
274int lookup_usb_device(int vendor_id, int product_id, int bcd_device,
275 usb_dev_info & info, usb_dev_info & info2)
276{
277 // Format strings to match
278 char usb_id_str[16], bcd_dev_str[16];
279 snprintf(usb_id_str, sizeof(usb_id_str), "0x%04x:0x%04x", vendor_id, product_id);
280 if (bcd_device >= 0)
281 snprintf(bcd_dev_str, sizeof(bcd_dev_str), "0x%04x", bcd_device);
282 else
283 bcd_dev_str[0] = 0;
284
285 int found = 0;
e9583e0c
GI
286 for (unsigned i = 0; i < knowndrives.size(); i++) {
287 const drive_settings & dbentry = knowndrives[i];
288
289 // Skip drive entries
290 if (!is_usb_entry(&dbentry))
291 continue;
292
293 // Check whether USB vendor:product ID matches
294 if (!match(dbentry.modelregexp, usb_id_str))
295 continue;
296
297 // Parse '-d type'
298 usb_dev_info d;
299 if (!parse_usb_type(dbentry.presets, d.usb_type))
300 return 0; // Syntax error
301 parse_usb_names(dbentry.modelfamily, d);
302
303 // If two entries with same vendor:product ID have different
304 // types, use bcd_device (if provided by OS) to select entry.
cfbba5b9
GI
305 if ( *dbentry.firmwareregexp && *bcd_dev_str
306 && match(dbentry.firmwareregexp, bcd_dev_str)) {
307 // Exact match including bcd_device
308 info = d; found = 1;
309 break;
310 }
311 else if (!found) {
312 // First match without bcd_device
e9583e0c 313 info = d; found = 1;
e9583e0c 314 }
cfbba5b9
GI
315 else if (info.usb_type != d.usb_type) {
316 // Another possible match with different type
e9583e0c
GI
317 info2 = d; found = 2;
318 break;
319 }
cfbba5b9
GI
320
321 // Stop search at first matching entry with empty bcd_device
322 if (!*dbentry.firmwareregexp)
323 break;
e9583e0c
GI
324 }
325
326 return found;
327}
328
2127e193
GI
329// Shows one entry of knowndrives[], returns #errors.
330static int showonepreset(const drive_settings * dbentry)
331{
832b75ed 332 // Basic error check
2127e193
GI
333 if (!( dbentry
334 && dbentry->modelfamily
335 && dbentry->modelregexp && *dbentry->modelregexp
336 && dbentry->firmwareregexp
337 && dbentry->warningmsg
338 && dbentry->presets )) {
339 pout("Invalid drive database entry. Please report\n"
832b75ed 340 "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
2127e193 341 return 1;
832b75ed 342 }
e9583e0c
GI
343
344 bool usb = is_usb_entry(dbentry);
345
2127e193
GI
346 // print and check model and firmware regular expressions
347 int errcnt = 0;
348 regular_expression regex;
e9583e0c
GI
349 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"),
350 dbentry->modelregexp);
2127e193
GI
351 if (!compile(regex, dbentry->modelregexp))
352 errcnt++;
832b75ed 353
e9583e0c
GI
354 pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"),
355 *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
2127e193
GI
356 if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
357 errcnt++;
358
e9583e0c
GI
359 if (!usb) {
360 pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
361
362 // if there are any presets, then show them
ee38a438 363 firmwarebug_defs firmwarebugs;
e9583e0c
GI
364 bool first_preset = true;
365 if (*dbentry->presets) {
366 ata_vendor_attr_defs defs;
ee38a438 367 if (!parse_presets(dbentry->presets, defs, firmwarebugs)) {
e9583e0c
GI
368 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
369 errcnt++;
370 }
371 for (int i = 0; i < MAX_ATTRIBUTE_NUM; i++) {
372 if (defs[i].priority != PRIOR_DEFAULT) {
d008864d 373 std::string name = ata_get_smart_attr_name(i, defs);
e9583e0c
GI
374 // Use leading zeros instead of spaces so that everything lines up.
375 pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
d008864d
GI
376 i, name.c_str());
377 // Check max name length suitable for smartctl -A output
378 const unsigned maxlen = 23;
379 if (name.size() > maxlen) {
380 pout("%*s\n", TABLEPRINTWIDTH+6+maxlen, "Error: Attribute name too long ------^");
381 errcnt++;
382 }
e9583e0c
GI
383 first_preset = false;
384 }
385 }
2127e193 386 }
e9583e0c
GI
387 if (first_preset)
388 pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
389
390 // describe firmwarefix
ee38a438
GI
391 for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) {
392 if (!firmwarebugs.is_set((firmwarebug_t)b))
393 continue;
e9583e0c 394 const char * fixdesc;
ee38a438
GI
395 switch ((firmwarebug_t)b) {
396 case BUG_NOLOGDIR:
397 fixdesc = "Avoids reading GP/SMART Log Directories (same as -F nologdir)";
398 break;
399 case BUG_SAMSUNG:
e9583e0c
GI
400 fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
401 break;
ee38a438 402 case BUG_SAMSUNG2:
e9583e0c
GI
403 fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
404 break;
ee38a438 405 case BUG_SAMSUNG3:
e9583e0c
GI
406 fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
407 break;
ee38a438
GI
408 case BUG_XERRORLBA:
409 fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)";
410 break;
e9583e0c
GI
411 default:
412 fixdesc = "UNKNOWN"; errcnt++;
413 break;
2127e193 414 }
e9583e0c 415 pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
2127e193 416 }
832b75ed 417 }
e9583e0c
GI
418 else {
419 // Print USB info
420 usb_dev_info info; parse_usb_names(dbentry->modelfamily, info);
421 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Device:",
422 (!info.usb_device.empty() ? info.usb_device.c_str() : "[unknown]"));
423 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Bridge:",
424 (!info.usb_bridge.empty() ? info.usb_bridge.c_str() : "[unknown]"));
425
426 if (*dbentry->presets && !parse_usb_type(dbentry->presets, info.usb_type)) {
427 pout("Syntax error in USB type string \"%s\"\n", dbentry->presets);
428 errcnt++;
2127e193 429 }
e9583e0c
GI
430 pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type",
431 (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]"));
832b75ed 432 }
2127e193 433
832b75ed 434 // Print any special warnings
2127e193
GI
435 if (*dbentry->warningmsg)
436 pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
437 return errcnt;
832b75ed
GG
438}
439
440// Shows all presets for drives in knowndrives[].
2127e193
GI
441// Returns #syntax errors.
442int showallpresets()
443{
832b75ed
GG
444 // loop over all entries in the knowndrives[] table, printing them
445 // out in a nice format
2127e193
GI
446 int errcnt = 0;
447 for (unsigned i = 0; i < knowndrives.size(); i++) {
448 errcnt += showonepreset(&knowndrives[i]);
832b75ed
GG
449 pout("\n");
450 }
451
2127e193
GI
452 pout("Total number of entries :%5u\n"
453 "Entries read from file(s):%5u\n\n",
454 knowndrives.size(), knowndrives.custom_size());
455
832b75ed
GG
456 pout("For information about adding a drive to the database see the FAQ on the\n");
457 pout("smartmontools home page: " PACKAGE_HOMEPAGE "\n");
2127e193
GI
458
459 if (errcnt > 0)
460 pout("\nFound %d syntax error(s) in database.\n"
461 "Please inform smartmontools developers at " PACKAGE_BUGREPORT "\n", errcnt);
462 return errcnt;
832b75ed
GG
463}
464
465// Shows all matching presets for a drive in knowndrives[].
466// Returns # matching entries.
2127e193
GI
467int showmatchingpresets(const char *model, const char *firmware)
468{
832b75ed
GG
469 int cnt = 0;
470 const char * firmwaremsg = (firmware ? firmware : "(any)");
832b75ed 471
2127e193
GI
472 for (unsigned i = 0; i < knowndrives.size(); i++) {
473 if (!match(knowndrives[i].modelregexp, model))
832b75ed 474 continue;
2127e193
GI
475 if ( firmware && *knowndrives[i].firmwareregexp
476 && !match(knowndrives[i].firmwareregexp, firmware))
832b75ed 477 continue;
2127e193 478 // Found
832b75ed
GG
479 if (++cnt == 1)
480 pout("Drive found in smartmontools Database. Drive identity strings:\n"
481 "%-*s %s\n"
482 "%-*s %s\n"
483 "match smartmontools Drive Database entry:\n",
484 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmwaremsg);
485 else if (cnt == 2)
486 pout("and match these additional entries:\n");
487 showonepreset(&knowndrives[i]);
488 pout("\n");
489 }
832b75ed
GG
490 if (cnt == 0)
491 pout("No presets are defined for this drive. Its identity strings:\n"
492 "MODEL: %s\n"
493 "FIRMWARE: %s\n"
494 "do not match any of the known regular expressions.\n",
495 model, firmwaremsg);
496 return cnt;
497}
498
499// Shows the presets (if any) that are available for the given drive.
cfbba5b9 500void show_presets(const ata_identify_device * drive)
2127e193 501{
832b75ed
GG
502 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
503
504 // get the drive's model/firmware strings
cfbba5b9
GI
505 ata_format_id_string(model, drive->model, sizeof(model)-1);
506 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
507
832b75ed 508 // and search to see if they match values in the table
2127e193
GI
509 const drive_settings * dbentry = lookup_drive(model, firmware);
510 if (!dbentry) {
832b75ed
GG
511 // no matches found
512 pout("No presets are defined for this drive. Its identity strings:\n"
513 "MODEL: %s\n"
514 "FIRMWARE: %s\n"
515 "do not match any of the known regular expressions.\n"
516 "Use -P showall to list all known regular expressions.\n",
517 model, firmware);
518 return;
519 }
520
521 // We found a matching drive. Print out all information about it.
522 pout("Drive found in smartmontools Database. Drive identity strings:\n"
523 "%-*s %s\n"
524 "%-*s %s\n"
525 "match smartmontools Drive Database entry:\n",
526 TABLEPRINTWIDTH, "MODEL:", model, TABLEPRINTWIDTH, "FIRMWARE:", firmware);
2127e193 527 showonepreset(dbentry);
832b75ed
GG
528}
529
cfbba5b9 530// Searches drive database and sets preset vendor attribute
ee38a438 531// options in defs and firmwarebugs.
cfbba5b9
GI
532// Values that have already been set will not be changed.
533// Returns pointer to database entry or nullptr if none found
534const drive_settings * lookup_drive_apply_presets(
535 const ata_identify_device * drive, ata_vendor_attr_defs & defs,
ee38a438 536 firmwarebug_defs & firmwarebugs)
2127e193 537{
832b75ed 538 // get the drive's model/firmware strings
2127e193 539 char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
cfbba5b9
GI
540 ata_format_id_string(model, drive->model, sizeof(model)-1);
541 ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
542
832b75ed 543 // Look up the drive in knowndrives[].
2127e193
GI
544 const drive_settings * dbentry = lookup_drive(model, firmware);
545 if (!dbentry)
cfbba5b9 546 return 0;
2127e193
GI
547
548 if (*dbentry->presets) {
549 // Apply presets
ee38a438 550 if (!parse_presets(dbentry->presets, defs, firmwarebugs))
2127e193
GI
551 pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
552 }
cfbba5b9 553 return dbentry;
2127e193
GI
554}
555
556
557/////////////////////////////////////////////////////////////////////////////
558// Parser for drive database files
559
560// Abstract pointer to read file input.
561// Operations supported: c = *p; c = p[1]; ++p;
562class stdin_iterator
563{
564public:
565 explicit stdin_iterator(FILE * f)
566 : m_f(f) { get(); get(); }
567
568 stdin_iterator & operator++()
569 { get(); return *this; }
570
571 char operator*() const
572 { return m_c; }
573
574 char operator[](int i) const
575 {
576 if (i != 1)
577 fail();
578 return m_next;
579 }
580
581private:
582 FILE * m_f;
583 char m_c, m_next;
584 void get();
585 void fail() const;
586};
587
588void stdin_iterator::get()
589{
590 m_c = m_next;
591 int ch = getc(m_f);
592 m_next = (ch != EOF ? ch : 0);
593}
594
595void stdin_iterator::fail() const
596{
597 throw std::runtime_error("stdin_iterator: wrong usage");
598}
599
600
601// Use above as parser input 'pointer'. Can easily be changed later
602// to e.g. 'const char *' if above is too slow.
603typedef stdin_iterator parse_ptr;
604
605// Skip whitespace and comments.
606static parse_ptr skip_white(parse_ptr src, const char * path, int & line)
607{
608 for ( ; ; ++src) switch (*src) {
609 case ' ': case '\t':
610 continue;
611
612 case '\n':
613 ++line;
614 continue;
615
616 case '/':
617 switch (src[1]) {
618 case '/':
619 // skip '// comment'
620 ++src; ++src;
621 while (*src && *src != '\n')
622 ++src;
623 if (*src)
624 ++line;
625 break;
626 case '*':
627 // skip '/* comment */'
628 ++src; ++src;
629 for (;;) {
630 if (!*src) {
631 pout("%s(%d): Missing '*/'\n", path, line);
632 return src;
633 }
634 char c = *src; ++src;
635 if (c == '\n')
636 ++line;
637 else if (c == '*' && *src == '/')
638 break;
639 }
640 break;
641 default:
642 return src;
832b75ed 643 }
2127e193
GI
644 continue;
645
646 default:
647 return src;
648 }
649}
650
651// Info about a token.
652struct token_info
653{
654 char type;
655 int line;
656 std::string value;
657
658 token_info() : type(0), line(0) { }
659};
660
661// Get next token.
662static parse_ptr get_token(parse_ptr src, token_info & token, const char * path, int & line)
663{
664 src = skip_white(src, path, line);
665 switch (*src) {
666 case '{': case '}': case ',':
667 // Simple token
668 token.type = *src; token.line = line;
669 ++src;
670 break;
671
672 case '"':
673 // String constant
674 token.type = '"'; token.line = line;
675 token.value = "";
676 do {
677 for (++src; *src != '"'; ++src) {
678 char c = *src;
679 if (!c || c == '\n' || (c == '\\' && !src[1])) {
680 pout("%s(%d): Missing terminating '\"'\n", path, line);
681 token.type = '?'; token.line = line;
682 return src;
683 }
684 if (c == '\\') {
685 c = *++src;
686 switch (c) {
687 case 'n' : c = '\n'; break;
688 case '\n': ++line; break;
689 case '\\': case '"': break;
690 default:
691 pout("%s(%d): Unknown escape sequence '\\%c'\n", path, line, c);
692 token.type = '?'; token.line = line;
693 continue;
694 }
695 }
696 token.value += c;
697 }
698 // Lookahead to detect string constant concatentation
699 src = skip_white(++src, path, line);
700 } while (*src == '"');
701 break;
702
703 case 0:
704 // EOF
705 token.type = 0; token.line = line;
706 break;
707
708 default:
709 pout("%s(%d): Syntax error, invalid char '%c'\n", path, line, *src);
710 token.type = '?'; token.line = line;
711 while (*src && *src != '\n')
712 ++src;
713 break;
714 }
715
716 return src;
717}
718
719// Parse drive database from abstract input pointer.
720static bool parse_drive_database(parse_ptr src, drive_database & db, const char * path)
721{
722 int state = 0, field = 0;
723 std::string values[5];
724 bool ok = true;
725
726 token_info token; int line = 1;
727 src = get_token(src, token, path, line);
728 for (;;) {
729 // EOF is ok after '}', trailing ',' is also allowed.
730 if (!token.type && (state == 0 || state == 4))
731 break;
732
733 // Check expected token
734 const char expect[] = "{\",},";
735 if (token.type != expect[state]) {
736 if (token.type != '?')
737 pout("%s(%d): Syntax error, '%c' expected\n", path, token.line, expect[state]);
738 ok = false;
739 // Skip to next entry
740 while (token.type && token.type != '{')
741 src = get_token(src, token, path, line);
742 state = 0;
743 if (token.type)
744 continue;
745 break;
832b75ed 746 }
2127e193
GI
747
748 // Interpret parser state
749 switch (state) {
750 case 0: // ... ^{...}
751 state = 1; field = 0;
752 break;
753 case 1: // {... ^"..." ...}
754 switch (field) {
755 case 1: case 2:
756 if (!token.value.empty()) {
757 regular_expression regex;
758 if (!regex.compile(token.value.c_str(), REG_EXTENDED)) {
759 pout("%s(%d): Error in regular expression: %s\n", path, token.line, regex.get_errmsg());
760 ok = false;
761 }
762 }
763 else if (field == 1) {
764 pout("%s(%d): Missing regular expression for drive model\n", path, token.line);
765 ok = false;
766 }
767 break;
768 case 4:
769 if (!token.value.empty()) {
e9583e0c 770 if (!is_usb_modelfamily(values[0].c_str())) {
ee38a438 771 ata_vendor_attr_defs defs; firmwarebug_defs fix;
e9583e0c
GI
772 if (!parse_presets(token.value.c_str(), defs, fix)) {
773 pout("%s(%d): Syntax error in preset option string\n", path, token.line);
774 ok = false;
775 }
776 }
777 else {
778 std::string type;
779 if (!parse_usb_type(token.value.c_str(), type)) {
780 pout("%s(%d): Syntax error in USB type string\n", path, token.line);
781 ok = false;
782 }
2127e193
GI
783 }
784 }
785 break;
786 }
787 values[field] = token.value;
788 state = (++field < 5 ? 2 : 3);
789 break;
790 case 2: // {... "..."^, ...}
791 state = 1;
792 break;
793 case 3: // {...^}, ...
794 {
795 drive_settings entry;
796 entry.modelfamily = values[0].c_str();
797 entry.modelregexp = values[1].c_str();
798 entry.firmwareregexp = values[2].c_str();
799 entry.warningmsg = values[3].c_str();
800 entry.presets = values[4].c_str();
801 db.push_back(entry);
802 }
803 state = 4;
804 break;
805 case 4: // {...}^, ...
806 state = 0;
807 break;
808 default:
809 pout("Bad state %d\n", state);
810 return false;
811 }
812 src = get_token(src, token, path, line);
832b75ed 813 }
2127e193
GI
814 return ok;
815}
816
817// Read drive database from file.
818bool read_drive_database(const char * path)
819{
a23d5117
GI
820 stdio_file f(path, "r"
821#ifdef __CYGWIN__ // Allow files with '\r\n'.
822 "t"
823#endif
824 );
2127e193
GI
825 if (!f) {
826 pout("%s: cannot open drive database file\n", path);
827 return false;
828 }
829
830 return parse_drive_database(parse_ptr(f), knowndrives, path);
831}
832
e9583e0c
GI
833// Get path for additional database file
834const char * get_drivedb_path_add()
2127e193
GI
835{
836#ifndef _WIN32
e9583e0c
GI
837 return SMARTMONTOOLS_SYSCONFDIR"/smart_drivedb.h";
838#else
839 static std::string path = get_exe_dir() + "/drivedb-add.h";
840 return path.c_str();
841#endif
842}
843
844#ifdef SMARTMONTOOLS_DRIVEDBDIR
845
846// Get path for default database file
847const char * get_drivedb_path_default()
848{
849#ifndef _WIN32
850 return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
2127e193 851#else
e9583e0c
GI
852 static std::string path = get_exe_dir() + "/drivedb.h";
853 return path.c_str();
854#endif
855}
856
2127e193 857#endif
e9583e0c
GI
858
859// Read drive databases from standard places.
860bool read_default_drive_databases()
861{
862 // Read file for local additions: /{,usr/local/}etc/smart_drivedb.h
863 const char * db1 = get_drivedb_path_add();
2127e193
GI
864 if (!access(db1, 0)) {
865 if (!read_drive_database(db1))
866 return false;
867 }
868
869#ifdef SMARTMONTOOLS_DRIVEDBDIR
e9583e0c
GI
870 // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h
871 const char * db2 = get_drivedb_path_default();
2127e193
GI
872 if (!access(db2, 0)) {
873 if (!read_drive_database(db2))
874 return false;
875 }
876 else
877#endif
878 {
879 // Append builtin table.
880 knowndrives.append(builtin_knowndrives,
881 sizeof(builtin_knowndrives)/sizeof(builtin_knowndrives[0]));
882 }
883
884 return true;
832b75ed 885}