other half of the sg_get_category_sense_str() changes

git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@578 6180dd3e-e324-4e3e-922d-17de1ae2f315
diff --git a/ChangeLog b/ChangeLog
index 537e978..2e0fe5f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,7 +2,7 @@
 some description at the top of its ".c" file. All utilities in the main
 directory have their own "man" pages. There is also a sg3_utils man page.
 
-Changelog for sg3_utils-1.39 [20140515] [svn: r577]
+Changelog for sg3_utils-1.39 [20140517] [svn: r578]
   - sg_ses: add --eiioe=auto|force option
     - fix AES dpage element indexing problems
     - add --readonly option
diff --git a/README b/README
index 750cbde..240bd0c 100644
--- a/README
+++ b/README
@@ -403,4 +403,4 @@
 
 
 Douglas Gilbert
-4th May 2014
+17th May 2014
diff --git a/debian/changelog b/debian/changelog
index c18078e..6f7481f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,7 +2,7 @@
 
   * New upstream version
 
- -- Douglas Gilbert <[email protected]>  Sun, 04 May 2014 22:00:00 -0400
+ -- Douglas Gilbert <[email protected]>  Sat, 17 May 2014 01:00:00 +0200
 
 sg3-utils (1.38-0.1) unstable; urgency=low
 
diff --git a/doc/sg_persist.8 b/doc/sg_persist.8
index 8acfedf..0390452 100644
--- a/doc/sg_persist.8
+++ b/doc/sg_persist.8
@@ -87,7 +87,8 @@
 \fB\-H\fR, \fB\-\-hex\fR
 the response to a valid PRIN sub\-command will be output in hexadecimal.
 By default (i.e. without this option) if the PRIN sub\-command is recognised
-then the response will be decoded as per SPC\-4.
+then the response will be decoded as per SPC\-4. May be used more than
+once for more hex and less text.
 .TP
 \fB\-i\fR, \fB\-\-in\fR
 specify that a SCSI PERSISTENT RESERVE IN command is required. This
diff --git a/examples/sg_persist_tst.sh b/examples/sg_persist_tst.sh
index e2640e5..4d68248 100755
--- a/examples/sg_persist_tst.sh
+++ b/examples/sg_persist_tst.sh
@@ -3,7 +3,7 @@
 # in the sg3_utils package. This script works as expected on the
 # author's Fujitsu MAM3184, Seagate ST373455 and ST9146803SS disks.
 #
-#  Version 1.7 20140508
+#  Version 1.8 20140516
 
 # N.B. make sure the device name is correct for your environment.
 
@@ -31,6 +31,8 @@
   case "$opt" in
     h|-help) usage ; exit 0 ;;
     s|-second) kk=${key2} ;;
+    vvv) verbose="-vvv" ;;
+    vv) verbose="-vv" ;;
     v|-verbose) verbose="-v" ;;
     *) echo "Unknown option: -$opt " ; exit 1 ;;
   esac
diff --git a/sg3_utils.spec b/sg3_utils.spec
index b663c5b..975532d 100644
--- a/sg3_utils.spec
+++ b/sg3_utils.spec
@@ -79,7 +79,7 @@
 %{_libdir}/*.la
 
 %changelog
-* Sun May 04 2014 - dgilbert at interlog dot com
+* Sat May 17 2014 - dgilbert at interlog dot com
 - track t10 changes
   * sg3_utils-1.39
 
diff --git a/src/sg_compare_and_write.c b/src/sg_compare_and_write.c
index 3097de1..7a59b74 100644
--- a/src/sg_compare_and_write.c
+++ b/src/sg_compare_and_write.c
@@ -1,5 +1,5 @@
 /*
-*  Copyright (c) 2012-2013, Kaminario Technologies LTD
+*  Copyright (c) 2012-2014, Kaminario Technologies LTD
 *  All rights reserved.
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
@@ -51,7 +51,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_pt.h"
 
-static const char * version_str = "1.08 20130824";
+static const char * version_str = "1.09 20140516";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_NUM_BLOCKS (1)
@@ -382,13 +382,6 @@
                 ;
         else if (-2 == ret) {
                 switch (sense_cat) {
-                case SG_LIB_CAT_NOT_READY:
-                case SG_LIB_CAT_INVALID_OP:
-                case SG_LIB_CAT_UNIT_ATTENTION:
-                case SG_LIB_CAT_ILLEGAL_REQ:
-                case SG_LIB_CAT_ABORTED_COMMAND:
-                        ret = sense_cat;
-                        break;
                 case SG_LIB_CAT_RECOVERED:
                 case SG_LIB_CAT_NO_SENSE:
                         ret = 0;
@@ -421,7 +414,7 @@
                                 fprintf(stderr, "Miscompare reported\n");
                         break;
                 default:
-                        ret = -1;
+                        ret = sense_cat;
                         break;
                 }
         } else
@@ -476,121 +469,106 @@
         int devfd = -1;
         unsigned char * wrkBuff = NULL;
         struct opts_t opts;
+        struct opts_t * op;
 
-        memset(&opts, 0, sizeof(opts));
-        res = parse_args(argc, argv, &opts);
+        op = &opts;
+        memset(op, 0, sizeof(opts));
+        res = parse_args(argc, argv, op);
         if (res != 0) {
                 fprintf(stderr, "Failed parsing args\n");
                 goto out;
         }
 
-        if (opts.verbose) {
+        if (op->verbose) {
                 fprintf(stderr, "Running COMPARE AND WRITE command with the "
-                        "following options:\n  in=%s ", opts.ifn);
-                if (opts.wfn_given)
-                        fprintf(stderr, "inw=%s ", opts.wfn);
+                        "following options:\n  in=%s ", op->ifn);
+                if (op->wfn_given)
+                        fprintf(stderr, "inw=%s ", op->wfn);
                 fprintf(stderr, "device=%s\n  lba=0x%" PRIx64
                         " num_blocks=%d xfer_len=%d timeout=%d\n",
-                        opts.device_name, opts.lba, opts.numblocks,
-                        opts.xfer_len, opts.timeout);
+                        op->device_name, op->lba, op->numblocks,
+                        op->xfer_len, op->timeout);
         }
-        ifn_stdin = ((1 == strlen(opts.ifn)) && ('-' == opts.ifn[0]));
-        infd = open_if(opts.ifn, ifn_stdin);
+        ifn_stdin = ((1 == strlen(op->ifn)) && ('-' == op->ifn[0]));
+        infd = open_if(op->ifn, ifn_stdin);
         if (infd < 0) {
                 res = -infd;
                 goto out;
         }
-        if (opts.wfn_given) {
-                if ((1 == strlen(opts.wfn)) && ('-' == opts.wfn[0])) {
+        if (op->wfn_given) {
+                if ((1 == strlen(op->wfn)) && ('-' == op->wfn[0])) {
                         fprintf(stderr, ME "don't allow stdin for write "
                                 "file\n");
                         res = SG_LIB_FILE_ERROR;
                         goto out;
                 }
-                wfd = open_if(opts.wfn, 0);
+                wfd = open_if(op->wfn, 0);
                 if (wfd < 0) {
                         res = -wfd;
                         goto out;
                 }
         }
 
-        devfd = open_dev(opts.device_name, opts.verbose);
+        devfd = open_dev(op->device_name, op->verbose);
         if (devfd < 0) {
                 res = -devfd;
                 goto out;
         }
 
-        wrkBuff = (unsigned char *)malloc(opts.xfer_len);
+        wrkBuff = (unsigned char *)malloc(op->xfer_len);
         if (0 == wrkBuff) {
                 fprintf(stderr, "Not enough user memory\n");
                 res = SG_LIB_CAT_OTHER;
                 goto out;
         }
 
-        if (opts.wfn_given) {
-                half_xlen = opts.xfer_len / 2;
+        if (op->wfn_given) {
+                half_xlen = op->xfer_len / 2;
                 res = read(infd, wrkBuff, half_xlen);
                 if (res < 0) {
-                        fprintf(stderr, "Could not read from %s", opts.ifn);
+                        fprintf(stderr, "Could not read from %s", op->ifn);
                         goto out;
                 } else if (res < half_xlen) {
                         fprintf(stderr, "Read only %d bytes (expected %d) "
-                                "from %s\n", res, half_xlen, opts.ifn);
+                                "from %s\n", res, half_xlen, op->ifn);
                         goto out;
                 }
                 res = read(wfd, wrkBuff + half_xlen, half_xlen);
                 if (res < 0) {
-                        fprintf(stderr, "Could not read from %s", opts.wfn);
+                        fprintf(stderr, "Could not read from %s", op->wfn);
                         goto out;
                 } else if (res < half_xlen) {
                         fprintf(stderr, "Read only %d bytes (expected %d) "
-                                "from %s\n", res, half_xlen, opts.wfn);
+                                "from %s\n", res, half_xlen, op->wfn);
                         goto out;
                 }
         } else {
-                res = read(infd, wrkBuff, opts.xfer_len);
+                res = read(infd, wrkBuff, op->xfer_len);
                 if (res < 0) {
-                        fprintf(stderr, "Could not read from %s", opts.ifn);
+                        fprintf(stderr, "Could not read from %s", op->ifn);
                         goto out;
-                } else if (res < opts.xfer_len) {
+                } else if (res < op->xfer_len) {
                         fprintf(stderr, "Read only %d bytes (expected %d) "
-                                "from %s\n", res, opts.xfer_len, opts.ifn);
+                                "from %s\n", res, op->xfer_len, op->ifn);
                         goto out;
                 }
         }
-        res = sg_compare_and_write(devfd, wrkBuff, opts.numblocks, opts.lba,
-                opts.xfer_len, opts.flags, !opts.quiet, opts.verbose);
+        res = sg_compare_and_write(devfd, wrkBuff, op->numblocks, op->lba,
+                op->xfer_len, op->flags, !op->quiet, op->verbose);
 
 out:
         if (0 != res) {
+                char b[80];
+
                 switch (res) {
-                case SG_LIB_CAT_ILLEGAL_REQ:
-                        fprintf(stderr, ME "SCSI COMPARE AND WRITE: "
-                                "illegal request\n");
-                        break;
-                case SG_LIB_CAT_INVALID_OP:
-                        fprintf(stderr, ME "SCSI COMPARE AND WRITE: "
-                                "invalid opcode\n");
-                        break;
-                case SG_LIB_CAT_NOT_READY:
-                        fprintf(stderr, ME "device not ready\n");
-                        break;
-                case SG_LIB_CAT_UNIT_ATTENTION:
-                        fprintf(stderr, ME "SCSI COMPARE AND WRITE unit "
-                                "attention\n");
-                        break;
-                case SG_LIB_CAT_ABORTED_COMMAND:
-                        fprintf(stderr, ME "SCSI COMPARE AND WRITE command "
-                                "aborted\n");
-                        break;
                 case SG_LIB_CAT_MEDIUM_HARD:
                 case SG_LIB_CAT_MISCOMPARE:
                 case SG_LIB_FILE_ERROR:
                         break;  /* already reported */
                 default:
-                        res = SG_LIB_CAT_OTHER;
-                        fprintf(stderr, ME "SCSI COMPARE AND WRITE failed, "
-                                "add '-vv' for more information\n");
+                        sg_get_category_sense_str(res, sizeof(b), b,
+                                                  op->verbose);
+                        fprintf(stderr, ME "SCSI COMPARE AND WRITE: %s\n", b);
                         break;
                 }
         }
diff --git a/src/sg_dd.c b/src/sg_dd.c
index 5475a60..13e1847 100644
--- a/src/sg_dd.c
+++ b/src/sg_dd.c
@@ -1,7 +1,7 @@
 /* A utility program for copying files. Specialised for "files" that
  * represent devices that understand the SCSI command set.
  *
- * Copyright (C) 1999 - 2013 D. Gilbert and P. Allworth
+ * Copyright (C) 1999 - 2014 D. Gilbert and P. Allworth
  * 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 Software Foundation; either version 2, or (at your option)
@@ -58,7 +58,7 @@
 #include "sg_cmds_extra.h"
 #include "sg_io_linux.h"
 
-static const char * version_str = "5.79 20131014";
+static const char * version_str = "5.80 20140516";
 
 
 #define ME "sg_dd: "
diff --git a/src/sg_emc_trespass.c b/src/sg_emc_trespass.c
index 4a9e06f..eeb9ebf 100644
--- a/src/sg_emc_trespass.c
+++ b/src/sg_emc_trespass.c
@@ -2,7 +2,7 @@
  * LUN ownership from one Service-Processor to this one on an EMC
  * CLARiiON and potentially other devices.
  *
- * Copyright (C) 2004 Lars Marowsky-Bree <[email protected]>
+ * Copyright (C) 2004-2014 Lars Marowsky-Bree <[email protected]>
  *
  * Based on sg_start.c; credits from there also apply.
  * Minor modifications for sg_lib, D. Gilbert 2004/10/19
@@ -28,7 +28,7 @@
 #include "sg_cmds_basic.h"
 
 
-static const char * version_str = "0.18 20130507";
+static const char * version_str = "0.19 20140516";
 
 static int debug = 0;
 
@@ -54,6 +54,7 @@
                   0xff,                 /* Trespass target */
         };
         int res;
+        char b[80];
 
         if (hr) {       /* override Trespass code + Honor reservation bit */
                 short_trespass_pg[6] = 0x01;
@@ -87,9 +88,9 @@
                 fprintf(stderr, "unit attention\n");
                 break;
         default:
-                if (debug)
-                        fprintf(stderr, "%s trespass failed\n",
-                                        short_cmd ? "short" : "long");
+                sg_get_category_sense_str(res, sizeof(b), b, debug);
+                fprintf(stderr, "%s trespass failed: %s\n",
+                        (short_cmd ? "short" : "long"), b);
                 break;
         }
         return res;
diff --git a/src/sg_format.c b/src/sg_format.c
index b7414a7..e84846e 100644
--- a/src/sg_format.c
+++ b/src/sg_format.c
@@ -47,7 +47,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "1.25 20140428";
+static const char * version_str = "1.26 20140516";
 
 #define RW_ERROR_RECOVERY_PAGE 1  /* every disk should have one */
 #define FORMAT_DEV_PAGE 3         /* Format Device Mode Page [now obsolete] */
@@ -187,6 +187,7 @@
         const char INIT_PATTERN_DESC_SZ = 4;
         unsigned char fmt_pl[LO_FORMAT_HEADER_SZ + INIT_PATTERN_DESC_SZ];
         unsigned char reqSense[MAX_BUFF_SZ];
+        char b[80];
 
         memset(fmt_pl, 0, sizeof(fmt_pl));
         longlist = (pie > 0);
