1 /* HETLIB.C    (c) Copyright Leland Lucius, 2000-2009                */
2 /*             Library for managing Hercules Emulated Tapes          */
3 
4 /*
5 || ----------------------------------------------------------------------------
6 ||
7 || HETLIB.C     (c) Copyright Leland Lucius, 2000-2009
8 ||              Released under terms of the Q Public License.
9 ||
10 || Library for managing Hercules Emulated Tapes.
11 ||
12 || ----------------------------------------------------------------------------
13 */
14 
15 #include "hstdinc.h"
16 
17 #define _HETLIB_C_
18 #define _HTAPE_DLL_
19 
20 #include "hercules.h"
21 #include "hetlib.h"
22 
23 #undef HETDEBUGR
24 #undef HETDEBUGW
25 
26 /*
27 || Local constant data
28 */
29 static const char *het_errstr[] =
30 {
31     "No error",
32     "File error",
33     "Tapemark read",
34     "Beginning of tape",
35     "End of tape",
36     "BOR not found",
37     "EOR not found",
38     "Unexpected tapemark",
39     "Buffer not big enough",
40     "Premature EOF",
41     "Decompression error",
42     "Unknown compression method",
43     "Compression error",
44     "Specified length to big",
45     "Write protected",
46     "Bad function code passed",
47     "Bad compression method",
48     "Bad compression level",
49     "Bad write chunk size",
50     "Invalid direction specified",
51     "Insufficient memory",
52     "Couldn't read block header",
53     "Inconsistent compression flags",
54     "Invalid error code",
55 };
56 #define HET_ERRSTR_MAX ( sizeof( het_errstr) / sizeof( het_errstr[ 0 ] ) )
57 
58 /*==DOC==
59 
60     NAME
61             het_open - Open an HET format file
62 
63     SYNOPSIS
64             #include "hetlib.h"
65 
66             int het_open( HETB **hetb, char *filename, int flags )
67 
68     DESCRIPTION
69             The het_open() function opens the file indicated by the "filename"
70             parameter and, if successful, places the address of an HETB at
71             the location pointed to by the "hetb" parameter.
72 
73             Currently, "HETOPEN_CREATE" is the only flag available and has
74             the same function as the O_CREAT flag of the open(3) function.
75 
76         @ISW@ Added flag HETOPEN_READONLY
77 
78         HETOPEN_CREATE and HETOPEN_READONLY are mutually exclusive.
79 
80         When HETOPEN_READONLY is set, the het file must exist.
81         It is opened read only. Any attempt to write will fail.
82 
83     RETURN VALUE
84             If no errors are detected then the return value will be >= 0
85             and the address of the newly allocated HETB will be place at the
86             "hetb" location.
87 
88             If an error occurs, then the return value will be < 0 and will be
89             one of the following:
90 
91             HETE_NOMEM          Insufficient memory to allocate an HETB
92 
93             HETE_ERROR          File system error - check errno(3)
94 
95             For other possible errors, see:
96                 het_read_header()
97                 het_tapemark()
98                 het_rewind()
99 
100     NOTES
101             Even if het_open() fails, you should still call het_close().
102 
103     EXAMPLE
104             //
105             // Create an NL tape
106             //
107 
108             #include "hetlib.h"
109 
110             int main( int argc, char *argv[] )
111             {
112                 HETB *hetb;
113                 int rc;
114 
115                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
116                 if( rc < 0 )
117                 {
118                     printf( "het_open() returned: %d\n", rc );
119                 }
120                 else
121                 {
122                     printf( "%s successfully created\n", argv[ 1 ] );
123                 }
124 
125                 het_close( &hetb );
126 
127                 return( 0 );
128             }
129 
130     SEE ALSO
131             het_read_header(), het_tapemark(), het_rewind(), het_close()
132 
133 ==DOC==*/
134 
135 DLL_EXPORT int
het_open(HETB ** hetb,char * filename,int flags)136 het_open( HETB **hetb, char *filename, int flags )
137 {
138     HETB *thetb;
139     char *omode;
140     int   rc;
141     int   fd;
142     int   oflags;
143     char  pathname[MAX_PATH];
144 
145     /*
146     || Initialize
147     */
148     *hetb = NULL;
149     hostpath(pathname, filename, sizeof(pathname));
150 
151     /*
152     || Allocate a new HETB
153     */
154     thetb = calloc( 1, sizeof( HETB ) );
155     if( thetb == NULL )
156     {
157         return( HETE_NOMEM );
158     }
159 
160     /*
161     || Set defaults
162     */
163     thetb->compress   = HETDFLT_COMPRESS;
164     thetb->decompress = HETDFLT_DECOMPRESS;
165     thetb->method     = HETDFLT_METHOD;
166     thetb->level      = HETDFLT_LEVEL;
167     thetb->chksize    = HETDFLT_CHKSIZE;
168 
169     /*
170     || clear HETOPEN_CREATE if HETOPEN_READONLY is specified
171     */
172     if(flags & HETOPEN_READONLY)
173     {
174         flags&=~HETOPEN_CREATE;
175     }
176     /*
177     || Translate HET create flag to filesystem flag
178     */
179     oflags = ( ( flags & HETOPEN_CREATE ) ? O_CREAT : 0 );
180 
181     /*
182     || Open the tape file
183     */
184     omode = "r+b";
185     if(!(flags & HETOPEN_READONLY))
186     {
187         fd = hopen( pathname, O_RDWR | O_BINARY | oflags, S_IRUSR | S_IWUSR | S_IRGRP );
188     }
189     if( (flags & HETOPEN_READONLY) || (fd == -1 && (errno == EROFS || errno == EACCES) ) )
190     {
191         /*
192         || Retry open if file resides on readonly file system
193         */
194         omode = "rb";
195         thetb->writeprotect = TRUE;
196         fd = hopen( pathname, O_RDONLY | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP );
197     }
198 
199     /*
200     || Error out if both opens failed
201     */
202     if( fd == -1 )
203     {
204         free( thetb );
205         return( HETE_ERROR );
206     }
207 
208     /*
209     || Associate stream with file descriptor
210     */
211     thetb->fd = fdopen( fd, omode );
212     if( thetb->fd == NULL )
213     {
214         rc = errno;
215         close( fd );
216         errno = rc;
217         free( thetb );
218         return( HETE_ERROR );
219     }
220 
221     /*
222     || If uninitialized tape, write 2 tapemarks to make it a valid NL tape
223     */
224     rc = het_read_header( thetb );
225     if( rc < 0 && rc != HETE_TAPEMARK )
226     {
227         if( rc != HETE_EOT )
228         {
229             return( rc );
230         }
231 
232         rc = het_tapemark( thetb );
233         if( rc < 0 )
234         {
235             return( rc );
236         }
237 
238         rc = het_tapemark( thetb );
239         if( rc < 0 )
240         {
241             return( rc );
242         }
243     }
244 
245     /*
246     || Reposition tape to load point
247     */
248     rc = het_rewind( thetb );
249     if( rc < 0 )
250     {
251         return( rc );
252     }
253 
254     /*
255     || Give the caller the new HETB
256     */
257     *hetb = thetb;
258 
259     return( 0 );
260 
261 }
262 
263 /*==DOC==
264 
265     NAME
266             het_close - Close an HET file
267 
268     SYNOPSIS
269             #include "hetlib.h"
270 
271             int het_close( HETB **hetb )
272 
273     DESCRIPTION
274             The het_close() function closes an HET file and releases the
275             HETB.
276 
277     RETURN VALUE
278             If no errors are detected then the return value will be >= 0
279             and the location specified by the "hetb" parameter will be set
280             to NULL.
281 
282             If an error occurs, then the return value will be < 0.  At this
283             time, no errors will be returned.
284 
285     EXAMPLE
286             //
287             // Create an NL tape
288             //
289 
290             #include "hetlib.h"
291 
292             int main( int argc, char *argv[] )
293             {
294                 HETB *hetb;
295                 int rc;
296 
297                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
298                 if( rc < 0 )
299                 {
300                     printf( "het_open() returned: %d\n", rc );
301                 }
302                 else
303                 {
304                     printf( "%s successfully created\n", argv[ 1 ] );
305                 }
306 
307                 het_close( &hetb );
308 
309                 return( 0 );
310             }
311 
312     SEE ALSO
313             het_open()
314 
315 ==DOC==*/
316 
317 DLL_EXPORT int
het_close(HETB ** hetb)318 het_close( HETB **hetb )
319 {
320 
321     /*
322     || Only free the HETB if we have one
323     */
324     if( *(hetb) != NULL )
325     {
326         /*
327         || Only close the file if opened
328         */
329         if( (*hetb)->fd != NULL )
330         {
331             fclose( (*hetb)->fd );
332         }
333         free( *(hetb) );
334     }
335 
336     /*
337     || Reinitialize pointer
338     */
339     *hetb = NULL;
340 
341     return( 0 );
342 }
343 
344 /*==DOC==
345 
346     NAME
347             het_cntl - Control HET file behavior
348 
349     SYNOPSIS
350             #include "hetlib.h"
351 
352             int het_cntl( HETB *hetb, int func, unsigned long val )
353 
354     DESCRIPTION
355             The het_cntl() function allows you to get/set several values
356             that control how an HET file behaves.  The value of the "val"
357             parameter depends on the function code.
358 
359             The possible modes are:
360 
361             HETCNTL_GET         Should be ORed (|) with the function codes
362                                 to retrieve the current setting.  (Default)
363 
364             HETCNTL_SET         Should be ORed (|) with the function codes
365                                 to set a new value.
366 
367             The possible function codes are:
368 
369             HETCNTL_COMPRESS    val=TRUE to enable write compression (see notes)
370                                 Values:     FALSE (disable)
371                                             TRUE (enable)
372                                 Default:    HETDFLT_COMPRESS (TRUE)
373 
374             HETCNTL_DECOMPRESS  val=TRUE to enable read decompression
375                                 Values:     FALSE (disable)
376                                             TRUE (enable)
377                                 Default:    HETDFLT_DECOMPRESS (TRUE)
378 
379             HETCNTL_METHOD      val=Compression method to use
380                                 Values:     HETMETH_ZLIB (1)
381                                             HETMETH_BZLIB (2)
382                                 Default:    HETDFLT_METHOD (HETMETH_ZLIB)
383 
384             HETCNTL_LEVEL       val=Level of compression
385                                 Min:        HETMIN_LEVEL (1)
386                                 Max:        HETMAX_LEVEL (9)
387                                 Default:    HETDFLT_LEVEL (4)
388 
389             HETCNTL_CHUNKSIZE   val=Size of output chunks (see notes)
390                                 Min:        HETMIN_CHUNKSIZE (4096)
391                                 Max:        HETMAX_CHUNKSIZE (65535)
392                                 Default:    HETDFLT_CHUNKSIZE (65535)
393 
394     RETURN VALUE
395             If no errors are detected then the return value will be either
396             the current setting for a "get" request or >= 0 for a "set"
397             request.
398 
399             If an error occurs, then the return value will be < 0 and can be
400             one of the following:
401 
402             HETE_BADMETHOD      Specified method out of range
403 
404             HETE_BADLEVEL       Specified level out of range
405 
406             HETE_BADCHUNKSIZE   Specified chunk size out of range
407 
408             HETE_BADFUNC        Unrecognized function code
409 
410     NOTES
411             Each block on an HET file is made up of "HETCNTL_CHUNKSIZE" sized
412             chunks.  There can be 1 or many chunks per block.
413 
414             If you wish to create an AWSTAPE compatible file, specify a chunk
415             size of 4096 and disable write compression.
416 
417     EXAMPLE
418             //
419             // Create an NL tape and write an uncompressed string to it
420             //
421 
422             #include "hetlib.h"
423 
424             char data[] = "This is a test";
425 
426             int main( int argc, char *argv[] )
427             {
428                 HETB *hetb;
429                 int rc;
430 
431                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
432                 if( rc >= 0 )
433                 {
434                     rc = het_cntl( hetb, HETCNTL_SET | HETCNTL_COMPRESS, FALSE );
435                     if( rc >= 0 )
436                     {
437                         rc = het_write( hetb, data, sizeof( data ) );
438                         if( rc >= 0 )
439                         {
440                             printf( "Data successfully written\n" );
441                         }
442                     }
443                 }
444 
445                 if( rc < 0 )
446                 {
447                     printf( "HETLIB error: %d\n", rc );
448                 }
449 
450                 het_close( &hetb );
451 
452                 return( 0 );
453             }
454 
455     SEE ALSO
456             het_open(), het_cntl(), het_write(), het_close()
457 
458 ==DOC==*/
459 
460 DLL_EXPORT int
het_cntl(HETB * hetb,int func,unsigned long val)461 het_cntl( HETB *hetb, int func, unsigned long val )
462 {
463     int mode;
464 
465     /*
466     || Isolate the mode
467     */
468     mode = func & HETCNTL_SET;
469 
470     /*
471     || Process the requested function
472     */
473     switch( func & ( ~( HETCNTL_GET | HETCNTL_SET ) ) )
474     {
475         case HETCNTL_COMPRESS:
476             if( mode == HETCNTL_GET )
477             {
478                 return( hetb->compress );
479             }
480 
481             hetb->compress = ( val ? TRUE : FALSE );
482         break;
483 
484         case HETCNTL_DECOMPRESS:
485             if( mode == HETCNTL_GET )
486             {
487                 return( hetb->decompress );
488             }
489 
490             hetb->decompress = ( val ? TRUE : FALSE );
491         break;
492 
493         case HETCNTL_METHOD:
494             if( mode == HETCNTL_GET )
495             {
496                 return( hetb->method );
497             }
498 
499             if( val < HETMIN_METHOD || val > HETMAX_METHOD )
500             {
501                 return( HETE_BADMETHOD );
502             }
503 
504             hetb->method = val;
505         break;
506 
507         case HETCNTL_LEVEL:
508             if( mode == HETCNTL_GET )
509             {
510                 return( hetb->level );
511             }
512 
513             if( val < HETMIN_LEVEL || val > HETMAX_LEVEL )
514             {
515                 return( HETE_BADLEVEL );
516             }
517 
518             hetb->level = val;
519         break;
520 
521         case HETCNTL_CHUNKSIZE:
522             if( mode == HETCNTL_GET )
523             {
524                 return( hetb->chksize );
525             }
526 
527             if( val < HETMIN_CHUNKSIZE || val > HETMAX_CHUNKSIZE )
528             {
529                 return( HETE_BADSIZE );
530             }
531 
532             hetb->chksize = val;
533         break;
534 
535         default:
536             return( HETE_BADFUNC );
537         break;
538     }
539 
540     /*
541     || Success
542     */
543 
544     return( 0 );
545 }
546 
547 /*==DOC==
548 
549     NAME
550             het_read_header - Retrieve the next chunk header from an HET file
551 
552     SYNOPSIS
553             #include "hetlib.h"
554 
555             int het_read_header( HETB *hetb )
556 
557     DESCRIPTION
558             Retrieves the next chunk header and stores it in the HETB.
559 
560     RETURN VALUE
561             If no errors are detected then the return value will be >= 0
562             and the current block count will be incremented.
563 
564             If an error occurs, then the return value will be < 0 and can be
565             one of the following:
566 
567             HETE_EOT            End of tape encountered
568 
569             HETE_ERROR          File system error - check errno(3)
570 
571             HETE_TAPEMARK       Tape mark encountered
572 
573     NOTES
574             This function is not normally called from user programs and its
575             behavior may change.
576 
577     EXAMPLE
578             //
579             // Read a chunk header from an HET file.
580             //
581 
582             #include "hetlib.h"
583 
584             int main( int argc, char *argv[] )
585             {
586                 HETB *hetb;
587                 int rc;
588 
589                 rc = het_open( &hetb, argv[ 1 ], 0 );
590                 if( rc >= 0 )
591                 {
592                     rc = het_read_header( hetb );
593                     if( rc >= 0 )
594                     {
595                         printf( "Header read:\n" );
596                         printf( "  Current length:  %d\n", HETHDR_CLEN( hetb ) );
597                         printf( "  Previous length: %d\n", HETHDR_PLEN( hetb ) );
598                         printf( "  Flags1:          %x\n", hetb->flags1 );
599                         printf( "  Flags2:          %x\n", hetb->flags2 );
600                     }
601                 }
602 
603                 if( rc < 0 )
604                 {
605                     printf( "HETLIB error: %d\n", rc );
606                 }
607 
608                 het_close( &hetb );
609 
610                 return( 0 );
611             }
612 
613     SEE ALSO
614             het_open(), het_close()
615 
616 ==DOC==*/
617 
618 DLL_EXPORT int
het_read_header(HETB * hetb)619 het_read_header( HETB *hetb )
620 {
621     int rc;
622 
623     /*
624     || Read in a headers worth of data
625     */
626     rc = fread( &hetb->chdr, sizeof( HETHDR ), 1, hetb->fd );
627     if( rc != 1 )
628     {
629         /*
630         || Return EOT if at end of physical file
631         */
632         if( feof( hetb->fd ) )
633         {
634             return( HETE_EOT );
635         }
636 
637         /*
638         || Something else must've happened
639         */
640         return( HETE_ERROR );
641     }
642 
643 #if defined( HETDEBUGR )
644     printf("read hdr: pl=%d, cl=%d, f1=%02x, f2=%02x\n",
645         HETHDR_PLEN( hetb ), HETHDR_CLEN( hetb ),
646         hetb->chdr.flags1, hetb->chdr.flags2);
647 #endif
648 
649     /*
650     || Bump block number if done with entire block
651     */
652     if( hetb->chdr.flags1 & ( HETHDR_FLAGS1_EOR | HETHDR_FLAGS1_TAPEMARK ) )
653     {
654         hetb->cblk++;
655     }
656 
657     /*
658     || Check for tape marks
659     */
660     if( hetb->chdr.flags1 & HETHDR_FLAGS1_TAPEMARK )
661     {
662         return( HETE_TAPEMARK );
663     }
664 
665     /*
666     || Success
667     */
668     return( 0 );
669 }
670 
671 /*==DOC==
672 
673     NAME
674             het_read - Retrieve the next block from an HET file
675 
676     SYNOPSIS
677             #include "hetlib.h"
678 
679             int het_read( HETB *hetb,  void *sbuf )
680 
681     DESCRIPTION
682             Read the next block of data into the "sbuf" memory location.  The
683             length of "sbuf" should be at least HETMAX_BLOCKSIZE bytes.
684 
685     RETURN VALUE
686             If no errors are detected then the return value will be the
687             size of the block read.  This will be either the compressed or
688             uncompressed length depending on the current AWSCNTL_DECOMPRESS
689             setting.
690 
691             If an error occurs, then the return value will be < 0 and can be
692             one of the following:
693 
694             HETE_ERROR          File system error - check errno(3)
695 
696             HETE_BADBOR         Beginning of record expected but not found
697 
698             HETE_BADCOMPRESS    Compression mismatch between related chunks
699 
700             HETE_OVERFLOW       Record too large for buffer
701 
702             HETE_PREMEOF        Premature EOF on file
703 
704             HETE_DECERR         Decompression error (stored in errno(3))
705 
706             HETE_UNKMETH        Unknown compression method encountered
707 
708             For other possible errors, see:
709                 het_read_header()
710 
711     EXAMPLE
712             //
713             // Read a block from an HET file.
714             //
715 
716             #include "hetlib.h"
717 
718             char buffer[ HETMAX_BLOCKSIZE ];
719 
720             int main( int argc, char *argv[] )
721             {
722                 HETB *hetb;
723                 int rc;
724 
725                 rc = het_open( &hetb, argv[ 1 ], 0 );
726                 if( rc >= 0 )
727                 {
728                     rc = het_read( hetb, buffer );
729                     if( rc >= 0 )
730                     {
731                         printf( "Block read - length: %d\n", rc );
732                     }
733                 }
734 
735                 if( rc < 0 )
736                 {
737                     printf( "HETLIB error: %d\n", rc );
738                 }
739 
740                 het_close( &hetb );
741 
742                 return( 0 );
743             }
744 
745     SEE ALSO
746             het_open(), het_read_header(), het_close()
747 
748 ==DOC==*/
749 
750 DLL_EXPORT int
het_read(HETB * hetb,void * sbuf)751 het_read( HETB *hetb, void *sbuf )
752 {
753     char *tptr;
754     int rc;
755     unsigned long slen;
756     int flags1, flags2;
757     unsigned long tlen;
758     char tbuf[ HETMAX_BLOCKSIZE ];
759 
760     /*
761     || Initialize
762     */
763     flags1 = flags2 = 0;
764     tlen = 0;
765     tptr = sbuf;
766 
767     /*
768     || Read chunks until entire block has been read
769     */
770     do
771     {
772         /*
773         || Get a header
774         */
775         rc = het_read_header( hetb );
776         if( rc < 0 )
777         {
778             return( rc );
779         }
780 
781         /*
782         || Have we seen a BOR chunk yet?
783         */
784         if( !( flags1 & HETHDR_FLAGS1_BOR ) )
785         {
786             /*
787             || Nope, so this chunk MUST have the BOR set
788             */
789             if( !( hetb->chdr.flags1 & HETHDR_FLAGS1_BOR ) )
790             {
791                 return( HETE_BADBOR );
792             }
793 
794             /*
795             || If block is compressed (and decompression is desired), set
796             || destination pointer.
797             */
798             if( hetb->decompress )
799             {
800                 if( hetb->chdr.flags1 & HETHDR_FLAGS1_COMPRESS ||
801                     hetb->chdr.flags2 & HETHDR_FLAGS2_COMPRESS )
802                 {
803                     if( ( hetb->chdr.flags1 & HETHDR_FLAGS1_COMPRESS ) &&
804                         ( hetb->chdr.flags2 & HETHDR_FLAGS2_COMPRESS ) )
805                     {
806                         return( HETE_BADCOMPRESS );
807                     }
808                     tptr = tbuf;
809                 }
810             }
811 
812             /*
813             || Save flags for later validation
814             */
815             flags1 = hetb->chdr.flags1;
816             flags2 = hetb->chdr.flags2;
817         }
818         else
819         {
820             /*
821             || Yep, so this chunk MUST NOT have the BOR flag set
822             */
823             if( hetb->chdr.flags1 & HETHDR_FLAGS1_BOR )
824             {
825                 return( HETE_BADBOR );
826             }
827         }
828 
829         /*
830         || Compression flags from related chunks must match
831         */
832         if( (            flags1 & HETHDR_FLAGS1_COMPRESS ) !=
833             ( hetb->chdr.flags1 & HETHDR_FLAGS1_COMPRESS ) )
834         {
835             return( HETE_BADCOMPRESS );
836         }
837         if( (            flags2 & HETHDR_FLAGS2_COMPRESS ) !=
838             ( hetb->chdr.flags2 & HETHDR_FLAGS2_COMPRESS ) )
839         {
840             return( HETE_BADCOMPRESS );
841         }
842 
843         /*
844         || Calculate running length
845         */
846         slen = HETHDR_CLEN( hetb );
847         tlen += slen;
848 
849         /*
850         || Can't be bigger than HETMAX_BLOCKSIZE
851         */
852         if( tlen > HETMAX_BLOCKSIZE )
853         {
854             return( HETE_OVERFLOW );
855         }
856 
857         /*
858         || Finally read in the chunk data
859         */
860         rc = fread( tptr, 1, slen, hetb->fd );
861         if( rc != (int)slen )
862         {
863             if( feof( hetb->fd ) )
864             {
865                 return( HETE_PREMEOF );
866             }
867 
868             return( HETE_ERROR );
869         }
870 
871         /*
872         || Bump destination pointer to next possible location
873         */
874         tptr += slen;
875     }
876     while( !( hetb->chdr.flags1 & HETHDR_FLAGS1_EOR ) );
877 
878     /*
879     || Save compressed length (means cblksize size and ublksize will be the
880     || same for uncompressed data)
881     */
882     hetb->cblksize = tlen;
883 
884     /*
885     || Decompress data if requested
886     */
887     if( hetb->decompress )
888     {
889         switch( hetb->chdr.flags1 & HETHDR_FLAGS1_COMPRESS )
890         {
891             case 0:
892                 switch( hetb->chdr.flags2 & HETHDR_FLAGS2_COMPRESS )
893                 {
894                     case 0:
895                     break;
896 #if defined( HAVE_LIBZ )
897                     case HETHDR_FLAGS2_ZLIB_BUSTECH:
898                         slen = HETMAX_BLOCKSIZE;
899 
900                         rc = uncompress( sbuf, &slen, (unsigned char *)tbuf, tlen );
901                         if( rc != Z_OK )
902                         {
903                             errno = rc;
904                             return( HETE_DECERR );
905                         }
906 
907                         tlen = slen;
908                     break;
909 #endif /* defined( HAVE_LIBZ ) */
910                     default:
911                         return( HETE_UNKMETH );
912                     break;
913                 }
914             break;
915 
916 #if defined( HAVE_LIBZ )
917             case HETHDR_FLAGS1_ZLIB:
918                 slen = HETMAX_BLOCKSIZE;
919 
920                 rc = uncompress( sbuf, &slen, (unsigned char *)tbuf, tlen );
921                 if( rc != Z_OK )
922                 {
923                     errno = rc;
924                     return( HETE_DECERR );
925                 }
926 
927                 tlen = slen;
928             break;
929 #endif /* defined( HAVE_LIBZ ) */
930 
931 #if defined( HET_BZIP2 )
932             case HETHDR_FLAGS1_BZLIB:
933                 slen = HETMAX_BLOCKSIZE;
934 
935                 rc = BZ2_bzBuffToBuffDecompress( sbuf,
936                                                  (void *) &slen,
937                                                  tbuf,
938                                                  tlen,
939                                                  0,
940                                                  0 );
941                 if (rc != BZ_OK)
942                 {
943                     errno = rc;
944                     return( HETE_DECERR );
945                 }
946 
947                 tlen = slen;
948             break;
949 #endif /* defined( HET_BZIP2 ) */
950 
951             default:
952                 return( HETE_UNKMETH );
953             break;
954         }
955     }
956 
957     /*
958     || Save uncompressed length
959     */
960     hetb->ublksize = tlen;
961 
962     /*
963     || Success
964     */
965     return( tlen );
966 }
967 
968 /*==DOC==
969 
970     NAME
971             het_write_header - Write a chunk header to an HET file
972 
973     SYNOPSIS
974             #include "hetlib.h"
975 
976             int het_write_header( HETB *hetb, int len, int flags1, int flags2 )
977 
978     DESCRIPTION
979             Constructs and writes a chunk header to an HET file.
980 
981     RETURN VALUE
982             If no errors are detected then the return value will be >= 0
983             and the current block count will be incremented.
984 
985             If an error occurs, then the return value will be < 0 and can be
986             one of the following:
987 
988             HETE_BADLEN         "len" parameter out of range
989 
990             HETE_PROTECTED      File is write protected
991 
992             HETE_ERROR          File system error - check errno(3)
993 
994     NOTES
995             This function is not normally called from user programs and its
996             behavior may change.
997 
998             If this function is called and the tape is not positioned at the
999             end, then the file will be truncated to the current position.  This
1000             means that rewriting blocks is not currently possible.
1001 
1002     EXAMPLE
1003             //
1004             // Write a chunk header to an HET file.
1005             //
1006 
1007             #include "hetlib.h"
1008 
1009             int main( int argc, char *argv[] )
1010             {
1011                 HETB *hetb;
1012                 int rc;
1013 
1014                 rc = het_open( &hetb, argv[ 1 ], 0 );
1015                 if( rc >= 0 )
1016                 {
1017                     rc = het_write_header( hetb, 0, HETHDR_TAPEMARK, 0 );
1018                     if( rc >= 0 )
1019                     {
1020                         printf( "Header written\n" );
1021                     }
1022                 }
1023 
1024                 if( rc < 0 )
1025                 {
1026                     printf( "HETLIB error: %d\n", rc );
1027                 }
1028 
1029                 het_close( &hetb );
1030 
1031                 return( 0 );
1032             }
1033 
1034     SEE ALSO
1035             het_open(), het_close()
1036 
1037 ==DOC==*/
1038 
1039 int
het_write_header(HETB * hetb,int len,int flags1,int flags2)1040 het_write_header( HETB *hetb, int len, int flags1, int flags2 )
1041 {
1042     int    rc;
1043     off_t  rcoff;
1044 
1045 #if defined( HETDEBUGW )
1046     printf("write hdr: pl=%d, cl=%d, f1=%02x, f2=%02x\n",
1047         HETHDR_PLEN( hetb ), HETHDR_CLEN( hetb ),
1048         hetb->chdr.flags1, hetb->chdr.flags2);
1049 #endif
1050 
1051     /*
1052     || Validate length
1053     */
1054     if( len > HETMAX_CHUNKSIZE )
1055     {
1056         return( HETE_BADLEN );
1057     }
1058 
1059     /*
1060     || Can't write anything on readonly media
1061     */
1062     if( hetb->writeprotect )
1063     {
1064         return( HETE_PROTECTED );
1065     }
1066 
1067     /*
1068     || For tapemarks, length must be zero.
1069     */
1070     if( flags1 & HETHDR_FLAGS1_TAPEMARK )
1071     {
1072         len = 0;
1073     }
1074 
1075     /*
1076     || According to Linux fopen() man page, a positioning function is required
1077     || between reads and writes.  Is this REALLY necessary???
1078     */
1079     if( !hetb->readlast )
1080     {
1081         fseek( hetb->fd, 0, SEEK_CUR );
1082         hetb->readlast = FALSE;
1083     }
1084 
1085     /*
1086     || If this is the first write, truncate the file
1087     */
1088     if( !hetb->truncated )
1089     {
1090         rcoff = ftell( hetb->fd );
1091         if( rcoff == -1 )
1092         {
1093             return( HETE_ERROR );
1094         }
1095 
1096         rc = ftruncate( fileno( hetb->fd ), rcoff );
1097         if( rc == -1 )
1098         {
1099             return( HETE_ERROR );
1100         }
1101 
1102         hetb->truncated = TRUE;
1103     }
1104 
1105     /*
1106     || Construct the header
1107     */
1108     hetb->chdr.plen[ 0 ] = hetb->chdr.clen[ 0 ];
1109     hetb->chdr.plen[ 1 ] = hetb->chdr.clen[ 1 ];
1110     hetb->chdr.clen[ 0 ] = len & 0xFF;
1111     hetb->chdr.clen[ 1 ] = ( len >> 8 ) & 0xFF;
1112     hetb->chdr.flags1    = flags1;
1113     hetb->chdr.flags2    = flags2;
1114 
1115     /*
1116     || Write it out
1117     */
1118     rc = fwrite( &hetb->chdr, sizeof( HETHDR ), 1, hetb->fd );
1119     if( rc != 1 )
1120     {
1121         return( HETE_ERROR );
1122     }
1123 
1124     /*
1125     || Bump block count if done with entire block
1126     */
1127     if( hetb->chdr.flags1 & ( HETHDR_FLAGS1_EOR | HETHDR_FLAGS1_TAPEMARK ) )
1128     {
1129         hetb->cblk++;
1130     }
1131 
1132     /*
1133     || Success
1134     */
1135     return 0;
1136 }
1137 
1138 /*==DOC==
1139 
1140     NAME
1141             het_write - Write a block to an HET file
1142 
1143     SYNOPSIS
1144             #include "hetlib.h"
1145 
1146             int het_write( HETB *hetb, void *sbuf, int slen )
1147 
1148     DESCRIPTION
1149             Writes a block of data specified by "sbuf" with a length of "slen"
1150             to an HET file.  Depending on the current HETCNTL_COMPRESS setting,
1151             the data may be compressed prior to writing.
1152 
1153     RETURN VALUE
1154             If no errors are detected then the return value will be the
1155             size of the block written.  This will be either the compressed or
1156             uncompressed length depending on the current AWSCNTL_COMPRESS
1157             setting.
1158 
1159             If an error occurs, then the return value will be < 0 and can be
1160             one of the following:
1161 
1162             HETE_ERROR          File system error - check errno(3)
1163 
1164             HETE_BADLEN         "slen" parameter out of range
1165 
1166             HETE_BADCOMPRESS    Compression mismatch between related chunks
1167 
1168             For other possible errors, see:
1169                 het_write_header()
1170 
1171     EXAMPLE
1172             //
1173             // Create an NL tape and write a string to it
1174             //
1175 
1176             #include "hetlib.h"
1177 
1178             char data[] = "This is a test";
1179 
1180             int main( int argc, char *argv[] )
1181             {
1182                 HETB *hetb;
1183                 int rc;
1184 
1185                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
1186                 if( rc >= 0 )
1187                 {
1188                     rc = het_write( hetb, data, sizeof( data ) );
1189                     if( rc >= 0 )
1190                     {
1191                         printf( "Block written - length: %d\n", rc );
1192                     }
1193                 }
1194 
1195                 if( rc < 0 )
1196                 {
1197                     printf( "HETLIB error: %d\n", rc );
1198                 }
1199 
1200                 het_close( &hetb );
1201 
1202                 return( 0 );
1203             }
1204 
1205     SEE ALSO
1206             het_open(), het_write_header(), het_close()
1207 
1208 ==DOC==*/
1209 
1210 DLL_EXPORT int
het_write(HETB * hetb,void * sbuf,int slen)1211 het_write( HETB *hetb, void *sbuf, int slen )
1212 {
1213     int rc;
1214     int flags;
1215     unsigned long tlen;
1216 #if defined(HAVE_LIBZ) || defined( HET_BZIP2 )
1217     char tbuf[ ((((HETMAX_BLOCKSIZE * 1001) + 999) / 1000) + 12) ];
1218 #endif
1219 
1220     /*
1221     || Validate
1222     */
1223     if( slen > HETMAX_BLOCKSIZE )
1224     {
1225         return( HETE_BADLEN );
1226     }
1227 
1228     /*
1229     || Initialize
1230     */
1231     flags = HETHDR_FLAGS1_BOR;
1232 
1233     /*
1234     || Save uncompressed length
1235     */
1236     hetb->ublksize = slen;
1237 
1238     /*
1239     || Compress data if requested
1240     */
1241     if( hetb->compress )
1242     {
1243         switch( hetb->method )
1244         {
1245 #if defined(HAVE_LIBZ)
1246             case HETHDR_FLAGS1_ZLIB:
1247                 tlen = sizeof( tbuf );
1248 
1249                 rc = compress2( (unsigned char *)tbuf, &tlen, sbuf, slen, hetb->level );
1250                 if( rc != Z_OK )
1251                 {
1252                     errno = rc;
1253                     return( HETE_COMPERR );
1254                 }
1255 
1256                 if( (int)tlen < slen )
1257                 {
1258                     sbuf = tbuf;
1259                     slen = tlen;
1260                     flags |= HETHDR_FLAGS1_ZLIB;
1261                 }
1262             break;
1263 #endif
1264 
1265 #if defined( HET_BZIP2 )
1266             case HETHDR_FLAGS1_BZLIB:
1267                 tlen = sizeof( tbuf );
1268 
1269                 rc = BZ2_bzBuffToBuffCompress( tbuf,
1270                                                (void *) &tlen,
1271                                                sbuf,
1272                                                slen,
1273                                                hetb->level,
1274                                                0,
1275                                                0 );
1276                 if( rc != BZ_OK )
1277                 {
1278                     errno = rc;
1279                     return( HETE_COMPERR );
1280                 }
1281 
1282                 if( (int)tlen < slen )
1283                 {
1284                     sbuf = tbuf;
1285                     slen = tlen;
1286                     flags |= HETHDR_FLAGS1_BZLIB;
1287                 }
1288             break;
1289 #endif /* defined( HET_BZIP2 ) */
1290         }
1291     }
1292 
1293     /*
1294     || Save compressed length
1295     */
1296     hetb->cblksize = slen;
1297 
1298     /*
1299     || Write block, breaking it into "chksize" chunks
1300     */
1301     do
1302     {
1303         /*
1304         || Last chunk for this block?
1305         */
1306         if( slen <= (int)hetb->chksize )
1307         {
1308             flags |= HETHDR_FLAGS1_EOR;
1309             tlen = slen;
1310         }
1311         else
1312         {
1313             tlen = hetb->chksize;
1314         }
1315 
1316         /*
1317         || Write the header
1318         */
1319         rc = het_write_header( hetb, tlen, flags, 0 );
1320         if( rc < 0 )
1321         {
1322             return( rc );
1323         }
1324 
1325         /*
1326         || Write the block
1327         */
1328         rc = fwrite( sbuf, 1, tlen, hetb->fd );
1329         if( rc != (int)tlen )
1330         {
1331             return( HETE_ERROR );
1332         }
1333 
1334         /*
1335         || Bump pointers and turn off BOR flag
1336         */
1337         {
1338             char    *csbuf;
1339             csbuf=(char *)sbuf;
1340             csbuf+=tlen;
1341             sbuf=(void *)csbuf;
1342         }
1343         slen -= tlen;
1344         flags &= (~HETHDR_FLAGS1_BOR);
1345     }
1346     while( slen > 0 );
1347 
1348     /*
1349     || Set new physical EOF
1350     */
1351     do rc = ftruncate( fileno( hetb->fd ), ftell( hetb->fd ) );
1352     while (EINTR == rc);
1353     if (rc != 0)
1354     {
1355         return( HETE_ERROR );
1356     }
1357 
1358     /*
1359     || Success
1360     */
1361     return( hetb->cblksize );
1362 }
1363 
1364 /*==DOC==
1365 
1366     NAME
1367             het_tapemark - Write a tape mark to an HET file
1368 
1369     SYNOPSIS
1370             #include "hetlib.h"
1371 
1372             int het_tapemark( HETB *hetb )
1373 
1374     DESCRIPTION
1375             Writes a special chunk header to an HET file to simulate a tape
1376             mark.
1377 
1378     RETURN VALUE
1379             If no errors are detected then the return value will be >= 0.
1380 
1381             If an error occurs, then the return value will be < 0 and will be
1382             the same as those returned by het_write_header().
1383 
1384     EXAMPLE
1385             //
1386             // Write a tapemark to an HET file
1387             //
1388 
1389             #include "hetlib.h"
1390 
1391             int main( int argc, char *argv[] )
1392             {
1393                 HETB *hetb;
1394                 int rc;
1395 
1396                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
1397                 if( rc >= 0 )
1398                 {
1399                     rc = het_tapemark( hetb );
1400                     if( rc >= 0 )
1401                     {
1402                         printf( "Tape mark written\n" );
1403                     }
1404                 }
1405 
1406                 if( rc < 0 )
1407                 {
1408                     printf( "HETLIB error: %d\n", rc );
1409                 }
1410 
1411                 het_close( &hetb );
1412 
1413                 return( 0 );
1414             }
1415 
1416     SEE ALSO
1417             het_open(), het_write_header(), het_close()
1418 
1419 ==DOC==*/
1420 
1421 DLL_EXPORT int
het_tapemark(HETB * hetb)1422 het_tapemark( HETB *hetb )
1423 {
1424     int rc;
1425 
1426     /*
1427     || Just write a tapemark header
1428     */
1429     rc = het_write_header( hetb, 0, HETHDR_FLAGS1_TAPEMARK, 0 );
1430     if( rc < 0 )
1431     {
1432         return( rc );
1433     }
1434 
1435     /*
1436     || Set new physical EOF
1437     */
1438     do rc = ftruncate( fileno( hetb->fd ), ftell( hetb->fd ) );
1439     while (EINTR == rc);
1440     if (rc != 0)
1441     {
1442         return( HETE_ERROR );
1443     }
1444 
1445     /*
1446     || Success
1447     */
1448     return( 0 );
1449 }
1450 
1451 /*==DOC==
1452 
1453     NAME
1454             het_sync - commit/flush a HET file's buffers to disk
1455 
1456     SYNOPSIS
1457             #include "hetlib.h"
1458 
1459             int het_sync( HETB *hetb )
1460 
1461     DESCRIPTION
1462             Calls the file system's "fdatasync" (or fsync) function to cause
1463             all data for the HET file to be transferred to disk by forcing a
1464             physical write of all data from the file's buffers or the file-
1465             system's cache, to the disk, thereby assuring that after a system
1466             crash or other failure, that all data up to the time of the call
1467             is thus recorded on the disk.
1468 
1469     RETURN VALUE
1470             If no errors are detected then the return value will be >= 0.
1471 
1472             If an error occurs, then the return value will be < 0 and will be
1473             one of the following:
1474 
1475             HETE_PROTECTED      File is write protected
1476 
1477             HETE_ERROR          File system error - check errno(3)
1478 
1479     EXAMPLE
1480             //
1481             // Flush a HET file's buffers to disk
1482             //
1483 
1484             #include "hetlib.h"
1485 
1486             char data[] = "This is a test";
1487 
1488             int main( int argc, char *argv[] )
1489             {
1490                 HETB *hetb;
1491                 int rc;
1492 
1493                 rc = het_open( &hetb, argv[ 1 ], HETOPEN_CREATE );
1494                 if( rc >= 0 )
1495                 {
1496                     rc = het_write( hetb, data, sizeof( data ) );
1497                     if( rc >= 0 )
1498                     {
1499                         printf( "Block successfully written\n" );
1500 
1501                         rc = het_sync( &hetb );
1502                         if( rc >= 0 )
1503                         {
1504                             printf( "Block successfully committed\n" );
1505                         }
1506                     }
1507                 }
1508 
1509                 if( rc < 0 )
1510                 {
1511                     printf( "HETLIB error: %d\n", rc );
1512                 }
1513 
1514                 het_close( &hetb );
1515 
1516                 return( 0 );
1517             }
1518 
1519     SEE ALSO
1520             het_open(), het_write(), het_close()
1521 
1522 ==DOC==*/
1523 
1524 DLL_EXPORT int
het_sync(HETB * hetb)1525 het_sync( HETB *hetb )
1526 {
1527     int rc;
1528 
1529     /*
1530     || Can't sync to readonly media
1531     */
1532     if( hetb->writeprotect )
1533     {
1534         return( HETE_PROTECTED );
1535     }
1536 
1537     /*
1538     || Perform the sync
1539     */
1540     do rc = fdatasync( fileno( hetb->fd ) );
1541     while (EINTR == rc);
1542     if (rc != 0)
1543     {
1544         return( HETE_ERROR );
1545     }
1546 
1547     /*
1548     || Success
1549     */
1550     return( 0 );
1551 }
1552 
1553 /*==DOC==
1554 
1555     NAME
1556             het_locate - Locate a block within an HET file
1557 
1558     SYNOPSIS
1559             #include "hetlib.h"
1560 
1561             int het_locate( HETB *hetb, int block )
1562 
1563     DESCRIPTION
1564             Repositions the HET file to the start of the block specified by
1565             the "block" parameter.
1566 
1567     RETURN VALUE
1568             If no errors are detected then the return value will be >= 0 and
1569             represents the new current block number.
1570 
1571             If an error occurs, then the return value will be < 0 and will
1572             the same as those returned by:
1573                 het_rewind()
1574                 het_fsb()
1575 
1576     NOTES
1577             Block numbers start at 0.
1578 
1579     EXAMPLE
1580             //
1581             // Seek to block #4 in HET file
1582             //
1583 
1584             #include "hetlib.h"
1585 
1586             int main( int argc, char *argv[] )
1587             {
1588                 HETB *hetb;
1589                 int rc;
1590 
1591                 rc = het_open( &hetb, argv[ 1 ], 0 );
1592                 if( rc >= 0 )
1593                 {
1594                     rc = het_locate( hetb, 4 );
1595                     if( rc >= 0 )
1596                     {
1597                         printf( "New tape position: %d\n", rc );
1598                     }
1599                 }
1600 
1601                 if( rc < 0 )
1602                 {
1603                     printf( "HETLIB error: %d\n", rc );
1604                 }
1605 
1606                 het_close( &hetb );
1607 
1608                 return( 0 );
1609             }
1610 
1611     SEE ALSO
1612             het_open(), het_rewind(), het_fsb(), het_close()
1613 
1614 ==DOC==*/
1615 
1616 DLL_EXPORT int
het_locate(HETB * hetb,int block)1617 het_locate( HETB *hetb, int block )
1618 {
1619     int rc;
1620 
1621     /*
1622     || Start the search from the beginning
1623     */
1624     rc = het_rewind( hetb );
1625     if( rc < 0 )
1626     {
1627         return( rc );
1628     }
1629 
1630     /*
1631     || Forward space until we reach the desired block
1632     */
1633     while( (int)hetb->cblk < block )
1634     {
1635         rc = het_fsb( hetb );
1636         if( rc < 0 && HETE_TAPEMARK != rc )
1637         {
1638             return( rc );
1639         }
1640     }
1641 
1642     return( hetb->cblk );
1643 }
1644 
1645 /*==DOC==
1646 
1647     NAME
1648             het_bsb - Backspace a block in an HET file
1649 
1650     SYNOPSIS
1651             #include "hetlib.h"
1652 
1653             int het_bsb( HETB *hetb )
1654 
1655     DESCRIPTION
1656             Repositions the current block pointer in an HET file to the
1657             previous block.
1658 
1659     RETURN VALUE
1660             If no errors are detected then the return value will be >= 0 and
1661             will be the new block number.
1662 
1663             If an error occurs, then the return value will be < 0 and can be
1664             one of the following:
1665 
1666             HETE_ERROR          File system error - check errno(3)
1667 
1668             HETE_BOT            Beginning of tape
1669 
1670             HETE_TAPEMARK       Tape mark encountered
1671 
1672             For other possible errors, see:
1673                 het_rewind()
1674                 het_read_header()
1675 
1676     EXAMPLE
1677             //
1678             // Backspace a block in an HET file
1679             //
1680 
1681             #include "hetlib.h"
1682 
1683             int main( int argc, char *argv[] )
1684             {
1685                 HETB *hetb;
1686                 int rc;
1687 
1688                 rc = het_open( &hetb, argv[ 1 ], 0 );
1689                 if( rc >= 0 )
1690                 {
1691                     rc = het_fsb( hetb );
1692                     if( rc >= 0 )
1693                     {
1694                         rc = het_bsb( hetb );
1695                         if( rc >= 0 )
1696                         {
1697                             printf( "New block number = %d\n", rc );
1698                         }
1699                     }
1700                 }
1701 
1702                 if( rc < 0 )
1703                 {
1704                     printf( "HETLIB error: %d\n", rc );
1705                 }
1706 
1707                 het_close( &hetb );
1708 
1709                 return( 0 );
1710             }
1711 
1712     SEE ALSO
1713             het_open(), het_rewind(), het_read_header(), het_fsb(), het_close()
1714 
1715 ==DOC==*/
1716 
1717 DLL_EXPORT int
het_bsb(HETB * hetb)1718 het_bsb( HETB *hetb )
1719 {
1720     int rc;
1721     int newblk;
1722     int offset;     // (note: safe to use 'int' as offset here
1723                     //  since we only ever seek from SEEK_CUR)
1724     int tapemark = FALSE;
1725 
1726     /*
1727     || Error if at BOT
1728     */
1729     if( hetb->cblk == 0 )
1730     {
1731         return( HETE_BOT );
1732     }
1733 
1734     /*
1735     || Get new block number
1736     */
1737     newblk = hetb->cblk - 1;
1738 
1739     /*
1740     || If new block is first on, then just rewind
1741     */
1742     if( newblk == 0 )
1743     {
1744         return( het_rewind( hetb ) );
1745     }
1746 
1747     /*
1748     || Calculate offset to get back to beginning of current block
1749     */
1750     offset = -((int)( HETHDR_CLEN( hetb ) + sizeof( HETHDR ) ));
1751 
1752     /*
1753     || Search backwards an entire block.  If the block is a tapemark, we can't
1754     || just return to caller since we must load the chunk header preceding it
1755     || to maintain the chdr in the HET.
1756     */
1757     do
1758     {
1759         /*
1760         || Reposition to start of chunk
1761         */
1762         rc = fseek( hetb->fd,
1763                     offset,
1764                     SEEK_CUR );
1765         if( rc == -1 )
1766         {
1767             return( HETE_ERROR );
1768         }
1769 
1770         /*
1771         || Read header, ignoring tapemarks
1772         */
1773         rc = het_read_header( hetb );
1774         if( rc < 0 && rc != HETE_TAPEMARK )
1775         {
1776             return( rc );
1777         }
1778 
1779         /*
1780         || Recalculate offset
1781         */
1782         offset = -((int)( HETHDR_PLEN( hetb ) + ( sizeof( HETHDR ) * 2 ) ));
1783     }
1784     while( hetb->chdr.flags1 & !( HETHDR_FLAGS1_BOR | HETHDR_FLAGS1_TAPEMARK ) );
1785 
1786     /*
1787     || Remember whether it's a tapemark or not
1788     */
1789     tapemark = ( hetb->chdr.flags1 & HETHDR_FLAGS1_TAPEMARK );
1790 
1791     /*
1792     || Reposition to chunk header preceding this one so we can load keep the
1793     || chdr in the HET current.
1794     */
1795     rc = fseek( hetb->fd,
1796                 offset,
1797                 SEEK_CUR );
1798     if( rc == -1 )
1799     {
1800         return( HETE_ERROR );
1801     }
1802 
1803     /*
1804     || Read header (ignore tapemarks)
1805     */
1806     rc = het_read_header( hetb );
1807     if( rc < 0 && ( rc != HETE_TAPEMARK ) )
1808     {
1809         return( rc );
1810     }
1811 
1812     /*
1813     || Finally reposition back to the where we should be
1814     */
1815     rc = fseek( hetb->fd,
1816                 HETHDR_CLEN( hetb ),
1817                 SEEK_CUR );
1818     if( rc == -1 )
1819     {
1820         return( HETE_ERROR );
1821     }
1822 
1823     /*
1824     || Store new block number
1825     */
1826     hetb->cblk = newblk;
1827 
1828     /*
1829     || Was it a tapemark?
1830     */
1831     if( tapemark )
1832     {
1833         return( HETE_TAPEMARK );
1834     }
1835 
1836     /*
1837     || Reset flag to force truncation if a write occurs
1838     */
1839     hetb->truncated = FALSE;
1840 
1841     /*
1842     || Return block number
1843     */
1844     return( hetb->cblk );
1845 }
1846 
1847 /*==DOC==
1848 
1849     NAME
1850             het_fsb - Foward space a block in an HET file
1851 
1852     SYNOPSIS
1853             #include "hetlib.h"
1854 
1855             int het_fsb( HETB *hetb )
1856 
1857     DESCRIPTION
1858             Repositions the current block pointer in an HET file to the
1859             next block.
1860 
1861     RETURN VALUE
1862             If no errors are detected then the return value will be >= 0 and
1863             will be the new block number.
1864 
1865             If an error occurs, then the return value will be < 0 and can be
1866             one of the following:
1867 
1868             HETE_ERROR          File system error - check errno(3)
1869 
1870             For other possible errors, see:
1871                 het_read_header()
1872 
1873     EXAMPLE
1874             //
1875             // Forward space a block in an HET file
1876             //
1877 
1878             #include "hetlib.h"
1879 
1880             int main( int argc, char *argv[] )
1881             {
1882                 HETB *hetb;
1883                 int rc;
1884 
1885                 rc = het_open( &hetb, argv[ 1 ], 0 );
1886                 if( rc >= 0 )
1887                 {
1888                     rc = het_fsb( hetb );
1889                     if( rc >= 0 )
1890                     {
1891                         printf( "New block number = %d\n", rc );
1892                     }
1893                 }
1894 
1895                 if( rc < 0 )
1896                 {
1897                     printf( "HETLIB error: %d\n", rc );
1898                 }
1899 
1900                 het_close( &hetb );
1901 
1902                 return( 0 );
1903             }
1904 
1905     SEE ALSO
1906             het_open(), het_read_header(), het_close()
1907 
1908 ==DOC==*/
1909 
1910 DLL_EXPORT int
het_fsb(HETB * hetb)1911 het_fsb( HETB *hetb )
1912 {
1913     int rc;
1914 
1915     /*
1916     || Loop until we've processed an entire block
1917     */
1918     do
1919     {
1920         /*
1921         || Read header to get length of next chunk
1922         */
1923         rc = het_read_header( hetb );
1924         if( rc < 0 )
1925         {
1926             return( rc );
1927         }
1928 
1929         /*
1930         || Seek to next chunk
1931         */
1932         rc = fseek( hetb->fd,
1933                     HETHDR_CLEN( hetb ),
1934                     SEEK_CUR );
1935         if( rc == -1 )
1936         {
1937             return( HETE_ERROR );
1938         }
1939     }
1940     while( !( hetb->chdr.flags1 & HETHDR_FLAGS1_EOR ) );
1941 
1942     /*
1943     || Reset flag to force truncation if a write occurs
1944     */
1945     hetb->truncated = FALSE;
1946 
1947     /*
1948     || Return block number
1949     */
1950     return( hetb->cblk );
1951 }
1952 
1953 /*==DOC==
1954 
1955     NAME
1956             het_bsf - Backspace a file in an HET file
1957 
1958     SYNOPSIS
1959             #include "hetlib.h"
1960 
1961             int het_bsf( HETB *hetb )
1962 
1963     DESCRIPTION
1964             Repositions the current block pointer in an HET file to the
1965             previous tapemark.
1966 
1967     RETURN VALUE
1968             If no errors are detected then the return value will be >= 0 and
1969             will be the new block number.
1970 
1971             If an error occurs, then the return value will be < 0 and will be
1972             the same as those returned by het_bsb() with the exception that
1973             HETE_TAPEMARK and HETE_BOT will not occur.
1974 
1975     EXAMPLE
1976             //
1977             // Backspace a file in an HET file
1978             //
1979 
1980             #include "hetlib.h"
1981 
1982             int main( int argc, char *argv[] )
1983             {
1984                 HETB *hetb;
1985                 int rc;
1986 
1987                 rc = het_open( &hetb, argv[ 1 ], 0 );
1988                 if( rc >= 0 )
1989                 {
1990                     rc = het_fsf( hetb );
1991                     if( rc >= 0 )
1992                     {
1993                         rc = het_bsf( hetb );
1994                         if( rc >= 0 )
1995                         {
1996                             printf( "Backspaced (sort of :-))\n" );
1997                         }
1998                     }
1999                 }
2000 
2001                 if( rc < 0 )
2002                 {
2003                     printf( "HETLIB error: %d\n", rc );
2004                 }
2005 
2006                 het_close( &hetb );
2007 
2008                 return( 0 );
2009             }
2010 
2011     SEE ALSO
2012             het_open(), het_bsb(), het_fsf(), het_close()
2013 
2014 ==DOC==*/
2015 
2016 DLL_EXPORT int
het_bsf(HETB * hetb)2017 het_bsf( HETB *hetb )
2018 {
2019     int rc;
2020 
2021     /*
2022     || Backspace until we hit a tapemark
2023     */
2024     do
2025     {
2026         rc = het_bsb( hetb );
2027     }
2028     while( rc >= 0 );
2029 
2030     /*
2031     || Success
2032     */
2033     if( ( rc == HETE_BOT ) || ( rc == HETE_TAPEMARK ) )
2034     {
2035         return( hetb->cblk );
2036     }
2037 
2038     /*
2039     || Failure
2040     */
2041     return( rc );
2042 }
2043 
2044 /*==DOC==
2045 
2046     NAME
2047             het_fsf - Forward space a file in an HET file
2048 
2049     SYNOPSIS
2050             #include "hetlib.h"
2051 
2052             int het_fsf( HETB *hetb )
2053 
2054     DESCRIPTION
2055             Repositions the current block pointer in an HET file to the
2056             next tapemark.
2057 
2058     RETURN VALUE
2059             If no errors are detected then the return value will be >= 0 and
2060             will be the new block number.
2061 
2062             If an error occurs, then the return value will be < 0 and will be
2063             the same as those returned by het_fsb() with the exception that
2064             HETE_TAPEMARK will not occur.
2065 
2066     EXAMPLE
2067             //
2068             // Forward space a file in an HET file
2069             //
2070 
2071             #include "hetlib.h"
2072 
2073             int main( int argc, char *argv[] )
2074             {
2075                 HETB *hetb;
2076                 int rc;
2077 
2078                 rc = het_open( &hetb, argv[ 1 ], 0 );
2079                 if( rc >= 0 )
2080                 {
2081                     rc = het_fsf( hetb );
2082                     if( rc >= 0 )
2083                     {
2084                         printf( "Forward spaced\n" );
2085                     }
2086                 }
2087 
2088                 if( rc < 0 )
2089                 {
2090                     printf( "HETLIB error: %d\n", rc );
2091                 }
2092 
2093                 het_close( &hetb );
2094 
2095                 return( 0 );
2096             }
2097 
2098     SEE ALSO
2099             het_open(), het_fsb(), het_close()
2100 
2101 ==DOC==*/
2102 
2103 DLL_EXPORT int
het_fsf(HETB * hetb)2104 het_fsf( HETB *hetb )
2105 {
2106     int rc;
2107 
2108     /*
2109     || Forward space until we hit a tapemark
2110     */
2111     do
2112     {
2113         rc = het_fsb( hetb );
2114     }
2115     while( rc >= 0 );
2116 
2117     /*
2118     || Success
2119     */
2120     if( rc == HETE_TAPEMARK )
2121     {
2122         return( hetb->cblk );
2123     }
2124 
2125     /*
2126     || Failure
2127     */
2128     return( rc );
2129 }
2130 
2131 /*==DOC==
2132 
2133     NAME
2134             het_rewind - Rewind an HET file
2135 
2136     SYNOPSIS
2137             #include "hetlib.h"
2138 
2139             int het_rewind( HETB *hetb )
2140 
2141     DESCRIPTION
2142             Repositions the current block pointer in an HET file to the
2143             load point.
2144 
2145     RETURN VALUE
2146             If no errors are detected then the return value will be >= 0 and
2147             represents the new block number (always 0).
2148 
2149             If an error occurs, then the return value will be < 0 and will be
2150             one of the following:
2151 
2152             HETE_ERROR          File system error - check errno(3)
2153 
2154     EXAMPLE
2155             //
2156             // Rewind an HET file to the load point.
2157             //
2158 
2159             #include "hetlib.h"
2160 
2161             int main( int argc, char *argv[] )
2162             {
2163                 HETB *hetb;
2164                 int rc;
2165 
2166                 rc = het_open( &hetb, argv[ 1 ], 0 );
2167                 if( rc >= 0 )
2168                 {
2169                     rc = het_rewind( hetb );
2170                     if( rc >= 0 )
2171                     {
2172                         printf( "Tape rewound\n" );
2173                     }
2174                 }
2175 
2176                 if( rc < 0 )
2177                 {
2178                     printf( "HETLIB error: %d\n", rc );
2179                 }
2180 
2181                 het_close( &hetb );
2182 
2183                 return( 0 );
2184             }
2185 
2186     SEE ALSO
2187             het_open(), het_close()
2188 
2189 ==DOC==*/
2190 
2191 DLL_EXPORT int
het_rewind(HETB * hetb)2192 het_rewind( HETB *hetb )
2193 {
2194     int rc;
2195 
2196     /*
2197     || Just seek to the beginning of the file
2198     */
2199     rc = fseek( hetb->fd,
2200                 0,
2201                 SEEK_SET );
2202     if( rc == -1 )
2203     {
2204         return( HETE_ERROR );
2205     }
2206 
2207     /*
2208     || Reset current block
2209     */
2210     hetb->cblk = 0;
2211 
2212     /*
2213     || Clear header for the heck of it
2214     */
2215     memset( &hetb->chdr, 0, sizeof( hetb->chdr ) );
2216 
2217     /*
2218     || Reset flag to force truncation if a write occurs
2219     */
2220     hetb->truncated = FALSE;
2221 
2222     /*
2223     || Return block number
2224     */
2225     return( hetb->cblk );
2226 }
2227 
2228 /*==DOC==
2229 
2230     NAME
2231             het_error - Returns a text message for an HET error code
2232 
2233     SYNOPSIS
2234             #include "hetlib.h"
2235 
2236             char *het_error( int rc )
2237 
2238     DESCRIPTION
2239             Simply returns a pointer to a string that describes the error
2240             code passed in the "rc" parameter.
2241 
2242     RETURN VALUE
2243             The return value is always valid and no errors are returned.
2244 
2245     EXAMPLE
2246             //
2247             // Print text of HETE_BADLEN.
2248             //
2249 
2250             #include "hetlib.h"
2251 
2252             int main( int argc, char *argv[] )
2253             {
2254                 printf( "HETLIB error: %d = %s\n",
2255                     HETE_BADLEN,
2256                     het_error( HETE_BADLEN ) );
2257 
2258                 return( 0 );
2259             }
2260 
2261     SEE ALSO
2262 
2263 ==DOC==*/
2264 
2265 DLL_EXPORT const char *
het_error(int rc)2266 het_error( int rc )
2267 {
2268     /*
2269     || If not an error just return the "OK" string
2270     */
2271     if( rc >= 0 )
2272     {
2273         rc = 0;
2274     }
2275 
2276     /*
2277     || Turn it into an index
2278     */
2279     rc = -rc;
2280 
2281     /*
2282     || Within range?
2283     */
2284     if( rc >= (int)HET_ERRSTR_MAX )
2285     {
2286         rc = HET_ERRSTR_MAX - 1;
2287     }
2288 
2289     /*
2290     || Return string
2291     */
2292     return( het_errstr[ rc ] );
2293 }
2294 /*==DOC==
2295 
2296     NAME
2297             het_tell - Returns the current read/write pointer offset
2298 
2299     SYNOPSIS
2300             #include "hetlib.h"
2301 
2302             off_t het_tell( HETB *hetb )
2303 
2304     DESCRIPTION
2305             Returns a off_t describing the actual read/write cursor
2306             within the HET file
2307 
2308     RETURN VALUE
2309             >=0 The actual cursor offset
2310             <0 An error occured.
2311                 Possible errors are :
2312                 HETE_ERROR - File system error occured
2313 
2314     EXAMPLE
2315             //
2316             // Get the current HET pointer
2317             //
2318 
2319             #include "hetlib.h"
2320 
2321             int main( int argc, char *argv[] )
2322             {
2323                 HETB *hetb;
2324                 int rc;
2325                 off_t rwptr;
2326 
2327                 rc = het_open( &hetb, argv[ 1 ], 0 );
2328                 if( rc >= 0 )
2329                 {
2330                     rwptr = het_tell( hetb );
2331                     if( rwptr >= 0 )
2332                     {
2333                         printf( "Current offset is %" I64_FMT "d\n" , (U64)rwptr);
2334                     }
2335                 }
2336 
2337                 if( rc < 0 )
2338                 {
2339                     printf( "HETLIB error: %d\n", rc );
2340                 }
2341 
2342                 het_close( &hetb );
2343 
2344                 return( 0 );
2345             }
2346 
2347     SEE ALSO
2348 
2349 ==DOC==*/
2350 
2351 DLL_EXPORT
2352 off_t
het_tell(HETB * hetb)2353 het_tell( HETB *hetb )
2354 {
2355     off_t rwptr = ftell( hetb->fd );
2356     if ( rwptr < 0 )
2357     {
2358         return HETE_ERROR;
2359     }
2360     return rwptr;
2361 }
2362