diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG
index 64056086829551c033adef451fdb039519e26330..888f47e0d88e746a20692accb00719bd69ae11e5 100644
--- a/sm5/CHANGELOG
+++ b/sm5/CHANGELOG
@@ -1,6 +1,6 @@
 CHANGELOG for smartmontools
 
-$Id: CHANGELOG,v 1.626 2007/07/23 15:33:13 ballen4705 Exp $
+$Id: CHANGELOG,v 1.627 2007/07/26 20:58:50 chrfranke Exp $
 
 The most recent version of this file is:
 http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup
@@ -33,6 +33,9 @@ NOTES FOR FUTURE RELEASES: see TODO file.
 
 <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE>
 
+  [CF] smartctl: Added ability to parse '-r ataioctl,2' output from
+       stdin ('-') and simulate the ATA commands for testing purposes.
+
   [BA] SMART Attributes: added 187, 189, more accurate name for 190.
 
   [CF] Windows: Added drive letters 'X:' as alternate disk device names.
diff --git a/sm5/atacmds.cpp b/sm5/atacmds.cpp
index c3e3241779ec95849fd349e708157f9a345b83a6..77129b6722ed3ac6fe257109b91ed73f5a8993f3 100644
--- a/sm5/atacmds.cpp
+++ b/sm5/atacmds.cpp
@@ -36,7 +36,7 @@
 #include "extern.h"
 #include "utility.h"
 
-const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.187 2007/07/23 15:33:13 ballen4705 Exp $"
+const char *atacmds_c_cvsid="$Id: atacmds.cpp,v 1.188 2007/07/26 20:58:50 chrfranke Exp $"
 ATACMDS_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID;
 
 // to hold onto exit code for atexit routine
@@ -563,6 +563,8 @@ static void prettyprint(const unsigned char *p, const char *name){
   pout("===== [%s] DATA END (512 Bytes) =====\n\n", name);
 }
 
+static int parsedev_command_interface(int fd, smart_command_set command, int select, char * data);
+
 // This function provides the pretty-print reporting for SMART
 // commands: it implements the various -r "reporting" options for ATA
 // ioctls.
@@ -636,6 +638,9 @@ int smartcommandhandler(int device, smart_command_set command, int select, char
   case CONTROLLER_HPT:
     retval=highpoint_command_interface(device, command, select, data);
     break;
+  case CONTROLLER_PARSEDEV:
+    retval=parsedev_command_interface(device, command, select, data);
+    break;
   default:
     retval=ata_command_interface(device, command, select, data);
   }
@@ -2211,3 +2216,266 @@ int ataSetSCTTempInterval(int device, unsigned interval, bool persistent)
   return 0;
 }
 