@@ -215,31 +216,11 @@
                                 cmplst, 0 /* DEFECT_LIST_FORMAT */,
                                 (immed ? SHORT_TIMEOUT : FORMAT_TIMEOUT),
                                 fmt_pl, fmt_pl_sz, 1, verbose);
-        switch (res) {
-        case 0:
-                break;
-        case SG_LIB_CAT_NOT_READY:
-                fprintf(stderr, "Format command, device not ready\n");
-                break;
-        case SG_LIB_CAT_INVALID_OP:
-                fprintf(stderr, "Format command not supported\n");
-                break;
-        case SG_LIB_CAT_ILLEGAL_REQ:
-                fprintf(stderr, "Format command, illegal parameter\n");
-                break;
-        case SG_LIB_CAT_UNIT_ATTENTION:
-                fprintf(stderr, "Format command, unit attention\n");
-                break;
-        case SG_LIB_CAT_ABORTED_COMMAND:
-                fprintf(stderr, "Format command, aborted command\n");
-                break;
-        default:
-                fprintf(stderr, "Format command failed\n");
-                break;
-        }
-        if (res)
+        if (res) {
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "Format command: %s\n", b);
                 return res;
-
+        }
         if (! immed)
                 return 0;
 
@@ -306,19 +287,8 @@
                                       1, verbose);
             if (res) {
                 ret = res;
-                if (SG_LIB_CAT_INVALID_OP == res)
-                    fprintf(stderr, "Request Sense command not supported\n");
-                else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                    fprintf(stderr, "bad field in Request Sense cdb\n");
-                else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                    fprintf(stderr, "Request Sense, aborted command\n");
-                else {
-                    fprintf(stderr, "Request Sense command unexpectedly "
-                            "failed\n");
-                    if (0 == verbose)
-                        fprintf(stderr, "    try the '-v' option for "
-                                "more information\n");
-                }
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "Request Sense command: %s\n", b);
                 break;
             }
             /* "Additional sense length" same in descriptor and fixed */
@@ -358,6 +328,7 @@
         unsigned char resp_buff[RCAP_REPLY_LEN];
         unsigned int last_blk_addr, block_size;
         uint64_t llast_blk_addr;
+        char b[80];
 
         if (do_16) {
                 res = sg_ll_readcap_16(fd, 0 /* pmi */, 0 /* llba */,
@@ -417,18 +388,8 @@
                         return (int)block_size;
                 }
         }
-        if (SG_LIB_CAT_NOT_READY == res)
-                fprintf(stderr, "READ CAPACITY (%d): device not ready\n",
-                        (do_16 ? 16 : 10));
-        else if (SG_LIB_CAT_INVALID_OP == res)
-                fprintf(stderr, "READ CAPACITY (%d) not supported\n",
-                        (do_16 ? 16 : 10));
-        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                fprintf(stderr, "bad field in READ CAPACITY (%d) "
-                        "cdb\n", (do_16 ? 16 : 10));
-        else if (verbose)
-                fprintf(stderr, "READ CAPACITY (%d) failed "
-                        "[res=%d]\n", (do_16 ? 16 : 10), res);
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "READ CAPACITY (%d): %s\n", (do_16 ? 16 : 10), b);
         return -1;
 }
 
@@ -462,6 +423,7 @@
         int early = 0;
         const char * device_name = NULL;
         char pdt_name[64];
+        char b[80];
         struct sg_simple_inquiry_resp inq_out;
         int ret = 0;
 
@@ -679,22 +641,7 @@
                                          MAX_BUFF_SZ, 1, verbose);
         ret = res;
         if (res) {
-                if (SG_LIB_CAT_NOT_READY == res)
-                        fprintf(stderr, "MODE SENSE (%d) command, device "
-                                "not ready\n", (mode6 ? 6 : 10));
-                else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-                        fprintf(stderr, "MODE SENSE (%d) command, unit "
-                                "attention\n", (mode6 ? 6 : 10));
-                else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                        fprintf(stderr, "MODE SENSE (%d) command, aborted "
-                                "command\n", (mode6 ? 6 : 10));
-                else if (SG_LIB_CAT_INVALID_OP == res) {
-                        fprintf(stderr, "MODE SENSE (%d) command is not "
-                                "supported\n", (mode6 ? 6 : 10));
-                        fprintf(stderr, "    try again %s the '--six' "
-                                "option\n", (mode6 ? "without" : "with"));
-
-                } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
+                if (SG_LIB_CAT_ILLEGAL_REQ == res) {
                         if (long_lba && (! mode6))
                                 fprintf(stderr, "bad field in MODE SENSE "
                                         "(%d) [longlba flag not supported?]"
@@ -703,12 +650,14 @@
                                 fprintf(stderr, "bad field in MODE SENSE "
                                         "(%d) [mode_page %d not supported?]"
                                         "\n", (mode6 ? 6 : 10), mode_page);
-                } else
-                        fprintf(stderr, "MODE SENSE (%d) command failed\n",
-                                (mode6 ? 6 : 10));
-                        if (0 == verbose)
-                                fprintf(stderr, "    try '-v' for more "
-                                        "information\n");
+                } else {
+                        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                        fprintf(stderr, "MODE SENSE (%d) command: %s\n",
+                                (mode6 ? 6 : 10), b);
+                }
+                if (0 == verbose)
+                        fprintf(stderr, "    try '-v' for more "
+                                "information\n");
                 goto out;
         }
         if (mode6) {
@@ -854,24 +803,8 @@
                                                   dbuff, calc_len, 1, verbose);
                 ret = res;
                 if (res) {
-                        if (SG_LIB_CAT_NOT_READY == res)
-                                fprintf(stderr, "MODE SELECT command, "
-                                        "device not ready\n");
-                        else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-                                fprintf(stderr, "MODE SELECT command, "
-                                        "unit attention\n");
-                        else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                                fprintf(stderr, "MODE SELECT command, "
-                                        "aborted command\n");
-                        else if (SG_LIB_CAT_INVALID_OP == res)
-                                fprintf(stderr, "MODE SELECT (%d) command is "
-                                        "not supported\n", (mode6 ? 6 : 10));
-                        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                                fprintf(stderr, "bad field in MODE SELECT "
-                                        "(%d)\n", (mode6 ? 6 : 10));
-                        else
-                                fprintf(stderr, "MODE SELECT (%d) command "
-                                        "failed\n", (mode6 ? 6 : 10));
+                        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                        fprintf(stderr, "MODE SELECT command: %s\n", b);
                         if (0 == verbose)
                                 fprintf(stderr, "    try '-v' for "
                                         "more information\n");
diff --git a/src/sg_get_config.c b/src/sg_get_config.c
index 0d0ebbd..4125b79 100644
--- a/src/sg_get_config.c
+++ b/src/sg_get_config.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2013 Douglas Gilbert.
+ * Copyright (c) 2004-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -27,7 +27,7 @@
 
 */
 
-static const char * version_str = "0.38 20130507";    /* mmc6r02 */
+static const char * version_str = "0.39 20140516";    /* mmc6r02 */
 
 #define MX_ALLOC_LEN 8192
 #define NAME_BUFF_SZ 64
@@ -1098,16 +1098,11 @@
         else
             decode_config(resp_buffer, sizeof(resp_buffer), len, brief,
                           inner_hex);
-    } else if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Get Configuration command not supported\n");
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "field in Get Configuration command illegal\n");
-    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-        fprintf(stderr, "Get Configuration received unit attention\n");
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Get Configuration: aborted command\n");
-    else {
-        fprintf(stderr, "Get Configuration command failed\n");
+    } else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Get Configuration command: %s\n", b);
         if (0 == verbose)
             fprintf(stderr, "    try '-v' option for more information\n");
     }
diff --git a/src/sg_ident.c b/src/sg_ident.c
index 36c45b2..cfd83cc 100644
--- a/src/sg_ident.c
+++ b/src/sg_ident.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2013 Douglas Gilbert.
+ * Copyright (c) 2005-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -27,7 +27,7 @@
  * DEVICE IDENTIFIER and SET DEVICE IDENTIFIER prior to spc4r07.
  */
 
-static const char * version_str = "1.11 20130603";
+static const char * version_str = "1.12 20140516";
 
 #define ME "sg_ident: "
 
@@ -110,6 +110,7 @@
 {
     int sg_fd, res, c, ii_len;
     unsigned char rdi_buff[REPORT_ID_INFO_SANITY_LEN + 4];
+    char b[80];
     unsigned char * ucp = NULL;
     int ascii = 0;
     int do_clear = 0;
@@ -229,27 +230,10 @@
             res = sg_ll_set_id_info(sg_fd, itype, rdi_buff, 0, 1, verbose);
         if (res) {
             ret = res;
-            if (SG_LIB_CAT_NOT_READY == res)
-                fprintf(stderr, "Set identifying information command, device "
-                        "not ready\n");
-            else if (SG_LIB_CAT_INVALID_OP == res)
-                fprintf(stderr, "Set identifying information command not "
-                        "supported\n");
-            else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-                fprintf(stderr, "Set identifying information, unit "
-                        "attention\n");
-            else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                fprintf(stderr, "Set identifying information, aborted "
-                        "command\n");
-            else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                fprintf(stderr, "bad field in Set identifying information "
-                        "cdb including unsupported service action\n");
-            else {
-                fprintf(stderr, "Set identifying information command "
-                        "failed\n");
-                if (0 == verbose)
-                    fprintf(stderr, "    try '-v' for more information\n");
-            }
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "Set identifying information: %s\n", b);
+            if (0 == verbose)
+                fprintf(stderr, "    try '-v' for more information\n");
         }
     } else {    /* do report identifying information */
         res = sg_ll_report_id_info(sg_fd, itype, rdi_buff, 4, 1, verbose);
@@ -283,29 +267,10 @@
         } else
             ret = res;
         if (ret) {
-            if (SG_LIB_CAT_NOT_READY == ret)
-                fprintf(stderr, "Report identifying information command, "
-                        "device not ready\n");
-            else if (SG_LIB_CAT_UNIT_ATTENTION == ret)
-                fprintf(stderr, "Report identifying information, unit "
-                        "attention\n");
-            else if (SG_LIB_CAT_ABORTED_COMMAND == ret)
-                fprintf(stderr, "Report identifying information, aborted "
-                        "command\n");
-            else if (SG_LIB_CAT_INVALID_OP == ret)
-                fprintf(stderr, "Report identifying information command "
-                        "not supported\n");
-            else if (SG_LIB_CAT_ILLEGAL_REQ == ret)
-                fprintf(stderr, "bad field in Report identifying "
-                        "information cdb including unsupported service "
-                        "action\n");
-            else {
-                fprintf(stderr, "Report identifying information command "
-                        "failed\n");
-                if (0 == verbose)
-                    fprintf(stderr, "    try '-v' for more "
-                            "information\n");
-            }
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "Report identifying information: %s\n", b);
+            if (0 == verbose)
+                fprintf(stderr, "    try '-v' for more information\n");
         }
     }
 
diff --git a/src/sg_persist.c b/src/sg_persist.c
index 4ce3c1c..23d528d 100644
--- a/src/sg_persist.c
+++ b/src/sg_persist.c
@@ -380,6 +380,8 @@
     if (PRIN_RCAP_SA == op->prin_sa) {
         if (8 != pr_buff[1]) {
             pr2serr("Unexpected response for PRIN Report Capabilities\n");
+            if (op->hex)
+                dStrHex((const char *)pr_buff, pr_buff[1], 1);
             return SG_LIB_CAT_MALFORMED;
         }
         if (op->hex)
@@ -420,16 +422,22 @@
         add_len = ((pr_buff[4] << 24) | (pr_buff[5] << 16) |
                    (pr_buff[6] << 8) | pr_buff[7]);
         if (op->hex) {
-            printf("  PR generation=0x%x, ", pr_gen);
-            if (add_len <= 0)
-                printf("Additional length=%d\n", add_len);
-            if (add_len > ((int)sizeof(pr_buff) - 8)) {
-                printf("Additional length too large=%d, truncate\n",
-                       add_len);
-                dStrHex((const char *)(pr_buff + 8), sizeof(pr_buff) - 8, 1);
-            } else {
-                printf("Additional length=%d\n", add_len);
-                dStrHex((const char *)(pr_buff + 8), add_len, 1);
+            if (op->hex > 1)
+                dStrHex((const char *)pr_buff, add_len + 8,
+                        ((2 == op->hex) ? 1 : -1));
+            else {
+                printf("  PR generation=0x%x, ", pr_gen);
+                if (add_len <= 0)
+                    printf("Additional length=%d\n", add_len);
+                if (add_len > ((int)sizeof(pr_buff) - 8)) {
+                    printf("Additional length too large=%d, truncate\n",
+                           add_len);
+                    dStrHex((const char *)(pr_buff + 8), sizeof(pr_buff) - 8,
+                            1);
+                } else {
+                    printf("Additional length=%d\n", add_len);
+                    dStrHex((const char *)(pr_buff + 8), add_len, 1);
+                }
             }
         } else if (PRIN_RKEY_SA == op->prin_sa) {
             printf("  PR generation=0x%x, ", pr_gen);
diff --git a/src/sg_prevent.c b/src/sg_prevent.c
index 85cbab3..955a674 100644
--- a/src/sg_prevent.c
+++ b/src/sg_prevent.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2013 Douglas Gilbert.
+ * Copyright (c) 2004-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -24,7 +24,7 @@
  * given SCSI device.
  */
 
-static const char * version_str = "1.06 20130507";
+static const char * version_str = "1.07 20140516";
 
 #define ME "sg_prevent: "
 
@@ -139,23 +139,12 @@
     }
     res = sg_ll_prevent_allow(sg_fd, prevent, 1, verbose);
     ret = res;
-    if (0 == res)
-        ;
-    else if (SG_LIB_CAT_NOT_READY == res)
-        fprintf(stderr, "Device not ready\n");
-    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-        fprintf(stderr, "Unit attention\n");
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Aborted command\n");
-    else if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Prevent allow medium removal command not "
-                "supported\n");
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "Prevent allow medium removal, bad field in "
-                "command\n");
-    else
-        fprintf(stderr, "Prevent allow medium removal command failed\n");
+    if (res) {
+        char b[80];
 
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Prevent allow medium removal: %s\n", b);
+    }
     res = sg_cmds_close_device(sg_fd);
     if (res < 0) {
         fprintf(stderr, "close error: %s\n", safe_strerror(-res));
diff --git a/src/sg_rdac.c b/src/sg_rdac.c
index 41ec544..c147043 100644
--- a/src/sg_rdac.c
+++ b/src/sg_rdac.c
@@ -93,6 +93,7 @@
         unsigned char fail_paths_pg[118];
         struct rdac_legacy_page *rdac_page;
         int res;
+        char b[80];
 
         memset(fail_paths_pg, 0, 118);
         memcpy(fail_paths_pg, mode6_hdr, 4);
@@ -113,24 +114,9 @@
                 if (do_verbose)
                         fprintf(stderr, "fail paths successful\n");
                 break;
-        case SG_LIB_CAT_INVALID_OP:
-                fprintf(stderr, "fail paths page failed (Invalid opcode)\n");
-                break;
-        case SG_LIB_CAT_ILLEGAL_REQ:
-                fprintf(stderr, "fail paths page failed (illegal request)\n");
-                break;
-        case SG_LIB_CAT_NOT_READY:
-                fprintf(stderr, "fail paths page failed (not ready)\n");
-                break;
-        case SG_LIB_CAT_UNIT_ATTENTION:
-                fprintf(stderr, "fail paths page failed (unit attention)\n");
-                break;
-        case SG_LIB_CAT_ABORTED_COMMAND:
-                fprintf(stderr, "fail paths page failed (aborted command)\n");
-                break;
         default:
-                if (do_verbose)
-                        fprintf(stderr, "fail paths failed\n");
+                sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+                fprintf(stderr, "fail paths failed: %s\n", b);
                 break;
         }
 
