From 582da09fd55fdf839a432c89c8917258a5b16f03 Mon Sep 17 00:00:00 2001
From: pjwilliams <pjwilliams@4ea69e1a-61f1-4043-bf83-b5c94c648137>
Date: Wed, 11 Dec 2002 00:11:31 +0000
Subject: [PATCH] smartctl now supports new options -- see README

git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@369 4ea69e1a-61f1-4043-bf83-b5c94c648137
---
 sm5/extern.h     |   3 +-
 sm5/smartctl.c   | 336 ++++++++++++++++++++++++++++++++++-------------
 sm5/smartctl.cpp | 336 ++++++++++++++++++++++++++++++++++-------------
 sm5/smartctl.h   |  47 ++++++-
 4 files changed, 536 insertions(+), 186 deletions(-)

diff --git a/sm5/extern.h b/sm5/extern.h
index 11ea651ef..90b1d6f66 100644
--- a/sm5/extern.h
+++ b/sm5/extern.h
@@ -27,7 +27,7 @@
 
 
 #ifndef CVSID3
-#define CVSID3 "$Id: extern.h,v 1.10 2002/11/07 11:00:56 ballen4705 Exp $\n"
+#define CVSID3 "$Id: extern.h,v 1.11 2002/12/11 00:11:30 pjwilliams Exp $\n"
 #endif
 
 // Block used for global control/communications.  If you need more
@@ -60,6 +60,7 @@ typedef struct ataprintmain_s {
   unsigned char permissive;
   unsigned char conservative;
   unsigned char checksumfail;
+  unsigned char checksumignore;
 } atamainctrl;
 
 #endif
diff --git a/sm5/smartctl.c b/sm5/smartctl.c
index 464bb8f28..77442a8cc 100644
--- a/sm5/smartctl.c
+++ b/sm5/smartctl.c
@@ -31,6 +31,9 @@
 #include <sys/types.h>
 #include <string.h>
 #include <stdarg.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
 #include "smartctl.h"
 #include "atacmds.h"
 #include "ataprint.h"
@@ -39,7 +42,7 @@
 #include "extern.h"
 
 extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; 
-const char* CVSid5="$Id: smartctl.c,v 1.29 2002/11/25 08:40:48 ballen4705 Exp $"
+const char* CVSid5="$Id: smartctl.c,v 1.30 2002/12/11 00:11:31 pjwilliams Exp $"
 CVSID1 CVSID2 CVSID3 CVSID4 CVSID5 CVSID6;
 
 // This is a block containing all the "control variables".  We declare
@@ -118,16 +121,38 @@ void Usage ( void){
   printf("  smartctl -qvL /dev/hda  (Prints Self-Test & Attribute errors.)\n");
 }
 
