fix lintian issues
[debian/mtx] / scsitape.c
1 /* Copyright 2001 Enhanced Software Technologies Inc.
2  * Copyright 2007-2008 by Robert Nelson <robertn@the-nelsons.org>
3  *   Released under terms of the GNU General Public License as
4  * required by the license on 'mtxl.c'.
5  * $Date: 2008-08-19 03:03:38 -0700 (Tue, 19 Aug 2008) $
6  * $Revision: 193 $
7  */
8
9 /* This is a generic SCSI tape control program. It operates by
10  * directly sending commands to the tape drive. If you are going
11  * through your operating system's SCSI tape driver, do *NOT* use 
12  * this program! If, on the other hand, you are using raw READ and WRITE
13  * commands through your operating system's generic SCSI interface (or
14  * through our built-in 'read' and 'write'), this is the place for you. 
15  */
16
17 /*#define DEBUG_PARTITION */
18 /*#define DEBUG 1 */
19
20 /* 
21         Commands:
22                 setblk <n> -- set the block size to <n>
23                 fsf <n> -- go forward by <n> filemarks
24                 bsf <n> -- go backward by <n> filemarks
25                 eod  -- go to end of data
26                 rewind -- rewind back to start of data
27                 eject  -- rewind, then eject the tape. 
28                 erase  -- (short) erase the tape (we have no long erase)
29                 mark <n> -- write <n> filemarks.
30                 seek <n> -- seek to position <n>.
31
32                 write <blksize> <-- write blocks from stdin to the tape 
33                 read  [<blksize>] [<#blocks/#bytes>] -- read blocks from tape, write to stdout. 
34
35         See the 'tapeinfo' program for status info about the tape drive.
36
37  */
38
39 #include <stdio.h>
40 #include <string.h>
41
42 #include "mtx.h"
43 #include "mtxl.h"
44
45 #if HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48
49 #if HAVE_SYS_TYPES_H
50 #include <sys/types.h>
51 #endif
52
53 #if HAVE_SYS_IOCTL_H
54 #include <sys/ioctl.h>
55 #endif
56
57 #if HAVE_SYS_MTIO_H
58 #include <sys/mtio.h> /* will try issuing some ioctls for Solaris, sigh. */
59 #endif
60
61 #ifdef _MSC_VER
62 #include <io.h>
63 #endif
64
65 void Usage(void) {
66   FatalError("Usage: scsitape -f <generic-device> <command> where <command> is:\n setblk <n> | fsf <n> | bsf <n> | eod | rewind | eject | mark <n> |\n  seek <n> | read [<blksize> [<numblocks]] | write [<blocksize>] \n");
67 }
68
69 #define arg1 (arg[0])  /* for backward compatibility, sigh */
70 static int arg[4];  /* the argument for the command, sigh. */
71
72 /* the device handle we're operating upon, sigh. */
73 static char *device;  /* the text of the device thingy. */
74 static DEVICE_TYPE MediumChangerFD = (DEVICE_TYPE) -1;
75
76
77
78 static int S_setblk(void);
79 static int S_fsf(void);
80 static int S_bsf(void);
81 static int S_eod(void);
82 static int S_rewind(void);
83 static int S_eject(void);
84 static int S_mark(void);
85 static int S_seek(void);
86 static int S_reten(void);
87 static int S_erase(void);
88
89 static int S_read(void);
90 static int S_write(void);
91
92
93 struct command_table_struct {
94         int num_args;
95         char *name;
96         int (*command)(void);
97 } command_table[] = {
98         { 1, "setblk", S_setblk },
99         { 1, "fsf", S_fsf },
100         { 1, "bsf", S_bsf },
101         { 0, "eod", S_eod },
102         { 0, "rewind", S_rewind },
103         { 0, "eject", S_eject },
104         { 0, "reten", S_reten },
105         { 0, "erase", S_erase },
106         { 1, "mark", S_mark },
107         { 1, "seek", S_seek },
108         { 2, "read", S_read },
109         { 2, "write",S_write },
110         { 0, NULL, NULL } /* terminate list */
111 };
112
113 char *argv0;
114
115
116 /* open_device() -- set the 'fh' variable.... */
117 void open_device(void)
118 {
119         if (MediumChangerFD != -1)
120         {
121                 SCSI_CloseDevice("Unknown",MediumChangerFD);  /* close it, sigh...  new device now! */
122         }
123
124         MediumChangerFD = SCSI_OpenDevice(device);
125 }
126
127 static int get_arg(char *arg)
128 {
129         int retval=-1;
130
131         if (*arg < '0' || *arg > '9')
132         {
133                 return -1;  /* sorry! */
134         }
135
136         retval=atoi(arg);
137         return retval;
138 }
139
140
141 /* we see if we've got a file open. If not, we open one :-(. Then
142  * we execute the actual command. Or not :-(. 
143  */ 
144 int execute_command(struct command_table_struct *command)
145 {
146         /* if the device is not already open, then open it from the 
147         * environment.
148         */
149         if (!MediumChangerFD == -1)
150         {
151                 /* try to get it from STAPE or TAPE environment variable... */
152                 device = getenv("STAPE");
153                 if (device == NULL)
154                 {
155                         device = getenv("TAPE");
156                         if (device == NULL)
157                         {
158                                 Usage();
159                         }
160                 }
161                 open_device();
162         }
163
164         /* okay, now to execute the command... */
165         return command->command();
166 }
167
168
169 /* parse_args():
170  *   Basically, we are parsing argv/argc. We can have multiple commands
171  * on a line now, such as "unload 3 0 load 4 0" to unload one tape and
172  * load in another tape into drive 0, and we execute these commands one
173  * at a time as we come to them. If we don't have a -f at the start, we
174  * barf. If we leave out a drive #, we default to drive 0 (the first drive
175  * in the cabinet). 
176  */ 
177
178 int parse_args(int argc, char **argv)
179 {
180         int i, cmd_tbl_idx,retval,arg_idx;
181         struct command_table_struct *command;
182
183         i=1;
184         arg_idx = 0;
185         while (i < argc)
186         {
187                 if (strcmp(argv[i],"-f") == 0)
188                 {
189                         i++;
190                         if (i >= argc)
191                         {
192                                 Usage();
193                         }
194                         device = argv[i++];
195                         open_device(); /* open the device and do a status scan on it... */
196                 }
197                 else
198                 {
199                         cmd_tbl_idx=0;
200                         command = &command_table[0]; /* default to the first command... */
201                         command = &command_table[cmd_tbl_idx];
202                         while (command->name)
203                         {
204                                 if (strcmp(command->name,argv[i]) == 0)
205                                 {
206                                         /* we have a match... */
207                                         break;
208                                 }
209                                 /* otherwise we don't have a match... */
210                                 cmd_tbl_idx++;
211                                 command = &command_table[cmd_tbl_idx];
212                         }
213                         /* if it's not a command, exit.... */
214                         if (command->name == NULL)
215                         {
216                                 Usage();
217                         }
218                         i++;  /* go to the next argument, if possible... */
219                         /* see if we need to gather arguments, though! */
220                         arg1 = -1; /* default it to something */
221                         for (arg_idx=0;arg_idx < command->num_args ; arg_idx++)
222                         {
223                                 if (i < argc)
224                                 {
225                                         arg[arg_idx] = get_arg(argv[i]);
226                                         if (arg[arg_idx] !=  -1)
227                                         {
228                                                 i++; /* increment i over the next cmd. */
229                                         }
230                                 }
231                                 else
232                                 {
233                                         arg[arg_idx] = 0; /* default to 0 setmarks or whatever */
234                                 }
235                         }
236                         retval=execute_command(command);  /* execute_command handles 'stuff' */
237                         exit(retval);
238                 }
239         }
240         return 0; /* should never get here */
241 }
242
243 /* For Linux, this allows us to do a short erase on a tape (sigh!).
244  * Note that you'll need to do a 'mt status' on the tape afterwards in
245  * order to get the tape driver in sync with the tape drive again. Also
246  * note that on other OS's, this might do other evil things to the tape
247  * driver. Note that to do an erase, you must first rewind!
248
249  */
250 static int S_erase(void)
251 {
252         int retval;
253         RequestSense_T *RequestSense;
254
255         retval=S_rewind();
256         if (retval)
257         {
258                 return retval;  /* we have an exit status :-(. */
259         } 
260
261         RequestSense=Erase(MediumChangerFD);
262         if (RequestSense)
263         {
264                 PrintRequestSense(RequestSense);
265                 exit(1);  /* exit with an error status. */
266         }
267         return 0;
268 }
269
270 /* This should eject a tape or magazine, depending upon the device sent
271  * to.
272  */
273 static int S_eject(void)
274 {
275         int i;
276         i = LoadUnload(MediumChangerFD, 0);
277         if ( i < 0)
278         {
279                 fprintf(stderr,"scsitape:eject failed\n");
280                 fflush(stderr);
281         }
282         return i;
283 }
284
285
286
287 /* We write a filemarks of 0 before going to grab position, in order
288  * to insure that data in the buffer is not a problem. 
289  */
290
291 static int S_mark(void)
292 {
293         RequestSense_T RequestSense; /* for result of ReadElementStatus */
294         CDB_T CDB;
295         unsigned char buffer[6];
296         int count = arg1; /* voila! */
297
298         CDB[0] = 0x10;  /* SET_MARK */
299         CDB[1] = 0;
300         CDB[2] = (unsigned char)(count >> 16);
301         CDB[3] = (unsigned char)(count >> 8);
302         CDB[4] = (unsigned char)count;
303         CDB[5] = 0; 
304
305         /* we really don't care if this command works or not, sigh.  */
306         slow_bzero((char *)&RequestSense, sizeof(RequestSense_T));
307
308         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &RequestSense)!= 0)
309         {
310                 PrintRequestSense(&RequestSense);
311                 return 1;
312         }
313         return 0;
314 }
315 /* let's rewind to bod! 
316  */
317
318 static int S_rewind(void)
319 {
320         RequestSense_T sense;
321         CDB_T CDB;
322         unsigned char buffer[6];
323
324         CDB[0] = 0x01;  /* REWIND */
325         CDB[1] = 0;
326         CDB[2] = 0;
327         CDB[3] = 0;
328         CDB[4] = 0;
329         CDB[5] = 0; 
330
331         /* we really don't care if this command works or not, sigh.  */
332         slow_bzero((char *)&sense,sizeof(RequestSense_T));
333         if (SCSI_ExecuteCommand(MediumChangerFD,Input,&CDB,6,buffer,0,&sense)!=0)
334         {
335                 PrintRequestSense(&sense);
336                 return 1;
337         }
338         return 0;
339 }
340
341
342
343
344 /* This is used for fsf and bsf. */
345 static int Space(int count, char code)
346 {
347         RequestSense_T sense;
348         CDB_T CDB;
349         unsigned char buffer[6];
350
351         CDB[0] = 0x11;  /* SET_MARK */
352         CDB[1] = code;
353         CDB[2] = (unsigned char)(count >> 16);
354         CDB[3] = (unsigned char)(count >> 8);
355         CDB[4] = (unsigned char)count;
356         CDB[5] = 0; 
357
358         /* we really don't care if this command works or not, sigh.  */
359         slow_bzero((char *)&sense,sizeof(RequestSense_T));
360         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
361         {
362                 PrintRequestSense(&sense);
363                 return 1;
364         }
365         return 0;
366 }
367
368
369 /* Let's try a fsf: */
370 /* We write a filemarks of 0 before going to grab position, in order
371  * to insure that data in the buffer is not a problem. 
372  */
373
374 static int S_fsf(void)
375 {
376         return Space(arg1,1); /* go forward! */
377 }
378
379 static int S_bsf(void)
380 {
381         return Space(-arg1,1); /* go backward! */
382 }
383
384 static int S_eod(void)
385 {
386         return Space(0,3); /* go to eod! */
387 }
388
389 /* sigh, abuse of the LOAD command...
390
391  */
392 static int S_reten(void)
393 {
394         RequestSense_T sense;
395         CDB_T CDB;
396         unsigned char buffer[6];
397
398         CDB[0] = 0x1B;  /* START_STOP */
399         CDB[1] = 0; /* wait */
400         CDB[2] = 0;
401         CDB[3] = 0;
402         CDB[4] = 3; /* reten. */ 
403         CDB[5] = 0; 
404
405         /* we really don't care if this command works or not, sigh.  */
406         slow_bzero((char *)&sense, sizeof(RequestSense_T));
407         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 6, buffer, 0, &sense) != 0)
408         {
409                 PrintRequestSense(&sense);
410                 return 1;
411         }
412         return 0;
413 }
414
415 /* seek a position on the tape (sigh!) */
416 static int S_seek(void)
417 {
418         RequestSense_T sense;
419         CDB_T CDB;
420         unsigned char buffer[6];
421         int count =  arg1;
422
423         /* printf("count=%d\n",arg1); */
424
425         CDB[0] = 0x2B;  /* LOCATE */
426         CDB[1] = 0;  /* Logical */
427         CDB[2] = 0; /* padding */
428         CDB[3] = (unsigned char)(count >> 24);
429         CDB[4] = (unsigned char)(count >> 16);
430         CDB[5] = (unsigned char)(count >> 8);
431         CDB[6] = (unsigned char)count;
432         CDB[7] = 0; 
433         CDB[8] = 0; 
434         CDB[9] = 0; 
435
436         /* we really don't care if this command works or not, sigh.  */
437         slow_bzero((char *)&sense,sizeof(RequestSense_T));
438         if (SCSI_ExecuteCommand(MediumChangerFD, Input, &CDB, 10, buffer, 0, &sense) != 0)
439         {
440                 PrintRequestSense(&sense);
441                 return 1;
442         }
443         return 0;
444 }
445
446 #ifdef MTSRSZ
447 static int Solaris_setblk(int fh,int count)
448 {
449         /* we get here only if we have a MTSRSZ, which means Solaris. */
450         struct mtop mt_com;  /* the struct used for the MTIOCTOP ioctl */
451         int result;
452
453         /* okay, we have fh and count.... */
454
455         /* Now to try the ioctl: */
456         mt_com.mt_op=MTSRSZ;
457         mt_com.mt_count=count;
458
459         /* surround the actual ioctl to enable threading, since fsf/etc. can be
460          * big time consumers and we want other threads to be able to run too. 
461          */
462
463         result=ioctl(fh, MTIOCTOP, (char *)&mt_com);
464
465         if (result < 0)
466         {
467                 return errno;
468         }
469
470         /* okay, we did okay. Return a value of None... */
471         return 0;
472 }
473 #endif
474
475
476 /* okay, this is a write: we need to set the block size to something: */
477 static int S_setblk(void)
478 {
479         RequestSense_T sense;
480         CDB_T CDB;
481         char buffer[12];
482         unsigned int count = (unsigned int) arg1;
483
484         CDB[0] = 0x15;  /* MODE SELECT */
485         CDB[1] = 0x10;  /* scsi2 */
486         CDB[2] = 0; 
487         CDB[3] = 0;
488         CDB[4] = 12; /* length of data */
489         CDB[5] = 0;
490
491         slow_bzero((char *)&sense, sizeof(RequestSense_T));
492         slow_bzero(buffer, 12);
493
494         /* Now to set the mode page header: */
495         buffer[0] = 0;
496         buffer[1] = 0;
497         buffer[2] = 0x10; /* we are in buffered mode now, people! */
498         buffer[3] = 8; /* block descriptor length. */ 
499         buffer[4] = 0; /* reset to default density, sigh. */ /* 0 */
500         buffer[5] = 0; /* 1 */
501         buffer[6] = 0; /* 2 */
502         buffer[7] = 0; /* 3 */
503         buffer[8] = 0; /* 4 */
504         buffer[9] = (unsigned char)(count >> 16); /* 5 */
505         buffer[10] = (unsigned char)(count >> 8); /* 6 */
506         buffer[11] = (unsigned char)count;  /* 7 */
507
508         if (SCSI_ExecuteCommand(MediumChangerFD,Output,&CDB,6,buffer,12,&sense)!=0)
509         {
510                 PrintRequestSense(&sense);
511                 return 1;
512         }
513 #ifdef MTSRSZ
514         /*   Solaris_setblk(MediumChangerFD,count);   */
515 #endif
516
517         return 0;
518 }
519
520 /*************************************************************************/
521 /* SCSI read/write calls. These are mostly pulled out of BRU 16.1, 
522  * modified to work within the mtxl.h framework rather than the
523  * scsi_lowlevel.h framework. 
524  *************************************************************************/ 
525
526 #define MAX_READ_SIZE 128*1024  /* max size of a variable-block read */
527
528 #define READ_OK 0
529 #define READ_FILEMARK 1
530 #define READ_EOD 2
531 #define READ_EOP 3
532 #define READ_SHORT 5
533 #define READ_ERROR 255
534
535 #define WRITE_OK 0
536 #define WRITE_ERROR 1
537 #define WRITE_EOM 2
538 #define WRITE_EOV 3
539
540
541 /* These are copied out of BRU 16.1, with all the boolean masks changed
542  * to our bitmasks.
543 */
544 #define S_NO_SENSE(s) ((s).SenseKey == 0x0)
545 #define S_RECOVERED_ERROR(s) ((s).SenseKey == 0x1)
546
547 #define S_NOT_READY(s) ((s).SenseKey == 0x2)
548 #define S_MEDIUM_ERROR(s) ((s).SenseKey == 0x3)
549 #define S_HARDWARE_ERROR(s) ((s).SenseKey == 0x4)
550 #define S_UNIT_ATTENTION(s) ((s).SenseKey == 0x6)
551 #define S_BLANK_CHECK(s) ((s).SenseKey == 0x8)
552 #define S_VOLUME_OVERFLOW(s) ((s).SenseKey == 0xd)
553
554 #define DEFAULT_TIMEOUT 3*60  /* 3 minutes here */
555
556 #define HIT_FILEMARK(s) (S_NO_SENSE((s)) && (s).Filemark && (s).Valid)
557 /* Sigh, the T-10 SSC spec says all of the following is needed to
558  * detect a short read while in variable block mode. We'll see.
559  */
560 #define SHORT_READ(s) (S_NO_SENSE((s)) && (s).ILI && (s).Valid &&  (s).AdditionalSenseCode==0  && (s).AdditionalSenseCodeQualifier==0)
561
562 #define HIT_EOD(s) (S_BLANK_CHECK((s)) && (s).Valid)
563 #define HIT_EOP(s) (S_MEDIUM_ERROR((s)) && (s).EOM && (s).Valid)
564 #define HIT_EOM(s) ((s).EOM && (s).Valid)
565 #define BECOMING_READY(s) (S_UNIT_ATTENTION((s)) && (s).AdditionalSenseCode == 0x28 && (s).AdditionalSenseCodeQualifier == 0)
566
567 /* Reading is a problem. We can hit a filemark, hit an EOD, or hit an
568  * EOP. Our caller may do something about that. Note that we assume that
569  * our caller has already put us into fixed block mode. If he has not, then
570  * we are in trouble anyhow. 
571  */
572 int SCSI_readt(DEVICE_TYPE fd, char * buf, unsigned int bufsize, unsigned int *len, unsigned int timeout) {
573         int rtnval;
574         CDB_T cmd;
575
576         int blockCount;
577         int info;
578
579         RequestSense_T RequestSense;
580
581         if (bufsize==0)
582         {
583                 /* we are in variable block mode */
584                 blockCount=MAX_READ_SIZE; /* variable block size. */
585         }
586         else
587         {
588                 blockCount= *len / bufsize;
589                 if ((*len % bufsize) != 0)
590                 {
591                         fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,bufsize);
592                         exit(1); /* we're finished, sigh. */
593                 }
594         }
595
596         if (timeout == 0)
597         {
598                 timeout = 1 * 60; /* 1 minutes */
599         }
600
601         memset(&cmd, 0, sizeof(CDB_T));
602         cmd[0] = 0x08; /* READ */
603         cmd[1] = (bufsize) ? 1 : 0; /* fixed length or var length blocks */
604         cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
605         cmd[3] = (unsigned char)(blockCount >> 8);
606         cmd[4] = (unsigned char)blockCount; /* LSB */
607
608         /* okay, let's read, look @ the result code: */
609         rtnval=READ_OK;
610         if (SCSI_ExecuteCommand(fd,Input,&cmd,6,buf,(bufsize) ? *len : MAX_READ_SIZE,&RequestSense))
611         {
612                 rtnval=READ_ERROR;
613                 if (HIT_EOP(RequestSense))
614                 {
615                         cmd[0]=0x08;
616                         rtnval=READ_EOP;
617                 }
618
619                 if (HIT_FILEMARK(RequestSense))
620                 {
621                         rtnval=READ_FILEMARK;
622                 }
623
624                 if (HIT_EOD(RequestSense))
625                 {
626                         rtnval=READ_EOD;
627                 }
628
629                 if ( (bufsize==0) && SHORT_READ(RequestSense))
630                 {
631                         rtnval=READ_SHORT; /* we only do short reads for variable block mode */
632                 }
633
634                 if (rtnval != READ_ERROR)
635                 {
636                         /* info contains number of blocks or bytes *not* read. May be 
637                         negative if the block we were trying to read was too big. So
638                         we will have to account for that and set it to zero if so, so that
639                         we return the proper # of blocks read. 
640                         */
641                         info = ((RequestSense.Information[0] << 24) +
642                                         (RequestSense.Information[1] << 16) +
643                                         (RequestSense.Information[2] << 8) +
644                                          RequestSense.Information[3]);
645
646                         /* on 64-bit platforms, we may need to turn 'info' into a negative # */
647                         if (info > 0x7fffffff)
648                                 info = 0;
649
650                         if (info < 0)
651                                 info=0;  /* make sure  we don't return too big len read. */
652
653                         /* Now set *len to # of bytes read. */
654                         *len= bufsize ? (blockCount-info) * bufsize : MAX_READ_SIZE-info ;
655                 }
656                 else
657                 {
658                         PrintRequestSense(&RequestSense);
659                         exit(1);  /* foo. */
660                 }
661         }
662
663         return rtnval;
664 }
665
666 /* Low level SCSI write. Modified from BRU 16.1,  with much BRU smarts
667  * taken out and with the various types changed to mtx types rather than
668  * BRU types.
669  */ 
670 int SCSI_write(DEVICE_TYPE fd, char * buf, unsigned int blocksize,
671                                 unsigned int *len)
672 {
673         CDB_T cmd;
674
675         int blockCount;
676         int rtnval=0;
677         RequestSense_T RequestSense;
678
679         if (blocksize == 0)
680         {
681                 /* we are in variable block mode */
682                 blockCount = *len; /* variable block size. */
683         }
684         else
685         {
686                 blockCount= *len / blocksize ;
687                 if ((*len % blocksize) != 0)
688                 {
689                         fprintf(stderr,"Error: Data (%d bytes) not even multiple of block size (%d bytes).\n",*len,blocksize);
690                         exit(1); /* we're finished, sigh. */
691                 }
692         }
693
694         fprintf(stderr,"Writing %d blocks\n",blockCount);
695
696         memset(&cmd, 0, sizeof(CDB_T));
697         cmd[0] = 0x0a; /* WRITE */
698         cmd[1] = (blocksize) ? 1 : 0; /* fixed length or var length blocks */
699         cmd[2] = (unsigned char)(blockCount >> 16); /* MSB */
700         cmd[3] = (unsigned char)(blockCount >> 8);
701         cmd[4] = (unsigned char)blockCount; /* LSB */
702
703
704         if (SCSI_ExecuteCommand(fd,Output,&cmd,6,buf, *len, &RequestSense))
705         {
706                 if (HIT_EOM(RequestSense))
707                 {
708                         /* we hit end of media. Return -1. */
709                         if (S_VOLUME_OVERFLOW(RequestSense))
710                         {
711                                 exit(WRITE_EOV);
712                         }
713                         exit(WRITE_EOM); /* end of media! */
714                 }
715                 else
716                 {
717                         /* it was plain old write error: */
718                         PrintRequestSense(&RequestSense);
719                         exit(WRITE_ERROR);
720                 }
721         }
722         else
723         {
724                 rtnval = *len; /* worked! */
725         }
726         return rtnval;
727 }
728
729 /* S_write is not implemented yet! */
730 static int S_write(void)
731 {
732         char *buffer; /* the buffer we're gonna read/write out of. */
733         int buffersize;
734         int len; /* the length of the data in the buffer */
735         int blocksize = arg[0];
736         int numblocks = arg[1];
737         int varsize=0; /* variable size block flag */
738         int result;
739         int eof_input;
740         int infile=fileno(stdin); /* sigh */
741
742         if (blocksize == 0)
743         {
744                 varsize = 1;
745                 buffersize = MAX_READ_SIZE;
746                 len = MAX_READ_SIZE;
747         }
748         else
749         {
750                 varsize = 0; /* fixed block mode */
751                 buffersize = blocksize;
752                 len = blocksize;
753         }
754
755         /* sigh, make it oversized just to have some */  
756         buffer = malloc(buffersize+8); 
757
758         eof_input = 0;
759         while (!eof_input)
760         {
761                 /* size_t could be 64 bit on a 32 bit platform, so do casts. */
762                 len=0;
763                 /* If it is a pipe, we could read 4096 bytes rather than the full
764                 * 128K bytes or whatever, so we must gather multiple reads into
765                 * the buffer.
766                 */
767                 while (len < buffersize)
768                 {
769                         result=(int)read(infile, buffer + len, (size_t)(buffersize - len));
770                         if (!result)
771                         {
772                                 eof_input = 1;
773                                 if (!len)
774                                 {
775                                         /* if we have no deata in our buffer, exit */
776                                         return 0; /* we're at end of file! */
777                                 }
778                                 break;          /* otherwise, break and write the data */
779                         }
780                         len += result;  /* add the result input to our length. */
781                 }
782
783                 result = SCSI_write(MediumChangerFD, buffer, blocksize, (unsigned int *)&len);
784                 if (!result)
785                 {
786                         return 1; /* at end of tape! */
787                 }
788
789                 /* Now see if we have numbytes or numblocks. If so, we may wish to exit
790                 this loop.
791                 */
792                 if (arg[1])
793                 {
794                         if (varsize)
795                         {
796                                 /***BUG***/
797                                 return 0; /* we will only write one block in variable size mode :-( */
798                         }
799                         else
800                         {
801                                 if (numblocks)
802                                 {
803                                         numblocks--;
804                                 }
805                                 else
806                                 {
807                                         return 0; /* we're done. */
808                                 }
809                         }
810                 }
811         }
812         /* and done! */
813         return 0;
814 }
815
816 /* Okay, the read thingy: */
817
818 /* We have a device opened (we hope!) by the parser. 
819  * we will have arg[0] and arg[1] being the blocksize and # of blocks
820  * (respectively).
821  */ 
822
823
824 static int S_read(void)
825 {
826         char *buffer; /* the buffer we're going to be reading out of */
827         int buffersize;
828         unsigned int len; /* the length of the data in the buffer */
829         int blocksize = arg[0];
830         int numblocks = arg[1];
831         int varsize = 0; /* variable size block flag. */
832
833         int result;
834
835         int outfile=fileno(stdout); /* sigh. */
836
837         if (blocksize == 0)
838         {
839                 varsize=1;
840                 buffersize=MAX_READ_SIZE;
841                 len=MAX_READ_SIZE;
842         }
843         else
844         {
845                 varsize=0; /* fixed block mode */
846                 buffersize=blocksize;
847                 len=blocksize;
848         }
849
850         /* sigh, make it oversized just to have some */  
851         buffer = malloc(buffersize + 8); 
852
853         for ( ; ; )
854         {
855                 if (varsize)
856                 {
857                         /* it could have gotten reset by prior short read... */
858                         len=MAX_READ_SIZE; 
859                 }
860                 result=SCSI_readt(MediumChangerFD,buffer,blocksize, &len, DEFAULT_TIMEOUT);
861
862                 if (result==READ_FILEMARK || result==READ_EOD || result==READ_EOP)
863                 {
864                         /* okay, normal end of file? */
865                         if (len > 0)
866                         {
867                                 write(outfile,buffer,len);
868                         }
869
870 #ifdef NEED_TO_GO_PAST_FILEMARK
871                         /* Now, let's try to go past the filemark if that's what we hit: */
872                         if (result==READ_FILEMARK)
873                         {
874                                 arg1 = 1;       /* arg for S_fsf. */
875                                 S_fsf();        /* and go forward 1 filemark, we hope! */
876                         }
877 #endif
878                         return 0; /* hit normal end of file. */
879                 }
880                 else if (result == READ_SHORT)
881                 {
882                         /* short reads are only valid in variable block mode. */
883                         if (varsize)
884                         {
885                                 if (len > 0)
886                                 {
887                                         write(outfile,buffer,len);
888                                 }
889                         }
890                         else
891                         {
892                                 fprintf(stderr,"scsitape:Short Read encountered on input. Aborting.\n");
893                                 fflush(stderr);
894                                 exit(1); /* error exit! */
895                         }
896                 }
897                 else if (result == READ_OK)
898                 {
899                         write(outfile,buffer,len);
900                 }
901                 else
902                 {
903                         fprintf(stderr,"scsitape:Read Error\n");
904                         fflush(stderr);
905                         exit(1);
906                 }
907
908                 /* Now see if we have numbytes or numblocks: if so, we may wish to
909                  * exit this loop.
910                  */
911                 if (arg[1])
912                 {
913                         if (varsize)
914                         {
915                                 /****BUG****/ 
916                                 return 0; /* we're only reading one block in var size mode! */
917                         }
918                         else if (numblocks)
919                         {
920                                 numblocks--;
921                         }
922                         else
923                         {
924                                 return 0; /* we're done. */
925                         }
926                 }
927         }
928 }
929
930
931 /* See parse_args for the scoop. parse_args does all. */
932 int main(int argc, char **argv)
933 {
934         argv0 = argv[0];
935         parse_args(argc, argv);
936
937         if (device)
938                 SCSI_CloseDevice(device,MediumChangerFD);
939
940         exit(0);
941 }