#ifdef	ournix
#include "ournix.h"
#endif
#ifndef LINT	/* { */
char sccsID[] =
 "@(#) slice.c V3.9 Copyright Julian H. Stacey, Munich, \
 1990 12 13 - 2005 01 02.\n";
#endif		/* } */

/* Manual exists */

#include <string.h>
#include	<sys/types.h>
#ifndef MSDOS	/* { unix */
#include	<sys/file.h>
#endif		/* } */
#include	<sys/stat.h>
#include	<stdio.h>
#include <stdlib.h>
#ifdef	MSDOS	/* { */
#include	<fcntl.h>
#endif		/* } */
#ifdef	i386	/* { */
#include	<sys/fcntl.h>
#endif		/* } */
#include	<ctype.h>
#include	<signal.h>

#include <unistd.h>

extern int	atoi() ;
#ifndef unix
extern char	*malloc() ;
#else
/* #include <malloc.h>	#error "<malloc.h> has been replaced by <stdlib.h>" */
#include <stdlib.h>
#endif

typedef char	FLAG ;
char	**ARGV;
FLAG	pipe_f = 0 ;	/* 1 = tape data is to be connected to a process pipe */
FLAG	zero_f = 0 ;	/* 1 = warn if suspicious all zeroes	*/
FLAG	ones_f = 0 ;	/* 1 = warn if suspicious all ones	*/
FLAG	write_f = 0 ;	/* 1 = write tape	*/
FLAG	read_f = 1 ;	/* 1 = read tape	*/
FLAG	delete_f = 0 ;	/* 1 = delete slice after writing slice to output */
FLAG	verbose_f = 0 ; /* 1 = provide verbose diagnostics	*/
FLAG	no_rewind_f = 0 ; /* 1 = dont rewind before testing tape, allows user
			   * to skip previous files on tape by reading
			   * a no_rewind_on_close device from a shell script,
			   * before then calling slice	
			   */
int	block_size = 8 * 1024 ; /* tar B is 20	*/
	/* Symmetric Unix device driver actually appears to write 8192 bytes
	 * at a time to /dev/rcst2, regardless of the block size this user
	 * program passes to the system	
	 */
char	*base_p ;	/* read pointer */
long	skip_count = 0L ;	/* allows us to skip some initial blocks */
#ifdef	unix	/* { */
char	default_tape[] = "/dev/rmt8" ;
#endif		/* } */
#ifdef	MSDOS	/* { */
char	default_tape[] = "cst:" ; /* need msdos adviser for name cst: ?	*/ 
#endif		/* } */

FILE *my_out ; /* prior to 386bsd, used to be: struct _iobuf *my_out	*/

char skeleton[] = "%s/tp_%07ld.%s" ;	/* slice file names	*/
					/* 7 digits allows a 4 gig
					 * DVD to be sliced into 2K
					 * files (though the top
					 * directory gets awfully big
					 * & ls very slow)
					 */

/* open & close device to ensure if the physical device was previously
 * used as `a no rewind on completion' tape drive, that it will now be
 * rewound prior to use.
 * fd_tape is not left open because although it would be slightly
 * more effecient on cpu, it would complicate the code too much,
 * and compared with the real physical device work we are
 * doing, a couple more system calls dont make much difference.
 */
int my_rewind(tape_name)
	char	*tape_name ;
	{
	int	fd_tape ;			/* file descrip	*/
	if (verbose_f) fprintf(my_out,
		"%s: Started rewinding %s.\n",*ARGV,tape_name) ;
	if ((fd_tape = open(tape_name, O_RDONLY )) == EOF)
		{
		fprintf(my_out,
			"%s: Error: failed to open %s to ensure rewound.\n",
			*ARGV, tape_name) ;
		perror(*ARGV) ;
		return(1) ;
		}
	/* now rewind tape	*/
	/* Used to do an lseek, but maybe this doesnt retention tape,
	 * so after a tape got mangled, repaired, mangled again, i
	 * decided to do a real read followed by close, to ensure
	 * tape is rewound, but I dont really know how the TEAC
	 * drive is controlled, so this read may be superfluous,
	 * however before taking it out, consider the possibility
	 * of multiple system crashes whilst the tape is inserted,
	 * and or multiple insertions before the tape is actually used.

	 * Only problem with a read is that if it is virgin media,
	 * tape driver reports an error on /dev/console.
	 */
	if ( read(fd_tape,base_p,block_size) != block_size)
		{
		fprintf(my_out, "%s: Error: failed to rewind %s.\n",
			*ARGV, tape_name) ;
		perror(*ARGV) ;
		return(1) ;
		}
	(void) close(fd_tape) ;
	if (verbose_f) fprintf(my_out, "%s: Finished rewinding %s.\n",
		*ARGV,tape_name) ;
	return(0);
	}