@@ -142,6 +128,7 @@
         unsigned char fail_paths_pg[118];
         struct rdac_legacy_page *rdac_page;
         int res;
+        char b[80];
 
         memset(fail_paths_pg, 0, 118);
         memcpy(fail_paths_pg, mode6_hdr, 4);
@@ -164,25 +151,10 @@
                 if (do_verbose)
                         fprintf(stderr, "fail paths successful\n");
                 break;
-        case SG_LIB_CAT_INVALID_OP:
-                fprintf(stderr, "fail paths page failed (Invalid opcode)\n");
-                break;
-        case SG_LIB_CAT_NOT_READY:
-                fprintf(stderr, "fail paths page failed (not ready)\n");
-                break;
-        case SG_LIB_CAT_UNIT_ATTENTION:
-                fprintf(stderr, "fail paths page failed (unit attention)\n");
-                break;
-        case SG_LIB_CAT_ABORTED_COMMAND:
-                fprintf(stderr, "fail paths page failed (aborted command)\n");
-                break;
-        case SG_LIB_CAT_ILLEGAL_REQ:
-                fprintf(stderr, "fail lun %d page failed (illegal request)\n",
-                        lun);
-                break;
         default:
-                if (do_verbose)
-                        fprintf(stderr, "fail paths failed\n");
+                sg_get_category_sense_str(res, sizeof(b), b, do_verbose);
+                fprintf(stderr, "fail paths page (lun=%d) failed: %s\n", lun,
+                        b);
                 break;
         }
 
@@ -376,19 +348,25 @@
                         if (do_verbose)
                                 dump_mode_page(rsp_buff, rsp_buff[0]);
                         print_rdac_mode(rsp_buff);
+                } else {
+                        if (SG_LIB_CAT_INVALID_OP == res)
+                                fprintf(stderr, ">>>>>> try again without "
+                                        "the '-6' switch for a 10 byte MODE "
+                                        "SENSE command\n");
+                        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
+                                fprintf(stderr, "mode sense: invalid field "
+                                        "in cdb (perhaps subpages or page "
+                                        "control (PC) not supported)\n");
+                        else {
+                                char b[80];
+
+                                sg_get_category_sense_str(res, sizeof(b), b,
+                                                          do_verbose);
+                                fprintf(stderr, "mode sense failed: %s\n", b);
+                        }
                 }
         }
         ret = res;
-        if (SG_LIB_CAT_INVALID_OP == res)
-                fprintf(stderr, ">>>>>> try again without the '-6' "
-                        "switch for a 10 byte MODE SENSE command\n");
-        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                fprintf(stderr, "invalid field in cdb (perhaps subpages "
-                        "or page control (PC) not supported)\n");
-        else if (SG_LIB_CAT_NOT_READY == res)
-                fprintf(stderr, "mode sense failed, device not ready\n");
-        else if (res)
-                fprintf(stderr," mode sense failed\n");
 
         res = sg_cmds_close_device(fd);
         if (res < 0) {
diff --git a/src/sg_read.c b/src/sg_read.c
index 28473c4..0f10286 100644
--- a/src/sg_read.c
+++ b/src/sg_read.c
@@ -1,5 +1,5 @@
 /* A utility program for the Linux OS SCSI generic ("sg") device driver.
-*  Copyright (C) 2001 - 2013 D. Gilbert
+*  Copyright (C) 2001 - 2014 D. Gilbert
 *  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 Software Foundation; either version 2, or (at your option)
@@ -48,7 +48,7 @@
 #include "sg_io_linux.h"
 
 
-static const char * version_str = "1.20 20131014";
+static const char * version_str = "1.21 20140516";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_BLOCKS_PER_TRANSFER 128
@@ -351,11 +351,11 @@
     if (verbose > 2)
         fprintf(stderr, "      duration=%u ms\n", io_hdr.duration);
     switch (sg_err_category3(&io_hdr)) {
+    case SG_LIB_CAT_CLEAN:
+        break;
     case SG_LIB_CAT_RECOVERED:
         if (verbose > 1)
                 sg_chk_n_print3("reading, continue", &io_hdr, 1);
-        /* fall through */
-    case SG_LIB_CAT_CLEAN:
         break;
     case SG_LIB_CAT_UNIT_ATTENTION:
         if (verbose)
diff --git a/src/sg_read_block_limits.c b/src/sg_read_block_limits.c
index 5ef3fc7..a061bd7 100644
--- a/src/sg_read_block_limits.c
+++ b/src/sg_read_block_limits.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2013 Douglas Gilbert.
+ * Copyright (c) 2009-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -28,7 +28,7 @@
  * SCSI device.
  */
 
-static const char * version_str = "1.02 20130507";
+static const char * version_str = "1.03 20140516";
 
 #define MAX_READ_BLOCK_LIMITS_LEN 6
 
@@ -175,14 +175,11 @@
       if (m != 0)
         fprintf(stderr, ", %d MB", m);
       fprintf(stderr, "\n");
-    } else if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Read block limits not supported\n");
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Read block limits, aborted command\n");
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "Read block limits command has bad field in cdb\n");
-    else {
-        fprintf(stderr, "Read block limits command failed\n");
+    } else {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Read block limits: %s\n", b);
         if (0 == verbose)
             fprintf(stderr, "    try '-v' option for more information\n");
     }
diff --git a/src/sg_read_long.c b/src/sg_read_long.c
index b6ff345..459ebd4 100644
--- a/src/sg_read_long.c
+++ b/src/sg_read_long.c
@@ -1,5 +1,5 @@
 /* A utility program for the Linux OS SCSI subsystem.
-   *  Copyright (C) 2004-2013 D. Gilbert
+   *  Copyright (C) 2004-2014 D. Gilbert
    *  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 Software Foundation; either version 2, or (at your option)
@@ -29,7 +29,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "1.18 20130507";
+static const char * version_str = "1.19 20140516";
 
 #define MAX_XFER_LEN 10000
 
@@ -90,6 +90,7 @@
 {
     int offset, res;
     const char * ten_or;
+    char b[80];
 
     if (do_16)
         res = sg_ll_read_long16(sg_fd, pblock, correct, llba, data_out,
@@ -101,32 +102,13 @@
     switch (res) {
     case 0:
         break;
-    case SG_LIB_CAT_NOT_READY:
-        fprintf(stderr, "  SCSI READ LONG (%s) failed, device not ready\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_UNIT_ATTENTION:
-        fprintf(stderr, "  SCSI READ LONG (%s) failed, unit attention\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_ABORTED_COMMAND:
-        fprintf(stderr, "  SCSI READ LONG (%s) failed, aborted command\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_INVALID_OP:
-        fprintf(stderr, "  SCSI READ LONG (%s) command not supported\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_ILLEGAL_REQ:
-        fprintf(stderr, "  SCSI READ LONG (%s) command, bad field in cdb\n",
-                ten_or);
-        break;
     case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
         fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
                 ">>>\n", xfer_len - offset);
         break;
     default:
-        fprintf(stderr, "  SCSI READ LONG (%s) command error\n", ten_or);
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "  SCSI READ LONG (%s): %s\n", ten_or, b);
         break;
     }
     return res;
diff --git a/src/sg_reassign.c b/src/sg_reassign.c
index de3fe9e..362859c 100644
--- a/src/sg_reassign.c
+++ b/src/sg_reassign.c
@@ -32,7 +32,7 @@
  * vendor specific data is written.
  */
 
-static const char * version_str = "1.15 20140126";
+static const char * version_str = "1.16 20140517";
 
 #define DEF_DEFECT_LIST_FORMAT 4        /* bytes from index */
 
@@ -254,6 +254,7 @@
     const char * device_name = NULL;
     uint64_t addr_arr[MAX_NUM_ADDR];
     unsigned char param_arr[4 + (MAX_NUM_ADDR * 8)];
+    char b[80];
     int param_len = 4;
     int ret = 0;
 
@@ -410,23 +411,9 @@
         res = sg_ll_reassign_blocks(sg_fd, eight, longlist, param_arr,
                                     param_len, 1, verbose);
         ret = res;
-        if (SG_LIB_CAT_NOT_READY == res) {
-            fprintf(stderr, "REASSIGN BLOCKS failed, device not ready\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
-            fprintf(stderr, "REASSIGN BLOCKS, unit attention\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
-            fprintf(stderr, "REASSIGN BLOCKS, aborted command\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_INVALID_OP == res) {
-            fprintf(stderr, "REASSIGN BLOCKS not supported\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
-            fprintf(stderr, "bad field in REASSIGN BLOCKS cdb\n");
-            goto err_out;
-        } else if (0 != res) {
-            fprintf(stderr, "REASSIGN BLOCKS failed\n");
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "REASSIGN BLOCKS: %s\n", b);
             goto err_out;
         }
     } else /* if (grown || primary) */ {
@@ -440,18 +427,9 @@
         res = sg_ll_read_defect10(sg_fd, primary, grown, dl_format,
                                   param_arr, param_len, 0, verbose);
         ret = res;
-        if (SG_LIB_CAT_NOT_READY == res) {
-            fprintf(stderr, "READ DEFECT DATA (10) failed, device not "
-                    "ready\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_INVALID_OP == res) {
-            fprintf(stderr, "READ DEFECT DATA (10) not supported\n");
-            goto err_out;
-        } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
-            fprintf(stderr, "bad field in READ DEFECT DATA (10) cdb\n");
-            goto err_out;
-        } else if (0 != res) {
-            fprintf(stderr, "READ DEFECT DATA (10) failed\n");
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "READ DEFECT DATA(10): %s\n", b);
             goto err_out;
         }
         if (do_hex) {
diff --git a/src/sg_requests.c b/src/sg_requests.c
index c5b3307..caa9d5f 100644
--- a/src/sg_requests.c
+++ b/src/sg_requests.c
@@ -25,7 +25,7 @@
  * This program issues the SCSI command REQUEST SENSE to the given SCSI device.
  */
 
-static const char * version_str = "1.25 20140514";
+static const char * version_str = "1.25 20140515";
 
 #define MAX_REQS_RESP_LEN 255
 #define DEF_REQS_RESP_LEN 252
@@ -114,12 +114,12 @@
     int do_progress = 0;
     int do_raw = 0;
     int do_status = 0;
-    int do_time = 0;
     int verbose = 0;
     const char * device_name = NULL;
     int ret = 0;
     char b[80];
 #ifndef SG_LIB_MINGW
+    int do_time = 0;
     struct timeval start_tm, end_tm;
 #endif
 
@@ -167,7 +167,9 @@
             do_status = 1;
             break;
         case 't':
+#ifndef SG_LIB_MINGW
             do_time = 1;
+#endif
             break;
         case 'v':
             ++verbose;
diff --git a/src/sg_rmsn.c b/src/sg_rmsn.c
index d9cb497..e481b76 100644
--- a/src/sg_rmsn.c
+++ b/src/sg_rmsn.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005-2013 Douglas Gilbert.
+ * Copyright (c) 2005-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -26,7 +26,7 @@
  * to the given SCSI device.
  */
 
-static const char * version_str = "1.09 20130507";
+static const char * version_str = "1.10 20140516";
 
 #define SERIAL_NUM_SANITY_LEN (16 * 1024)
 
@@ -174,27 +174,13 @@
             }
         }
     }
-    if (0 != res) {
-        if (SG_LIB_CAT_INVALID_OP == res)
-            fprintf(stderr, "Read Media Serial Number command not "
-                    "supported\n");
-        else if (SG_LIB_CAT_NOT_READY == res)
-            fprintf(stderr, "Read Media Serial Number failed, device not "
-                    "ready\n");
-        else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-            fprintf(stderr, "Read Media Serial Number failed, unit "
-                    "attention\n");
-        else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-            fprintf(stderr, "Read Media Serial Number failed, aborted "
-                    "command\n");
-        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-            fprintf(stderr, "bad field in Read Media Serial Number cdb "
-                    "including unsupported service action\n");
-        else {
-            fprintf(stderr, "Read Media Serial Number failed\n");
-            if (0 == verbose)
-                fprintf(stderr, "    try '-v' for more information\n");
-        }
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Read Media Serial Number: %s\n", b);
+        if (0 == verbose)
+            fprintf(stderr, "    try '-v' for more information\n");
     }
 
 err_out:
diff --git a/src/sg_safte.c b/src/sg_safte.c
index e8c3446..a5005d7 100644
--- a/src/sg_safte.c
+++ b/src/sg_safte.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2013 Hannes Reinecke and Douglas Gilbert.
+ * Copyright (c) 2004-2014 Hannes Reinecke and Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -26,7 +26,7 @@
  *  to the 'SCSI Accessed Fault-Tolerant Enclosures' (SAF-TE) spec.
  */
 
-static const char * version_str = "0.24 20130507";
+static const char * version_str = "0.25 20140516";
 
 
 #define SENSE_BUFF_LEN 64       /* Arbitrary, could be larger */
@@ -524,6 +524,7 @@
     int do_insertions = 0;
     const char * cp;
     char buff[48];
+    char b[80];
     struct sg_simple_inquiry_resp inq_resp;
     const char op_name[] = "READ BUFFER";
 
@@ -718,23 +719,9 @@
     case 0:
     case SG_LIB_CAT_RECOVERED:
         break;
