Annotation of embedaddon/smartmontools/knowndrives.cpp, revision 1.1.1.2
1.1 misho 1: /*
2: * knowndrives.cpp
3: *
4: * Home page of code is: http://smartmontools.sourceforge.net
5: * Address of support mailing list: smartmontools-support@lists.sourceforge.net
6: *
7: * Copyright (C) 2003-11 Philip Williams, Bruce Allen
1.1.1.2 ! misho 8: * Copyright (C) 2008-12 Christian Franke <smartmontools-support@lists.sourceforge.net>
1.1 misho 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
1.1.1.2 ! misho 16: * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
1.1 misho 17: *
18: */
19:
20: #include "config.h"
21: #include "int64.h"
22: #include <stdio.h>
23: #include "atacmds.h"
24: #include "knowndrives.h"
25: #include "utility.h"
26:
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:
1.1.1.2 ! misho 36: const char * knowndrives_cpp_cvsid = "$Id: knowndrives.cpp 3719 2012-12-03 21:19:33Z chrfranke $"
1.1 misho 37: KNOWNDRIVES_H_CVSID;
38:
39: #define MODEL_STRING_LENGTH 40
40: #define FIRMWARE_STRING_LENGTH 8
41: #define TABLEPRINTWIDTH 19
42:
43:
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.
50: const drive_settings builtin_knowndrives[] = {
51: #include "drivedb.h"
52: };
53:
54:
55: /// Drive database class. Stores custom entries read from file.
56: /// Provides transparent access to concatenation of custom and
57: /// default table.
58: class drive_database
59: {
60: public:
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:
83: private:
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:
96: drive_database::drive_database()
97: : m_builtin_tab(0), m_builtin_size(0)
98: {
99: }
100:
101: drive_database::~drive_database()
102: {
103: for (unsigned i = 0; i < m_custom_strings.size(); i++)
104: delete [] m_custom_strings[i];
105: }
106:
107: const 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:
113: void 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:
124: const char * drive_database::copy_string(const char * src)
125: {
1.1.1.2 ! misho 126: size_t len = strlen(src);
! 127: char * dest = new char[len+1];
! 128: memcpy(dest, src, len+1);
1.1 misho 129: try {
130: m_custom_strings.push_back(dest);
131: }
132: catch (...) {
133: delete [] dest; throw;
134: }
1.1.1.2 ! misho 135: return dest;
1.1 misho 136: }
137:
138:
139: /// The drive database.
140: static drive_database knowndrives;
141:
142:
143: // Return true if modelfamily string describes entry for USB ID
144: static bool is_usb_modelfamily(const char * modelfamily)
145: {
146: return !strncmp(modelfamily, "USB:", 4);
147: }
148:
149: // Return true if entry for USB ID
150: static inline bool is_usb_entry(const drive_settings * dbentry)
151: {
152: return is_usb_modelfamily(dbentry->modelfamily);
153: }
154:
155: // Compile regular expression, print message on failure.
156: static 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.
168: static 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:
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
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.
180: static const drive_settings * lookup_drive(const char * model, const char * firmware)
181: {
182: if (!model)
183: model = "";
184: if (!firmware)
185: firmware = "";
186:
187: for (unsigned i = 0; i < knowndrives.size(); i++) {
188: // Skip USB entries
189: if (is_usb_entry(&knowndrives[i]))
190: continue;
191:
192: // Check whether model matches the regular expression in knowndrives[i].
193: if (!match(knowndrives[i].modelregexp, model))
194: continue;
195:
196: // Model matches, now check firmware. "" matches always.
197: if (!( !*knowndrives[i].firmwareregexp
198: || match(knowndrives[i].firmwareregexp, firmware)))
199: continue;
200:
201: // Found
202: return &knowndrives[i];
203: }
204:
205: // Not found
206: return 0;
207: }
208:
209:
210: // Parse drive or USB options in preset string, return false on error.
211: static bool parse_db_presets(const char * presets, ata_vendor_attr_defs * defs,
1.1.1.2 ! misho 212: firmwarebug_defs * firmwarebugs, std::string * type)
1.1 misho 213: {
214: for (int i = 0; ; ) {
215: i += strspn(presets+i, " \t");
216: if (!presets[i])
217: break;
218: char opt, arg[80+1+13]; int len = -1;
219: if (!(sscanf(presets+i, "-%c %80[^ ]%n", &opt, arg, &len) >= 2 && len > 0))
220: return false;
221: if (opt == 'v' && defs) {
222: // Parse "-v N,format[,name]"
223: if (!parse_attribute_def(arg, *defs, PRIOR_DATABASE))
224: return false;
225: }
1.1.1.2 ! misho 226: else if (opt == 'F' && firmwarebugs) {
! 227: firmwarebug_defs bug;
! 228: if (!parse_firmwarebug_def(arg, bug))
1.1 misho 229: return false;
1.1.1.2 ! misho 230: // Don't set if user specified '-F none'.
! 231: if (!firmwarebugs->is_set(BUG_NONE))
! 232: firmwarebugs->set(bug);
1.1 misho 233: }
234: else if (opt == 'd' && type) {
235: // TODO: Check valid types
236: *type = arg;
237: }
238: else
239: return false;
240:
241: i += len;
242: }
243: return true;
244: }
245:
246: // Parse '-v' and '-F' options in preset string, return false on error.
247: static inline bool parse_presets(const char * presets,
248: ata_vendor_attr_defs & defs,
1.1.1.2 ! misho 249: firmwarebug_defs & firmwarebugs)
1.1 misho 250: {
1.1.1.2 ! misho 251: return parse_db_presets(presets, &defs, &firmwarebugs, 0);
1.1 misho 252: }
253:
254: // Parse '-d' option in preset string, return false on error.
255: static 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
261: static 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.
274: int 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;
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.
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
313: info = d; found = 1;
314: }
315: else if (info.usb_type != d.usb_type) {
316: // Another possible match with different type
317: info2 = d; found = 2;
318: break;
319: }
320:
321: // Stop search at first matching entry with empty bcd_device
322: if (!*dbentry.firmwareregexp)
323: break;
324: }
325:
326: return found;
327: }
328:
329: // Shows one entry of knowndrives[], returns #errors.
330: static int showonepreset(const drive_settings * dbentry)
331: {
332: // Basic error check
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"
340: "this error to smartmontools developers at " PACKAGE_BUGREPORT ".\n");
341: return 1;
342: }
343:
344: bool usb = is_usb_entry(dbentry);
345:
346: // print and check model and firmware regular expressions
347: int errcnt = 0;
348: regular_expression regex;
349: pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "MODEL REGEXP:" : "USB Vendor:Product:"),
350: dbentry->modelregexp);
351: if (!compile(regex, dbentry->modelregexp))
352: errcnt++;
353:
354: pout("%-*s %s\n", TABLEPRINTWIDTH, (!usb ? "FIRMWARE REGEXP:" : "USB bcdDevice:"),
355: *dbentry->firmwareregexp ? dbentry->firmwareregexp : ".*"); // preserve old output (TODO: Change)
356: if (*dbentry->firmwareregexp && !compile(regex, dbentry->firmwareregexp))
357: errcnt++;
358:
359: if (!usb) {
360: pout("%-*s %s\n", TABLEPRINTWIDTH, "MODEL FAMILY:", dbentry->modelfamily);
361:
362: // if there are any presets, then show them
1.1.1.2 ! misho 363: firmwarebug_defs firmwarebugs;
1.1 misho 364: bool first_preset = true;
365: if (*dbentry->presets) {
366: ata_vendor_attr_defs defs;
1.1.1.2 ! misho 367: if (!parse_presets(dbentry->presets, defs, firmwarebugs)) {
1.1 misho 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) {
373: std::string name = ata_get_smart_attr_name(i, defs);
374: // Use leading zeros instead of spaces so that everything lines up.
375: pout("%-*s %03d %s\n", TABLEPRINTWIDTH, first_preset ? "ATTRIBUTE OPTIONS:" : "",
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: }
383: first_preset = false;
384: }
385: }
386: }
387: if (first_preset)
388: pout("%-*s %s\n", TABLEPRINTWIDTH, "ATTRIBUTE OPTIONS:", "None preset; no -v options are required.");
389:
390: // describe firmwarefix
1.1.1.2 ! misho 391: for (int b = BUG_NOLOGDIR; b <= BUG_XERRORLBA; b++) {
! 392: if (!firmwarebugs.is_set((firmwarebug_t)b))
! 393: continue;
1.1 misho 394: const char * fixdesc;
1.1.1.2 ! misho 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:
1.1 misho 400: fixdesc = "Fixes byte order in some SMART data (same as -F samsung)";
401: break;
1.1.1.2 ! misho 402: case BUG_SAMSUNG2:
1.1 misho 403: fixdesc = "Fixes byte order in some SMART data (same as -F samsung2)";
404: break;
1.1.1.2 ! misho 405: case BUG_SAMSUNG3:
1.1 misho 406: fixdesc = "Fixes completed self-test reported as in progress (same as -F samsung3)";
407: break;
1.1.1.2 ! misho 408: case BUG_XERRORLBA:
! 409: fixdesc = "Fixes LBA byte ordering in Ext. Comprehensive SMART error log (same as -F xerrorlba)";
! 410: break;
1.1 misho 411: default:
412: fixdesc = "UNKNOWN"; errcnt++;
413: break;
414: }
415: pout("%-*s %s\n", TABLEPRINTWIDTH, "OTHER PRESETS:", fixdesc);
416: }
417: }
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++;
429: }
430: pout("%-*s %s\n", TABLEPRINTWIDTH, "USB Type",
431: (!info.usb_type.empty() ? info.usb_type.c_str() : "[unsupported]"));
432: }
433:
434: // Print any special warnings
435: if (*dbentry->warningmsg)
436: pout("%-*s %s\n", TABLEPRINTWIDTH, "WARNINGS:", dbentry->warningmsg);
437: return errcnt;
438: }
439:
440: // Shows all presets for drives in knowndrives[].
441: // Returns #syntax errors.
442: int showallpresets()
443: {
444: // loop over all entries in the knowndrives[] table, printing them
445: // out in a nice format
446: int errcnt = 0;
447: for (unsigned i = 0; i < knowndrives.size(); i++) {
448: errcnt += showonepreset(&knowndrives[i]);
449: pout("\n");
450: }
451:
452: pout("Total number of entries :%5u\n"
453: "Entries read from file(s):%5u\n\n",
454: knowndrives.size(), knowndrives.custom_size());
455:
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");
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;
463: }
464:
465: // Shows all matching presets for a drive in knowndrives[].
466: // Returns # matching entries.
467: int showmatchingpresets(const char *model, const char *firmware)
468: {
469: int cnt = 0;
470: const char * firmwaremsg = (firmware ? firmware : "(any)");
471:
472: for (unsigned i = 0; i < knowndrives.size(); i++) {
473: if (!match(knowndrives[i].modelregexp, model))
474: continue;
475: if ( firmware && *knowndrives[i].firmwareregexp
476: && !match(knowndrives[i].firmwareregexp, firmware))
477: continue;
478: // Found
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: }
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.
500: void show_presets(const ata_identify_device * drive)
501: {
502: char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
503:
504: // get the drive's model/firmware strings
505: ata_format_id_string(model, drive->model, sizeof(model)-1);
506: ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
507:
508: // and search to see if they match values in the table
509: const drive_settings * dbentry = lookup_drive(model, firmware);
510: if (!dbentry) {
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);
527: showonepreset(dbentry);
528: }
529:
530: // Searches drive database and sets preset vendor attribute
1.1.1.2 ! misho 531: // options in defs and firmwarebugs.
1.1 misho 532: // Values that have already been set will not be changed.
533: // Returns pointer to database entry or nullptr if none found
534: const drive_settings * lookup_drive_apply_presets(
535: const ata_identify_device * drive, ata_vendor_attr_defs & defs,
1.1.1.2 ! misho 536: firmwarebug_defs & firmwarebugs)
1.1 misho 537: {
538: // get the drive's model/firmware strings
539: char model[MODEL_STRING_LENGTH+1], firmware[FIRMWARE_STRING_LENGTH+1];
540: ata_format_id_string(model, drive->model, sizeof(model)-1);
541: ata_format_id_string(firmware, drive->fw_rev, sizeof(firmware)-1);
542:
543: // Look up the drive in knowndrives[].
544: const drive_settings * dbentry = lookup_drive(model, firmware);
545: if (!dbentry)
546: return 0;
547:
548: if (*dbentry->presets) {
549: // Apply presets
1.1.1.2 ! misho 550: if (!parse_presets(dbentry->presets, defs, firmwarebugs))
1.1 misho 551: pout("Syntax error in preset option string \"%s\"\n", dbentry->presets);
552: }
553: return dbentry;
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;
562: class stdin_iterator
563: {
564: public:
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:
581: private:
582: FILE * m_f;
583: char m_c, m_next;
584: void get();
585: void fail() const;
586: };
587:
588: void stdin_iterator::get()
589: {
590: m_c = m_next;
591: int ch = getc(m_f);
592: m_next = (ch != EOF ? ch : 0);
593: }
594:
595: void 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.
603: typedef stdin_iterator parse_ptr;
604:
605: // Skip whitespace and comments.
606: static 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;
643: }
644: continue;
645:
646: default:
647: return src;
648: }
649: }
650:
651: // Info about a token.
652: struct 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.
662: static 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.
720: static 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;
746: }
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()) {
770: if (!is_usb_modelfamily(values[0].c_str())) {
1.1.1.2 ! misho 771: ata_vendor_attr_defs defs; firmwarebug_defs fix;
1.1 misho 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: }
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);
813: }
814: return ok;
815: }
816:
817: // Read drive database from file.
818: bool read_drive_database(const char * path)
819: {
820: stdio_file f(path, "r"
821: #ifdef __CYGWIN__ // Allow files with '\r\n'.
822: "t"
823: #endif
824: );
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:
833: // Get path for additional database file
834: const char * get_drivedb_path_add()
835: {
836: #ifndef _WIN32
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
847: const char * get_drivedb_path_default()
848: {
849: #ifndef _WIN32
850: return SMARTMONTOOLS_DRIVEDBDIR"/drivedb.h";
851: #else
852: static std::string path = get_exe_dir() + "/drivedb.h";
853: return path.c_str();
854: #endif
855: }
856:
857: #endif
858:
859: // Read drive databases from standard places.
860: bool 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();
864: if (!access(db1, 0)) {
865: if (!read_drive_database(db1))
866: return false;
867: }
868:
869: #ifdef SMARTMONTOOLS_DRIVEDBDIR
870: // Read file from package: /usr/{,local/}share/smartmontools/drivedb.h
871: const char * db2 = get_drivedb_path_default();
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;
885: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>