void my_exit(val)
	int	val ;
	{
	if (verbose_f) fprintf(my_out,"%s: Finished.\n",*ARGV);
	exit(val) ;
	}

/* Warns if a defective driver or drive or media has delivered a block
   with all bits low or high. */

void block_faked( blok_size, slice )
	unsigned blok_size ;	/* buffer size	*/
	char	*slice ;	/* name of slice	*/
	{
	char	*bas_p = base_p ;	/* buffer base	*/
	unsigned char	c ;

	if (!(zero_f || ones_f)) return ;
	while (blok_size--)
		{
		c = *bas_p++ ;
		if (( c == 0 ) && zero_f ) continue ;
		if (( c == 0xFF ) && ones_f )  continue ;
		return ;
		}
	fprintf(my_out, "%s: Warning: %s just %s.\n",
		*ARGV, slice, ( ones_f ) ? "0xFF" : "0x00" ) ;

	}

	/* return 0 = ok, 1 = error	*/
int do_tape(tape_name)
	char	*tape_name ;
	{
	int	fd_tape ;			/* tape file descrip	*/
	int	fd_slice ;			/* slice file descrip	*/
	char	*message ;
	int	read_count ;
	int	write_count ;
	char	dir_name[20] ;		/* eg "sl_12345"	*/
	char	slice_name[2*20] ;	/* eg "sl_21224/tp_2122415.ok"	*/
	int	errs = 0 ;
	long	prefix  = 0L ;	/* sequential 1 2 3 4	*/
	char	*postfix ;	/* "ok" or "bad"	*/
	/*
	If read values from tape happened to return
		1024 1024 1023 1 1024	(as happens on SCS occasionally)
	file names written would be
		tp_0000001.ok tp_0000002.ok tp_0000003.bad
		tp_000004.bad tp_0000005.ok
		(which is not what is wanted)
	*/

	message = write_f ? "writing" : "reading" ;
	if (verbose_f) fprintf(my_out,"%s %s %s.\n",*ARGV, message, tape_name);

	if (read_f)
		{
		if (!(pipe_f || no_rewind_f)) (void)my_rewind(tape_name);
		if (pipe_f) fd_tape = fileno(stdin) ;
		else if ((fd_tape = open(tape_name,O_RDONLY
#ifdef	MSDOS	/* { */
			| O_BINARY , S_IREAD
#else		/* }{ */
			,0640
#endif		/* } */
			)) == EOF )
			{
			perror(*ARGV) ;
			my_exit(1) ;
			}
		for (;;)
			{
			read_count = read(fd_tape, base_p, block_size ) ;
			if (read_count == 0 ) break ;	/* EOF	*/
#define	AMOUNT	100
			if ((prefix % AMOUNT) == 0L)
				{
				sprintf(dir_name,"sl_%05d",
					(int)(prefix / AMOUNT) ) ;
				mkdir(dir_name,0755);
				}
			postfix = (read_count == block_size ) ? "ok" : "bad" ;
			(void)sprintf(slice_name, skeleton, dir_name,
				prefix,postfix);
			if (read_count != block_size) printf(
				"%s: for %s, tried to read %d, got %d.\n",
				*ARGV, slice_name, block_size, read_count ) ;
				/* maybe we tried to read 20K of tape, when
				 * only 10K remained for input, or maybe
				 * an scs tape driver lost a byte, & we are
				 * in the middle of reading 8191 bytes,
				 * followed by 1 byte !
				 */
			if (read_count < 0 )
				{
				/* error	*/
				perror(*ARGV) ;
				errs++ ;
				break ;
				}
			if (zero_f || ones_f)
				block_faked( read_count, slice_name ) ;
			if (skip_count > 0L) skip_count-- ;
			else	{
				if ((fd_slice = open(slice_name,
					O_WRONLY | O_CREAT | O_TRUNC
#ifdef		MSDOS	/* { */
					| O_BINARY , S_IWRITE
#else			/* }{ */
					,0640
#endif			/* } */
					)) == EOF )
					{
					perror(*ARGV) ;
					fprintf(my_out,
				"%s: Error: failed to open %s for writing.\n",
						*ARGV, slice_name) ;
					errs++ ;
					break ;
					}
				if ((write_count =
					 write(fd_slice,base_p,read_count))
					!= read_count)
					{
					fprintf(my_out,
 "%s: Error: failed to write %d bytes to %s (only wrote %d), so deleted.\n",
						*ARGV, read_count, slice_name,
						write_count) ;
					/* if disk becomes full by writing
					   slice, we get a short slice with
					   name .ok, this could be embarassing,
					   so it is deleted	*/
					unlink(slice_name);
					errs++ ;
					break ;
					}
				(void) close(fd_slice) ;
				}
			prefix++ ;
			}
		if (!pipe_f) (void) close(fd_tape) ;
		}
	else	/* write_f	*/
		{
		if (!pipe_f && !no_rewind_f) (void)my_rewind(tape_name);

		if (pipe_f) fd_tape = fileno(stdout) ; else
		if ((fd_tape = open(tape_name,O_WRONLY | O_CREAT | O_TRUNC
#ifdef	MSDOS	/* { */
			| O_BINARY , S_IWRITE
#else		/* }{ */
			,0640
#endif		/* } */
			)) == EOF )
			{
			perror(*ARGV) ;
			my_exit(1) ;
			}
		for (;;)
			{
			if ((prefix % AMOUNT) == 0L)
				(void) sprintf(dir_name,"sl_%05d",
					(int)(prefix / AMOUNT) ) ;
			(void)sprintf(slice_name, skeleton, dir_name,
				prefix, "ok");
			if ( skip_count > 0L ) skip_count-- ;
			else	{
				if ((fd_slice = open(slice_name, O_RDONLY
#ifdef	MSDOS	/* { */
					| O_BINARY , S_IREAD
#endif		/* } */
					)) == EOF )
					{
					fprintf(my_out,
					"%s: Error: failed to open %s for reading.\n",
						*ARGV, slice_name) ;
					perror(*ARGV) ;
					errs++ ;
					break ;
					}
				if ((read_count =
					 read(fd_slice,base_p,block_size))
					!= block_size)
					{
					perror(*ARGV) ;
					fprintf(my_out,
	"%s: Warning: failed to read %d bytes from %s (only read %d).\n",
						*ARGV, block_size, slice_name,
						read_count) ;
					/* maybe we tried to read 20K when only
					 * 10K remained in input file, or maybe
					 * an scs tape driver lost a byte !,
					 * in which case we have just read
					 * 8191 bytes, 1 byte, or 0 bytes
					 * if a human has concatenated
					 * 2 slices together, & created a dummy
					 * slice to get us to skip to next real
					 * 8192 slice	
					 */
					}
				if (read_count < 0 )
					{
					/* error	*/
					perror(*ARGV) ;
					errs++ ;
					break ;
					}
				if (zero_f || ones_f) block_faked(
					 read_count, slice_name ) ;
				if ((write_count =
					 write(fd_tape, base_p, read_count ) )
					!= read_count)
					{
					printf(
				"%s: for %s, tried to write %d, wrote %d.\n",
						*ARGV, slice_name, read_count,
						write_count ) ;
					perror(*ARGV) ;
					errs++ ;
					break ;	/* EOF	*/
					}
				if (close(fd_slice))
					{
					errs++ ;
					printf(
					 "%s: Error: failed to close %s.\n",
						*ARGV, slice_name ) ;
					perror(*ARGV) ;
					break ;
					}
				}
			if (delete_f && unlink(slice_name))
				{
				errs++ ;
				printf("%s: Caution: failed to unlink %s.\n",
					*ARGV, slice_name ) ;
				perror(*ARGV) ;
				}
			if (delete_f && ((prefix % AMOUNT) == AMOUNT - 1) )
				{
				/* remove used directory	*/
				(void) rmdir(dir_name);
				}
			prefix++ ;
			}
		if (delete_f && !errs && ((prefix % AMOUNT) != 0L) )
			/* remove last directory	*/
			(void) rmdir(dir_name);
		if (!pipe_f) (void) close(fd_tape) ;
		}
	return(errs?1:0) ;
	}