-    case SG_LIB_CAT_ABORTED_COMMAND:
-        fprintf(stderr, "%s: aborted command\n", op_name);
-        break;
-    case SG_LIB_CAT_NOT_READY:
-        fprintf(stderr, "%s: device not ready\n", op_name);
-        break;
-    case SG_LIB_CAT_UNIT_ATTENTION:
-        fprintf(stderr, "%s: unit attention\n", op_name);
-        break;
-    case SG_LIB_CAT_INVALID_OP:
-        fprintf(stderr, "%s: operation not supported\n", op_name);
-        break;
-    case SG_LIB_CAT_ILLEGAL_REQ:
-        fprintf(stderr, "%s: bad field in cdb\n", op_name);
-        break;
     default:
-        fprintf(stderr, "%s failed\n", op_name);
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "%s failed: %s\n", op_name, b);
         break;
     }
     ret = res;
diff --git a/src/sg_sanitize.c b/src/sg_sanitize.c
index 69c300a..a21cb7b 100644
--- a/src/sg_sanitize.c
+++ b/src/sg_sanitize.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2013 Douglas Gilbert.
+ * Copyright (c) 2011-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -26,7 +26,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "0.93 20130927";
+static const char * version_str = "0.94 20140516";
 
 /* Not all environments support the Unix sleep() */
 #if defined(MSC_VER) || defined(__MINGW32__)
@@ -206,13 +206,6 @@
         ;
     else if (-2 == ret) {
         switch (sense_cat) {
-        case SG_LIB_CAT_NOT_READY:
-        case SG_LIB_CAT_UNIT_ATTENTION:
-        case SG_LIB_CAT_INVALID_OP:
-        case SG_LIB_CAT_ILLEGAL_REQ:
-        case SG_LIB_CAT_ABORTED_COMMAND:
-            ret = sense_cat;
-            break;
         case SG_LIB_CAT_RECOVERED:
         case SG_LIB_CAT_NO_SENSE:
             ret = 0;
@@ -231,7 +224,7 @@
             ret = sense_cat;
             break;
         default:
-            ret = -1;
+            ret = sense_cat;
             break;
         }
     } else
@@ -251,15 +244,18 @@
     const char * device_name = NULL;
     char ebuff[EBUFF_SZ];
     char pdt_name[32];
+    char b[80];
     unsigned char requestSenseBuff[DEF_REQS_RESP_LEN];
     unsigned char * wBuff = NULL;
     int ret = -1;
     struct opts_t opts;
+    struct opts_t * op;
     struct stat a_stat;
     struct sg_simple_inquiry_resp inq_out;
 
