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>