+
+/////////////////////////////////////////////////////////////////////////////
+// Pseudo-device to parse "smartctl -r ataioctl,2 ..." output and simulate
+// an ATA device with same behaviour
+
+// Table of parsed commands, return value, data
+struct parsed_ata_command
+{ 
+  smart_command_set command;
+  int select;
+  int retval, errval;
+  char * data;
+};
+
+const int max_num_parsed_commands = 32;
+static parsed_ata_command parsed_command_table[max_num_parsed_commands];
+static int num_parsed_commands;
+static int next_replay_command;
+static bool replay_out_of_sync;
+
+
+static const char * nextline(const char * s, int & lineno)
+{
+  for (s += strcspn(s, "\r\n"); *s == '\r' || *s == '\n'; s++) {
+    if (*s == '\r' && s[1] == '\n')
+      s++;
+    lineno++;
+  }
+  return s;
+}
+
+static int name2command(const char * s)
+{
+  for (int i = 0; i < (int)(sizeof(commandstrings)/sizeof(commandstrings[0])); i++) {
+    if (!strcmp(s, commandstrings[i]))
+      return i;
+  }
+  return -1;
+}
+
+static bool matchcpy(char * dest, size_t size, const char * src, const regmatch_t & srcmatch)
+{
+  if (srcmatch.rm_so < 0)
+    return false;
+  size_t n = srcmatch.rm_eo - srcmatch.rm_so;
+  if (n >= size)
+    n = size-1;
+  memcpy(dest, src + srcmatch.rm_so, n);
+  dest[n] = 0;
+  return true;
+}
+
+static inline int matchtoi(const char * src, const regmatch_t & srcmatch, int defval)
+{
+  if (srcmatch.rm_so < 0)
+    return defval;
+  return atoi(src + srcmatch.rm_so);
+}
+
+
+// Parse stdin and build command table
+int parsedev_open(const char * pathname)
+{
+  if (strcmp(pathname, "-")) {
+    errno = EINVAL; return -1;
+  }
+  pathname = "<stdin>";
+  // Fill buffer
+  char buffer[64*1024];
+  int size = 0;
+  while (size < (int)sizeof(buffer)) {
+    int nr = fread(buffer, 1, sizeof(buffer), stdin);
+    if (nr <= 0)
+      break;
+    size += nr;
+  }
+  if (size <= 0) {
+    pout("%s: Unexpected EOF\n", pathname);
+    errno = ENOENT; return -1;
+  }
+  if (size >= (int)sizeof(buffer)) {
+    pout("%s: Buffer overflow\n", pathname);
+    errno = EIO; return -1;
+  }
+  buffer[size] = 0;
+
+  // Regex to match output from "-r ataioctl,2"
+  static const char pattern[] = "^"
+  "(" // (1
+    "REPORT-IOCTL: DeviceFD=[0-9]+ Command=([A-Z ]*[A-Z])" // (2)
+    "(" // (3
+      "( InputParameter=([0-9]+))?" // (4 (5))
+    "|"
+      "( returned (-?[0-9]+)( errno=([0-9]+)[^\r\n]*)?)" // (6 (7) (8 (9)))
+    ")" // )
+    "[\r\n]" // EOL match necessary to match optional parts above
+  "|"
+    "===== \\[([A-Z ]*[A-Z])\\] DATA START " // (10)
+  ")"; // )
+
+  // Compile regex
+  regex_t rex;
+  if (compileregex(&rex, pattern, REG_EXTENDED)) {
+    errno = EIO; return -1;
+  }
+
+  // Parse buffer
+  const char * errmsg = 0;
+  int i = -1, state = 0, lineno = 1;
+  for (const char * line = buffer; *line; line = nextline(line, lineno)) {
+    // Match line
+    if (!(line[0] == 'R' || line[0] == '='))
+      continue;
+    const int nmatch = 1+10;
+    regmatch_t match[nmatch];
+    if (regexec(&rex, line, nmatch, match, 0))
+      continue;
+
+    char cmdname[40];
+    if (matchcpy(cmdname, sizeof(cmdname), line, match[2])) { // "REPORT-IOCTL:... Command=%s ..."
+      int nc = name2command(cmdname);
+      if (nc < 0) {
+        errmsg = "Unknown ATA command name"; break;
+      }
+      if (match[7].rm_so < 0) { // "returned %d"
+        // Start of command
+        if (!(state == 0 || state == 2)) {
+          errmsg = "Missing REPORT-IOCTL result"; break;
+        }
+        if (++i >= max_num_parsed_commands) {
+          errmsg = "Too many ATA commands"; break;
+        }
+        parsed_command_table[i].command = (smart_command_set)nc;
+        parsed_command_table[i].select = matchtoi(line, match[5], 0); // "InputParameter=%d"
+        state = 1;
+      }
+      else {
+        // End of command
+        if (!(state == 1 && (int)parsed_command_table[i].command == nc)) {
+          errmsg = "Missing REPORT-IOCTL start"; break;
+        }
+        parsed_command_table[i].retval = matchtoi(line, match[7], -1); // "returned %d"
+        parsed_command_table[i].errval = matchtoi(line, match[9], 0); // "errno=%d"
+        state = 2;
+      }
+    }
+    else if (matchcpy(cmdname, sizeof(cmdname), line, match[10])) { // "===== [%s] DATA START "
+      // Start of sector hexdump
+      int nc = name2command(cmdname);
+      if (!(state == (nc == WRITE_LOG ? 1 : 2) && (int)parsed_command_table[i].command == nc)) {
+          errmsg = "Unexpected DATA START"; break;
+      }
+      line = nextline(line, lineno);
+      char * data = (char *)malloc(512);
+      unsigned j;
+      for (j = 0; j < 32; j++) {
+        unsigned b[16];
+        unsigned u1, u2; int n1 = -1;
+        if (!(sscanf(line, "%3u-%3u: "
+                        "%2x %2x %2x %2x %2x %2x %2x %2x "
+                        "%2x %2x %2x %2x %2x %2x %2x %2x%n",
+                     &u1, &u2,
+                     b+ 0, b+ 1, b+ 2, b+ 3, b+ 4, b+ 5, b+ 6, b+ 7,
+                     b+ 8, b+ 9, b+10, b+11, b+12, b+13, b+14, b+15, &n1) == 18
+              && n1 >= 56 && u1 == j*16 && u2 == j*16+15))
+          break;
+        for (unsigned k = 0; k < 16; k++)
+          data[j*16+k] = b[k];
+        line = nextline(line, lineno);
+      }
+      if (j < 32) {
+        free(data);
+        errmsg = "Incomplete sector hex dump"; break;
+      }
+      parsed_command_table[i].data = data;
+      if (nc != WRITE_LOG)
+        state = 0;
+    }
+  }
+
+  if (!(state == 0 || state == 2))
+    errmsg = "Missing REPORT-IOCTL result";
+
+  if (!errmsg && i < 0)
+    errmsg = "No information found";
+
+  num_parsed_commands = i+1;
+  next_replay_command = 0;
+  replay_out_of_sync = false;
+
+  if (errmsg) {
+    pout("%s(%d): Syntax error: %s\n", pathname, lineno, errmsg);
+    errno = EIO;
+    parsedev_close(0);
+    return -1;
+  }
+  return 0;
+}
+
+// Report warnings and free command table 
+void parsedev_close(int /*fd*/)
+{
+  if (replay_out_of_sync)
+      pout("REPLAY-IOCTL: Warning: commands replayed out of sync\n");
+  else if (next_replay_command != 0)
+      pout("REPLAY-IOCTL: Warning: %d command(s) not replayed\n", num_parsed_commands-next_replay_command);
+
+  for (int i = 0; i < num_parsed_commands; i++) {
+    if (parsed_command_table[i].data) {
+      free(parsed_command_table[i].data); parsed_command_table[i].data = 0;
+    }
+  }
+  num_parsed_commands = 0;
+}
+
+// Simulate ATA command from command table
+static int parsedev_command_interface(int /*fd*/, smart_command_set command, int select, char * data)
+{
+  // Find command, try round-robin of out of sync
+  int i = next_replay_command;
+  for (int j = 0; ; j++) {
+    if (j >= num_parsed_commands) {
+      pout("REPLAY-IOCTL: Warning: Command not found\n");
+      errno = ENOSYS;
+      return -1;
+    }
+    if (parsed_command_table[i].command == command && parsed_command_table[i].select == select)
+      break;
+    if (!replay_out_of_sync) {
+      replay_out_of_sync = true;
+      pout("REPLAY-IOCTL: Warning: Command #%d is out of sync\n", i+1);
+    }
+    if (++i >= num_parsed_commands)
+      i = 0;
+  }
+  next_replay_command = i;
+  if (++next_replay_command >= num_parsed_commands)
+    next_replay_command = 0;
+
+  // Return command data
+  switch (command) {
+    case IDENTIFY:
+    case PIDENTIFY:
+    case READ_VALUES:
+    case READ_THRESHOLDS:
+    case READ_LOG:
+      if (parsed_command_table[i].data)
+        memcpy(data, parsed_command_table[i].data, 512);
+      break;
+    case WRITE_LOG:
+      if (!(parsed_command_table[i].data && !memcmp(data, parsed_command_table[i].data, 512)))
+        pout("REPLAY-IOCTL: Warning: WRITE LOG data does not match\n");
+      break;
+    case CHECK_POWER_MODE:
+      data[0] = (char)0xff;
+    default:
+      break;
+  }
+
+  if (parsed_command_table[i].errval)
+    errno = parsed_command_table[i].errval;
+  return parsed_command_table[i].retval;
+}
diff --git a/sm5/atacmds.h b/sm5/atacmds.h
index 2ce48fed1034125d8d5860b0f87be96704e84f53..5f69c9c4b82e5519eb7fe25b17d6c939c75e36a9 100644
--- a/sm5/atacmds.h
+++ b/sm5/atacmds.h
@@ -25,7 +25,7 @@
 #ifndef ATACMDS_H_
 #define ATACMDS_H_
 