-    memset(&opts, 0, sizeof(opts));
-    opts.count = 1;
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->count = 1;
     while (1) {
         int option_index = 0;
 
@@ -270,67 +266,67 @@
 
         switch (c) {
         case 'A':
-            ++opts.ause;
+            ++op->ause;
             break;
         case 'B':
-            ++opts.block;
+            ++op->block;
             break;
         case 'c':
-            opts.count = sg_get_num(optarg);
-            if ((opts.count < 1) || (opts.count > 31))  {
+            op->count = sg_get_num(optarg);
+            if ((op->count < 1) || (op->count > 31))  {
                 fprintf(stderr, "bad argument to '--count', expect 1 to "
                         "31\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
         case 'C':
-            ++opts.crypto;
+            ++op->crypto;
             break;
         case 'e':
-            ++opts.early;
+            ++op->early;
             break;
         case 'F':
-            ++opts.fail;
+            ++op->fail;
             break;
         case 'h':
         case '?':
             usage();
             return 0;
         case 'i':
-            opts.ipl = sg_get_num(optarg);
-            if ((opts.ipl < 1) || (opts.ipl > 65535))  {
+            op->ipl = sg_get_num(optarg);
+            if ((op->ipl < 1) || (op->ipl > 65535))  {
                 fprintf(stderr, "bad argument to '--ipl', expect 1 to "
                         "65535\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
         case 'I':
-            ++opts.invert;
+            ++op->invert;
             break;
         case 'O':
-            ++opts.overwrite;
+            ++op->overwrite;
             break;
         case 'p':
-            opts.pattern_fn = optarg;
+            op->pattern_fn = optarg;
             break;
         case 'Q':
-            ++opts.quick;
+            ++op->quick;
             break;
         case 'T':
-            opts.test = sg_get_num(optarg);
-            if ((opts.test < 0) || (opts.test > 3))  {
+            op->test = sg_get_num(optarg);
+            if ((op->test < 0) || (op->test > 3))  {
                 fprintf(stderr, "bad argument to '--test', expect 0 to 3\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
         case 'v':
-            ++opts.verbose;
+            ++op->verbose;
             break;
         case 'V':
             fprintf(stderr, ME "version: %s\n", version_str);
             return 0;
         case 'w':
-            ++opts.wait;
+            ++op->wait;
             break;
         default:
             fprintf(stderr, "unrecognised option code 0x%x ??\n", c);
@@ -356,37 +352,37 @@
         usage();
         return SG_LIB_SYNTAX_ERROR;
     }
-    vb = opts.verbose;
-    n = !!opts.block + !!opts.crypto + !!opts.fail + !!opts.overwrite;
+    vb = op->verbose;
+    n = !!op->block + !!op->crypto + !!op->fail + !!op->overwrite;
     if (1 != n) {
         fprintf(stderr, "one and only one of '--block', '--crypto', "
                 "'--fail' or '--overwrite' please\n");
         return SG_LIB_SYNTAX_ERROR;
     }
-    if (opts.overwrite) {
-        if (NULL == opts.pattern_fn) {
+    if (op->overwrite) {
+        if (NULL == op->pattern_fn) {
             fprintf(stderr, "'--overwrite' requires '--pattern=PF' "
                     "option\n");
             return SG_LIB_SYNTAX_ERROR;
         }
-        got_stdin = (0 == strcmp(opts.pattern_fn, "-")) ? 1 : 0;
+        got_stdin = (0 == strcmp(op->pattern_fn, "-")) ? 1 : 0;
         if (! got_stdin) {
             memset(&a_stat, 0, sizeof(a_stat));
-            if (stat(opts.pattern_fn, &a_stat) < 0) {
+            if (stat(op->pattern_fn, &a_stat) < 0) {
                 fprintf(stderr, "pattern file: unable to stat(%s): %s\n",
-                        opts.pattern_fn, safe_strerror(errno));
+                        op->pattern_fn, safe_strerror(errno));
                 return SG_LIB_FILE_ERROR;
             }
-            if (opts.ipl <= 0) {
-                opts.ipl = (int)a_stat.st_size;
-                if (opts.ipl > MAX_XFER_LEN) {
+            if (op->ipl <= 0) {
+                op->ipl = (int)a_stat.st_size;
+                if (op->ipl > MAX_XFER_LEN) {
                     fprintf(stderr, "pattern file length exceeds 65535 "
                             "bytes, need '--ipl=LEN' option\n");
                      return SG_LIB_FILE_ERROR;
                 }
             }
         }
-        if (opts.ipl < 1) {
+        if (op->ipl < 1) {
             fprintf(stderr, "'--overwrite' requires '--ipl=LEN' "
                     "option if can't get PF length\n");
             return SG_LIB_SYNTAX_ERROR;
@@ -414,12 +410,12 @@
            inq_out.peripheral_type);
 
 
-    if (opts.overwrite) {
-        param_lst_len = opts.ipl + 4;
-        wBuff = (unsigned char*)calloc(opts.ipl + 4, 1);
+    if (op->overwrite) {
+        param_lst_len = op->ipl + 4;
+        wBuff = (unsigned char*)calloc(op->ipl + 4, 1);
         if (NULL == wBuff) {
             fprintf(stderr, "unable to allocate %d bytes of memory with "
-                    "calloc()\n", opts.ipl + 4);
+                    "calloc()\n", op->ipl + 4);
             ret = SG_LIB_SYNTAX_ERROR;
             goto err_out;
         }
@@ -428,43 +424,43 @@
             if (sg_set_binary_mode(STDIN_FILENO) < 0)
                 perror("sg_set_binary_mode");
         } else {
-            if ((infd = open(opts.pattern_fn, O_RDONLY)) < 0) {
+            if ((infd = open(op->pattern_fn, O_RDONLY)) < 0) {
                 snprintf(ebuff, EBUFF_SZ,
-                         ME "could not open %s for reading", opts.pattern_fn);
+                         ME "could not open %s for reading", op->pattern_fn);
                 perror(ebuff);
                 ret = SG_LIB_FILE_ERROR;
                 goto err_out;
             } else if (sg_set_binary_mode(infd) < 0)
                 perror("sg_set_binary_mode");
         }
-        res = read(infd, wBuff + 4, opts.ipl);
+        res = read(infd, wBuff + 4, op->ipl);
         if (res < 0) {
             snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
-                     opts.pattern_fn);
+                     op->pattern_fn);
             perror(ebuff);
             if (! got_stdin)
                 close(infd);
             ret = SG_LIB_FILE_ERROR;
             goto err_out;
         }
-        if (res < opts.ipl) {
+        if (res < op->ipl) {
             fprintf(stderr, "tried to read %d bytes from %s, got %d "
-                    "bytes\n", opts.ipl, opts.pattern_fn, res);
+                    "bytes\n", op->ipl, op->pattern_fn, res);
             fprintf(stderr, "  so pad with 0x0 bytes and continue\n");
         }
         if (! got_stdin)
             close(infd);
 
-        wBuff[0] = opts.count & 0x1f;;
-        if (opts.test)
-            wBuff[0] |= ((opts.test & 0x3) << 5);
-        if (opts.invert)
+        wBuff[0] = op->count & 0x1f;;
+        if (op->test)
+            wBuff[0] |= ((op->test & 0x3) << 5);
+        if (op->invert)
             wBuff[0] |= 0x80;
-        wBuff[2] = ((opts.ipl >> 8) & 0xff);
-        wBuff[3] = (opts.ipl & 0xff);
+        wBuff[2] = ((op->ipl >> 8) & 0xff);
+        wBuff[3] = (op->ipl & 0xff);
     }
 
-    if ((0 == opts.quick) && (! opts.fail)) {
+    if ((0 == op->quick) && (! op->fail)) {
         printf("\nA SANITIZE will commence in 15 seconds\n");
         printf("    ALL data on %s will be DESTROYED\n", device_name);
         printf("        Press control-C to abort\n");
@@ -479,36 +475,13 @@
         sleep_for(5);
     }
 
-    ret = do_sanitize(sg_fd, &opts, wBuff, param_lst_len);
+    ret = do_sanitize(sg_fd, op, wBuff, param_lst_len);
     if (ret) {
-        switch (ret) {
-        case SG_LIB_CAT_NOT_READY:
-            fprintf(stderr, "Sanitize failed, device not ready\n");
-            break;
-        case SG_LIB_CAT_UNIT_ATTENTION:
-            fprintf(stderr, "Sanitize, unit attention\n");
-            break;
-        case SG_LIB_CAT_ABORTED_COMMAND:
-            fprintf(stderr, "Sanitize, aborted command\n");
-            break;
-        case SG_LIB_CAT_INVALID_OP:
-            fprintf(stderr, "Sanitize command not supported\n");
-            break;
-        case SG_LIB_CAT_ILLEGAL_REQ:
-            fprintf(stderr, "bad field in Sanitize cdb, option "
-                    "probably not supported\n");
-            break;
-        case SG_LIB_CAT_MEDIUM_HARD:
-            fprintf(stderr, "Sanitize command reported medium or "
-                    "hardware error\n");
-            break;
-        default:
-            fprintf(stderr, "Sanitize command failed\n");
-            break;
-        }
+        sg_get_category_sense_str(ret, sizeof(b), b, vb);
+        fprintf(stderr, "Sanitize failed: %s\n", b);
     }
 
-    if ((0 == ret) && (0 == opts.early) && (0 == opts.wait)) {
+    if ((0 == ret) && (0 == op->early) && (0 == op->wait)) {
         for (k = 0, desc = 1 ;; ++k) {
             sleep_for(POLL_DURATION_SECS);
             memset(requestSenseBuff, 0x0, sizeof(requestSenseBuff));
@@ -526,12 +499,9 @@
                         desc = 0;
                         continue;
                     }
-                }
-                else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                    fprintf(stderr, "Request Sense, aborted command\n");
-                else {
-                    fprintf(stderr, "Request Sense command unexpectedly "
-                            "failed\n");
+                } else {
+                    sg_get_category_sense_str(res, sizeof(b), b, vb);
+                    fprintf(stderr, "Request Sense: %s\n", b);
                     if (0 == vb)
                         fprintf(stderr, "    try the '-v' option for "
                                 "more information\n");
diff --git a/src/sg_sat_phy_event.c b/src/sg_sat_phy_event.c
index a3752ad..7db267a 100644
--- a/src/sg_sat_phy_event.c
+++ b/src/sg_sat_phy_event.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006-2013 Douglas Gilbert.
+ * Copyright (c) 2006-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -21,7 +21,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "1.03 20130507";
+static const char * version_str = "1.04 20140515";
 
 /* This program uses a ATA PASS-THROUGH SCSI command. This usage is
  * defined in the SCSI to ATA Translation (SAT) drafts and standards.
@@ -266,8 +266,18 @@
                             "hardware error\n", cdb_len);
                 return SG_LIB_CAT_MEDIUM_HARD;
             case SPC_SK_ABORTED_COMMAND:
-                fprintf(stderr, "Aborted command\n");
-                return SG_LIB_CAT_ABORTED_COMMAND;
+                if (0x10 == ssh.asc) {
+                    fprintf(stderr, "Aborted command: protection "
+                            "information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    fprintf(stderr, "Aborted command\n");
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                fprintf(stderr, "ATA PASS-THROUGH (%d): data protect, read "
+                            "only media?\n", cdb_len);
+                return SG_LIB_CAT_DATA_PROTECT;
             default:
                 if (verbose < 2)
                     fprintf(stderr, "ATA PASS-THROUGH (%d), some sense "
@@ -284,8 +294,13 @@
             return SG_LIB_CAT_MALFORMED;
         }
     } else if (res > 0) {
-        fprintf(stderr, "Unexpected SCSI status=0x%x\n", res);
-        return SG_LIB_CAT_MALFORMED;
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            fprintf(stderr, "SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            fprintf(stderr, "Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
     } else {
         fprintf(stderr, "ATA pass through (%d) failed\n", cdb_len);
         if (verbose < 2)
diff --git a/src/sg_sat_set_features.c b/src/sg_sat_set_features.c
index a193ef5..633df61 100644
--- a/src/sg_sat_set_features.c
+++ b/src/sg_sat_set_features.c
@@ -46,7 +46,7 @@
 
 #define DEF_TIMEOUT 20
 
-static const char * version_str = "1.07 20140405";
+static const char * version_str = "1.08 20140515";
 
 static struct option long_options[] = {
         {"count", required_argument, 0, 'c'},
@@ -215,8 +215,18 @@
                             "hardware error\n", cdb_len);
                 return SG_LIB_CAT_MEDIUM_HARD;
             case SPC_SK_ABORTED_COMMAND:
-                fprintf(stderr, "Aborted command\n");
-                return SG_LIB_CAT_ABORTED_COMMAND;
+                if (0x10 == ssh.asc) {
+                    fprintf(stderr, "Aborted command: protection "
+                            "information\n");
+                    return SG_LIB_CAT_PROTECTION;
+                } else {
+                    fprintf(stderr, "Aborted command\n");
+                    return SG_LIB_CAT_ABORTED_COMMAND;
+                }
+            case SPC_SK_DATA_PROTECT:
+                fprintf(stderr, "ATA PASS-THROUGH (%d): data protect, read "
+                            "only media?\n", cdb_len);
+                return SG_LIB_CAT_DATA_PROTECT;
             default:
                 if (verbose < 2)
                     fprintf(stderr, "ATA PASS-THROUGH (%d), some sense "
@@ -233,8 +243,13 @@
             return SG_LIB_CAT_MALFORMED;
         }
     } else if (res > 0) {
-        fprintf(stderr, "Unexpected SCSI status=0x%x\n", res);
-        return SG_LIB_CAT_MALFORMED;
+        if (SAM_STAT_RESERVATION_CONFLICT == res) {
+            fprintf(stderr, "SCSI status: RESERVATION CONFLICT\n");
+            return SG_LIB_CAT_RES_CONFLICT;
+        } else {
+            fprintf(stderr, "Unexpected SCSI status=0x%x\n", res);
+            return SG_LIB_CAT_MALFORMED;
+        }
     } else {
         fprintf(stderr, "ATA pass through (%d) failed\n", cdb_len);
         if (verbose < 2)
diff --git a/src/sg_senddiag.c b/src/sg_senddiag.c
index 3708adb..3b7333c 100644
--- a/src/sg_senddiag.c
+++ b/src/sg_senddiag.c
@@ -24,7 +24,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "0.40 20140110";
+static const char * version_str = "0.41 20140517";
 
 #define ME "sg_senddiag: "
 
@@ -369,21 +369,12 @@
     else
         res = sg_ll_mode_sense10(sg_fd, 0 /* llbaa */, 1 /* dbd */, 0, 0xa, 0,
                                  resp, mx_resp_len, noisy, verbose);
-    if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Mode sense (%s) command not supported\n",
-                (mode6 ? "6" : "10"));
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "bad field in Mode sense (%s) command\n",
-                (mode6 ? "6" : "10"));
-    else if (SG_LIB_CAT_NOT_READY == res)
-        fprintf(stderr, "Mode sense (%s) failed, device not ready\n",
-                (mode6 ? "6" : "10"));
-    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-        fprintf(stderr, "Mode sense (%s) failed, unit attention\n",
-                (mode6 ? "6" : "10"));
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Mode sense (%s) failed, aborted command\n",
-                (mode6 ? "6" : "10"));
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Mode sense (%s): %s\n", (mode6 ? "6" : "10"), b);
+    }
     return res;
 }
 
diff --git a/src/sg_ses.c b/src/sg_ses.c
index 2d6e3ce..1627c01 100644
--- a/src/sg_ses.c
+++ b/src/sg_ses.c
@@ -31,7 +31,7 @@
 
 static const char * version_str = "1.91 20140515";    /* ses3r06 */
 
-#define MX_ALLOC_LEN ((64 * 1024) - 1)  /* max allowable for big enclosures */
+#define MX_ALLOC_LEN ((64 * 1024) - 4)  /* max allowable for big enclosures */
 #define MX_ELEM_HDR 1024
 #define MX_DATA_IN 2048
 #define MX_JOIN_ROWS 260
diff --git a/src/sg_start.c b/src/sg_start.c
index 8b438b1..58c7e1b 100644
--- a/src/sg_start.c
+++ b/src/sg_start.c
@@ -164,7 +164,7 @@
 }
 
 static int
-process_cl_new(struct opts_t * optsp, int argc, char * argv[])
+process_cl_new(struct opts_t * op, int argc, char * argv[])
 {
     int c, n, err;
 
@@ -178,8 +178,8 @@
 
         switch (c) {
         case 'e':
-            ++optsp->do_eject;
-            ++optsp->do_loej;
+            ++op->do_eject;
+            ++op->do_loej;
             break;
         case 'f':
             n = sg_get_num(optarg);
@@ -188,23 +188,23 @@
                 usage();
                 return SG_LIB_SYNTAX_ERROR;
             }
-            ++optsp->do_loej;
-            ++optsp->do_start;
-            optsp->do_fl = n;
+            ++op->do_loej;
+            ++op->do_start;
+            op->do_fl = n;
             break;
         case 'h':
         case '?':
-            ++optsp->do_help;
+            ++op->do_help;
             break;
         case 'i':
-            ++optsp->do_immed;
+            ++op->do_immed;
             break;
         case 'l':
-            ++optsp->do_load;
-            ++optsp->do_loej;
+            ++op->do_load;
+            ++op->do_loej;
             break;
         case 'L':
-            ++optsp->do_loej;
+            ++op->do_loej;
             break;
         case 'm':
             n = sg_get_num(optarg);
@@ -213,15 +213,15 @@
                 usage();
                 return SG_LIB_SYNTAX_ERROR;
             }
-            optsp->do_mod = n;
+            op->do_mod = n;
             break;
         case 'n':
-            ++optsp->do_noflush;
+            ++op->do_noflush;
             break;
         case 'N':
             break;      /* ignore */
         case 'O':
-            optsp->opt_new = 0;
+            op->opt_new = 0;
             return 0;
         case 'p':
             n = sg_get_num(optarg);
@@ -230,26 +230,26 @@
                 usage();
                 return SG_LIB_SYNTAX_ERROR;
             }
-            optsp->do_pc = n;
+            op->do_pc = n;
             break;
         case 'r':
-            ++optsp->do_readonly;
+            ++op->do_readonly;
             break;
         case 's':
-            ++optsp->do_start;
+            ++op->do_start;
             break;
         case 'S':
-            ++optsp->do_stop;
+            ++op->do_stop;
             break;
         case 'v':
-            ++optsp->do_verbose;
+            ++op->do_verbose;
             break;
         case 'V':
-            ++optsp->do_version;
+            ++op->do_version;
             break;
         default:
             fprintf(stderr, "unrecognised option code %c [0x%x]\n", c, c);
-            if (optsp->do_help)
+            if (op->do_help)
                 break;
             usage();
             return SG_LIB_SYNTAX_ERROR;
@@ -259,15 +259,15 @@
     for (; optind < argc; ++optind) {
         if (1 == strlen(argv[optind])) {
             if (0 == strcmp("0", argv[optind])) {
-                ++optsp->do_stop;
+                ++op->do_stop;
                 continue;
             } else if (0 == strcmp("1", argv[optind])) {
-                ++optsp->do_start;
+                ++op->do_start;
                 continue;
             }
         }
-        if (NULL == optsp->device_name)
-            optsp->device_name = argv[optind];
+        if (NULL == op->device_name)
+            op->device_name = argv[optind];
         else {
             fprintf(stderr, "Unexpected extra argument: %s\n", argv[optind]);
             ++err;
@@ -281,7 +281,7 @@
 }
 
 static int
-process_cl_old(struct opts_t * optsp, int argc, char * argv[])
+process_cl_old(struct opts_t * op, int argc, char * argv[])
 {
     int k, jmp_out, plen, num;
     int ambigu = 0;
@@ -300,25 +300,25 @@
                 switch (*cp) {
                 case 'i':
                     if ('\0' == *(cp + 1))
-                        optsp->do_immed = 1;
+                        op->do_immed = 1;
                     else
                         jmp_out = 1;
                     break;
                 case 'r':
-                    ++optsp->do_readonly;
+                    ++op->do_readonly;
                     break;
                 case 'v':
-                    ++optsp->do_verbose;
+                    ++op->do_verbose;
                     break;
                 case 'V':
-                    ++optsp->do_version;
+                    ++op->do_version;
                     break;
                 case 'h':
                 case '?':
-                    ++optsp->do_help;
+                    ++op->do_help;
                     break;
                 case 'N':
-                    optsp->opt_new = 1;
+                    op->opt_new = 1;
                     return 0;
                 case 'O':
                     break;
@@ -338,7 +338,7 @@
                 continue;
 
             if (0 == strncmp(cp, "eject", 5)) {
-                optsp->do_loej = 1;
+                op->do_loej = 1;
                 if (startstop == 1)
                     ambigu = 1;
                 else
@@ -351,8 +351,8 @@
                     return SG_LIB_SYNTAX_ERROR;
                 }
                 startstop = 1;
-                optsp->do_loej = 1;
-                optsp->do_fl = u;
+                op->do_loej = 1;
+                op->do_fl = u;
             } else if (0 == strncmp("imm=", cp, 4)) {
                 num = sscanf(cp + 4, "%x", &u);
                 if ((1 != num) || (u > 1)) {
@@ -360,15 +360,15 @@
                     usage_old();
                     return SG_LIB_SYNTAX_ERROR;
                 }
-                optsp->do_immed = u;
+                op->do_immed = u;
             } else if (0 == strncmp(cp, "load", 4)) {
-                optsp->do_loej = 1;
+                op->do_loej = 1;
                 if (startstop == 0)
                     ambigu = 1;
                 else
                     startstop = 1;
             } else if (0 == strncmp(cp, "loej", 4))
-                optsp->do_loej = 1;
+                op->do_loej = 1;
             else if (0 == strncmp("pc=", cp, 3)) {
                 num = sscanf(cp + 3, "%x", &u);
                 if ((1 != num) || (u > 15)) {
@@ -376,7 +376,7 @@
                     usage_old();
                     return SG_LIB_SYNTAX_ERROR;
                 }
-                optsp->do_pc = u;
+                op->do_pc = u;
             } else if (0 == strncmp("mod=", cp, 4)) {
                 num = sscanf(cp + 3, "%x", &u);
                 if (1 != num) {
@@ -384,9 +384,9 @@
                     usage_old();
                     return SG_LIB_SYNTAX_ERROR;
                 }
-                optsp->do_mod = u;
+                op->do_mod = u;
             } else if (0 == strncmp(cp, "noflush", 7)) {
-                optsp->do_noflush = 1;
+                op->do_noflush = 1;
             } else if (0 == strncmp(cp, "start", 5)) {
                 if (startstop == 0)
                     ambigu = 1;
@@ -414,11 +414,11 @@
                 ambigu = 1;
             else
                 startstop = 1;
-        } else if (0 == optsp->device_name)
-                optsp->device_name = cp;
+        } else if (0 == op->device_name)
+                op->device_name = cp;
         else {
             fprintf(stderr, "too many arguments, got: %s, not "
-                    "expecting: %s\n", optsp->device_name, cp);
+                    "expecting: %s\n", op->device_name, cp);
             usage_old();
             return SG_LIB_SYNTAX_ERROR;
         }
@@ -428,30 +428,30 @@
             usage_old();
             return SG_LIB_SYNTAX_ERROR;
         } else if (0 == startstop)
-            ++optsp->do_stop;
+            ++op->do_stop;
         else if (1 == startstop)
-            ++optsp->do_start;
+            ++op->do_start;
     }
     return 0;
 }
 
 static int
-process_cl(struct opts_t * optsp, int argc, char * argv[])
+process_cl(struct opts_t * op, int argc, char * argv[])
 {
     int res;
     char * cp;
 
     cp = getenv("SG3_UTILS_OLD_OPTS");
     if (cp) {
-        optsp->opt_new = 0;
-        res = process_cl_old(optsp, argc, argv);
-        if ((0 == res) && optsp->opt_new)
-            res = process_cl_new(optsp, argc, argv);
+        op->opt_new = 0;
+        res = process_cl_old(op, argc, argv);
+        if ((0 == res) && op->opt_new)
+            res = process_cl_new(op, argc, argv);
     } else {
-        optsp->opt_new = 1;
-        res = process_cl_new(optsp, argc, argv);
-        if ((0 == res) && (0 == optsp->opt_new))
-            res = process_cl_old(optsp, argc, argv);
+        op->opt_new = 1;
+        res = process_cl_new(op, argc, argv);
+        if ((0 == res) && (0 == op->opt_new))
+            res = process_cl_old(op, argc, argv);
     }
     return res;
 }
@@ -463,99 +463,95 @@
     int fd, res;
     int ret = 0;
     struct opts_t opts;
+    struct opts_t * op;
 
-    memset(&opts, 0, sizeof(opts));
-    opts.do_fl = -1;    /* only when >= 0 set FL bit */
-    res = process_cl(&opts, argc, argv);
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->do_fl = -1;    /* only when >= 0 set FL bit */
+    res = process_cl(op, argc, argv);
     if (res)
         return SG_LIB_SYNTAX_ERROR;
-    if (opts.do_help) {
-        if (opts.opt_new)
+    if (op->do_help) {
+        if (op->opt_new)
             usage();
         else
             usage_old();
         return 0;
     }
-    if (opts.do_version) {
+    if (op->do_version) {
         fprintf(stderr, "Version string: %s\n", version_str);
         return 0;
     }
 
-    if (opts.do_start && opts.do_stop) {
+    if (op->do_start && op->do_stop) {
         fprintf(stderr, "Ambiguous to give both '--start' and '--stop'\n");
         return SG_LIB_SYNTAX_ERROR;
     }
-    if (opts.do_load && opts.do_eject) {
+    if (op->do_load && op->do_eject) {
         fprintf(stderr, "Ambiguous to give both '--load' and '--eject'\n");
         return SG_LIB_SYNTAX_ERROR;
     }
-    if (opts.do_load)
-       opts.do_start = 1;
-    else if ((opts.do_eject) || (opts.do_stop))
-       opts.do_start = 0;
-    else if (opts.opt_new && opts.do_loej && (0 == opts.do_start))
-        opts.do_start = 1;      /* --loej alone in new interface is load */
-    else if ((0 == opts.do_loej) && (-1 == opts.do_fl) && (0 == opts.do_pc))
-       opts.do_start = 1;
+    if (op->do_load)
+       op->do_start = 1;
+    else if ((op->do_eject) || (op->do_stop))
+       op->do_start = 0;
+    else if (op->opt_new && op->do_loej && (0 == op->do_start))
+        op->do_start = 1;      /* --loej alone in new interface is load */
+    else if ((0 == op->do_loej) && (-1 == op->do_fl) && (0 == op->do_pc))
+       op->do_start = 1;
     /* default action is to start when no other active options */
 
-    if (0 == opts.device_name) {
+    if (0 == op->device_name) {
         fprintf(stderr, "No DEVICE argument given\n");
-        if (opts.opt_new)
+        if (op->opt_new)
             usage();
         else
             usage_old();
         return SG_LIB_SYNTAX_ERROR;
     }
 
-    if (opts.do_fl >= 0) {
-        if (opts.do_start == 0) {
+    if (op->do_fl >= 0) {
+        if (op->do_start == 0) {
             fprintf(stderr, "Giving '--fl=FL' with '--stop' (or "
                     "'--eject') is invalid\n");
             return SG_LIB_SYNTAX_ERROR;
         }
-        if (opts.do_pc > 0) {
+        if (op->do_pc > 0) {
             fprintf(stderr, "Giving '--fl=FL' with '--pc=PC' "
                     "when PC is non-zero is invalid\n");
             return SG_LIB_SYNTAX_ERROR;
         }
     }
 
-    fd = sg_cmds_open_device(opts.device_name,  opts.do_readonly /* rw */,
-                             opts.do_verbose);
+    fd = sg_cmds_open_device(op->device_name, op->do_readonly,
+                             op->do_verbose);
     if (fd < 0) {
         fprintf(stderr, "Error trying to open %s: %s\n",
-                opts.device_name, safe_strerror(-fd));
+                op->device_name, safe_strerror(-fd));
         return SG_LIB_FILE_ERROR;
     }
 
     res = 0;
-    if (opts.do_fl >= 0)
-        res = sg_ll_start_stop_unit(fd, opts.do_immed, opts.do_fl, 0 /* pc */,
+    if (op->do_fl >= 0)
+        res = sg_ll_start_stop_unit(fd, op->do_immed, op->do_fl, 0 /* pc */,
                                     1 /* fl */, 1 /* loej */,
                                     1 /*start */, 1 /* noisy */,
-                                    opts.do_verbose);
-    else if (opts.do_pc > 0)
-        res = sg_ll_start_stop_unit(fd, opts.do_immed, opts.do_mod,
-                                    opts.do_pc, opts.do_noflush, 0, 0, 1,
-                                    opts.do_verbose);
+                                    op->do_verbose);
+    else if (op->do_pc > 0)
+        res = sg_ll_start_stop_unit(fd, op->do_immed, op->do_mod,
+                                    op->do_pc, op->do_noflush, 0, 0, 1,
+                                    op->do_verbose);
     else
-        res = sg_ll_start_stop_unit(fd, opts.do_immed, 0, 0, opts.do_noflush,
-                                    opts.do_loej, opts.do_start, 1,
-                                    opts.do_verbose);
+        res = sg_ll_start_stop_unit(fd, op->do_immed, 0, 0, op->do_noflush,
+                                    op->do_loej, op->do_start, 1,
+                                    op->do_verbose);
     ret = res;
     if (res) {
-        if (opts.do_verbose < 2) {
-            if (SG_LIB_CAT_INVALID_OP == res)
-                fprintf(stderr, "command not supported\n");
-            else if (SG_LIB_CAT_NOT_READY == res)
-                fprintf(stderr, "device not ready\n");
-            else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-                fprintf(stderr, "unit attention\n");
-            else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-                fprintf(stderr, "aborted command\n");
-            else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-                fprintf(stderr, "invalid field in cdb\n");
+        if (op->do_verbose < 2) {
+            char b[80];
+
+            sg_get_category_sense_str(res, sizeof(b), b, op->do_verbose);
+            fprintf(stderr, "%s\n", b);
         }
         fprintf(stderr, "START STOP UNIT command failed\n");
     }
diff --git a/src/sg_stpg.c b/src/sg_stpg.c
index 6ee5aeb..912cc3b 100644
--- a/src/sg_stpg.c
+++ b/src/sg_stpg.c
@@ -423,6 +423,7 @@
     int port_arr[MAX_PORT_LIST_ARR_LEN];
     int port_arr_len = 0;
     int state_arr[MAX_PORT_LIST_ARR_LEN];
+    char b[80];
     int state_arr_len = 0;
     int portgroup = -1;
     int relport = -1;
@@ -641,18 +642,9 @@
                 tgtStatePtr++;
                 off = 8 + tgt_port_count * 4;
             }
-        } else if (SG_LIB_CAT_INVALID_OP == res)
-            fprintf(stderr, "Report Target Port Groups command not "
-                    "supported\n");
-        else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-            fprintf(stderr, "bad field in Report Target Port Groups cdb "
-                    "including unsupported service action\n");
-        else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-            fprintf(stderr, "Report Target Port Groups, unit attention\n");
-        else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-            fprintf(stderr, "Report Target Port Groups, aborted command\n");
-        else {
-            fprintf(stderr, "Report Target Port Groups command failed\n");
+        } else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "Report Target Port Groups: %s\n", b);
             if (0 == verbose)
                 fprintf(stderr, "    try '-v' for more information\n");
         }
@@ -684,17 +676,9 @@
 
     if (0 == res)
         goto err_out;
-    else if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Set Target Port Groups command not supported\n");
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "bad field in Report Target Port Groups cdb "
-                "including unsupported service action\n");
-    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-        fprintf(stderr, "Set Target Port Groups, unit attention\n");
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Set Target Port Groups, aborted command\n");
     else {
-        fprintf(stderr, "Set Target Port Groups command failed\n");
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Set Target Port Groups: %s\n", b);
         if (0 == verbose)
             fprintf(stderr, "    try '-v' for more information\n");
     }
diff --git a/src/sg_sync.c b/src/sg_sync.c
index 55f789f..64de284 100644
--- a/src/sg_sync.c
+++ b/src/sg_sync.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2013 Douglas Gilbert.
+ * Copyright (c) 2004-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -25,7 +25,7 @@
  * (e.g. disks).
  */
 
-static const char * version_str = "1.10 20130516";
+static const char * version_str = "1.11 20140516";
 
 #define SYNCHRONIZE_CACHE16_CMD     0x91
 #define SYNCHRONIZE_CACHE16_CMDLEN  16
@@ -132,19 +132,12 @@
         ;
     else if (-2 == ret) {
         switch (sense_cat) {
-        case SG_LIB_CAT_NOT_READY:
-        case SG_LIB_CAT_UNIT_ATTENTION:
-        case SG_LIB_CAT_INVALID_OP:
-        case SG_LIB_CAT_ILLEGAL_REQ:
-        case SG_LIB_CAT_ABORTED_COMMAND:
-            ret = sense_cat;
-            break;
         case SG_LIB_CAT_RECOVERED:
         case SG_LIB_CAT_NO_SENSE:
             ret = 0;
             break;
         default:
-            ret = -1;
+            ret = sense_cat;
             break;
         }
     } else
@@ -266,20 +259,12 @@
         res = sg_ll_sync_cache_10(sg_fd, sync_nv, immed, group,
                                   (unsigned int)lba, num_lb, 1, verbose);
     ret = res;
-    if (0 == res)
-        ;
-    else if (SG_LIB_CAT_NOT_READY == res)
-        fprintf(stderr, "Synchronize cache failed, device not ready\n");
-    else if (SG_LIB_CAT_UNIT_ATTENTION == res)
-        fprintf(stderr, "Synchronize cache, unit attention\n");
-    else if (SG_LIB_CAT_ABORTED_COMMAND == res)
-        fprintf(stderr, "Synchronize cache, aborted command\n");
-    else if (SG_LIB_CAT_INVALID_OP == res)
-        fprintf(stderr, "Synchronize cache command not supported\n");
-    else if (SG_LIB_CAT_ILLEGAL_REQ == res)
-        fprintf(stderr, "bad field in Synchronize cache command\n");
-    else
-        fprintf(stderr, "Synchronize cache failed\n");
+    if (res) {
+        char b[80];
+
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "Synchronize cache failed: %s\n", b);
+    }
 
     res = sg_cmds_close_device(sg_fd);
     if (res < 0) {
diff --git a/src/sg_verify.c b/src/sg_verify.c
index 41cd1ca..c0a551f 100644
--- a/src/sg_verify.c
+++ b/src/sg_verify.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2004-2013 Douglas Gilbert.
+ * Copyright (c) 2004-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -33,7 +33,7 @@
  * the possibility of protection data (DIF).
  */
 
-static const char * version_str = "1.20 20130825";    /* sbc3r34 */
+static const char * version_str = "1.21 20140516";    /* sbc4r01 */
 
 #define ME "sg_verify: "
 
@@ -359,20 +359,10 @@
                                  (unsigned int)lba, num, ref_data,
                                  ndo, &info, !quiet, verbose);
         if (0 != res) {
+            char b[80];
+
             ret = res;
             switch (res) {
-            case SG_LIB_CAT_NOT_READY:
-                fprintf(stderr, "%s failed, device not ready\n", vc);
-                break;
-            case SG_LIB_CAT_UNIT_ATTENTION:
-                fprintf(stderr, "%s, unit attention\n", vc);
-                break;
-            case SG_LIB_CAT_ABORTED_COMMAND:
-                fprintf(stderr, "%s, aborted command\n", vc);
-                break;
-            case SG_LIB_CAT_INVALID_OP:
-                fprintf(stderr, "%s command not supported\n", vc);
-                break;
             case SG_LIB_CAT_ILLEGAL_REQ:
                 fprintf(stderr, "bad field in %s cdb, near lba=0x%" PRIx64
                         "\n", vc, lba);
@@ -394,8 +384,10 @@
                     fprintf(stderr, "%s reported MISCOMPARE\n", vc);
                 break;
             default:
-                fprintf(stderr, "%s failed near lba=%" PRIu64 " [0x%" PRIx64
-                        "]\n", vc, lba, lba);
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "%s: %s\n", vc, b);
+                fprintf(stderr, "    failed near lba=%" PRIu64 " [0x%" PRIx64
+                        "]\n", lba, lba);
                 break;
             }
             break;
diff --git a/src/sg_wr_mode.c b/src/sg_wr_mode.c
index 0d6476c..54de322 100644
--- a/src/sg_wr_mode.c
+++ b/src/sg_wr_mode.c
@@ -25,7 +25,7 @@
  * mode page on the given device.
  */
 
-static const char * version_str = "1.12 20140110";
+static const char * version_str = "1.13 20140517";
 
 #define ME "sg_wr_mode: "
 
@@ -321,6 +321,7 @@
     unsigned char mask_in[MX_ALLOC_LEN];
     unsigned char ref_md[MX_ALLOC_LEN];
     char ebuff[EBUFF_SZ];
+    char b[80];
     struct sg_simple_inquiry_resp inq_data;
     int ret = 0;
 
@@ -459,28 +460,14 @@
                                  pg_code, sub_pg_code, ref_md, alloc_len, 1,
                                  verbose);
     ret = res;
-    if (SG_LIB_CAT_INVALID_OP == res) {
-        fprintf(stderr, "MODE SENSE (%d) not supported, try '--len=%d'\n",
-                (mode_6 ? 6 : 10), (mode_6 ? 10 : 6));
-        goto err_out;
-    } else if (SG_LIB_CAT_NOT_READY == res) {
-        fprintf(stderr, "MODE SENSE (%d) failed, device not ready\n",
-                (mode_6 ? 6 : 10));
-        goto err_out;
-    } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
-        fprintf(stderr, "MODE SENSE (%d) failed, unit attention\n",
-                (mode_6 ? 6 : 10));
-        goto err_out;
-    } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
-        fprintf(stderr, "MODE SENSE (%d) failed, aborted command\n",
-                (mode_6 ? 6 : 10));
-        goto err_out;
-    } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
-        fprintf(stderr, "bad field in MODE SENSE (%d) command\n",
-                (mode_6 ? 6 : 10));
-        goto err_out;
-    } else if (0 != res) {
-        fprintf(stderr, "MODE SENSE (%d) failed\n", (mode_6 ? 6 : 10));
+    if (res) {
+        if (SG_LIB_CAT_INVALID_OP == res)
+            fprintf(stderr, "MODE SENSE (%d) not supported, try '--len=%d'\n",
+                    (mode_6 ? 6 : 10), (mode_6 ? 10 : 6));
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "MODE SENSE (%d): %s\n", (mode_6 ? 6 : 10), b);
+        }
         goto err_out;
     }
     off = sg_mode_page_offset(ref_md, alloc_len, mode_6, ebuff, EBUFF_SZ);
@@ -561,28 +548,9 @@
             res = sg_ll_mode_select10(sg_fd, 1, save, ref_md, md_len, 1,
                                       verbose);
         ret = res;
-        if (SG_LIB_CAT_INVALID_OP == res) {
-            fprintf(stderr, "MODE SELECT (%d) not supported\n",
-                    (mode_6 ? 6 : 10));
-            goto err_out;
-        } else if (SG_LIB_CAT_NOT_READY == res) {
-            fprintf(stderr, "MODE SELECT (%d) failed, device not ready\n",
-                    (mode_6 ? 6 : 10));
-            goto err_out;
-        } else if (SG_LIB_CAT_UNIT_ATTENTION == res) {
-            fprintf(stderr, "MODE SELECT (%d) failed, unit attention\n",
-                    (mode_6 ? 6 : 10));
-            goto err_out;
-        } else if (SG_LIB_CAT_ABORTED_COMMAND == res) {
-            fprintf(stderr, "MODE SELECT (%d) failed, aborted command\n",
-                    (mode_6 ? 6 : 10));
-            goto err_out;
-        } else if (SG_LIB_CAT_ILLEGAL_REQ == res) {
-            fprintf(stderr, "bad field in MODE SELECT (%d) command\n",
-                    (mode_6 ? 6 : 10));
-            goto err_out;
-        } else if (0 != res) {
-            fprintf(stderr, "MODE SELECT (%d) failed\n", (mode_6 ? 6 : 10));
+        if (res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            fprintf(stderr, "MODE SELECT (%d): %s\n", (mode_6 ? 6 : 10), b);
             goto err_out;
         }
     } else {
diff --git a/src/sg_write_long.c b/src/sg_write_long.c
index 0084e10..356fbed 100644
--- a/src/sg_write_long.c
+++ b/src/sg_write_long.c
@@ -1,5 +1,5 @@
 /* A utility program for the Linux OS SCSI subsystem.
- *  Copyright (C) 2004-2013 D. Gilbert
+ *  Copyright (C) 2004-2014 D. Gilbert
  *  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 Software Foundation; either version 2, or (at your option)
@@ -31,7 +31,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "1.18 20130507";
+static const char * version_str = "1.1( 20140516";
 
 
 #define MAX_XFER_LEN 10000
@@ -108,6 +108,7 @@
     int got_stdin;
     const char * device_name = NULL;
     char file_name[256];
+    char b[80];
     char ebuff[EBUFF_SZ];
     const char * ten_or;
     int ret = 1;
@@ -265,32 +266,13 @@
     switch (res) {
     case 0:
         break;
-    case SG_LIB_CAT_NOT_READY:
-        fprintf(stderr, "  SCSI WRITE LONG (%s) failed, device not ready\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_UNIT_ATTENTION:
-        fprintf(stderr, "  SCSI WRITE LONG (%s), unit attention\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_ABORTED_COMMAND:
-        fprintf(stderr, "  SCSI WRITE LONG (%s), aborted command\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_INVALID_OP:
-        fprintf(stderr, "  SCSI WRITE LONG (%s) command not supported\n",
-                ten_or);
-        break;
-    case SG_LIB_CAT_ILLEGAL_REQ:
-        fprintf(stderr, "  SCSI WRITE LONG (%s) command, bad field in cdb\n",
-                ten_or);
-        break;
     case SG_LIB_CAT_ILLEGAL_REQ_WITH_INFO:
         fprintf(stderr, "<<< device indicates 'xfer_len' should be %d "
                 ">>>\n", xfer_len - offset);
         break;
     default:
-        fprintf(stderr, "  SCSI WRITE LONG (%s) command error\n", ten_or);
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        fprintf(stderr, "  SCSI WRITE LONG (%s): %s\n", ten_or, b);
         break;
     }
 
diff --git a/src/sg_write_same.c b/src/sg_write_same.c
index d4665d9..b77f9b6 100644
--- a/src/sg_write_same.c
+++ b/src/sg_write_same.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2013 Douglas Gilbert.
+ * Copyright (c) 2009-2014 Douglas Gilbert.
  * All rights reserved.
  * Use of this source code is governed by a BSD-style
  * license that can be found in the BSD_LICENSE file.
@@ -26,7 +26,7 @@
 #include "sg_cmds_basic.h"
 #include "sg_cmds_extra.h"
 
-static const char * version_str = "1.05 20131201";
+static const char * version_str = "1.06 20140516";
 
 
 #define ME "sg_write_same: "
@@ -273,13 +273,6 @@
         ;
     else if (-2 == ret) {
         switch (sense_cat) {
-        case SG_LIB_CAT_NOT_READY:
-        case SG_LIB_CAT_UNIT_ATTENTION:
-        case SG_LIB_CAT_INVALID_OP:
-        case SG_LIB_CAT_ILLEGAL_REQ:
-        case SG_LIB_CAT_ABORTED_COMMAND:
-            ret = sense_cat;
-            break;
         case SG_LIB_CAT_RECOVERED:
         case SG_LIB_CAT_NO_SENSE:
             ret = 0;
@@ -298,7 +291,7 @@
             ret = sense_cat;
             break;
         default:
-            ret = -1;
+            ret = sense_cat;
             break;
         }
     } else
@@ -321,16 +314,19 @@
     uint32_t block_size;
     const char * device_name = NULL;
     char ebuff[EBUFF_SZ];
+    char b[80];
     unsigned char resp_buff[RCAP16_RESP_LEN];
     unsigned char * wBuff = NULL;
     int ret = -1;
     struct opts_t opts;
+    struct opts_t * op;
     struct stat a_stat;
 
-    memset(&opts, 0, sizeof(opts));
-    opts.numblocks = DEF_WS_NUMBLOCKS;
-    opts.pref_cdb_size = DEF_WS_CDB_SIZE;
-    opts.timeout = DEF_TIMEOUT_SECS;
+    op = &opts;
+    memset(op, 0, sizeof(opts));
+    op->numblocks = DEF_WS_NUMBLOCKS;
+    op->pref_cdb_size = DEF_WS_CDB_SIZE;
+    op->timeout = DEF_TIMEOUT_SECS;
     vb = 0;
     while (1) {
         int option_index = 0;
@@ -342,11 +338,11 @@
 
         switch (c) {
         case 'a':
-            ++opts.anchor;
+            ++op->anchor;
             break;
         case 'g':
-            opts.grpnum = sg_get_num(optarg);
-            if ((opts.grpnum < 0) || (opts.grpnum > 31))  {
+            op->grpnum = sg_get_num(optarg);
+            if ((op->grpnum < 0) || (op->grpnum > 31))  {
                 fprintf(stderr, "bad argument to '--grpnum'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
@@ -356,7 +352,7 @@
             usage();
             return 0;
         case 'i':
-            strncpy(opts.ifilename, optarg, sizeof(opts.ifilename));
+            strncpy(op->ifilename, optarg, sizeof(op->ifilename));
             if_given = 1;
             break;
         case 'l':
@@ -365,71 +361,71 @@
                 fprintf(stderr, "bad argument to '--lba'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
-            opts.lba = (uint64_t)ll;
+            op->lba = (uint64_t)ll;
             lba_given = 1;
             break;
         case 'L':
-            ++opts.lbdata;
+            ++op->lbdata;
             break;
         case 'n':
-            opts.numblocks = sg_get_num(optarg);
-            if (opts.numblocks < 0)  {
+            op->numblocks = sg_get_num(optarg);
+            if (op->numblocks < 0)  {
                 fprintf(stderr, "bad argument to '--num'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             num_given = 1;
             break;
         case 'N':
-            ++opts.ndob;
+            ++op->ndob;
             break;
         case 'P':
-            ++opts.pbdata;
+            ++op->pbdata;
             break;
         case 'R':
-            ++opts.want_ws10;
+            ++op->want_ws10;
             break;
         case 'S':
-            if (DEF_WS_CDB_SIZE != opts.pref_cdb_size) {
+            if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
                 fprintf(stderr, "only one '--10', '--16' or '--32' "
                         "please\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
-            opts.pref_cdb_size = 16;
+            op->pref_cdb_size = 16;
             break;
         case 't':
-            opts.timeout = sg_get_num(optarg);
-            if (opts.timeout < 0)  {
+            op->timeout = sg_get_num(optarg);
+            if (op->timeout < 0)  {
                 fprintf(stderr, "bad argument to '--timeout'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
         case 'T':
-            if (DEF_WS_CDB_SIZE != opts.pref_cdb_size) {
+            if (DEF_WS_CDB_SIZE != op->pref_cdb_size) {
                 fprintf(stderr, "only one '--10', '--16' or '--32' "
                         "please\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
-            opts.pref_cdb_size = 32;
+            op->pref_cdb_size = 32;
             break;
         case 'U':
-            ++opts.unmap;
+            ++op->unmap;
             break;
         case 'v':
-            ++opts.verbose;
+            ++op->verbose;
             break;
         case 'V':
             fprintf(stderr, ME "version: %s\n", version_str);
             return 0;
         case 'w':
-            opts.wrprotect = sg_get_num(optarg);
-            if ((opts.wrprotect < 0) || (opts.wrprotect > 7))  {
+            op->wrprotect = sg_get_num(optarg);
+            if ((op->wrprotect < 0) || (op->wrprotect > 7))  {
                 fprintf(stderr, "bad argument to '--wrprotect'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
             break;
         case 'x':
-            opts.xfer_len = sg_get_num(optarg);
-            if (opts.xfer_len < 0) {
+            op->xfer_len = sg_get_num(optarg);
+            if (op->xfer_len < 0) {
                 fprintf(stderr, "bad argument to '--xferlen'\n");
                 return SG_LIB_SYNTAX_ERROR;
             }
@@ -453,7 +449,7 @@
             return SG_LIB_SYNTAX_ERROR;
         }
     }
-    if (opts.want_ws10 && (DEF_WS_CDB_SIZE != opts.pref_cdb_size)) {
+    if (op->want_ws10 && (DEF_WS_CDB_SIZE != op->pref_cdb_size)) {
         fprintf(stderr, "only one '--10', '--16' or '--32' please\n");
         return SG_LIB_SYNTAX_ERROR;
     }
@@ -462,7 +458,7 @@
         usage();
         return SG_LIB_SYNTAX_ERROR;
     }
-    vb = opts.verbose;
+    vb = op->verbose;
 
     if ((! if_given) && (! lba_given) && (! num_given)) {
         fprintf(stderr, "As a precaution require one of '--in=', '--lba=' "
@@ -470,28 +466,28 @@
         return SG_LIB_SYNTAX_ERROR;
     }
 
-    if (opts.ndob) {
+    if (op->ndob) {
         if (if_given) {
             fprintf(stderr, "Can't have both --ndob and '--in='\n");
             return SG_LIB_SYNTAX_ERROR;
         }
-        if (0 != opts.xfer_len) {
+        if (0 != op->xfer_len) {
             fprintf(stderr, "With --ndob only '--xferlen=0' (or not given) "
                     "is acceptable\n");
             return SG_LIB_SYNTAX_ERROR;
         }
-    } else if (opts.ifilename[0]) {
-        got_stdin = (0 == strcmp(opts.ifilename, "-")) ? 1 : 0;
+    } else if (op->ifilename[0]) {
+        got_stdin = (0 == strcmp(op->ifilename, "-")) ? 1 : 0;
         if (! got_stdin) {
             memset(&a_stat, 0, sizeof(a_stat));
-            if (stat(opts.ifilename, &a_stat) < 0) {
+            if (stat(op->ifilename, &a_stat) < 0) {
                 if (vb)
                     fprintf(stderr, "unable to stat(%s): %s\n",
-                            opts.ifilename, safe_strerror(errno));
+                            op->ifilename, safe_strerror(errno));
                 return SG_LIB_FILE_ERROR;
             }
-            if (opts.xfer_len <= 0)
-                opts.xfer_len = (int)a_stat.st_size;
+            if (op->xfer_len <= 0)
+                op->xfer_len = (int)a_stat.st_size;
         }
     }
 
@@ -502,9 +498,9 @@
         return SG_LIB_FILE_ERROR;
     }
 
-    if (! opts.ndob) {
+    if (! op->ndob) {
         prot_en = 0;
-        if (0 == opts.xfer_len) {
+        if (0 == op->xfer_len) {
             res = sg_ll_readcap_16(sg_fd, 0 /* pmi */, 0 /* llba */, resp_buff,
                                    RCAP16_RESP_LEN, 1, (vb ? (vb - 1): 0));
             if (SG_LIB_CAT_UNIT_ATTENTION == res) {
@@ -521,9 +517,9 @@
                               (resp_buff[10] << 8) |
                               resp_buff[11]);
                 prot_en = !!(resp_buff[12] & 0x1);
-                opts.xfer_len = block_size;
-                if (prot_en && (opts.wrprotect > 0))
-                    opts.xfer_len += 8;
+                op->xfer_len = block_size;
+                if (prot_en && (op->wrprotect > 0))
+                    op->xfer_len += 8;
             } else if ((SG_LIB_CAT_INVALID_OP == res) ||
                        (SG_LIB_CAT_ILLEGAL_REQ == res)) {
                 if (vb)
@@ -540,61 +536,65 @@
                                   (resp_buff[5] << 16) |
                                   (resp_buff[6] << 8) |
                                   resp_buff[7]);
-                    opts.xfer_len = block_size;
-                } else
-                    fprintf(stderr, "Read capacity(10) failed. Unable to "
-                            "calculate block size\n");
-            } else if (vb)
-                fprintf(stderr, "Read capacity(16) failed. Unable to "
-                        "calculate block size\n");
+                    op->xfer_len = block_size;
+                } else {
+                    sg_get_category_sense_str(res, sizeof(b), b, vb);
+                    fprintf(stderr, "Read capacity(10): %s\n", b);
+                    fprintf(stderr, "Unable to calculate block size\n");
+                }
+            } else if (vb) {
+                sg_get_category_sense_str(res, sizeof(b), b, vb);
+                fprintf(stderr, "Read capacity(16): %s\n", b);
+                fprintf(stderr, "Unable to calculate block size\n");
+            }
         }
