Annotation of embedaddon/smartmontools/knowndrives.cpp, revision 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>