void syntax()
	{
	fprintf(my_out, "%s\n%s %s [-- or special_file]\n",
		"Syntax error, correct syntax is:", *ARGV,
		"[-b block_size] [-d] [-r] [-n] [-s skip_blocks] [-v] [-w] [-0] [-1] [-- or file or special_file]"
		) ;
	my_exit(1) ;
	}

int main(argc,argv)
	int	argc ;
	char	**argv ;
	{
	int	errs = 0 ;		/* cumulative error count	*/
	int	byte_count ;
	char	*p_tmp ;

	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */

	my_out = stdout ;
	while (--argc)
		{
		if (**++argv == '-')
			{
			switch ( *++*argv )
				{
				case '-':	pipe_f = 1 ;	break ;
				case 'v' :	verbose_f = 1 ;	break ;
				case 'n' :	no_rewind_f = 1 ;	break ;
				case 'r' :	read_f = 1 ;
						write_f = 0 ; break ;
				case 'w' :	write_f = 1 ;
						read_f = 0 ; break ;
				case 'd' :	delete_f = 1 ; break ;
				case 'b' :	if (--argc <= 0) syntax() ;
						++argv ;
						p_tmp = *argv+strlen(*argv) -1 ;
						byte_count = 1 ;
						if ((*p_tmp == 'k')  ||
							(*p_tmp == 'K')  )
							{
							byte_count = 1024 ;
							*p_tmp = '\0' ;
							}
						else if ((*p_tmp == 'b')  ||
							(*p_tmp == 'B')  )
							{
							byte_count = 512 ;
							*p_tmp = '\0' ;
							}
						else if ((*p_tmp == 'w')  ||
							(*p_tmp == 'W')  )
							{
							byte_count = 2 ;
							*p_tmp = '\0' ;
							}
						byte_count = byte_count *
								atoi(*argv);
						if (byte_count < 0 ) syntax() ;
						block_size = byte_count ;
						break ;
				case 's' :	if (--argc <= 0) syntax() ;
						skip_count = atol(*++argv);
						if (skip_count < 0 ) syntax() ;
						break ;
				case '0' :	zero_f = 1 ; break ;
				case '1' :	ones_f = 1 ; break ;
				default:	syntax() ;
						break ;
				}
			}
		else break ;
		}

	if ( zero_f && ones_f )
		{
		fprintf(stderr,
                "%s: -0 & -1 not allowed together.\n", *ARGV) ;
                syntax() ;
                }
	if (verbose_f) fprintf(my_out,"Using a block size of %d\n",block_size);
	if ((argc == 0) && !pipe_f)
		/* user has not specified file names or pipe IO, we will use
			default name, but in case user actually wanted pipe
			IO, we warn him	*/
		fprintf(my_out, "%s: %s %s %s %s\n", *ARGV,
			"Will use", default_tape,
			"As neither file names given,",
			"nor pipe IO specified with --"
			) ;
	if (argc > 1) syntax() ;
	if ((argc == 1) && pipe_f)
		{
		fprintf(stderr,
		"%s: Pipes output and file output not allowed together.\n",
			*ARGV) ;
		syntax() ;
		}
	if (delete_f && read_f) syntax() ;
	if (pipe_f && write_f)
		{
		/* Avoid errors going into pipe	*/
		fprintf(stderr,
		 "%s: Status to <stderr>, avoids data on <stdout> pipe.\n",
		*ARGV );
		my_out = stderr ;
		}
	/* End of checking parameters	*/
	if ((base_p = malloc((unsigned)block_size)) == (void *)0)
		{
		fprintf(stderr,"%s: malloc(%u) failed.\n",*ARGV,block_size) ;
		perror(*ARGV) ;
		my_exit(1) ;
		}

	if (pipe_f) my_exit(do_tape((write_f) ? "pipe_output" : "pipe_input")) ;
	else if (argc == 0) my_exit(do_tape(default_tape)) ;
	else while (argc--) /* currently only 1 file is allowable	*/
		errs += do_tape(*argv++) ;
	if (base_p != (void *)0) free(base_p);
	my_exit(errs) ;
	}