-        if (opts.xfer_len < 1) {
+        if (op->xfer_len < 1) {
             fprintf(stderr, "unable to deduce block size, please give "
                     "'--xferlen=' argument\n");
             ret = SG_LIB_SYNTAX_ERROR;
             goto err_out;
         }
-        if (opts.xfer_len > MAX_XFER_LEN) {
+        if (op->xfer_len > MAX_XFER_LEN) {
             fprintf(stderr, "'--xferlen=%d is out of range ( want <= %d)\n",
-                    opts.xfer_len, MAX_XFER_LEN);
+                    op->xfer_len, MAX_XFER_LEN);
             ret = SG_LIB_SYNTAX_ERROR;
             goto err_out;
         }
-        wBuff = (unsigned char*)calloc(opts.xfer_len, 1);
+        wBuff = (unsigned char*)calloc(op->xfer_len, 1);
         if (NULL == wBuff) {
             fprintf(stderr, "unable to allocate %d bytes of memory with "
-                    "calloc()\n", opts.xfer_len);
+                    "calloc()\n", op->xfer_len);
             ret = SG_LIB_SYNTAX_ERROR;
             goto err_out;
         }
-        if (opts.ifilename[0]) {
+        if (op->ifilename[0]) {
             if (got_stdin) {
                 infd = STDIN_FILENO;
                 if (sg_set_binary_mode(STDIN_FILENO) < 0)
                     perror("sg_set_binary_mode");
             } else {
-                if ((infd = open(opts.ifilename, O_RDONLY)) < 0) {
+                if ((infd = open(op->ifilename, O_RDONLY)) < 0) {
                     snprintf(ebuff, EBUFF_SZ, ME "could not open %s for "
-                             "reading", opts.ifilename);
+                             "reading", op->ifilename);
                     perror(ebuff);
                     ret = SG_LIB_FILE_ERROR;
                     goto err_out;
                 } else if (sg_set_binary_mode(infd) < 0)
                     perror("sg_set_binary_mode");
             }
-            res = read(infd, wBuff, opts.xfer_len);
+            res = read(infd, wBuff, op->xfer_len);
             if (res < 0) {
                 snprintf(ebuff, EBUFF_SZ, ME "couldn't read from %s",
-                         opts.ifilename);
+                         op->ifilename);
                 perror(ebuff);
                 if (! got_stdin)
                     close(infd);
                 ret = SG_LIB_FILE_ERROR;
                 goto err_out;
             }
-            if (res < opts.xfer_len) {
+            if (res < op->xfer_len) {
                 fprintf(stderr, "tried to read %d bytes from %s, got %d "
-                        "bytes\n", opts.xfer_len, opts.ifilename, res);
+                        "bytes\n", op->xfer_len, op->ifilename, res);
                 fprintf(stderr, "  so pad with 0x0 bytes and continue\n");
             }
             if (! got_stdin)
@@ -602,10 +602,10 @@
         } else {
             if (vb)
                 fprintf(stderr, "Default data-out buffer set to %d zeros\n",
-                        opts.xfer_len);
-            if (prot_en && (opts.wrprotect > 0)) {
+                        op->xfer_len);
+            if (prot_en && (op->wrprotect > 0)) {
                /* default for protection is 0xff, rest get 0x0 */
-                memset(wBuff + opts.xfer_len - 8, 0xff, 8);
+                memset(wBuff + op->xfer_len - 8, 0xff, 8);
                 if (vb)
                     fprintf(stderr, " ... apart from last 8 bytes which are "
                             "set to 0xff\n");
@@ -613,35 +613,10 @@
         }
     }
 
