home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1993 #2 / Image.iso / os2 / gtak212b.zip / SOURCE.ZIP / TAPE / tctl.c < prev   
C/C++ Source or Header  |  1992-10-12  |  25KB  |  917 lines

  1. /*****************************************************************************
  2.  * $Id: tctl.c,v 1.4 1992/10/12 19:48:41 ak Exp $
  3.  *****************************************************************************
  4.  * $Log: tctl.c,v $
  5.  * Revision 1.4  1992/10/12  19:48:41  ak
  6.  * Upper/lower case was significant.
  7.  *
  8.  * Revision 1.3  1992/09/12  18:10:59  ak
  9.  * Added scsi_name
  10.  * Added device name support to tctl.c
  11.  *
  12.  * Revision 1.2  1992/09/02  19:05:40  ak
  13.  * Version 2.0
  14.  * - EMX version
  15.  * - AIX version
  16.  * - SCSI-2 commands
  17.  * - ADD Driver
  18.  * - blocksize support
  19.  *
  20.  * Revision 1.1.1.1  1992/01/06  20:27:09  ak
  21.  * Interface now based on ST01 and ASPI.
  22.  * AHA_DRVR no longer supported.
  23.  * Files reorganized.
  24.  *
  25.  * Revision 1.1  1992/01/06  20:27:08  ak
  26.  * Initial revision
  27.  *
  28.  *****************************************************************************/
  29.  
  30. static char *rcsid = "$Id: tctl.c,v 1.4 1992/10/12 19:48:41 ak Exp $";
  31.  
  32. /*
  33.  *    tctl.c
  34.  *
  35.  * Tape Control.
  36.  */
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <ctype.h>
  41. #include <fcntl.h>
  42. #ifndef unix
  43. #include <io.h>
  44. #endif
  45. #include <limits.h>
  46. #include <sys/types.h>
  47. #include <sys/stat.h>
  48. #include "scsi.h"
  49. #include "tape.h"
  50. #include "scsitape.h"
  51.  
  52. #define NBLOCKS     60
  53.  
  54. #define INT16(x) ((x)[0] << 8 | (x)[1])
  55. #define INT24(x) ((unsigned long)INT16(x) << 8 | (x)[2])
  56. #define INT32(x) (INT24(x) << 8 | (x)[3])
  57.  
  58. #ifdef __EMX__
  59.   #define strtoul    strtol
  60. #endif
  61.  
  62. #ifndef O_BINARY
  63. #define O_BINARY 0
  64. #endif
  65.  
  66. static ExtSenseData    sense;
  67. static unsigned char    cdb [12];
  68. static unsigned char    inq [255];
  69. static unsigned char    mode [255];
  70. static unsigned char    buffer [NBLOCKS][512];
  71. static int        trace = 0;
  72. static FILE *        out;
  73.  
  74. extern int        scsiLevel;
  75.  
  76. typedef int    (*fcnptr)(void _far *, long);
  77.  
  78. #ifdef unix
  79.  
  80. int
  81. strnicmp(char *x, char *y, unsigned n)
  82. {
  83.     while (n--) {
  84.         char xx = isupper(*x) ? tolower(*x) : *x;
  85.         char yy = isupper(*y) ? tolower(*y) : *y;
  86.         if (xx < yy)
  87.             return -1;
  88.         if (xx > yy)
  89.             return 1;
  90.         if (xx == '\0')
  91.             return 0;
  92.     }
  93.     return 0;
  94. }
  95.  
  96. int
  97. stricmp(char *x, char *y)
  98. {
  99.     return strnicmp(x, y, UINT_MAX);
  100. }
  101.  
  102. #endif
  103.  
  104. static void
  105. print_inquiry(void)
  106. {
  107.     int k;
  108.  
  109.     if ((inq[0] & 0x1F) == 0x01)
  110.         fprintf(out, "Tape device");
  111.     else
  112.         fprintf(out, "Device type %02X", inq[0] & 0x1F);
  113.     if (inq[0] & 0xE0)
  114.         fprintf(out, ", qualifier %X", inq[0] >> 5);
  115.     if (inq[1] & 0x7F)
  116.         fprintf(out, ", modifier %02X", inq[1] & 0x7F);
  117.     fprintf(out, ", %sremovable\n", (inq[1] & 0x80) ? "" : "not ");
  118.     fprintf(out, "ISO %d, ECMA %d, ANSI %d\n",
  119.         inq[2] >> 6 & 3, inq[2] >> 3 & 7, inq[2] >> 0 & 3);
  120.     fprintf(out, "can%s terminate i/o process\n", (inq[3] & 0x40) ? "" : "not");
  121.     if (inq[3] & 0x0F)
  122.         fprintf(out, "response data format is SCSI-%d\n", inq[3] & 0x0F);
  123.     else
  124.         fprintf(out, "old response data format (perhaps CCS)\n");
  125.     fprintf(out, "relative addressing %ssupported\n", (inq[7] & 0x80) ? "" : "not ");
  126.     if (inq[7] & 0x40)
  127.         fprintf(out, "32-bit bus width supported\n");
  128.     if (inq[7] & 0x20)
  129.         fprintf(out, "16-bit bus width supported\n");
  130.     fprintf(out, "synchronous transfers %ssupported\n", (inq[7] & 0x10) ? "" : "not ");
  131.     fprintf(out, "linked commands %ssupported\n", (inq[7] & 0x08) ? "" : "not ");
  132.     fprintf(out, "caching %ssupported\n", (inq[7] & 0x04) ? "" : "not ");
  133.     fprintf(out, "command queueing %ssupported\n", (inq[7] & 0x02) ? "" : "not ");
  134.     if (!(inq[7] & 0x01))
  135.         fprintf(out, "soft reset is hard reset\n");
  136.     fprintf(out, "identification '%.48s'\n", inq+8);
  137.     fprintf(out, "\n");
  138. }
  139.  
  140. static void
  141. dump(unsigned char *p, int len)
  142. {
  143.     int r, c;
  144.     for (r = 0; r < len; r += 16) {
  145.         fprintf(out, "\t");
  146.         for (c = r; c < r + 16; c += 1)
  147.             if (c < len)
  148.                 fprintf(out, "%02X ", p[c]);
  149.             else
  150.                 fprintf(out, "   ");
  151.         fprintf(out, "  ");
  152.         for (c = r; c < r + 16; c += 1)
  153.             if (c < len)
  154.                 fprintf(out, "%c", isprint(p[c]) ? p[c] : '.');
  155.         fprintf(out, "\n");
  156.     }
  157. }
  158.  
  159. static void
  160. print_mode(int page, int skip)
  161. {
  162.     int e, b, k, df;
  163.     static char * pageval[] = {
  164.         "current",
  165.         "changeable",
  166.         "default",
  167.         "saved"
  168.     };
  169.     static char * ifcid[] = {
  170.         "SCSI (X3.131)",
  171.         "SMI (X3.91M-1987",
  172.         "ESDI (X3.170)",
  173.         "IPI-2 (X3.130-1986; X3T9.3(87-002",
  174.         "IPI-3 (X3.132-1987; X3.147-1988",
  175.     };
  176.  
  177.     if (skip)
  178.         b = 4 + mode[3];
  179.     else {
  180.         fprintf(out, "Header:\tmedia type %02X", mode[1]);
  181.         if (mode[2] & 0x80)
  182.             fprintf(out, ", write protected");
  183.         if (mode[2] & 0x70)
  184.             fprintf(out, ", buffer mode %d", (mode[2] & 0x70) >> 4);
  185.         else
  186.             fprintf(out, ", unbuffered");
  187.         fprintf(out, ", speed code %d\n", mode[2] & 0xF);
  188.         fprintf(out, "\n");
  189.         if (trace)
  190.             dump(mode, 4);
  191.  
  192.         for (b = 4; b < 4 + mode[3]; b += 8) {
  193.             fprintf(out, "Block:\tdensity code %02X, %ld blocks of %ld bytes\n",
  194.                 mode[b+0], INT24(mode+b+1), INT24(mode+b+5));
  195.             if (trace)
  196.                 dump(mode+4+b, 8);
  197.         }
  198.     }
  199.     
  200.     fprintf(out, "Page %02X (%s values, %ssavable): ",
  201.         page & 0x3F,
  202.         pageval[(page & 0xC0) >> 6],
  203.         (mode[b+0] & 0x80) ? "" : "not ");
  204.  
  205.     switch(k = mode[b+0] & 0x3F) {
  206.  
  207.     case 0x00:
  208.         fprintf(out, "Vendor specific\n");
  209.         dump(mode+b, 12);
  210.         return;
  211.  
  212.     case 0x01:
  213.         fprintf(out, "Read-Write Error Recovery\n");
  214.         fprintf(out, "\t%stransfer block on error\n",
  215.             mode[b+2] & 0x20 ? "" : "do not ");
  216.         fprintf(out, "\tearly recovery %sabled\n",
  217.             mode[b+2] & 0x08 ? "en" : "dis");
  218.         fprintf(out, "\t%spost recovered errors\n",
  219.             mode[b+2] & 0x04 ? "" : "do not ");
  220.         fprintf(out, "\t%sterminate transfer on error\n",
  221.             mode[b+2] & 0x02 ? "" : "do not ");
  222.         fprintf(out, "\terror correction %sabled\n",
  223.             mode[b+2] & 0x08 ? "en" : "dis");
  224.         fprintf(out, "\twrite retry count: %d\n", mode[b+8]);
  225.         fprintf(out, "\tpost log ready %sabled\n",
  226.             mode[b+9] & 0x80 ? "en" : "dis");
  227.         fprintf(out, "\tattach log to sense %sabled\n",
  228.             mode[b+9] & 0x80 ? "en" : "dis");
  229.         fprintf(out, "\trelative treshold: %d\n", mode[b+9] & 0x0F);
  230.         break;
  231.  
  232.     case 0x02:
  233.         fprintf(out, "Disconnect-Reconnect\n");
  234.         fprintf(out, "\tbuffer full ratio: %u\n", mode[b+2]);
  235.         fprintf(out, "\tbuffer empty ratio: %u\n", mode[b+3]);
  236.         fprintf(out, "\tbus inactivity limit: %lu\n", INT16(mode+b+4));
  237.         fprintf(out, "\tdisconnect time limit: %lu\n", INT16(mode+b+6));
  238.         fprintf(out, "\tconnect time limit: %lu\n", INT16(mode+b+8));
  239.         fprintf(out, "\tmaximum burst size: %lu\n", INT16(mode+b+10));
  240.         if (mode[b+1] >= 13 && mode[b+12] & 3)
  241.             fprintf(out, "\tdon't disconnect within transfer\n");
  242.         return;
  243.  
  244.     case 0x09:
  245.         fprintf(out, "Periperal Device\n");
  246.         fprintf(out, "\tinterface: ");
  247.         if ((k = INT16(mode+b+2)) < 5)
  248.             fprintf(out, "%s\n", ifcid[k]);
  249.         else
  250.             fprintf(out, "%04X\n", k);
  251.         break;
  252.  
  253.     case 0x0A:
  254.         fprintf(out, "Control Mode\n");
  255.         break;
  256.  
  257.     case 0x10:
  258.         fprintf(out, "Device Configuration\n");
  259.         if (mode[b+2] & 0x40)
  260.             fprintf(out, "\tchange active partition to %d\n", mode[b+3]);
  261.         if (mode[b+2] & 0x20)
  262.             fprintf(out, "\tchange active format to %02X\n", mode[b+2] & 0x1F);
  263.         if (mode[b+4] || mode[b+5])
  264.             fprintf(out, "\twrite buffer full ratio %d"
  265.                    ", read buffer empty ratio %d\n",
  266.                     mode[b+4], mode[b+5]);
  267.         if ((k = INT16(mode+b+6)) != 0)
  268.             fprintf(out, "\twrite delay %d.%1d sec\n", k / 10, k % 10);
  269.         if (mode[b+8] & 0x80)
  270.             fprintf(out, "\tsupports data buffer recovery\n");
  271.         if (mode[b+8] & 0x40)
  272.             fprintf(out, "\tsupports block IDs\n");
  273.         if (mode[b+8] & 0x20)
  274.             fprintf(out, "\trecognizes setmarks\n");
  275.         if (mode[b+8] & 0x10)
  276.             fprintf(out, "\tautomatic velocity control\n");
  277.         if (mode[b+8] & 0x0C)
  278.             fprintf(out, "\tstops pre-read on %d consecutive filemarks\n",
  279.                 mode[b+8] >> 2 & 3);
  280.         else
  281.             fprintf(out, "\tno stop on consecutive filemarks\n");
  282.         fprintf(out, "\tbuffer recovery in %s order\n",
  283.                 (mode[b+8] & 0x02) ? "LIFO" : "FIFO");
  284.         if (mode[b+8] & 0x01)
  285.             fprintf(out, "\treturns %s at early warning position for read and write\n",
  286.                 (mode[b+19] & 0x08) ? "EOM" : "VOLUME OVERFLOW");
  287.         else
  288.             fprintf(out, "\tdoes not report early warning on read\n");
  289.         fprintf(out, "\tgap size %02X\n", mode[b+9]);
  290.         fprintf(out, "\t%s data at early warning\n",
  291.             (mode[b+10] & 0x08) ? "synchronizes (flushes)" : "retains");
  292.         switch (mode[b+10] & 0xF0) {
  293.         case 0x10: fprintf(out, "\tdefault EOD generation\n"); break;
  294.         case 0x30: fprintf(out, "\tformat defined EOD generation\n"); break;
  295.         case 0x50: fprintf(out, "\tsee SOCF (\"consecutive filemarks\")\n"); break;
  296.         case 0x70: fprintf(out, "\tEOD not supported\n"); break;
  297.         default:   fprintf(out, "\tEOD generation disabled\n"); break;
  298.         }
  299.         fprintf(out, "\tbuffer size reduced by %d at early warning\n",
  300.             INT24(mode+b+11));
  301.         if (mode[b+1] > 14)
  302.             if (mode[b+14])
  303.                 fprintf(out, "\tcompression mode %d\n", mode[b+14]);
  304.             else
  305.                 fprintf(out, "\tno compression\n");
  306.         break;
  307.  
  308.     case 0x11:
  309.         fprintf(out, "Medium Partition (1)\n");
  310.         fprintf(out, "\t%d additional partitions supported\n", mode[b+2]);
  311.         if (mode[b+4] & 0x80)
  312.             fprintf(out, "\tfixed partition assignments\n");
  313.         else if (mode[b+4] & 0x40)
  314.             fprintf(out, "\tdefine %d device partitions\n",
  315.                 mode[b+3]);
  316.         else if (mode[b+4] & 0x20) {
  317.             static char * psum[] = {"bytes","KB","MB","?"};
  318.             fprintf(out, "\tdefine %d partitions (%s):",
  319.                 psum[mode[b+4] >> 3 & 3]);
  320.             for (k = 0; k < mode[b+3]; ++k)
  321.                 fprintf(out, " %d", INT16(mode+b+8+2*k));
  322.             fprintf(out, "\n");
  323.         }
  324.         switch (mode[b+5]) {
  325.         case 0x00: fprintf(out, "\tdoes not recognize format or partitions\n"); break;
  326.         case 0x01: fprintf(out, "\trecognizes format\n"); break;
  327.         case 0x02: fprintf(out, "\trecognizes partitions\n"); break;
  328.         case 0x03: fprintf(out, "\trecognizes format and partitions\n"); break;
  329.         default:   fprintf(out, "\tformat recognition: %02X\n");
  330.         }
  331.         break;
  332.  
  333.     case 0x12:
  334.         fprintf(out, "Medium Partition (2)\n");
  335.         break;
  336.  
  337.     case 0x13:
  338.         fprintf(out, "Medium Partition (3)\n");
  339.         break;
  340.  
  341.     case 0x14:
  342.         fprintf(out, "Medium Partition (4)\n");
  343.         break;
  344.  
  345.     case 0x20:
  346.         fprintf(out, "Miscellaneous Parameters\n");
  347.         fprintf(out, "\tforced streaming count: %u\n", INT16(mode+b+2));
  348.         fprintf(out, "\tcopy sense allocation: %u\n", mode[b+4]);
  349.         fprintf(out, "\tcopy disconnect %sabled\n",
  350.             mode[b+5] & 1 ? "dis" : "en");
  351.         switch (mode[b+6]) {
  352.         case 0:    fprintf(out, "\tauto load\n"); break;
  353.         case 1:    fprintf(out, "\tauto retension\n"); break;
  354.         case 2:    fprintf(out, "\tno auto load, no auto retension\n"); break;
  355.         }
  356.         fprintf(out, "\tpower-up retension delay: %u.%u sec\n",
  357.             mode[b+7] / 10, mode[b+7] % 10);
  358.         fprintf(out, "\tfast space mode %sabled\n",
  359.             mode[b+8] & 1 ? "en" : "dis");
  360.         break;
  361.  
  362.     default:
  363.         if (k < 0x15)
  364.             fprintf(out, "Reserved\n");
  365.         else if (k == 0x3F)
  366.             fprintf(out, "All pages\n");
  367.         else
  368.             fprintf(out, "Vendor specific\n");
  369.         break;
  370.     }
  371.     dump(mode+b, 2 + mode[b+1]);
  372. }
  373.  
  374. int
  375. read_tape(FILE *file)
  376. {
  377.     int r;
  378.     long actual, count = 0, size = tape_get_blocksize();
  379.     if (size <= 0)
  380.         size = 512;
  381.     else if (size > sizeof buffer) {
  382.         fprintf(out, "blocksize %ld too large\n", size);
  383.         exit(1);
  384.     } else
  385.         size = (sizeof buffer / size) * size;
  386.     do {
  387.         r = tape_read(buffer, size, &actual);
  388.         if (actual != 0 && actual != TapeUndefLength) {
  389.             if (fwrite(buffer, 1, actual, file) != actual) {
  390.                 perror("writing");
  391.                 exit(1);
  392.             }
  393.             count += actual;
  394.         }
  395.     } while (r == 0);
  396.     fflush(file);
  397.     if (ferror(file)) {
  398.         perror("reading");
  399.         exit(1);
  400.     }
  401.     fprintf(out, "    %ld bytes read\n", count);
  402.     return r;
  403. }
  404.  
  405. int
  406. write_tape(FILE *file)
  407. {
  408.     int n, r;
  409.     long count = 0;
  410.     long actual;
  411.     long blocksize = tape_get_blocksize();
  412.     long size = blocksize;
  413.     if (blocksize < 0)
  414.         return blocksize;
  415.     if (size <= 0)
  416.         size = 512;
  417.     else if (size > sizeof buffer) {
  418.         fprintf(out, "blocksize %ld too large\n", size);
  419.         exit(1);
  420.     } else
  421.         size = (sizeof buffer / size) * size;
  422.     while ((n = fread(buffer, 1, size, file)) > 0) {
  423.         if (n % blocksize) {
  424.             int ob = n % blocksize;
  425.             int nb = blocksize - ob;
  426.             memset(buffer[0] + ob, 0, nb);
  427.             fprintf(out, "\t%d null bytes appended to align to block boundary\n", nb);
  428.             n += nb;
  429.         }
  430.         r = tape_write(buffer, n, &actual);
  431.         if (actual != TapeUndefLength) {
  432.             count += actual;
  433.         }
  434.         if (r)
  435.             return r;
  436.     }
  437.     if ((r = tape_filemark(0, 0, NULL)) < 0)
  438.         return r;
  439.     if (ferror(file)) {
  440.         perror("writing");
  441.         exit(1);
  442.     }
  443.     fprintf(out, "    %ld bytes written\n", count);
  444.     return 0;
  445. }
  446.  
  447. int
  448. eq(const char *p, const char *q, int len)
  449. {
  450.     int n = strlen(p);
  451.     if (n < len)
  452.         return 0;
  453.     return (isupper(*q) ? strncmp(p, q, n) : strnicmp(p, q, n)) == 0;
  454. }
  455.  
  456. void
  457. help(void)
  458. {
  459.     static char *text[] = {
  460.         "tape <option|command>+",
  461.         "",
  462.         "Options:",
  463.         "    -0..7        Device TAPE$<n>",
  464.         "    -Wait        always wait for completion (default)",
  465.         "    -Nowait        don't wait for completion",
  466.         "    -Abort        abort on errors (default)",
  467.         "    -Stdout        print on stdout",
  468.         "    -Ignore        ignore errors",
  469.         "    -Trace        trace mode",
  470.         "",
  471.         "General commands:",
  472.         "    Load        Load, rewind",
  473.         "    RETension    Load, retension",
  474.         "    Unload        Unload, rewind",
  475.         "    UNLOADEnd    Unload, position to end of tape",
  476.         "    REWind        Rewind",
  477.         "    STatus        Print status",
  478.         "    Inquiry        Print inquiry data",
  479.         "    MOde [<n>]    Print mode page <n> (default=all)",
  480.         "    File [<n>]    Seek files forward/backward (default=1)",
  481.         "    Block [<n>]    Seek blocks forward/backward (default=1)",
  482.         "    TEll        Current block number (TDC specific)",
  483.         "    SEek <n>    Seek block <n> (TDC specific)",
  484.         "    ENd        Position to logical end of media",
  485.         "    BLOCKSize <n>    Set blocksize, 0 = variable",
  486.         "    MARK [<n>]    Write <n:1> filemarks",
  487.         "    SETMark [<n>]    Write <n:1> setmarks",
  488.         "    ERASE        Erase tape",
  489.         "    Verify [<n>]    Verify <n> files (default=1)",
  490.         "    REad <file>|-    Read to file|stdout",
  491.         "    WRite <file>|-    Write from file|stdin",
  492.         "    RESET        Device reset",
  493.         "    BUSRESET    SCSI bus reset",
  494.         "    TRACE <n>    Set driver trace mode",
  495.         "",
  496.         "    SPeed <n>    Set speed code",
  497.         "    SET <x>        Set mode page(s)",
  498.         "        <x> split into items by comma or blank",
  499.         "        items:    save    save page(s)",
  500.         "            scsi-1    page format is SCSI-1",
  501.         "            scsi-2    page format is SCSI-2",
  502.         "            <hex>    mode data",
  503.         "        page format defaults to the driver mode",
  504.         "        or TAPEMODE, if set",
  505.         "",
  506.         "  The next commands should be used with extreme care,",
  507.         "  since they can cause the system to hang! The command",
  508.         "  names must be written in upper-case.",
  509.         "",
  510.         "    CMD <cmd>    SCSI command",
  511.         "    CMDF <file>    SCSI commands from file",
  512.         "        command        cdb=byte,byte,..",
  513.         "                rlen=length",
  514.         "                rfile=file",
  515.         "                wdata=byte,byte,...",
  516.         "                wfile=file",
  517.         "        Each command in one line, separated by space or",
  518.         "        ';', lines can be continued by a trailing '\\'.",
  519.         "",
  520.         "        The system will hang if you forget RLEN for",
  521.         "        a read-type command or WDATA/WFILE for a",
  522.         "        write-type command!",
  523.         NULL
  524.     };
  525.     char **p;
  526.     for (p = text; *p; ++p)
  527.         fprintf(out, "%s\n", *p);
  528.     exit(1);
  529. }
  530.  
  531. static int
  532. getstr(char *s, unsigned char *d, int maxlen)
  533. {
  534.     int i;
  535.     for (i = 0; i < maxlen; ) {
  536.         char *t = NULL;
  537.         d[i++] = strtoul(s, &t, 16);
  538.         if (!t || *t != ',')
  539.             break;
  540.         s = t + 1;
  541.     }
  542.     return i;
  543. }
  544.  
  545. static int
  546. getfile(char *name, unsigned char *buf, int maxlen)
  547. {
  548.     struct stat st;
  549.     FILE *fp;
  550.     if ((fp = fopen(name, "rb")) == NULL || fstat(fileno(fp), &st) != 0) {
  551.         perror(name);
  552.         exit(1);
  553.     }
  554.     if (st.st_size > maxlen) {
  555.         fprintf(out, "file %s too large\n", name);
  556.         st.st_size = maxlen;
  557.     }
  558.     fread(buf, st.st_size, 1, fp);
  559.     fclose(fp);
  560.     return st.st_size;
  561. }
  562.  
  563. static void
  564. putfile(char *name, unsigned char *buf, int len)
  565. {
  566.     FILE *fp;
  567.     if ((fp = fopen(name, "wb")) == NULL) {
  568.         perror(name);
  569.         exit(1);
  570.     }
  571.     fwrite(buf, len, 1, fp);
  572.     fclose(fp);
  573. }
  574.  
  575. int
  576. scsicmdln(char *cmd)
  577. {
  578.     /*
  579.         "        command        cdb=byte,byte,..",
  580.         "                rlen=length",
  581.         "                rfile=file",
  582.         "                wdata=byte,byte,...",
  583.         "                wfile=file",
  584.     */
  585.     int cdblen =  6, rdflag = 0, wrflag = 0, datalen = 0;
  586.     char *rfile = NULL;
  587.     char *p;
  588.     int r;
  589.  
  590.     for (p = strtok(cmd, " \t;"); p; p = strtok(NULL, " \t;")) {
  591.         while (isspace(*p))
  592.             ++p;
  593.         if (!*p)
  594.             continue;
  595.         if (strnicmp(p, "cdb=", 4) == 0) {
  596.             cdblen = getstr(p+4, cdb, 12);
  597.         } else if (strnicmp(p, "rlen=", 5) == 0) {
  598.             rdflag = 1;
  599.             datalen = strtoul(p+5, 0, 10);
  600.         } else if (strnicmp(p, "rfile=", 6) == 0) {
  601.             rfile = p+6;
  602.         } else if (strnicmp(p, "wdata=", 6) == 0) {
  603.             wrflag = 1;
  604.             datalen = getstr(p+6, buffer[0], NBLOCKS * 512);
  605.         } else if (strnicmp(p, "wfile=", 6) == 0) {
  606.             wrflag = 1;
  607.             datalen = getfile(p+6, buffer[0], NBLOCKS * 512);
  608.         } else {
  609.             fprintf(stderr, "unknown control %s\n", p);
  610.             exit(1);
  611.         }
  612.     }
  613.     if (rdflag)
  614.         r = tape_read_cmd(cdb, cdblen, buffer[0], datalen);
  615.     else if (wrflag)
  616.         r = tape_write_cmd(cdb, cdblen, buffer[0], datalen);
  617.     else
  618.         r = tape_cmd(cdb, cdblen);
  619.     if (rdflag)
  620.         if (rfile)
  621.             putfile(rfile, buffer[0], datalen);
  622.         else {
  623.             int i;
  624.             fprintf(out, "data, %d bytes:\n", datalen);
  625.             dump(buffer[0], datalen);
  626.         }
  627.     return r;
  628. }    
  629.  
  630. int
  631. set_mode(char *arg)
  632. {
  633.     char *p;
  634.     int save = 0;
  635.     int level = scsiLevel;
  636.     int i = 4;
  637.     for (p = strtok(arg, " ,"); p; p = strtok(NULL, " ,")) {
  638.         if (stricmp(p, "save") == 0)
  639.             save = 1;
  640.         else if (stricmp(p, "scsi-1") == 0)
  641.             level = 1;
  642.         else if (stricmp(p, "scsi-2") == 0)
  643.             level = 2;
  644.         else if (isdigit(*p))
  645.             buffer[0][i++] = strtol(p, &p, 16);
  646.         else {
  647.             fprintf(out, "Bad page data: %s\n", arg);
  648.             exit(1);
  649.         }
  650.     }
  651.     cdb[0] = CmdModeSelect;
  652.     cdb[1] = (level >= 2 ? 0x10 : 0x00) | (save ? 0x01 : 0x00);
  653.     cdb[2] = 0;
  654.     cdb[3] = 0;
  655.     cdb[4] = i;
  656.     cdb[5] = 0;
  657.     buffer[0][0] = 0;
  658.     buffer[0][1] = 0;
  659.     buffer[0][2] = 0x10;
  660.     buffer[0][3] = 0;
  661.     return tape_write_cmd(cdb, 6, buffer[0], i);
  662. }
  663.  
  664. main(int argc, char **argv)
  665. {
  666.     int immed = 0, ignore = 0;
  667.     int unit = -1;
  668.     char *p, devname[20];
  669.  
  670.     out = stderr;
  671.  
  672.     for (++argv; argc >= 2; --argc, ++argv) {
  673.         char *cmd = *argv;
  674.         if (eq(cmd, "-wait", 2))
  675.             immed = 0;
  676.         else if (eq(cmd, "-nowait", 2))
  677.             immed = 1;
  678.         else if (eq(cmd, "-ignore", 2))
  679.             ignore = 1;
  680.         else if (eq(cmd, "-abort", 2))
  681.             ignore = 0;
  682.         else if (eq(cmd, "-stdout", 2))
  683.             out = stdout;
  684.         else if (eq(cmd, "-trace", 2))
  685.             tape_trace(++trace);
  686.         else if (cmd[0] == '-' && isdigit(cmd[1]))
  687.             unit = cmd[1] - '0';
  688.         else
  689.             break;
  690.     }
  691.  
  692.     if (argc <= 1)
  693.         help();
  694.  
  695.     if (unit < 0) {
  696.         if ((p = getenv("TAPE")) != NULL)
  697.             strncpy(devname, p, 19);
  698.         else
  699.             strcpy(devname, "TAPE$0");
  700.         if ((p = strrchr(devname, '$')) != NULL && isdigit(*(p+1)))
  701.             unit = *p - '0';
  702.     } else
  703.         sprintf(devname, "TAPE$%d", unit);
  704.  
  705.     tape_name(devname);
  706.  
  707.     if (unit >= 0)
  708.         tape_target(unit);
  709.  
  710.     tape_sense(&sense, sizeof sense);
  711.  
  712.     while (argc-- >= 2) {
  713.         char *cmd = *argv++;
  714.         int r = 0;
  715.         if (eq(cmd, "load", 1)) {
  716.             fprintf(out, "Load\n"),
  717.             r = tape_load(immed, 0);
  718.         } else if (eq(cmd, "retension", 3)) {
  719.             fprintf(out, "Retension\n");
  720.             r = tape_load(immed, 1);
  721.         } else if (eq(cmd, "unload", 1)) {
  722.             fprintf(out, "Unload\n"),
  723.             r = tape_unload(immed, UnloadRewind);
  724.         } else if (eq(cmd, "unloadend", 7)) {
  725.             fprintf(out, "Unload end\n"),
  726.             r = tape_unload(immed, UnloadEndOfTape);
  727.         } else if (eq(cmd, "rewind", 3)) {
  728.             fprintf(out, "Rewind\n");
  729.             r = tape_rewind(immed);
  730.         } else if (eq(cmd, "status", 2)) {
  731.             tape_print_sense(out, tape_ready());
  732.         } else if (eq(cmd, "inquiry", 1)) {
  733.             if ((r = tape_inquiry(inq, sizeof inq)) == 0)
  734.                 print_inquiry();
  735.         } else if (eq(cmd, "tell", 2)) {
  736.             long n;
  737.             fprintf(out, "Current block number\n");
  738.             n = tape_tell();
  739.             if (n >= 0)
  740.                 fprintf(out, "    %ld\n", n);
  741.             else
  742.                 r = n;
  743.         } else if (eq(cmd, "end", 2)) {
  744.             fprintf(out, "Logical end of media\n");
  745.             r = tape_space(SpaceLogEndOfMedia, 0L, NULL);
  746.         } else if (eq(cmd, "erase", 5)) {
  747.             fprintf(out, "Erase\n");
  748.             r = tape_erase();
  749.         } else if (eq(cmd, "read", 2)) {
  750.             char *name = (argc-- >= 2) ? *argv++ : "-";
  751.             if (strcmp(name, "-") == 0) {
  752.                 fprintf(out, "Read tape to stdout\n");
  753.                 out = stderr;
  754. #if defined(__ZTC__)
  755.                 stdout->_flag &= ~_IOTRAN;
  756. #elif !defined(unix)
  757.                 setmode(1, O_BINARY);
  758. #endif
  759.                 r = read_tape(stdout);
  760.             } else {
  761.                 FILE *file = fopen(name, "wb");
  762.                 fprintf(out, "Read tape to \"%s\"\n", name);
  763.                 if (file == NULL) {
  764.                     perror(name);
  765.                     exit(1);
  766.                 }
  767.                 r = read_tape(file);
  768.                 fclose(file);
  769.             }
  770.         } else if (eq(cmd, "write", 2)) {
  771.             char *name = (argc-- >= 2) ? *argv++ : "-";
  772.             if (strcmp(name, "-") == 0) {
  773.                 fprintf(out, "Write tape from stdin\n");
  774. #if defined(__ZTC__)
  775.                 stdin->_flag &= ~_IOTRAN;
  776. #elif !defined(unix)
  777.                 setmode(0, O_BINARY);
  778. #endif
  779.                 r = write_tape(stdin);
  780.             } else {
  781.                 FILE *file = fopen(name, "rb");
  782.                 fprintf(out, "Write tape from \"%s\"\n", name);
  783.                 if (file == NULL) {
  784.                     perror(name);
  785.                     exit(1);
  786.                 }
  787.                 r = write_tape(file);
  788.             }
  789.         } else if (eq(cmd, "reset", 5)) {
  790.             fprintf(out, "SCSI reset\n");
  791.             r = tape_reset(0);
  792.         } else if (eq(cmd, "busreset", 8)) {
  793.             fprintf(out, "SCSI bus reset\n");
  794.             r = tape_reset(1);
  795.         } else if (eq(cmd, "set", 3)) {
  796.             fprintf(out, "Set mode\n");
  797.             r = set_mode((argc-- >= 2) ? *argv++ : "");
  798.         } else if (eq(cmd, "CMD", 3)) {
  799.             if (argc-- < 2) {
  800.                 fprintf(out, "Missing command\n");
  801.                 exit(1);
  802.             }
  803.             fprintf(out, "SCSI command %s\n", *argv);
  804.             r = scsicmdln(*argv++);
  805.         } else if (eq(cmd, "CMDF", 4)) {
  806.             char cmdln[1000];
  807.             FILE *cmdf;
  808.  
  809.             if (argc-- < 2) {
  810.                 fprintf(out, "Missing file\n");
  811.                 exit(1);
  812.             }
  813.             if ((cmdf = fopen(*argv, "r")) == NULL) {
  814.                 perror(*argv);
  815.                 exit(1);
  816.             }
  817.             fprintf(out, "SCSI command file %s\n", *argv);
  818.             ++argv;
  819.             while (!feof(cmdf)) {
  820.                 int x = 0;
  821.                 while (fgets(cmdln + x, 1000 - x, cmdf)) {
  822.                     int len = strlen(cmdln);
  823.                     if (len < 2 || cmdln[len - 2] != '\\')
  824.                         break;
  825.                     x += len - 1;
  826.                 }
  827.                 for (x = 0; isspace(cmdln[x]); ++x)
  828.                     ;
  829.                 if (cmdln[x] && cmdln[0] != ';')
  830.                     if ((r = scsicmdln(cmdln)) == 0)
  831.                         goto end;
  832.             }
  833.         end:    fclose(cmdf);
  834.         } else {
  835.             int present = 0;
  836.             long n = 1, actual;
  837.             if (argc >= 2 && strchr("0123456789+-", **argv)) {
  838.                 --argc;
  839.                 n = strtol(*argv++, NULL, 0);
  840.                 present = 1;
  841.             }
  842.             if (eq(cmd, "trace", 5)) {
  843.                 scsi_set_trace(present ? n : 0);
  844.             } else if (eq(cmd, "mode", 2)) {
  845.                 if (present) {
  846.                     if ((r = tape_mode_sense((int)n, mode, sizeof mode)) == 0)
  847.                         print_mode((int)n, 0);
  848.                 } else {
  849.                     for (n = 0; n < 0x3F; ++n)
  850.                         if ((r = tape_mode_sense((int)n, mode, sizeof mode)) == 0)
  851.                             print_mode((int)n, present++);
  852.                     r = 0;
  853.                 }
  854.             } else if (eq(cmd, "file", 1)) {
  855.                 fprintf(out, "Space over %ld filemark%s\n",
  856.                     n, n==1 ? "" : "s");
  857.                 r = tape_space(SpaceFileMarks, n, &actual);
  858.                 fprintf(out, "    spaced over %ld filemark%s\n",
  859.                     actual, actual==1 ? "" : "s");
  860.             } else if (eq(cmd, "block", 1)) {
  861.                 fprintf(out, "Space over %ld block%s\n",
  862.                     n, n==1 ? "" : "s");
  863.                 r = tape_space(SpaceBlocks, n, &actual);
  864.                 fprintf(out, "    spaced over %ld block%s\n",
  865.                     actual, actual==1 ? "" : "s");
  866.             } else if (eq(cmd, "seek", 2)) {
  867.                 fprintf(out, "Seek to block %ld\n", n);
  868.                 r = tape_seek(immed, n);
  869.             } else if (eq(cmd, "blocksize", 6)) {
  870.                 fprintf(out, "Blocksize %ld\n", n);
  871.                 r = tape_set_blocksize(n);
  872.             } else if (eq(cmd, "verify", 3)) {
  873.                 long files = 0;
  874.                 fprintf(out, "Verify %ld file%s\n",
  875.                     n, n==1 ? "" : "s");
  876.                 while (--n >= 0) {
  877.                     do {
  878.                         r = tape_verify(200L * 512, NULL);
  879.                     } while (r == 0);
  880.                     if (r == SenseKey+BlankCheck
  881.                      || r == EndOfData
  882.                      || r == EndOfTape
  883.                      || r != FileMark && r != 0 && !ignore)
  884.                         break;
  885.                     ++files;
  886.                 }
  887.                 fprintf(out, "    verified %ld file%s\n",
  888.                     files, files==1 ? "" : "s");
  889.             } else if (eq(cmd, "mark", 4)) {
  890.                 fprintf(out, "Write %ld filemark%s\n", n, n==1 ? "" : "s");
  891.                 r = tape_filemark(immed, n, &actual);
  892.                 fprintf(out, "    %ld filemark%s written\n",
  893.                     actual, actual==1 ? "" : "s");
  894.             } else if (eq(cmd, "setmark", 4)) {
  895.                 fprintf(out, "Write %ld setmark%s\n", n, n==1 ? "" : "s");
  896.                 r = tape_setmark(immed, n, &actual);
  897.                 fprintf(out, "    %ld setmark%s written\n",
  898.                     actual, actual==1 ? "" : "s");
  899.             } else if (eq(cmd, "speed", 2)) {
  900.                 fprintf(out, "Set speed %d\n", n);
  901.                 r = tape_speed((int)n);
  902.             } else {
  903.                 fprintf(out, "Unkown command: %s\n\n", cmd);
  904.                 help();
  905.             }
  906.         }
  907.         if (r < 0) {
  908.             tape_print_sense(out, r);
  909.             if (!ignore)
  910.                 exit(2);
  911.         } else if (trace && cmd[0] != '-')
  912.             tape_print_sense(out, r);
  913.     }
  914.     tape_term();
  915.     exit(0);
  916. }
  917.