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