-    ret = do_write_same(sg_fd, &opts, wBuff, &act_cdb_len);
+    ret = do_write_same(sg_fd, op, wBuff, &act_cdb_len);
     if (ret) {
-        switch (ret) {
-        case SG_LIB_CAT_NOT_READY:
-            fprintf(stderr, "Write same(%d) failed, device not ready\n",
-                    act_cdb_len);
-            break;
-        case SG_LIB_CAT_UNIT_ATTENTION:
-            fprintf(stderr, "Write same(%d), unit attention\n", act_cdb_len);
-            break;
-        case SG_LIB_CAT_ABORTED_COMMAND:
-            fprintf(stderr, "Write same(%d), aborted command\n", act_cdb_len);
-            break;
-        case SG_LIB_CAT_INVALID_OP:
-            fprintf(stderr, "Write same(%d) command not supported\n",
-                    act_cdb_len);
-            break;
-        case SG_LIB_CAT_ILLEGAL_REQ:
-            fprintf(stderr, "bad field in Write same(%d) cdb, option "
-                    "probably not supported\n", act_cdb_len);
-            break;
-        case SG_LIB_CAT_MEDIUM_HARD:
-            fprintf(stderr, "Write same(%d) command reported medium or "
-                    "hardware error\n", act_cdb_len);
-            break;
-        default:
-            fprintf(stderr, "Write same(%d) command failed\n", act_cdb_len);
-            break;
-        }
+        sg_get_category_sense_str(ret, sizeof(b), b, vb);
+        fprintf(stderr, "Write same(%d): %s\n", act_cdb_len, b);
     }
 
 err_out:
diff --git a/src/sg_xcopy.c b/src/sg_xcopy.c
index 42e2389..5b1f4cd 100644
--- a/src/sg_xcopy.c
+++ b/src/sg_xcopy.c
@@ -62,7 +62,7 @@
 #include "sg_cmds_extra.h"
 #include "sg_io_linux.h"
 
-static const char * version_str = "0.44 20140312";
+static const char * version_str = "0.45 20140516";
 
 #define ME "sg_xcopy: "
 
@@ -638,7 +638,8 @@
     unsigned char xcopyBuff[256];
     int desc_offset = 16;
     int seg_desc_len;
-    int verb;
+    int verb, res;
+    char b[80];
 
     verb = (verbose > 1) ? (verbose - 2) : 0;
     memset(xcopyBuff, 0, 256);
@@ -656,9 +657,14 @@
     xcopyBuff[11] = seg_desc_len; /* One segment descriptor */
     desc_offset += seg_desc_len;
     /* set noisy so if a UA happens it will be printed to stderr */
-    return sg_ll_3party_copy_out(sg_fd, SA_XCOPY_LID1, list_id,
-                                 DEF_GROUP_NUM, DEF_3PC_OUT_TIMEOUT,
-                                 xcopyBuff, desc_offset, 1, verb);
+    res = sg_ll_3party_copy_out(sg_fd, SA_XCOPY_LID1, list_id,
+                                DEF_GROUP_NUM, DEF_3PC_OUT_TIMEOUT,
+                                xcopyBuff, desc_offset, 1, verb);
+    if (res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Xcopy(LID1): %s\n", b);
+    }
+    return res;
 }
 
 /* Return of 0 -> success, see sg_ll_read_capacity*() otherwise */
@@ -669,12 +675,16 @@
     unsigned int ui;
     unsigned char rcBuff[RCAP16_REPLY_LEN];
     int verb;
+    char b[80];
 
     verb = (verbose ? verbose - 1: 0);
     res = sg_ll_readcap_10(xfp->sg_fd, 0, 0, rcBuff,
                            READ_CAP_REPLY_LEN, 1, verb);
-    if (0 != res)
+    if (0 != res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Read capacity(10): %s\n", b);
         return res;
+    }
 
     if ((0xff == rcBuff[0]) && (0xff == rcBuff[1]) && (0xff == rcBuff[2]) &&
         (0xff == rcBuff[3])) {
@@ -682,8 +692,11 @@
 
         res = sg_ll_readcap_16(xfp->sg_fd, 0, 0, rcBuff,
                                RCAP16_REPLY_LEN, 1, verb);
-        if (0 != res)
+        if (0 != res) {
+            sg_get_category_sense_str(res, sizeof(b), b, verb);
+            pr2serr("Read capacity(16): %s\n", b);
             return res;
+        }
         for (k = 0, ls = 0; k < 8; ++k) {
             ls <<= 8;
             ls |= rcBuff[k];
@@ -715,6 +728,7 @@
     unsigned long num, max_target_num, max_segment_num, max_segment_len;
     unsigned long max_desc_len, max_inline_data, held_data_limit;
     int verb, valid = 0;
+    char b[80];
 
     verb = (verbose ? verbose - 1: 0);
     ftype = xfp->sg_type;
@@ -726,8 +740,11 @@
     }
     res = sg_ll_receive_copy_results(xfp->sg_fd, SA_COPY_OP_PARAMS, 0, rcBuff,
                                      rcBuffLen, 1, verb);
-    if (0 != res)
+    if (0 != res) {
+        sg_get_category_sense_str(res, sizeof(b), b, verb);
+        pr2serr("Xcopy operating parameters: %s\n", b);
         return -res;
+    }
 
     len = ((rcBuff[0] << 24) | (rcBuff[1] << 16) | (rcBuff[2] << 8) |
            rcBuff[3]) + 4;
@@ -1210,6 +1227,7 @@
     unsigned char rcBuff[256], *ucp, *best = NULL;
     unsigned int len = 254;
     int off = -1, u, i_len, best_len = 0, assoc, desig, f_desig = 0;
+    char b[80];
 
     verb = (verbose ? verbose - 1: 0);
     memset(rcBuff, 0xff, len);
@@ -1217,9 +1235,11 @@
     if (0 != res) {
         if (SG_LIB_CAT_ILLEGAL_REQ == res)
             pr2serr("Device identification VPD page not found\n");
-        else
-            pr2serr("VPD inquiry failed with %d, try again with '-vv'\n",
-                    res);
+        else {
+            sg_get_category_sense_str(res, sizeof(b), b, verbose);
+            pr2serr("VPD inquiry (Device ID): %s\n", b);
+            pr2serr("   try again with '-vv'\n");
+        }
         return res;
     } else if (rcBuff[1] != VPD_DEVICE_ID) {
         pr2serr("invalid VPD response\n");
@@ -1228,7 +1248,8 @@
     len = ((rcBuff[2] << 8) + rcBuff[3]) + 4;
     res = sg_ll_inquiry(sg_fd, 0, 1, VPD_DEVICE_ID, rcBuff, len, 1, verb);
     if (0 != res) {
-        pr2serr("VPD inquiry failed with %d\n", res);
+        sg_get_category_sense_str(res, sizeof(b), b, verbose);
+        pr2serr("VPD inquiry (Device ID): %s\n", b);
         return res;
     } else if (rcBuff[1] != VPD_DEVICE_ID) {
         pr2serr("invalid VPD response\n");
diff --git a/src/sgm_dd.c b/src/sgm_dd.c
index 49d5376..49e3b10 100644
--- a/src/sgm_dd.c
+++ b/src/sgm_dd.c
@@ -1,7 +1,7 @@
 /* A utility program for copying files. Specialised for "files" that
  * represent devices that understand the SCSI command set.
  *
- * Copyright (C) 1999 - 2013 D. Gilbert and P. Allworth
+ * Copyright (C) 1999 - 2014 D. Gilbert and P. Allworth
  * 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 Software Foundation; either version 2, or (at your option)
@@ -65,9 +65,9 @@
 /* #define SG_WANT_SHARED_MMAP_IO 1 */
 
 #ifdef SG_WANT_SHARED_MMAP_IO
-static const char * version_str = "1.38 20131014 shared_mmap";
+static const char * version_str = "1.39 20140516 shared_mmap";
 #else
-static const char * version_str = "1.38 20131014";
+static const char * version_str = "1.39 20140516";
 #endif
 
 #define DEF_BLOCK_SIZE 512
@@ -518,10 +518,8 @@
     return 0;
 }
 
-/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_SYNTAX_ERROR,
- * SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, SG_LIB_CAT_ILLEGAL_REQ,
- * SG_LIB_CAT_ABORTED_COMMAND, -2 -> recoverable (ENOMEM),
- * -1 -> unrecoverable error */
+/* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
 static int
 sg_read(int sg_fd, unsigned char * buff, int blocks, int64_t from_block,
         int bs, int cdbsz, int fua, int dpo, int do_mmap)
@@ -609,10 +607,8 @@
     return 0;
 }
 
-/* 0 -> successful, SG_LIB_CAT_UNIT_ATTENTION, SG_LIB_SYNTAX_ERROR,
- * SG_LIB_CAT_NOT_READY, SG_LIB_CAT_MEDIUM_HARD, SG_LIB_CAT_ILLEGAL_REQ,
- * SG_LIB_CAT_ABORTED_COMMAND, -2 -> recoverable (ENOMEM),
- * -1 -> unrecoverable error */
+/* Returns 0 -> successful, various SG_LIB_CAT_* positive values,
+ * -2 -> recoverable (ENOMEM), -1 -> unrecoverable error */
 static int
 sg_write(int sg_fd, unsigned char * buff, int blocks, int64_t to_block,
          int bs, int cdbsz, int fua, int dpo, int do_mmap,
@@ -807,6 +803,7 @@
     int in_sect_sz, out_sect_sz;
     int n, flags;
     char ebuff[EBUFF_SZ];
+    char b[80];
     int blocks_per;
     size_t psz;
     struct flags_t in_flags;
@@ -1183,14 +1180,8 @@
                 res = scsi_read_capacity(infd, &in_num_sect, &in_sect_sz);
             }
             if (0 != res) {
-                if (res == SG_LIB_CAT_INVALID_OP)
-                    fprintf(stderr, "read capacity not supported on %s\n",
-                            inf);
-                else if (res == SG_LIB_CAT_NOT_READY)
-                    fprintf(stderr, "read capacity failed, %s not ready\n",
-                            inf);
-                else
-                    fprintf(stderr, "Unable to read capacity on %s\n", inf);
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "Read capacity (if=%s): %s\n", inf, b);
                 in_num_sect = -1;
             }
         } else if (FT_BLOCK == in_type) {
@@ -1218,14 +1209,8 @@
                 res = scsi_read_capacity(outfd, &out_num_sect, &out_sect_sz);
             }
             if (0 != res) {
-                if (res == SG_LIB_CAT_INVALID_OP)
-                    fprintf(stderr, "read capacity not supported on %s\n",
-                            outf);
-                else if (res == SG_LIB_CAT_NOT_READY)
-                    fprintf(stderr, "read capacity failed, %s not ready\n",
-                            outf);
-                else
-                    fprintf(stderr, "Unable to read capacity on %s\n", outf);
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "Read capacity (of=%s): %s\n", inf, b);
                 out_num_sect = -1;
             }
         } else if (FT_BLOCK == out_type) {
@@ -1470,8 +1455,10 @@
                 fprintf(stderr, "Unit attention(out), continuing\n");
                 res = sg_ll_sync_cache_10(outfd, 0, 0, 0, 0, 0, 0, 0);
             }
-            if (0 != res)
-                fprintf(stderr, "Unable to synchronize cache\n");
+            if (0 != res) {
+                sg_get_category_sense_str(res, sizeof(b), b, verbose);
+                fprintf(stderr, "Synchronize cache(out): %s\n", b);
+            }
         }
     }
 
diff --git a/src/sgp_dd.c b/src/sgp_dd.c
index 017ad97..40b7350 100644
--- a/src/sgp_dd.c
+++ b/src/sgp_dd.c
@@ -1,28 +1,27 @@
 /* A utility program for copying files. Specialised for "files" that
-*  represent devices that understand the SCSI command set.
-*
-*  Copyright (C) 1999 - 2013 D. Gilbert and P. Allworth
-*  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 Software Foundation; either version 2, or (at your option)
-*  any later version.
-
-   This program is a specialisation of the Unix "dd" command in which
-   one or both of the given files is a scsi generic device or a raw
-   device. A block size ('bs') is assumed to be 512 if not given. This
-   program complains if 'ibs' or 'obs' are given with some other value
-   than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
-   'of' is not given or 'of=-' then stdout assumed.
-
-   A non-standard argument "bpt" (blocks per transfer) is added to control
-   the maximum number of blocks in each transfer. The default value is 128.
-   For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
-   in this case) are transferred to or from the sg device in a single SCSI
-   command.
-
-   This version is designed for the linux kernel 2.4, 2.6 and 3 series.
-
-*/
+ * represent devices that understand the SCSI command set.
+ *
+ * Copyright (C) 1999 - 2014 D. Gilbert and P. Allworth
+ * 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 Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is a specialisation of the Unix "dd" command in which
+ * one or both of the given files is a scsi generic device or a raw
+ * device. A block size ('bs') is assumed to be 512 if not given. This
+ * program complains if 'ibs' or 'obs' are given with some other value
+ * than 'bs'. If 'if' is not given or 'if=-' then stdin is assumed. If
+ * 'of' is not given or 'of=-' then stdout assumed.
+ *
+ * A non-standard argument "bpt" (blocks per transfer) is added to control
+ * the maximum number of blocks in each transfer. The default value is 128.
+ * For example if "bs=512" and "bpt=32" then a maximum of 32 blocks (16 KiB
+ * in this case) are transferred to or from the sg device in a single SCSI
+ * command.
+ *
+ * This version is designed for the linux kernel 2.4, 2.6 and 3 series.
+ */
 
 #define _XOPEN_SOURCE 500
 #ifndef _GNU_SOURCE
@@ -57,7 +56,7 @@
 #include "sg_io_linux.h"
 
 
-static const char * version_str = "5.46 20131110";
+static const char * version_str = "5.47 20140516";
 
 #define DEF_BLOCK_SIZE 512
 #define DEF_BLOCKS_PER_TRANSFER 128