-const char opts[] = { 
-  DRIVEINFO, CHECKSMART, SMARTVERBOSEALL, SMARTVENDORATTRIB,
-  GENERALSMARTVALUES, SMARTERRORLOG, SMARTSELFTESTLOG, SMARTDISABLE,
-  SMARTENABLE, SMARTAUTOOFFLINEENABLE, SMARTAUTOOFFLINEDISABLE,
-  SMARTEXEOFFIMMEDIATE, SMARTSHORTSELFTEST, SMARTEXTENDSELFTEST, 
-  SMARTSHORTCAPSELFTEST, SMARTEXTENDCAPSELFTEST, SMARTSELFTESTABORT,
-  SMARTAUTOSAVEENABLE,SMARTAUTOSAVEDISABLE,PRINTCOPYLEFT,SMART009MINUTES,
-  QUIETMODE,VERYQUIETMODE,NOTSCSIDEVICE,NOTATADEVICE,
-  EXITCHECKSUMERROR,ULTRACONSERVATIVE,PERMISSIVE,
-  'h','?','\0'
+const char shortopts[] = {
+  S_OPT_HELP,
+  S_OPT_ALT_HELP,
+  S_OPT_VERSION,
+  S_OPT_QUIETMODE,
+  ':',
+  S_OPT_DEVICE,
+  ':',
+  S_OPT_TOLERANCE,
+  ':',
+  S_OPT_BADSUM,
+  ':',
+  S_OPT_SMART,
+  ':',
+  S_OPT_OFFLINEAUTO,
+  ':',
+  S_OPT_SAVEAUTO,
+  ':',
+  S_OPT_HEALTH,
+  S_OPT_CAPABILITIES,
+  S_OPT_ATTRIBUTES,
+  S_OPT_LOG,
+  ':',
+  S_OPT_INFO,
+  S_OPT_ALL,
+  S_OPT_VENDORATTRIBUTE,
+  ':',
+  S_OPT_TEST,
+  ':',
+  S_OPT_CAPTIVE,
+  S_OPT_ABORT,
+  '\0'
 };
 
 unsigned char printcopyleft=0,tryata=0,tryscsi=0;
@@ -135,124 +160,240 @@ unsigned char printcopyleft=0,tryata=0,tryscsi=0;
 /*      Takes command options and sets features to be run */	
 void ParseOpts (int argc, char** argv){
   int optchar;
+  int badarg;
+  int captive;
+  struct {
+    int n;
+    char *option;
+  } vendorattribute;
   extern char *optarg;
   extern int optopt, optind, opterr;
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  struct option longopts[] = {
+    { L_OPT_HELP,            no_argument,       0, S_OPT_HELP            },
+    { L_OPT_USAGE,           no_argument,       0, S_OPT_HELP            },
+    { L_OPT_VERSION,         no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_COPYRIGHT,       no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_LICENSE,         no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_QUIETMODE,       required_argument, 0, S_OPT_QUIETMODE       },
+    { L_OPT_DEVICE,          required_argument, 0, S_OPT_DEVICE          },
+    { L_OPT_TOLERANCE,       required_argument, 0, S_OPT_TOLERANCE       },
+    { L_OPT_BADSUM,          required_argument, 0, S_OPT_BADSUM          },
+    { L_OPT_SMART,           required_argument, 0, S_OPT_SMART           },
+    { L_OPT_OFFLINEAUTO,     required_argument, 0, S_OPT_OFFLINEAUTO     },
+    { L_OPT_SAVEAUTO,        required_argument, 0, S_OPT_SAVEAUTO        },
+    { L_OPT_HEALTH,          no_argument,       0, S_OPT_HEALTH          },
+    { L_OPT_CAPABILITIES,    no_argument,       0, S_OPT_CAPABILITIES    },
+    { L_OPT_ATTRIBUTES,      no_argument,       0, S_OPT_ATTRIBUTES      },
+    { L_OPT_LOG,             required_argument, 0, S_OPT_LOG             },
+    { L_OPT_INFO,            no_argument,       0, S_OPT_INFO            },
+    { L_OPT_ALL,             no_argument,       0, S_OPT_ALL             },
+    { L_OPT_VENDORATTRIBUTE, required_argument, 0, S_OPT_VENDORATTRIBUTE },
+    { L_OPT_TEST,            required_argument, 0, S_OPT_TEST            },
+    { L_OPT_CAPTIVE,         no_argument,       0, S_OPT_CAPTIVE         },
+    { L_OPT_ABORT,           no_argument,       0, S_OPT_ABORT           },
+    { 0,                     0,                 0, 0                     }
+  };
+#endif
   
   memset(con,0,sizeof(*con));
   con->testcase=-1;
   opterr=optopt=0;
-  while (-1 != (optchar = getopt(argc, argv, opts))) {
+  badarg = captive = FALSE;
+#ifdef HAVE_GETOPT_LONG
+  while (-1 != (optchar = getopt_long(argc, argv, shortopts, longopts, NULL))) {
+#else
+  while (-1 != (optchar = getopt(argc, argv, shortopts))) {
+#endif
     switch (optchar){
-    case EXITCHECKSUMERROR:
-      con->checksumfail=1;
-      break;
-    case PERMISSIVE:
-      con->permissive=1;
+    case S_OPT_VERSION:
+      printcopyleft=TRUE;
       break;
-    case ULTRACONSERVATIVE:
-      con->conservative=1;
+    case S_OPT_QUIETMODE:
+      if (!strcmp(optarg,"errorsonly")) {
+        con->quietmode     = TRUE;
+        con->veryquietmode = FALSE;
+      } else if (!strcmp(optarg,"silent")) {
+        con->veryquietmode = TRUE;
+        con->quietmode     = TRUE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case NOTATADEVICE:
-      tryata=0;
-      tryscsi=1;
+    case S_OPT_DEVICE:
+      if (!strcmp(optarg,"ata")) {
+        tryata  = TRUE;
+        tryscsi = FALSE;
+      } else if (!strcmp(optarg,"scsi")) {
+        tryata  = TRUE;
+        tryscsi = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case NOTSCSIDEVICE:
-      tryata=1;
-      tryscsi=0;
+    case S_OPT_TOLERANCE:
+      if (!strcmp(optarg,"normal")) {
+        con->conservative = FALSE;
+        con->permissive   = FALSE;
+      } else if (!strcmp(optarg,"conservative")) {
+        con->conservative = TRUE;
+        con->permissive   = FALSE;
+      } else if (!strcmp(optarg,"permissive")) {
+        con->permissive   = TRUE;
+        con->conservative = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case QUIETMODE:
-      con->quietmode=TRUE;
+    case S_OPT_BADSUM:
+      if (!strcmp(optarg,"warn")) {
+        con->checksumfail   = FALSE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"exit")) {
+        con->checksumfail   = TRUE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"ignore")) {
+        con->checksumignore = TRUE;
+        con->checksumfail   = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case VERYQUIETMODE:
-      con->veryquietmode=TRUE;
+    case S_OPT_SMART:
+      if (!strcmp(optarg,"on")) {
+        con->smartenable  = TRUE;
+        con->smartdisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartdisable = TRUE;
+        con->smartenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMART009MINUTES:
-      con->smart009minutes=TRUE;
+    case S_OPT_OFFLINEAUTO:
+      if (!strcmp(optarg,"on")) {
+        con->smartautoofflineenable  = TRUE;
+        con->smartautoofflinedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautoofflinedisable = TRUE;
+        con->smartautoofflineenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case PRINTCOPYLEFT :
-      printcopyleft=TRUE;
+    case S_OPT_SAVEAUTO:
+      if (!strcmp(optarg,"on")) {
+        con->smartautosaveenable  = TRUE;
+        con->smartautosavedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautosavedisable = TRUE;
+        con->smartautosaveenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case DRIVEINFO :
-      con->driveinfo  = TRUE;
-      break;		
-    case CHECKSMART :
+    case S_OPT_HEALTH:
       con->checksmart = TRUE;		
       break;
-    case SMARTVERBOSEALL :
-      con->driveinfo = TRUE;
-      con->checksmart = TRUE;
+    case S_OPT_CAPABILITIES:
       con->generalsmartvalues = TRUE;
-      con->smartvendorattrib = TRUE;
-      con->smarterrorlog = TRUE;
-      con->smartselftestlog = TRUE;
       break;
-    case SMARTVENDORATTRIB :
+    case S_OPT_ATTRIBUTES:
       con->smartvendorattrib = TRUE;
       break;
-    case GENERALSMARTVALUES :
-      con->generalsmartvalues = TRUE;
-      break;
-    case SMARTERRORLOG :
-      con->smarterrorlog = TRUE;
-      break;
-    case SMARTSELFTESTLOG :
-      con->smartselftestlog = TRUE;
-      break;
-    case SMARTDISABLE :
-      con->smartdisable = TRUE;
-      break;
-    case SMARTENABLE :
-      con->smartenable   = TRUE;
-      break;
-    case SMARTAUTOSAVEENABLE:
-      con->smartautosaveenable = TRUE;
-      break;
-    case SMARTAUTOSAVEDISABLE:
-      con->smartautosavedisable = TRUE;
-      break;
-    case SMARTAUTOOFFLINEENABLE: 
-      con->smartautoofflineenable = TRUE;
-      break;
-    case SMARTAUTOOFFLINEDISABLE:
-      con->smartautoofflinedisable = TRUE;
-      break;
-    case SMARTEXEOFFIMMEDIATE:
-      con->smartexeoffimmediate = TRUE;
-      con->testcase=OFFLINE_FULL_SCAN;
+    case S_OPT_LOG:
+      if (!strcmp(optarg,"error")) {
+        con->smarterrorlog = TRUE;
+      } else if (!strcmp(optarg,"selftest")) {
+        con->smartselftestlog = TRUE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMARTSHORTSELFTEST :
-      con->smartshortselftest = TRUE;
-      con->testcase=SHORT_SELF_TEST;
+    case S_OPT_INFO:
+      con->driveinfo = TRUE;
+      break;		
+    case S_OPT_ALL:
+      con->driveinfo          = TRUE;
+      con->checksmart         = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib  = TRUE;
+      con->smarterrorlog      = TRUE;
+      con->smartselftestlog   = TRUE;
       break;
-    case SMARTEXTENDSELFTEST :
-      con->smartextendselftest = TRUE;
-      con->testcase=EXTEND_SELF_TEST;
+    case S_OPT_VENDORATTRIBUTE:
+      vendorattribute.option = (char *)malloc(strlen(optarg)+1);
+      if (sscanf(optarg,"%u,%s",&(vendorattribute.n),vendorattribute.option) != 2) {
+        badarg = TRUE;
+      }
+      if (vendorattribute.n == 9 && !strcmp(vendorattribute.option,"minutes")) {
+        con->smart009minutes=TRUE;
+      } else {
+        // Should handle this better
+        badarg = TRUE;
+      }
+      free(vendorattribute.option);
       break;
-    case SMARTSHORTCAPSELFTEST:
-      con->smartshortcapselftest = TRUE;
-      con->testcase=SHORT_CAPTIVE_SELF_TEST;
+    case S_OPT_TEST:
+      if (!strcmp(optarg,"offline")) {
+        con->smartexeoffimmediate = TRUE;
+        con->testcase             = OFFLINE_FULL_SCAN;
+      } else if (!strcmp(optarg,"short")) {
+        con->smartshortselftest = TRUE;
+        con->testcase           = SHORT_SELF_TEST;
+      } else if (!strcmp(optarg,"long")) {
+        con->smartextendselftest = TRUE;
+        con->testcase            = EXTEND_SELF_TEST;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMARTEXTENDCAPSELFTEST:
-      con->smartextendcapselftest = TRUE;
-      con->testcase=EXTEND_CAPTIVE_SELF_TEST;
+    case S_OPT_CAPTIVE:
+      captive = TRUE;
       break;
-    case SMARTSELFTESTABORT:
+    case S_OPT_ABORT:
       con->smartselftestabort = TRUE;
-      con->testcase=ABORT_SELF_TEST;
+      con->testcase           = ABORT_SELF_TEST;
       break;
-    case 'h':
-    case '?':
+    case S_OPT_HELP:
+    case S_OPT_ALT_HELP:
     default:
       con->veryquietmode=FALSE;
       printslogan();
-      if (optopt){
-	pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",(int)optopt);
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option and options that map to -h.
+      if (arg[1] == '-' && optchar != S_OPT_HELP) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n\n",arg+2);
+        } else {
+          pout("=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
+        }
+        Usage();
+        exit(FAILCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (strchr(shortopts, optopt) != NULL){
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n\n",optopt);
+        } else {
+	  pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
+        }
 	Usage();
 	exit(FAILCMD);
       }
       Usage();
       exit(0);	
     }
+    if (badarg) {
+        pout("=======> INVALID ARGUMENT: %s <======= \n\n",optarg);
+        Usage();
+	exit(FAILCMD);
+    }
   }
   // Do this here, so results are independent of argument order	
   if (con->quietmode)
@@ -268,6 +409,17 @@ void ParseOpts (int argc, char** argv){
     exit(FAILCMD);
   }
 
+  // If captive option was used, change test type if appropriate.
+  if (captive && con->smartshortselftest) {
+      con->smartshortselftest    = FALSE;
+      con->smartshortcapselftest = TRUE;
+      con->testcase              = SHORT_CAPTIVE_SELF_TEST;
+  } else if (captive && con->smartextendselftest) {
+      con->smartextendselftest    = FALSE;
+      con->smartextendcapselftest = TRUE;
+      con->testcase               = EXTEND_CAPTIVE_SELF_TEST;
+  }
+
   // From here on, normal operations...
   printslogan();
   
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index 34b528ec9..c214dc0a1 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -31,6 +31,9 @@
 #include <sys/types.h>
 #include <string.h>
 #include <stdarg.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
 #include "smartctl.h"
 #include "atacmds.h"
 #include "ataprint.h"
@@ -39,7 +42,7 @@
 #include "extern.h"
 
 extern const char *CVSid1, *CVSid2, *CVSid3, *CVSid4; 
-const char* CVSid5="$Id: smartctl.cpp,v 1.29 2002/11/25 08:40:48 ballen4705 Exp $"
+const char* CVSid5="$Id: smartctl.cpp,v 1.30 2002/12/11 00:11:31 pjwilliams Exp $"
 CVSID1 CVSID2 CVSID3 CVSID4 CVSID5 CVSID6;
 
 // This is a block containing all the "control variables".  We declare
@@ -118,16 +121,38 @@ void Usage ( void){
   printf("  smartctl -qvL /dev/hda  (Prints Self-Test & Attribute errors.)\n");
 }
 
-const char opts[] = { 
-  DRIVEINFO, CHECKSMART, SMARTVERBOSEALL, SMARTVENDORATTRIB,
-  GENERALSMARTVALUES, SMARTERRORLOG, SMARTSELFTESTLOG, SMARTDISABLE,
-  SMARTENABLE, SMARTAUTOOFFLINEENABLE, SMARTAUTOOFFLINEDISABLE,
-  SMARTEXEOFFIMMEDIATE, SMARTSHORTSELFTEST, SMARTEXTENDSELFTEST, 
-  SMARTSHORTCAPSELFTEST, SMARTEXTENDCAPSELFTEST, SMARTSELFTESTABORT,
-  SMARTAUTOSAVEENABLE,SMARTAUTOSAVEDISABLE,PRINTCOPYLEFT,SMART009MINUTES,
-  QUIETMODE,VERYQUIETMODE,NOTSCSIDEVICE,NOTATADEVICE,
-  EXITCHECKSUMERROR,ULTRACONSERVATIVE,PERMISSIVE,
-  'h','?','\0'
+const char shortopts[] = {
+  S_OPT_HELP,
+  S_OPT_ALT_HELP,
+  S_OPT_VERSION,
+  S_OPT_QUIETMODE,
+  ':',
+  S_OPT_DEVICE,
+  ':',
+  S_OPT_TOLERANCE,
+  ':',
+  S_OPT_BADSUM,
+  ':',
+  S_OPT_SMART,
+  ':',
+  S_OPT_OFFLINEAUTO,
+  ':',
+  S_OPT_SAVEAUTO,
+  ':',
+  S_OPT_HEALTH,
+  S_OPT_CAPABILITIES,
+  S_OPT_ATTRIBUTES,
+  S_OPT_LOG,
+  ':',
+  S_OPT_INFO,
+  S_OPT_ALL,
+  S_OPT_VENDORATTRIBUTE,
+  ':',
+  S_OPT_TEST,
+  ':',
+  S_OPT_CAPTIVE,
+  S_OPT_ABORT,
+  '\0'
 };
 
 unsigned char printcopyleft=0,tryata=0,tryscsi=0;
@@ -135,124 +160,240 @@ unsigned char printcopyleft=0,tryata=0,tryscsi=0;
 /*      Takes command options and sets features to be run */	
 void ParseOpts (int argc, char** argv){
   int optchar;
+  int badarg;
+  int captive;
+  struct {
+    int n;
+    char *option;
+  } vendorattribute;
   extern char *optarg;
   extern int optopt, optind, opterr;
+#ifdef HAVE_GETOPT_LONG
+  char *arg;
+  struct option longopts[] = {
+    { L_OPT_HELP,            no_argument,       0, S_OPT_HELP            },
+    { L_OPT_USAGE,           no_argument,       0, S_OPT_HELP            },
+    { L_OPT_VERSION,         no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_COPYRIGHT,       no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_LICENSE,         no_argument,       0, S_OPT_VERSION         },
+    { L_OPT_QUIETMODE,       required_argument, 0, S_OPT_QUIETMODE       },
+    { L_OPT_DEVICE,          required_argument, 0, S_OPT_DEVICE          },
+    { L_OPT_TOLERANCE,       required_argument, 0, S_OPT_TOLERANCE       },
+    { L_OPT_BADSUM,          required_argument, 0, S_OPT_BADSUM          },
+    { L_OPT_SMART,           required_argument, 0, S_OPT_SMART           },
+    { L_OPT_OFFLINEAUTO,     required_argument, 0, S_OPT_OFFLINEAUTO     },
+    { L_OPT_SAVEAUTO,        required_argument, 0, S_OPT_SAVEAUTO        },
+    { L_OPT_HEALTH,          no_argument,       0, S_OPT_HEALTH          },
+    { L_OPT_CAPABILITIES,    no_argument,       0, S_OPT_CAPABILITIES    },
+    { L_OPT_ATTRIBUTES,      no_argument,       0, S_OPT_ATTRIBUTES      },
+    { L_OPT_LOG,             required_argument, 0, S_OPT_LOG             },
+    { L_OPT_INFO,            no_argument,       0, S_OPT_INFO            },
+    { L_OPT_ALL,             no_argument,       0, S_OPT_ALL             },
+    { L_OPT_VENDORATTRIBUTE, required_argument, 0, S_OPT_VENDORATTRIBUTE },
+    { L_OPT_TEST,            required_argument, 0, S_OPT_TEST            },
+    { L_OPT_CAPTIVE,         no_argument,       0, S_OPT_CAPTIVE         },
+    { L_OPT_ABORT,           no_argument,       0, S_OPT_ABORT           },
+    { 0,                     0,                 0, 0                     }
+  };
+#endif
   
   memset(con,0,sizeof(*con));
   con->testcase=-1;
   opterr=optopt=0;
-  while (-1 != (optchar = getopt(argc, argv, opts))) {
+  badarg = captive = FALSE;
+#ifdef HAVE_GETOPT_LONG
+  while (-1 != (optchar = getopt_long(argc, argv, shortopts, longopts, NULL))) {
+#else
+  while (-1 != (optchar = getopt(argc, argv, shortopts))) {
+#endif
     switch (optchar){
-    case EXITCHECKSUMERROR:
-      con->checksumfail=1;
-      break;
-    case PERMISSIVE:
-      con->permissive=1;
+    case S_OPT_VERSION:
+      printcopyleft=TRUE;
       break;
-    case ULTRACONSERVATIVE:
-      con->conservative=1;
+    case S_OPT_QUIETMODE:
+      if (!strcmp(optarg,"errorsonly")) {
+        con->quietmode     = TRUE;
+        con->veryquietmode = FALSE;
+      } else if (!strcmp(optarg,"silent")) {
+        con->veryquietmode = TRUE;
+        con->quietmode     = TRUE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case NOTATADEVICE:
-      tryata=0;
-      tryscsi=1;
+    case S_OPT_DEVICE:
+      if (!strcmp(optarg,"ata")) {
+        tryata  = TRUE;
+        tryscsi = FALSE;
+      } else if (!strcmp(optarg,"scsi")) {
+        tryata  = TRUE;
+        tryscsi = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case NOTSCSIDEVICE:
-      tryata=1;
-      tryscsi=0;
+    case S_OPT_TOLERANCE:
+      if (!strcmp(optarg,"normal")) {
+        con->conservative = FALSE;
+        con->permissive   = FALSE;
+      } else if (!strcmp(optarg,"conservative")) {
+        con->conservative = TRUE;
+        con->permissive   = FALSE;
+      } else if (!strcmp(optarg,"permissive")) {
+        con->permissive   = TRUE;
+        con->conservative = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case QUIETMODE:
-      con->quietmode=TRUE;
+    case S_OPT_BADSUM:
+      if (!strcmp(optarg,"warn")) {
+        con->checksumfail   = FALSE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"exit")) {
+        con->checksumfail   = TRUE;
+        con->checksumignore = FALSE;
+      } else if (!strcmp(optarg,"ignore")) {
+        con->checksumignore = TRUE;
+        con->checksumfail   = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case VERYQUIETMODE:
-      con->veryquietmode=TRUE;
+    case S_OPT_SMART:
+      if (!strcmp(optarg,"on")) {
+        con->smartenable  = TRUE;
+        con->smartdisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartdisable = TRUE;
+        con->smartenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMART009MINUTES:
-      con->smart009minutes=TRUE;
+    case S_OPT_OFFLINEAUTO:
+      if (!strcmp(optarg,"on")) {
+        con->smartautoofflineenable  = TRUE;
+        con->smartautoofflinedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautoofflinedisable = TRUE;
+        con->smartautoofflineenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case PRINTCOPYLEFT :
-      printcopyleft=TRUE;
+    case S_OPT_SAVEAUTO:
+      if (!strcmp(optarg,"on")) {
+        con->smartautosaveenable  = TRUE;
+        con->smartautosavedisable = FALSE;
+      } else if (!strcmp(optarg,"off")) {
+        con->smartautosavedisable = TRUE;
+        con->smartautosaveenable  = FALSE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case DRIVEINFO :
-      con->driveinfo  = TRUE;
-      break;		
-    case CHECKSMART :
+    case S_OPT_HEALTH:
       con->checksmart = TRUE;		
       break;
-    case SMARTVERBOSEALL :
-      con->driveinfo = TRUE;
-      con->checksmart = TRUE;
+    case S_OPT_CAPABILITIES:
       con->generalsmartvalues = TRUE;
-      con->smartvendorattrib = TRUE;
-      con->smarterrorlog = TRUE;
-      con->smartselftestlog = TRUE;
       break;
-    case SMARTVENDORATTRIB :
+    case S_OPT_ATTRIBUTES:
       con->smartvendorattrib = TRUE;
       break;
-    case GENERALSMARTVALUES :
-      con->generalsmartvalues = TRUE;
-      break;
-    case SMARTERRORLOG :
-      con->smarterrorlog = TRUE;
-      break;
-    case SMARTSELFTESTLOG :
-      con->smartselftestlog = TRUE;
-      break;
-    case SMARTDISABLE :
-      con->smartdisable = TRUE;
-      break;
-    case SMARTENABLE :
-      con->smartenable   = TRUE;
-      break;
-    case SMARTAUTOSAVEENABLE:
-      con->smartautosaveenable = TRUE;
-      break;
-    case SMARTAUTOSAVEDISABLE:
-      con->smartautosavedisable = TRUE;
-      break;
-    case SMARTAUTOOFFLINEENABLE: 
-      con->smartautoofflineenable = TRUE;
-      break;
-    case SMARTAUTOOFFLINEDISABLE:
-      con->smartautoofflinedisable = TRUE;
-      break;
-    case SMARTEXEOFFIMMEDIATE:
-      con->smartexeoffimmediate = TRUE;
-      con->testcase=OFFLINE_FULL_SCAN;
+    case S_OPT_LOG:
+      if (!strcmp(optarg,"error")) {
+        con->smarterrorlog = TRUE;
+      } else if (!strcmp(optarg,"selftest")) {
+        con->smartselftestlog = TRUE;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMARTSHORTSELFTEST :
-      con->smartshortselftest = TRUE;
-      con->testcase=SHORT_SELF_TEST;
+    case S_OPT_INFO:
+      con->driveinfo = TRUE;
+      break;		
+    case S_OPT_ALL:
+      con->driveinfo          = TRUE;
+      con->checksmart         = TRUE;
+      con->generalsmartvalues = TRUE;
+      con->smartvendorattrib  = TRUE;
+      con->smarterrorlog      = TRUE;
+      con->smartselftestlog   = TRUE;
       break;
-    case SMARTEXTENDSELFTEST :
-      con->smartextendselftest = TRUE;
-      con->testcase=EXTEND_SELF_TEST;
+    case S_OPT_VENDORATTRIBUTE:
+      vendorattribute.option = (char *)malloc(strlen(optarg)+1);
+      if (sscanf(optarg,"%u,%s",&(vendorattribute.n),vendorattribute.option) != 2) {
+        badarg = TRUE;
+      }
+      if (vendorattribute.n == 9 && !strcmp(vendorattribute.option,"minutes")) {
+        con->smart009minutes=TRUE;
+      } else {
+        // Should handle this better
+        badarg = TRUE;
+      }
+      free(vendorattribute.option);
       break;
-    case SMARTSHORTCAPSELFTEST:
-      con->smartshortcapselftest = TRUE;
-      con->testcase=SHORT_CAPTIVE_SELF_TEST;
+    case S_OPT_TEST:
+      if (!strcmp(optarg,"offline")) {
+        con->smartexeoffimmediate = TRUE;
+        con->testcase             = OFFLINE_FULL_SCAN;
+      } else if (!strcmp(optarg,"short")) {
+        con->smartshortselftest = TRUE;
+        con->testcase           = SHORT_SELF_TEST;
+      } else if (!strcmp(optarg,"long")) {
+        con->smartextendselftest = TRUE;
+        con->testcase            = EXTEND_SELF_TEST;
+      } else {
+        badarg = TRUE;
+      }
       break;
-    case SMARTEXTENDCAPSELFTEST:
-      con->smartextendcapselftest = TRUE;
-      con->testcase=EXTEND_CAPTIVE_SELF_TEST;
+    case S_OPT_CAPTIVE:
+      captive = TRUE;
       break;
-    case SMARTSELFTESTABORT:
+    case S_OPT_ABORT:
       con->smartselftestabort = TRUE;
-      con->testcase=ABORT_SELF_TEST;
+      con->testcase           = ABORT_SELF_TEST;
       break;
-    case 'h':
-    case '?':
+    case S_OPT_HELP:
+    case S_OPT_ALT_HELP:
     default:
       con->veryquietmode=FALSE;
       printslogan();
-      if (optopt){
-	pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",(int)optopt);
+#ifdef HAVE_GETOPT_LONG
+      // Point arg to the argument in which this option was found.
+      arg = argv[optind-1];
+      // Check whether the option is a long option and options that map to -h.
+      if (arg[1] == '-' && optchar != S_OPT_HELP) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (optopt && (strchr(shortopts, optopt) != NULL)) {
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %s <=======\n\n",arg+2);
+        } else {
+          pout("=======> UNRECOGNIZED OPTION: %s <=======\n\n",arg+2);
+        }
+        Usage();
+        exit(FAILCMD);
+      }
+#endif
+      if (optopt) {
+        // Iff optopt holds a valid option then argument must be missing.
+        if (strchr(shortopts, optopt) != NULL){
+          pout("=======> ARGUMENT REQUIRED FOR OPTION: %c <=======\n\n",optopt);
+        } else {
+	  pout("=======> UNRECOGNIZED OPTION: %c <=======\n\n",optopt);
+        }
 	Usage();
 	exit(FAILCMD);
       }
       Usage();
       exit(0);	
     }
+    if (badarg) {
+        pout("=======> INVALID ARGUMENT: %s <======= \n\n",optarg);
+        Usage();
+	exit(FAILCMD);
+    }
   }
   // Do this here, so results are independent of argument order	
   if (con->quietmode)
@@ -268,6 +409,17 @@ void ParseOpts (int argc, char** argv){
     exit(FAILCMD);
   }
 
+  // If captive option was used, change test type if appropriate.
+  if (captive && con->smartshortselftest) {
+      con->smartshortselftest    = FALSE;
+      con->smartshortcapselftest = TRUE;
+      con->testcase              = SHORT_CAPTIVE_SELF_TEST;
+  } else if (captive && con->smartextendselftest) {
+      con->smartextendselftest    = FALSE;
+      con->smartextendcapselftest = TRUE;
+      con->testcase               = EXTEND_CAPTIVE_SELF_TEST;
+  }
+
   // From here on, normal operations...
   printslogan();
   
diff --git a/sm5/smartctl.h b/sm5/smartctl.h
index 5a59a2ef7..79e3eec7d 100644
--- a/sm5/smartctl.h
+++ b/sm5/smartctl.h
@@ -26,7 +26,7 @@
 #define __SMARTCTL_H_
 
 #ifndef CVSID6
-#define CVSID6 "$Id: smartctl.h,v 1.13 2002/11/07 19:07:20 ballen4705 Exp $\n"
+#define CVSID6 "$Id: smartctl.h,v 1.14 2002/12/11 00:11:31 pjwilliams Exp $\n"
 #endif
 
 /* Defines for command line options */ 
@@ -59,6 +59,51 @@
 #define ULTRACONSERVATIVE       'U'
 #define PERMISSIVE              'P'
 
+#define S_OPT_HELP              'h'
+#define S_OPT_ALT_HELP          '?'
+#define S_OPT_VERSION           'V'
+#define S_OPT_QUIETMODE         'q'
+#define S_OPT_DEVICE            'd'
+#define S_OPT_TOLERANCE         'T'
+#define S_OPT_BADSUM            'b'
+#define S_OPT_SMART             's'
+#define S_OPT_OFFLINEAUTO       'o'
+#define S_OPT_SAVEAUTO          'S'
+#define S_OPT_HEALTH            'H'
+#define S_OPT_CAPABILITIES      'c'
+#define S_OPT_ATTRIBUTES        'A'
+#define S_OPT_LOG               'l'
+#define S_OPT_INFO              'i'
+#define S_OPT_ALL               'a'
+#define S_OPT_VENDORATTRIBUTE   'v'
+#define S_OPT_TEST              't'
+#define S_OPT_CAPTIVE           'C'
+#define S_OPT_ABORT             'X'
+#ifdef HAVE_GETOPT_LONG
+#define L_OPT_HELP              "help"
+#define L_OPT_USAGE             "usage"
+#define L_OPT_VERSION           "version"
+#define L_OPT_COPYRIGHT         "copyright"
+#define L_OPT_LICENSE           "license"
+#define L_OPT_QUIETMODE         "quietmode"
+#define L_OPT_DEVICE            "device"
+#define L_OPT_TOLERANCE         "tolerance"
+#define L_OPT_BADSUM            "badsum"
+#define L_OPT_SMART             "smart"
+#define L_OPT_OFFLINEAUTO       "offlineauto"
+#define L_OPT_SAVEAUTO          "saveauto"
+#define L_OPT_HEALTH            "health"
+#define L_OPT_CAPABILITIES      "capabilities"
+#define L_OPT_ATTRIBUTES        "attributes"
+#define L_OPT_LOG               "log"
+#define L_OPT_INFO              "info"
+#define L_OPT_ALL               "all"
+#define L_OPT_VENDORATTRIBUTE   "vendorattribute"
+#define L_OPT_TEST              "test"
+#define L_OPT_CAPTIVE           "captive"
+#define L_OPT_ABORT             "abort"
+#endif
+
 
 /* Boolean Values */
 #define TRUE 0x01
-- 
GitLab