-#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.88 2007/02/27 09:40:02 chrfranke Exp $\n"
+#define ATACMDS_H_CVSID "$Id: atacmds.h,v 1.89 2007/07/26 20:58:50 chrfranke Exp $\n"
 
 // Macro to check expected size of struct at compile time using a
 // dummy typedef.  On size mismatch, compiler reports a negative array
@@ -629,6 +629,11 @@ int ata_command_interface(int device, smart_command_set command, int select, cha
 int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
 int marvell_command_interface(int device, smart_command_set command, int select, char *data);
 int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
+
+// "smartctl -r ataioctl,2 ..." output parser pseudo-device
+int parsedev_open(const char * name);
+void parsedev_close(int fd);
+
 // Optional functions of os_*.c
 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
 // Return true if OS caches the ATA identify sector
diff --git a/sm5/smartctl.8.in b/sm5/smartctl.8.in
index 9ac278111ae47282c9689bedc4adeec7b19040cc..40e64d3f19f7424a27da3045e3a0e3e0c0308830 100644
--- a/sm5/smartctl.8.in
+++ b/sm5/smartctl.8.in
@@ -1,7 +1,7 @@
 .ig
  Copyright (C) 2002-7 Bruce Allen <smartmontools-support@lists.sourceforge.net>
 
- $Id: smartctl.8.in,v 1.100 2007/07/22 19:38:44 chrfranke Exp $
+ $Id: smartctl.8.in,v 1.101 2007/07/26 20:58:50 chrfranke Exp $
  
  This program is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the Free
@@ -115,6 +115,10 @@ See "WINDOWS NT4/2000/XP/2003/Vista" above.
 .IP \fBOS/2,eComStation\fP: 9
 Use the form \fB"/dev/hd[a\-z]"\fP for IDE/ATA devices.
 .PP
+if \'\-\' is specified as the device path, \fBsmartctl\fP reads and
+interprets it's own debug output from standard input.
+See \'\-r ataioctl\' below for details.
+.PP
 Based on the device path, \fBsmartctl\fP will guess the device type
 (ATA or SCSI).  If necessary, the \'\-d\' option can be used to over\-ride
 this guess
@@ -403,6 +407,13 @@ the integer with no spaces.  For example,
 The default
 level is 1, so \'\-r ataioctl,1\' and \'\-r ataioctl\' are equivalent.
 
+For testing purposes, the output of \'\-r ataioctl,2\' can later be parsed
+by \fBsmartctl\fP itself if \'\-\' is used as device path argument.
+The ATA command input parameters, sector data and return values are
+reconstructed from the debug report read from stdin.
+Then \fBsmartctl\fP internally simulates an ATA device with the same
+behaviour. This is does not work for SCSI devices yet.
+
 .TP
 .B \-n POWERMODE, \-\-nocheck=POWERMODE
 Specifieds if \fBsmartctl\fP should exit before performing any checks
@@ -1488,7 +1499,7 @@ these documents may be found in the References section of the
 
 .SH
 CVS ID OF THIS PAGE:
-$Id: smartctl.8.in,v 1.100 2007/07/22 19:38:44 chrfranke Exp $
+$Id: smartctl.8.in,v 1.101 2007/07/26 20:58:50 chrfranke Exp $
 .\" Local Variables:	         
 .\" mode: nroff         
 .\" End:
diff --git a/sm5/smartctl.cpp b/sm5/smartctl.cpp
index 2fdbec9059deacc3247bbf86e774d42f746898ce..d29d26c3ec2e89c4f5f2f0ab2f54d2fbeef56e46 100644
--- a/sm5/smartctl.cpp
+++ b/sm5/smartctl.cpp
@@ -50,7 +50,7 @@
 extern const char *os_solaris_ata_s_cvsid;
 #endif
 extern const char *atacmdnames_c_cvsid, *atacmds_c_cvsid, *ataprint_c_cvsid, *knowndrives_c_cvsid, *os_XXXX_c_cvsid, *scsicmds_c_cvsid, *scsiprint_c_cvsid, *utility_c_cvsid;
-const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.165 2007/07/21 20:59:41 chrfranke Exp $"
+const char* smartctl_c_cvsid="$Id: smartctl.cpp,v 1.166 2007/07/26 20:58:50 chrfranke Exp $"
 ATACMDS_H_CVSID ATAPRINT_H_CVSID CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID KNOWNDRIVES_H_CVSID SCSICMDS_H_CVSID SCSIPRINT_H_CVSID SMARTCTL_H_CVSID UTILITY_H_CVSID;
 
 // This is a block containing all the "control variables".  We declare
@@ -960,6 +960,16 @@ int main (int argc, char **argv){
 
   device = argv[argc-1];
 
+  // Device name "-": Parse "smartctl -r ataioctl,2 ..." output
+  if (!strcmp(device,"-")) {
+    if (con->controller_type != CONTROLLER_UNKNOWN) {
+      pout("Smartctl: -d option is not allowed in conjunction with device name \"-\".\n");
+      UsageSummary();
+      return FAILCMD;
+    }
+    con->controller_type = CONTROLLER_PARSEDEV;
+  }
+
   // If use has specified 3ware controller, determine which interface 
   if (con->controller_type == CONTROLLER_3WARE) {
     con->controller_type=guess_device_type(device);
@@ -1002,7 +1012,10 @@ int main (int argc, char **argv){
   // present (e.g. with st).  Opening is retried O_RDONLY if read-only
   // media prevents opening O_RDWR (it cannot happen for scsi generic
   // devices, but it can for the others).
-  fd = deviceopen(device, mode);
+  if (con->controller_type != CONTROLLER_PARSEDEV)
+    fd = deviceopen(device, mode);
+  else
+    fd = parsedev_open(device);
   if (fd<0) {
     char errmsg[256];
     snprintf(errmsg,256,"Smartctl open device: %s failed",argv[argc-1]);
@@ -1034,5 +1047,10 @@ int main (int argc, char **argv){
     break;
   }
   
+  if (con->controller_type != CONTROLLER_PARSEDEV)
+    deviceclose(fd);
+  else
+    parsedev_close(fd);
+
   return retval;
 }
diff --git a/sm5/utility.h b/sm5/utility.h
index 93b3a8fc0b72242fb1bde917f20d673647db4776..e6cf2791b8b63cd119018639fbdde9125db13819 100644
--- a/sm5/utility.h
+++ b/sm5/utility.h
@@ -25,7 +25,7 @@
 #ifndef UTILITY_H_
 #define UTILITY_H_
 
-#define UTILITY_H_CVSID "$Id: utility.h,v 1.49 2007/02/03 15:14:14 chrfranke Exp $\n"
+#define UTILITY_H_CVSID "$Id: utility.h,v 1.50 2007/07/26 20:58:50 chrfranke Exp $\n"
 
 #include <time.h>
 #include <sys/types.h> // for regex.h (according to POSIX)
@@ -193,5 +193,6 @@ void MsecToText(unsigned int msec, char *txt);
 #define CONTROLLER_SAT         	        0x08  // SATA device behind a SCSI ATA Translation (SAT) layer
 #define CONTROLLER_HPT                  0x09  // SATA drives behind HighPoint Raid controllers
 #define CONTROLLER_CCISS		0x10  // CCISS controller 
+#define CONTROLLER_PARSEDEV             0x11  // "smartctl -r ataioctl,2 ..." output parser pseudo-device
 
 #endif