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>