/* valid on unix when reading a bad sector, doesnt create a good or a .bad */
#ifdef	ournix
#include "ournix.h"
#endif
char sccsID[] = "@(#) valid.c V2.30 Copyright Julian H. Stacey 1987.\n";

/* FUNCTION :		validates floppy discs, recovers data on faulty discs,
|			reads & writes sectors from discs, even if disc
|			has crc errors.
| COPYRIGHT		Julian H. Stacey, Munich 1987.
| COMPILATION : 	Compiled under MSDOS Microsoft 4.0 C Compiler,
|			& UCB BSD 4.2 Unix (Symmetric).
|			& FreeBSD-current Sept 96
| DOCUMENTATION :	See manual (in standard unix style).
| FUTURE DEVELOPMENTS :
|			Symmetric device driver regarding averaging of failed
|			reads, see File = /usr/src/sys/scsdev/wd.c lines 656/680
| EMAIL :		jhs@
| BUGS :
|			head is sometimes int & sometimes unsigned,
|			they need all converting to unsigned
|
| DEFINES :
| 	Optional compile defines user can pass in.
|	-DFLOPPY_40TPS="/dev/floppy_40_tracks_per_inch"
|	-DFLOPPY_80TPS="/dev/floppy_80_tracks_per_inch"
|	-DSECTORS_PER_TRK=9
|	-DMAXIMUM_BYTES_PER_SECTOR=1024
|	-DDEFAULT_BYTES_PER_SECTOR=512
|	-DMINIMUM_BYTES_PER_SECTOR=2
|	-DTRY_40TPS_BEFORE_80TPS
|	-DVSL_SJA	for hard discs under dos, by stuart
*/

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

typedef char	FLAG ;			/* binary flag true or false */
typedef int	FILE_DESCRIPTOR ;	/* file descriptor */

// extern char *malloc() ;

#ifdef	MSDOS	/* { */
union	REGS	in, out ;
#endif		/* } */

/* filename extensions to represent whether recovered data was good or bad */
#define NAME_END_OK	"ok"
#define NAME_END_BAD	"bad"

/* create meaningful file names, CREAT_SKELETON has components:
	t	track
	h	head
	s	sector (first & last)
  RECOG_SKELETON uses %c rather than t h s, because unix returns t h s,
  and msdos probably returns T H S */
#define	CREAT_SKELETON	"%s%st%02dh%1ds%c%c.%.3s" /* first %s%s is for path */
#define RECOG_SKELETON	"%c%02d%c%1d%c%c%c.%s"

#define MINIMUM_HEAD 0		/* minimum allowable head number	*/
#define MAXIMUM_HEAD 1		/* default maximum head number	*/
#define MINIMUM_SECTOR	1	/* minimum allowable sector number*/
#define 	NINE	9
#ifndef SECTORS_PER_TRK	/* { */
#ifndef i386	/* { */
#define SECTORS_PER_TRK	NINE	/* default maximum sector number
				9 for Msdos 3.2 PC or XT 3.5" or 5.25",
					720 K or 360K
				8 for Msdos 1.x
				15 for ibm pc at 5.25" 1.2 Mbyte
				18 for 1.4 Mbyte 3.25" Msdos 3.3 IBM PS[30/60?]
				*/
#else	/* }{ */
#define SECTORS_PER_TRK	18
#endif	/* } */
#endif	/* } */
#define MAXIMUM_SECTOR	18	/* maximum sector number, above which a
					warning is issued */
int	bytes_in_buf ;
char	*p_main_buf ;
char	*p_chk_buf ;
char	*p_test_buf ;		/* used to test what sort of disc is in
					unix 80tps drive, & which software
					driver is appropriate */

#define MINIMUM_TRACK	0	/* minimum allowable track number */
#define SMALL_TRACK 40		/* small capacity 40 track 5.25" 360K byte floppy */
#define LARGE_TRACK 80		/* large capacity 80 track 3.5"  720K byte floppy */
#define MAXIMUM_TRACK	(LARGE_TRACK - 1) /* default maximum track number */

#ifndef MAXIMUM_BYTES_PER_SECTOR /* { */
#define MAXIMUM_BYTES_PER_SECTOR 1024	/* maximum bytes per sector	*/
#endif				/* } */
#ifndef DEFAULT_BYTES_PER_SECTOR /* { */
#define DEFAULT_BYTES_PER_SECTOR 512	/* default bytes per sector	*/
#endif				/* } */
#ifndef MINIMUM_BYTES_PER_SECTOR /* { */
#define MINIMUM_BYTES_PER_SECTOR 2	/* minimum bytes per sector	*/
#endif				/* } */
unsigned *av_bits ;		 /* used to count how many times each bit was
				set, for when CRC is allways in error, to return
				the most frequent bit setting	*/

FLAG	no_reverse_flag = 0  ; /*  1: suppress reverse read tracking, go 0 to 79
				0: allow reverse read tracking, go 79 to 0 */
FLAG	prompt_flag = 0 ; /* wait before doing a disc, & repeat until no */
char	rw_flag = 'r' ;	/*	'w' == disc will be written (possibly after
					reading it first).
				'r' == read only.
				.... 'v' == verify: In theory we could do this,
				however msdos Int 0x13, Func 4, with a PC AT
				is said to return unpredictable results.
				Similarly, under unix, through a device driver
				we only have read & write possibilities,
				so there is not much incentive to restructure
				this flag for verify */

unsigned repeat = 1 ;	/* number of repeat attempts that will be made before
			error is considered irrecoverable (though each error
			will be reported)	*/
char	ia_flag = 'a' ; /* 'i' = ignore errors, continue regardless
			(useful for fixing isolated crc errors);
			'a' = abort on error	*/

FLAG	clean_flag = 0; /* if set clean current display line
			before displaying next status line
			doing it this way enables us to display on
			screen for maximum time */

FLAG	io_flag =	0 ;	/* 0 == no filenames used,
				   1 == io to filenames should be done on a per
					sector (or group of sectors) basis */
unsigned erase_type =	0 ;	/* 0 == sectors should not be erased
				   1 == specified sectors should be erased,
				   2 == all sectors should be erased	*/
FLAG 	comment_sectors = 0 ; 	/* 1 if sectors to have names as content */
unsigned calibrate =	10 ;	/* how often to recalibrate floppy controller
					by calling a reseek to track 0	*/

char	**ARGV ;

/* FLOPPY DISC NAMES {
  - A TEDIOUS BUSINESS, MANY NAMES ON MANY MACHINES FOR MANY FLOPPIES 
  SKIP THIS IF YOU CAN !
*/

static	FILE_DESCRIPTOR floppy_fd ;

#ifndef MSDOS	/* { */
char	specific_floppy = 0 ;
#endif		/* } */

#ifdef	scs	/* { */
#define	TRY_40TPS_BEFORE_80TPS
#endif		/* } */

#ifdef	TRY_40TPS_BEFORE_80TPS	/* { */
#ifndef FLOPPY_40TPS	/* { Floppy drive name with 40 tracks per side */
#ifdef	scs	/* { */
/* default floppy drive name to access an msdos floppy (5.25",
   360 Kbyte, 40 track, 9 sectors, 512 bytes per sector, 40tps,
   as used by a PC/XT ) usually this is linked to
   /dev/rflp, the default initial driver used by mtools
   (wrongly used to be "/dev/rfdG0") 
*/
#define	FLOPPY_40TPS	"/dev/fd_35/rt40_h2_s9"
#elif 	i386	/* }{ /* esix */
#define	FLOPPY_40TPS	"/dev/fd048ds18" /* "/dev/dsk/something" */
#elif	MSDOS	/* }{ */
	/* Under msdos, this program doesnt use this name, but its here in case
	someone writes a floppy device driver & wants to test the driver
	with this program */
#define	FLOPPY_40TPS	"floppy.dev"
#else		/* }{ */
/* hook to hang a sym link on */
#define	FLOPPY_40TPS	"/dev/floppy_40tps"
#endif	 	/* } */
#endif	 	/* } */
#endif	/* TRY_40TPS_BEFORE_80TPS	} */

#ifndef FLOPPY_80TPS	/* { Floppy drive name with 80 tracks per side */
#ifdef	scs	/* { */
	/* alternate if first unusable (wrongly used to be "/dev/rfdg0") */
#define FLOPPY_80TPS	"/dev/fd_35/rt80_h2_s9"
#elif	i386	/* }{ esix */
#ifdef	__386BSD__	/* { */
#define FLOPPY_80TPS	"/dev/rfd0c"
#else			/* }{ */
#define FLOPPY_80TPS	"/dev/fd096ds18" /* "/dev/dsk/f03dt" ; */
#endif			/* } */
#elif	MSDOS	/* }{ */
	/* Under msdos, this program doesnt use this name, but its here in case
	someone writes a floppy device driver & wants to test the driver
	with this program */
#define	FLOPPY_40TPS	"floppy.dev"
#else		/* }{ */
/* hook to hang a sym link on */
#define FLOPPY_80TPS	"/dev/floppy_80tps"
#endif	 	/* } */
#endif	 	/* } */

/* Notes about JHS Symmetric with 80tps=96 tpi drive. */
	/* Extract from man 4 wd:
	      0(a)-  80-track double-sided (5 1024-byte sectors, 800K total)
	      1(b)-  80-track double-sided (9 512-byte sectors, 720K total)
	      2(c)-  80-track double-sided (8 512-byte sectors, 640K total)
	      3(d)-  80-track single-sided (10 512-byte sectors, 400K total)
	      4(e)-  80-track single-sided (9 512-byte sectors, 360K total)
	      5(f)-  80-track single-sided (8 512-byte sectors, 320K total)
	      6(g)-  40-track double-sided (9 512-byte sectors, 360K total)
	      7(h)-  40-track double-sided (8 512-byte sectors, 320K total)
	      In addition, the high-order bit (bit 7) is set in the minor
	      device number for a unit which is used to read 48TPI (40
	      track) floppies in a 96TPI drive.
	/dev/rfd[gh]0 is presumably for a computer with a 48tpi drive,
		so not useful on my Symmetric,
		/dev/rfd[GH]0 presumably have no meaning.
	Mtools require that /dev/rflp is some sort of 512 byte/sector driver.
		Mtools decides what driver it will finally use, after accessing
		the media byte in the Msdos FAT, the fat occurs after the
		512 byte boot sector, and init(0) lseek fails to skip 512 bytes
		on a 512 b/s floppy, if using a 1024 b/s driver.
	Shell script to run to give more meaningful names on Symmetric:
		rm /dev/rflp*
		ln /dev/rfdA /dev/rflp0_40t5s2h
		ln /dev/rfdB /dev/rflp0_40t9s2h
		ln /dev/rfdC /dev/rflp0_40t8s2h
		ln /dev/rfdD /dev/rflp0_40t10s1h
		ln /dev/rfdE /dev/rflp0_40t9s1h
		ln /dev/rfdF /dev/rflp0_40t8s1h
		ln /dev/rfda /dev/rflp0_80t5s2h
		ln /dev/rfdb /dev/rflp0_80t9s2h
		ln /dev/rfdc /dev/rflp0_80t8s2h
		ln /dev/rfdd /dev/rflp0_80t10s1h
		ln /dev/rfde /dev/rflp0_80t9s1h
		ln /dev/rfdf /dev/rflp0_80t8s1h
		ln /dev/rflp40t9s2h /dev/rflp
		ln /dev/fdA /dev/flp0_40t5s2h
		ln /dev/fdB /dev/flp0_40t9s2h
		ln /dev/fdC /dev/flp0_40t8s2h
		ln /dev/fdD /dev/flp0_40t10s1h
		ln /dev/fdE /dev/flp0_40t9s1h
		ln /dev/fdF /dev/flp0_40t8s1h
		ln /dev/fda /dev/flp0_80t5s2h
		ln /dev/fdb /dev/flp0_80t9s2h
		ln /dev/fdc /dev/flp0_80t8s2h
		ln /dev/fdd /dev/flp0_80t10s1h
		ln /dev/fde /dev/flp0_80t9s1h
		ln /dev/fdf /dev/flp0_80t8s1h
		ln /dev/flp40t9s2s /dev/flp
     */

char	floppy_u_80tps[] = FLOPPY_80TPS ;
/* Default drive name */
#ifdef TRY_40TPS_BEFORE_80TPS	/* { */
	char	floppy_u_40tps[] = FLOPPY_40TPS ;
	char	*drive_p = floppy_u_40tps ;
#else	/* }{ */
	char	*drive_p = floppy_u_80tps ;
#endif	/* } */
/* FLOPPY DISC NAMES - END } */

int bytes_per_sector = DEFAULT_BYTES_PER_SECTOR ;	/* bytes per sector	*/
#ifdef	MSDOS	/* { */
int	drive_no = -1 ;	/* -1 = unasigned, 0 = 'A' for msdos
			- dont change the convention, used by low_disc
			calling microsoft msdos int86 service call */
#endif		/* } */
FLAG	ms_device = 0 ;	/* 1 if we must be aware we are to access an msdos
			   floppy using special direct access code,
			   0 if the msdos `floppy' is actual a pseudo floppy,
			   known by a normal msdos file name,
			   also 0 if Unix, (where even special devices
			   can be handled as normal files). */
unsigned f_sec	= MINIMUM_SECTOR ;	/* first sector number	*/
unsigned l_sec	= SECTORS_PER_TRK ;	/* last sector number,
				this is the topmost sector you want to
				access, under unix if l_sec is not the
				topmost sector implemented on the unix floppy
				driver you have chosen to use on a disc,
				you must set seek_sec to number of sectors
				per track,
				else lseek() will foul up! */
unsigned l_head = MAXIMUM_HEAD ;	/* last head number,
				this is the topmost head you want to
				access, under unix if l_head is not the
				topmost (= logically maximal, not physical as
				in which way up, of course!)  head
				accessed by  the unix floppy driver you are
				using, you must set seek_head to
				number of heads, else lseek will foul up! */
unsigned	seek_head = MAXIMUM_HEAD + 1 ;
				/* how many heads for seek purposes */
unsigned	seek_sec = SECTORS_PER_TRK ;
				/* how many sectors / track for seek purposes */
FLAG specific_head = 0 ;	/* memory of if seek_head has been reset */
FLAG specific_sec = 0 ;		/* memory of if seek_sec has been reset */

char txt_error[] =	"Error" ;
char txt_floppy[] =	"floppy" ;
char txt_files[] =	"files" ;
char txt_bla[] = 	"%s%s Track:%02d, Head:%1d, Sectors %02d:%02d" ;
char txt_failed_to_open[] = "%s: Failed to open %s\n" ;

char	*path = "\0" ;		/* typically ~/ok/360 */

unsigned c_to_n(c)		/* char to number */
	char	c ;
	{
	if ( ( c >= '0' ) && ( c <= '9' ) ) return((unsigned)(c - '0')) ;
	if ( ( c >= 'a' ) && ( c <= 'z' ) ) return((unsigned)(c - 'a' + 10)) ;
	if ( ( c >= 'A' ) && ( c <= 'Z' ) ) return((unsigned)(c - 'A' + 10)) ;
	return(-1) ;
	}

char n_to_c(n)		/* number to char */
	unsigned	n ;
	{
	if ( n <= 9 ) return((char)(n + '0')) ;
	if ( ( n >= 10 ) && ( n <= 26 + 10 ) ) return((char)(n -10 + 'a' )) ;
	return('\0') ;
	}

main(argc,argv)
	int	argc ;
	char	**argv ;
	{
	char	c ;
#ifdef	MSDOS	/* { */
	char	drive_s[2] ;
#endif		/* } */
	unsigned f_trk	= MINIMUM_TRACK ;	/* first track number	*/
	unsigned l_trk	= MAXIMUM_TRACK ;	/* last track number	*/
	unsigned f_head = MINIMUM_HEAD ;	/* first head number	*/
	FLAG	quiet	= 1 ;

	ARGV = argv ;
#ifdef	VSL	/* { */
#include	"../../include/vsl.h"
#endif		/* } */
	argc-- ;
	while ((argc--) && ((c = **++argv) == '-'))
		{
		switch((c = *++*argv))
			{
			case 'd':		/* drive name	*/
				drive_p = ++*argv ;
				c = *drive_p ;
#ifdef MSDOS	/* { */
				/* check for special msdos device names
					such as a,b,a:,b:	*/
#ifdef VSL_SJA
/* BIOS drive numbers are not simply related to A:, B:, C: etc.
   The first floppy drive is 0, the second 1 etc.
   The first fixed disk is 128, the second 129 etc.
   On a system with two or less floppies C: will be fixed disk 1 = 128
   With one fixed disk D: = second partition on first disk.
   With two fixed disks, D: = first partition on second disk = 129 etc.
   For the present one can input 2 for the first fixed disk
   and 3 for the second and let the cludge work it out.
   The clever bit can come later if necessary.
   Also nice would be to use the BIOS to find the drive parameters
   so that -n and -N are not necessary. */
#endif
				if ( (c != '\0') &&
					( ( *(drive_p + 1) == '\0' )
					|| ( *(drive_p + 1) == ':' ) ) )
					{
					ms_device = 1 ;
					if ((c >= 'a') && (c <= 'z'))
						drive_no = c - 'a'  ;
					else if ((c >= 'A') && (c <= 'Z'))
						drive_no = c - 'A'  ;
					else if ((c >= '0') && (c <= '9'))
						drive_no = c - '0'  ;
					}
#ifdef VSL_SJA	/* { */
				/* sorry about the cludge */
				if (drive_no == 2) drive_no = 128;
				if (drive_no == 3) drive_no = 129;
#endif		/* } */
#endif		/* } */

#ifndef MSDOS	/* { */
				specific_floppy = 1 ;
#endif		/* } */
				break ;
			case 'p': prompt_flag = 1 ; break ;
			case 'w': io_flag = 1 ; /* no break, drop through */
			case 'r': rw_flag = c ; break ;
			case 'i': case 'a': ia_flag = c  ; break ;
			case '0': no_reverse_flag = 1  ; break ;
			case 'q': quiet = 1 ; break ;
			case 'v': quiet = 0 ; break ;
			case 'h': f_head = atoi(++*argv) ; break ;
			case 'H': l_head = atoi(++*argv) ;
				break ;
			case 's': f_sec = atoi(++*argv) ; break ;
			case 'S': l_sec = atoi(++*argv) ; break ;
			/* case 'N' & 'n' used to be #ifndef MSDOS, however,
				now we allow also msdos to access a normal
				file as a pseudo floppy, these may possibly
				be wanted by msdos users */
			case 'N': seek_head = atoi(++*argv) ;
				specific_head = 1 ; break ;
			case 'n': seek_sec = atoi(++*argv) ;
				specific_sec = 1 ; break ;
			case 't': f_trk = atoi(++*argv) ; break ;
			case 'T': l_trk = atoi(++*argv) ; break ;
			case 'b': f_head = 0 ; l_head = 1 ; break ;
			case 'B': bytes_per_sector = atoi(++*argv) ; break ;
			case 'R': repeat = atoi(++*argv); ia_flag = 'i'; break ;
			case 'P': printf("%s",sccsID) ; break ;
			case 'c': calibrate = atoi(++*argv) ; ia_flag = 'i';
				  break ;
			case 'f': io_flag = 1 ; break ;
			case 'C': comment_sectors = 1 ;
				/* no break, drop to 'e' */
			case 'e': erase_type = 1 ; rw_flag = 'w' ;
				  io_flag =  0 ;
				  break ;
			default: printf("%s: %s bad parameter %c.\n",
					*ARGV, txt_error, c) ;
				syntax(1) ; break ;
			}
		}
	/* decide if we will need to generate a list of all sectors. */
	if ((argc == -1 ) && (erase_type) ) erase_type = 2 ;
	if (erase_type && ( (rw_flag == 'r' ) || io_flag ))
		{
		printf(
		"%s: Erase flag incompatible with {read or file_io} flags.\n",
			*ARGV);
		syntax(1) ;
		}
#ifdef	MSDOS	/* { */
	if ( bytes_per_sector != 512 )
		{
		printf(
		"%s: INT 13 Functions 2 & 3 only support 512 bytes/sector.\n",
			*ARGV) ;
		syntax(1) ;
		}
#endif		/* } */
	if ( ( bytes_per_sector < MINIMUM_BYTES_PER_SECTOR ) ||
		( bytes_per_sector & 1 ) /* odd no */ )
		/* JJ later I could check for power of 2, as this doesnt
			catch 6, 14, or 1004 etc. */
		{
		printf("%s: Bad sector size: %d.\n",*ARGV, bytes_per_sector) ;
		syntax(1) ;
		}
	if ( bytes_per_sector > MAXIMUM_BYTES_PER_SECTOR)
		{
		printf("%s: Sector size unusually high: %d.\n",
			*ARGV, bytes_per_sector) ;
		syntax(0) ;
		}
	if (f_trk < MINIMUM_TRACK )	/* ignore lint message for this line */
		{
		printf("%s: First track too low: %d.\n",*ARGV,f_trk) ;
		syntax(1) ;
		}
	if (l_trk > MAXIMUM_TRACK)
		{
		printf("%s: Last track unusually high: %d.\n",*ARGV,l_trk) ;
		syntax(0) ;
		}
	if (f_sec < MINIMUM_SECTOR)
		{
		printf("%s: First sector too low: %d.\n",*ARGV,f_sec) ;
		syntax(1) ;
		}
	if (l_sec > MAXIMUM_SECTOR)
		{
		printf("%s: Last sector unusually high: %d.\n",*ARGV,l_sec) ;
		syntax(0) ;
		}
	if (f_head < MINIMUM_HEAD)	/* ignore lint message for this line */
		{
		printf("%s: First head too low: %d.\n",*ARGV,f_head) ;
		syntax(1) ;
		}
	if (l_head > MAXIMUM_HEAD)
		{
		printf("%s: Last head unusually high: %d.\n",*ARGV,l_head) ;
		syntax(0) ;
		}
	if ( (f_trk > l_trk) || (f_sec > l_sec) || (f_head > l_head) )
		{
		printf(
	"%s: %s: a `first' parameter was greater than a `last' parameter.\n",
			*ARGV, txt_error) ;
		syntax(1) ;
		}
	if ((l_sec > seek_sec) || ((l_sec != seek_sec ) && !specific_sec ) )
		{
		printf("%s: %s (%d), %s\n\t%s (%d).\n\t%s.\n", *ARGV,
			"Maximum sector number", l_sec,
			"is different from",
			"Sectors per track side assumed for lseek()",
			seek_sec,
			"Consider using -n option") ;
		syntax(1) ;
		}
	if ( ( l_head + 1 > seek_head )
		|| ((l_head + 1 != seek_head ) && !specific_head ) )
		{
		printf("%s: %s (%d),\n\t%s (%d).\n\t%s.\n", *ARGV,
			"Number of heads per drive specified for seek",
			seek_head,
			"is different from [max head + 1]", l_head + 1,
			"Consider using -N option") ;
		syntax(1) ;
		}
#ifdef MSDOS	/* { */
	if (ms_device)
		{
		if (drive_no == -1)
			{
			printf("%s: drive name bad.\n",*ARGV) ;
			syntax(1) ;
			}
		drive_s[0] = 'A'+ drive_no ; drive_s[1] = '\0' ;
		drive_p = drive_s ;
		}
#endif		/* } */
	/* Parameters Finished*/

	if ( (rw_flag == 'w' ) && !erase_type && (argc == -1 ))
		{
		fprintf(stderr, "%s: %s No files specified.\n",
			*ARGV, txt_error );
		my_abort(1) ;
		}

	/* Announce functionality */
	printf("Will access: Disc %s,\n\t%s,  %s,  Maximum attempts: %d,\n",
		drive_p,
		(rw_flag == 'r') ? "Read only" : "Read/write",
		(ia_flag == 'a') ? "Abort on error" : "Ignore errors", repeat) ;
	if ( ! ( (io_flag) && (rw_flag == 'w') ) )
		/* not meaningfull if numbers will come from filenames,
			so in that case we dont display it. */
		printf("\tTracks: %d to %d,  Sectors: %d to %d,  Heads: %d to %d\n",
			f_trk,l_trk,f_sec,l_sec,f_head,l_head ) ;
	printf("\t%s\n\t\t%s %d,  %s %d.\n",
		"Assuming these device driver parameters :",
		"Heads per drive:", seek_head, 
		"Sectors per track:", seek_sec
		) ;
	if (io_flag) printf("Reading %s,  then Writing %s.\n",
			(rw_flag == 'r') ? txt_floppy : txt_files,
			(rw_flag == 'r') ? txt_files : txt_floppy
			) ;
	else if (erase_type) printf("\tErasing %s sectors%s.\n",
			(erase_type == 2) ? "all" : "specified",
			comment_sectors ?
				",  then putting name of sector in data"
				: "\0" ) ;

	buf_alloc() ;
	/* Start Work */
	if (!prompt_flag) /* do a single disc */
		do_disc(argc,argv,quiet,f_trk,l_trk,f_sec,l_sec,f_head,l_head) ;
	else while(pause_for_user()) /* do disc till told to stop */
		do_disc(argc,argv,quiet,f_trk,l_trk,f_sec,l_sec,f_head,l_head) ;
	exit(0)  ;
	}

buf_alloc()
	{
	/* allocate buffer space */
	bytes_in_buf = bytes_per_sector * ( l_sec - f_sec + 1 ) ;
	if	( ( (p_main_buf = malloc( (unsigned)bytes_in_buf ) ) ==
			(char *)0 ) ||
		( (p_chk_buf = malloc( (unsigned)bytes_in_buf ) ) ==
			(char *)0 ) )
		{
		fprintf(stderr,
			"%s: Fatal error,  failed to malloc space,  aborting.\n",
			*ARGV) ;
		perror(*ARGV);
		exit(1) ;
		}
#ifndef MSDOS	/* { */
	if ((p_test_buf=malloc((unsigned)bytes_per_sector))==(char*)0)
		{
		perror(*ARGV);
		my_abort(1) ;
		}
#endif	/* } */
	av_bits = (unsigned *)malloc(
		(unsigned)(bytes_per_sector * 8 * sizeof(unsigned) ) 	) ;
	if ( av_bits == (unsigned *)0 )
		{
		perror(*ARGV);
		my_abort(1) ;
		}
	}

/* wait till user says a floppy is ready */
pause_for_user()
	{
	int	our_ch = '\0' ;
	char	again = 0 ;
	while (((char)our_ch != 'y' ) && ((char)our_ch != 'n' ) )
		{
		if (again) printf("Bad input, try again, ") ;
		else again = 1 ;
		printf("Is disc ready (y/n) ? ") ;
		our_ch = getchar() ;
		if (our_ch == EOF) return(0);
		while ((our_ch == '\n') || ( our_ch == '\r') )
			{ /* absorb CR or LF as we are not in raw mode */
			our_ch = getchar() ;
			if (our_ch == EOF) return(0);
			}
		our_ch &= 0x7f ;
		if (isupper((char)our_ch)) our_ch = tolower((char)our_ch) ;
		}
	if ((char) our_ch == 'y' ) return(1) ;
	return(0) ;
	}

/* Do a single disc */
do_disc(argc,argv,quiet,f_trk,l_trk,f_sec,l_sec,f_head,l_head)
	int	argc ;
	char	**argv ;
	unsigned	f_trk,l_trk,f_sec,l_sec,f_head,l_head ;
	FLAG	quiet ;
	{

#if ( MSDOS || v7 || i386 )	/* { */
#define rindex	strrchr
#endif	/* } */
	extern char *rindex() ;
	unsigned	head ;
	int	track ;
	char	ext[4] ;
	char	*ch_p ;
	int	rslt ;
	char	ch_hd,ch_tk,ch_sc ;	/* chars to seperate filename
						components	*/
	unsigned null_count ;
	char	*basename ;		/* typically t05s35h0.ok */
	char	null_ch ;
	char	comment[100] ;
	unsigned cmt_sec ;
	char	*cmt_p ;
	unsigned cmt_l ;

	floppy_fd = -1 ;
#ifdef MSDOS	/* { */
	if (ms_device) init_fl_msdos(quiet,f_head,l_head) ;
	else
#endif		/* } */
 	     if (init_fl_unix(rw_flag) )
		{
		fprintf(stderr,"Failed to initialise disc\n") ;
		/* exit(1); */
		}

	if (erase_type == 2 )
		{	/* erase all sectors */
		/* prepare a null buffer */
		for (ch_p = p_main_buf, null_count = bytes_in_buf ,
				null_ch = (comment_sectors) ? ' ' :
				0xF6 /* I used to use '\0', but Symmetric
				formats empty bytes as 0xA5, and
				Dos 3.2 (NEC Multispeed & Toshiba T1100+)
				format uses F6,
				so I use dos convention */ ;
			null_count-- ; *ch_p++ = null_ch ) ;
		for (track = f_trk ; track <= l_trk ; track++)
			{
			/* start writing from outermost track, so we can write
			   most of disc before error occur (which normally occur
			   on the smaller radius tracks 
			   */
			for (head = f_head ; head <= l_head ; head++ )
				{
				/* put name of sector into sector data */
				if ( comment_sectors )
				for ( cmt_sec = f_sec ; cmt_sec <= l_sec ;
					cmt_sec++ )
					{
					(void)sprintf(comment,CREAT_SKELETON,
						path, (*path=='\0') ? "\0" :"/",
						track, head,
						n_to_c(cmt_sec),
						n_to_c(cmt_sec),"cmt") ;
					strcat(comment," created by Valid.\n");
					if ( (cmt_l = strlen(comment) )
						< bytes_per_sector)
						{
						cmt_p = p_main_buf +
							bytes_per_sector *
							(cmt_sec - f_sec) ;
						strcpy(cmt_p, comment) ;
						/* remove null from strcpy */
						*(cmt_p + cmt_l) = ' ' ;
						/* avoid massively long lines
							that editors dont like*/
						*(cmt_p + bytes_per_sector - 1)
							= '\n' ;
						}
					}
				/* erase sector */
				if ((high_disc(repeat,'w',track,f_sec,
					l_sec, head, p_main_buf, ia_flag,
					quiet)) && (ia_flag == 'a'))
					my_abort(1) ;
				}
			}
		}
	else	{ /* non total erasure */
		if (rw_flag == 'w')	/* write floppy */
			{
			if (erase_type)
				{ /* null the buffer */
				for (ch_p = p_main_buf,
					null_count = bytes_in_buf  ;
					null_count-- ; *ch_p++ = 0 ) ;
				}
			/* calculate sectors from file names */
			argc++ ;
			while (argc--)
				{
				/* break names such as
				  ~/src/empty.ok/720/t00h0s19.ok
				  to separate components
				  	~/src/empty.ok/720
					& t00h0s19.ok
				*/
				if (((basename = rindex(*argv,'/'))==(char *)0)
#ifdef	MSDOS	/* { */
				/* damn msdos backslash directory delimeter */
				&& ((basename = rindex(*argv,(int)'\\'))
					==(char *)0)
#endif	/* } */
					)
					{	/* no / or \ found */
					basename = *argv ;
					path = "\0" ;
					}
				else	{	/* typically fred/t03s46ho.ok */
					path = *argv ;
					*basename++ = '\0' ;
					}
#undef 	DEBUG
#ifdef	DEBUG	/* { */
				printf("path is <%s>, basename is <%s>\n",
					path,basename);
#endif		/* } */
				argv++ ;
				rslt = sscanf(basename, RECOG_SKELETON,
					&ch_tk,&track,&ch_hd,&head,&ch_sc,
					&f_sec,&l_sec,
					/* &(char()f_sec),&(char()l_sec), */
					ext) ;
				if ( ( rslt != 8 ) ||
					!(( (ch_hd == 'h') || (ch_hd == 'H')) &&
					((ch_tk == 't') || (ch_tk == 'T')) &&
					((ch_sc == 's') || (ch_sc == 'S')) ) )
					{
					printf("Bad file name %s.",*--argv) ;
					/* have to abort as we wont be able
					to tell later where the data is to be
					written */
					my_abort(1) ;
					}
				f_sec = c_to_n((char)f_sec) ;
				l_sec = c_to_n((char)l_sec) ;
#ifdef	DEBUG	/* { */
					printf("Reading data from file ");
					printf(CREAT_SKELETON,
						path, (*path=='\0') ? "\0" :"/",
						track,head,n_to_c(f_sec),
						n_to_c(l_sec),ext) ;
					printf(".\n");
#endif		/* } */
				(void) fflush(stdout);
				if (io_flag) rw_file(p_main_buf, head,track,
						f_sec, l_sec,ext) ;
#define MIN_ATTEMPTS	1		/* 1 attempt, no retries	*/
				if ((high_disc(MIN_ATTEMPTS,'w',track,f_sec,
					l_sec, head, p_main_buf,
					ia_flag ,quiet))
					&& (ia_flag == 'a')) my_abort(1) ;
				}
			}
		else	/* Read a floppy, for storage in a file
			   (possibly rescuing difficult to read data),
			   or possibly simply read disk to validate the media.
			   Errors occur normally on the (smaller radius)
			   higher numbered tracks .
			   If the media isnt good, we might as well know as 
			   soon as possible, so unless no_reverse_flag is set,
			   start reading from the innermost track toward
			   the outermost track.
			   */
		for (track =
			(no_reverse_flag) ? f_trk : l_trk ;
			(no_reverse_flag) ? (track <= l_trk) : 
					((track >= f_trk) && (track >= 0)) ;
			(no_reverse_flag) ? track++ : track--
			)
			{
			for (head = f_head ; head <= l_head ; head++ )
				{
				/* read floppy (& possibly later write file*/
				if ((high_disc(repeat,'r',track,f_sec,
					l_sec, head, p_main_buf, ia_flag,quiet))
					&& (ia_flag == 'a')) my_abort(1) ;
						/* data is not saved to files
						here, as by doing it inside
						lower procedures we can be
						more specific as to which
						sectors were & were not read
						succesfully	*/
				if (rw_flag == 'w')
					{
						/* we did retries on initial
						read, but not on this later
						write & read.
						If program is used to salvage
						data from a bad disc, it will
						not retry writes, thus
						encouraging user to
						reformat if misaligned).
						*/
					if ( high_disc(1,'w',track,f_sec,l_sec,
						head, p_main_buf,ia_flag,quiet)
						&& (ia_flag == 'a') )
						my_abort(1) ;
					/* now verify	*/
					if ( high_disc(1,'r',track,f_sec,l_sec,
						head, p_chk_buf,ia_flag,quiet)
						&& (ia_flag == 'a') )
						my_abort(1) ;
					if (memcmp(p_main_buf,p_chk_buf,
						(unsigned)bytes_in_buf ) )
						{
						printf(
					"Data verification failed, track %d.\n",
							track) ;
						if (ia_flag == 'a') my_abort(1);
						}
					}
				}
			}
		}
	printf("\nDisc %s.\n", rw_flag == 'r' ? "read" : "written") ;
	if ( floppy_fd >= 0 ) (void) close(floppy_fd) ;
	}

#define SEC_ERR 	 4
#define CTL_ERR 	32
#define SEEK_ERR	64

#ifdef	MSDOS	/* { */
init_fl_msdos(quiet,f_head,l_head)
	FLAG	quiet ;
	int	f_head,l_head ;
	{
	char	junk_buf[ MAXIMUM_BYTES_PER_SECTOR] ;
	if (!quiet) printf("Starting disc initialisation. ") ;
	in.h.ah = 0 ;		/* reset floppy disk system via rom driver */
	int86(0x13, &in, &out) ;
	in.h.ah = 1 ;		/* obtain status of disk drive controller */
	int86(0x13, &in, &out) ;
#ifdef VSL_SJA	/* { */
	/* How does BIOS know whether we want floppy or fixed disk controller?*/
	if (!quiet) printf("Driver status code = %x\n", out.h.ah);
	/* Following bit looks like a cludge to me.
   		I guess it would be better to determine the error code
   		and act accordingly.
		After making my mods I discovered valid was aborting for all
		sorts of reasonable reasons which could be corrected 
	*/
#else	/* }{ */
	/* above may not work, so initialise disc by dummy read */
	/* next line used to just read head 0, but in attempt to get PC ATs
		(both R&S & IBM to not fail down to single sector reads
		on head 1, this now does each head */
	do	{
		low_disc('r',0,1,1,f_head,junk_buf,'i',quiet) ;
		}
		while (f_head++ < l_head ) ;
#endif	/* } */
	if (!quiet) printf(" Disc Initialised.\n") ;
	}
#endif	/* } */

/* both for unix (dev driver or file), and msdos (file only) */
	int
init_fl_unix(rd_wr)	/* returns 0 if ok */
	char	rd_wr ; /* JJ add quiet later */
	{
	off_t	count ;

#ifndef MSDOS	/* { */
	if (specific_floppy)
#endif		/* } */
		{
		if ((floppy_fd = open(drive_p, ( rd_wr == 'r' ) ? O_RDONLY
#ifdef	MSDOS	/* { */
								| O_BINARY
#endif		/* } */
			: O_WRONLY | O_CREAT | O_TRUNC
#ifdef	MSDOS	/* { */
							| O_BINARY , S_IWRITE
#else		/* }{ */
					, 0640
#endif		/* } */
			)) < 0 )
			return(CTL_ERR) ;
		return(0) ;
		}
#ifndef MSDOS	/* { */
	else
#endif		/* } */
		{ /* user has not specified drive name */
#ifdef	MSDOS	/* { */
		fprintf(stderr,"%s: Must specify a specific drive.\n",*ARGV);
		my_abort(1) ;
#endif		/* }{ */
#ifdef	TRY_40TPS_BEFORE_80TPS	/* { */
		/*
		- assume a 2 * 40 * 9 * 512 layout,
		- not assume whether the floppy formatted as 40 tps or 80 tps.
		- Try default 40tps, reading 2nd track, if floppy was
		  formatted on an scs 80tps drive, this will cause an error,
		  (which will also unfortunately appear on /dev/console),
		  if such an error occurs we switch to accessing floppy
		  through 80 tps driver software.
		*/
		if ((floppy_fd = open(drive_p,O_RDONLY)) < 0 ) return(CTL_ERR) ;
		count = 512 * NINE * 2 ;
		fprintf(stderr, "%s,\n\t(%s or %s):\n",
			"About to detect which software driver to use",
			drive_p, floppy_u_80tps ) ;
		if (	( lseek(floppy_fd, count , 0) == count ) &&
			( read(floppy_fd, p_test_buf, bytes_per_sector )
				== bytes_per_sector ) )
			{	/* device ok */
			if (rd_wr == 'r' )
				{
				if (lseek(floppy_fd, (off_t)0 , 0) < 0)
					{
					perror(*ARGV) ;
					exit(1) ;
				}	}
			else	{
				(void) close(floppy_fd) ;
				if ((floppy_fd = open(drive_p,O_WRONLY,
					0640)) < 0 ) return(CTL_ERR);
				}
			fprintf(stderr,
 "A 40 track/side emulator will be used to access the 80 tps drive.\n" );
			}
		else	{	/*floppy probably formatted on a 80tps*/
			fprintf(stderr, "\t%s %s,\n\t%s\n\t%s\n\t%s\n\t%s\n",
 "Failed to read 2nd track 1st sector using", drive_p,
 "so will not treat disc as a 40tps disc in a 80tps drive,",
 "so will treat disc as a 80tps disc in a 80tps drive." ,
 "These combinations often occur (tps=tracks per side, tpi=tracks per inch):",
 "  80tps with { 96tpi 5.25\" or 135tpi 3.5\" }, 40tps with { 48tpi 5.25\" }"
				);
			(void) close(floppy_fd) ;
#endif	/* TRY_40TPS_BEFORE_80TPS	} */
			drive_p = floppy_u_80tps ;
			if ((floppy_fd = open(drive_p,
				( rd_wr == 'r' ) ? O_RDONLY : O_WRONLY )) < 0 )
					return(CTL_ERR) ;
#ifdef	TRY_40TPS_BEFORE_80TPS	/* { */
			}
		fprintf(stderr,
			"\tDriver that will be used for this floppy is %s.\n",
			drive_p) ;
#endif	/* TRY_40TPS_BEFORE_80TPS	} */
		}
	return(0) ;
	}

/* Logically reads or writes disc, returns number of faulty sectors,
	if bad sectors encountered, each sector is attempted individually. */
	int
high_disc(attempts,rd_wr,track,f_sec,l_sec,head,buf,err,quiet)
	unsigned attempts ;
	char	rd_wr ; /* r = read w = write	*/
	unsigned track ;
	unsigned f_sec ;	/* first sector */
	unsigned l_sec ;	/* last sector	*/
	unsigned head ;
	char	*buf ;
	char	err ;	/* if set exit if error, if unset, remain	*/
	FLAG	quiet ; 	/* if set be quiet on errors,
					if unset complain on errors */
	{
	unsigned each = 0 ;
	unsigned j_sec ;
	char	*buf_p ;

	if (mid_disc(MIN_ATTEMPTS,rd_wr,track,f_sec,l_sec,head,buf,err,quiet))
		for (j_sec = f_sec , buf_p = buf ; j_sec <= l_sec ;
			j_sec++, buf_p += bytes_per_sector )
				if (mid_disc(attempts,rd_wr,track,j_sec,
					j_sec,head, buf_p,'i',quiet)) each++ ;
	return(each) ;
	}

/* reads or writes disc, returns 0 for no error , does retries if necessary */
	int
mid_disc(attempts,rd_wr,track,f_sec,l_sec,head,buf,err,quiet)
	unsigned attempts ;
	char	rd_wr ; 	/* r = read w = write	*/
	unsigned track, f_sec, l_sec, head ;
	char	*buf ;
	FLAG	quiet ; 	/* set=quiet on error, unset=complain errors */
	char	err ;		/* if set exit if error, if unset, remain */
	{
	char	*p_b ;		/* temporary pointer to byte buffer	*/
	int	result ;
	unsigned *p_u ; 	/* temporary pointer to integer array	*/
	unsigned tries = 0 ;
	unsigned sp_count ;
	unsigned byte_cnt ;	/* byte count	*/
	int	bit_cnt  ;	/* bit count must be signed as counts down*/
	unsigned mask ; 	/* bit mask	*/

	if (clean_flag)
		{
		putchar('\r') ;
		for (sp_count = 39; sp_count--	 ; )
			/* chars to blank out track-sector-head display */
			putchar(' ') ;
		putchar('\r') ;
		}
	printf(txt_bla, "\0", rd_wr == 'r' ? "Reading" : "Writing",
		track, head, f_sec, l_sec) ;
	(void) fflush(stdout);

	if (attempts > 1)
		{
#ifdef DEBUG	/* { */
		printf("Initialising array in case averaging needed.\n") ;
#endif	/* } */
			/* if more than one attempt will be allowed,
			store data for averaging, in case we dont
			get a read without error, we can average of
			each bit over all read attempts, & return
			average for each bit, rounded to 0 or 1 */
		p_u = av_bits ; byte_cnt = bytes_per_sector * 8  ;
		while (byte_cnt--) *p_u++ = 0 ;
#ifdef DEBUG	/* { */
		printf("Array Initialised.\n") ;
#endif	/* } */
		}

	while ( (++tries <= attempts) && (result =
		low_disc(rd_wr,track,f_sec,l_sec,head,buf,err,quiet)))
		if (attempts > 1)
			{
/* NASTY FIX LATER JJ the following piece of code only works because i know
			we only call repeated reads with ignore flag on
			single sectors - unleasant assumption */
#ifdef DEBUG	/* { */
			printf("Adding newly read data to average counts.\n") ;
#endif	/* } */
			for(p_u=av_bits,mask=1,bit_cnt=7 ; bit_cnt-- >= 0 ; )
				{
				p_b = buf ;
				for (byte_cnt = bytes_per_sector ;
					byte_cnt-- ; p_u++ )
					if (*p_b++ & mask) (*p_u) ++ ;
				mask <<= 1 ;
				}
#ifdef	DEBUG	/* { */
			printf("New data added to average counters.\n") ;
#endif	/* } */
			if ((calibrate) && ((tries == 1)||!(tries % calibrate)))
				{
#ifdef DEBUG	/* { */
				printf("Recalibrating.\n") ;
#endif	/* } */
#ifdef MSDOS	/* { */
				if (ms_device) init_fl_msdos((FLAG)1,head,head);
#endif		/* } */
				}
			}
	if (result) tries-- ;
	else 	/* Announce if succesful,
		   Added because scs unix 40 track floppy device driver
		   reports an error on track 39 head 1,
		   when reading 40 tps discs on a 80 tps drive (both with
		   valid & dd), & I wanted valid to clearly indicate success
		   before false driver error message;
		   however it looks like driver returns error message
		   before data, so im stuck with it, but it seems good
		   idea to report success as soon as possible, so this printf
		   stays */
		printf(txt_bla, "\r",
			rd_wr == 'r' ? "Read \040 " : "Wrote \040",
			track, head, f_sec, l_sec) ;
	if (tries > 1)
		{
#ifdef DEBUG	/* { */
		printf("Generating buffer from average counts.\n") ;
#endif		/* } */
		/* zero all bits	*/
		for (byte_cnt = bytes_per_sector , p_b = buf ; byte_cnt-- ;
			*p_b++ = 0 ) ;
		for (p_u=av_bits, mask=1, bit_cnt=7 ; bit_cnt-- >= 0  ; )
			{
			p_b = buf ;
			for (byte_cnt = bytes_per_sector ; byte_cnt-- ; p_b++ )
				if (*p_u++ * 2 /tries ) (*p_b) |= mask ;
					/* if exactly half 0 & half 1, this
						algorithm will return a 1 */
			mask <<= 1 ;
			}
#ifdef DEBUG	/* { */
		printf("Data was averaged to %s\n",buf) ;
#endif		/* } */
		}

	if (!result || (result && ((l_sec - f_sec) > 1 )))
		{
		/* succesfull first time , or we will try again with smaller
		sectors so clean the screen display line	*/
		clean_flag = 1 ;
		}
	else printf(" attempts:%d%s.\n", tries, result ?
		" failed" : ", last succeeded" ) ;
	if (io_flag && (rd_wr == 'r') &&
		/* avoid creating h0t00s19.bad when we will have the more
			usable h0t00s11.ok h0t00s22.ok h0t00s33.ok h0t00s44.bad
			h0t00s55.ok etc available	*/
		(!result || ((f_sec == l_sec) && ( attempts > 1) ) ) )
		rw_file(buf,head,track,f_sec,l_sec, result ?
			NAME_END_BAD : NAME_END_OK ) ;
	return(result) ;
	}

#if 0
/* verbal dos winchester info from joe to integrate some time:
winch_io()
	{
	/* info from joe 9107014: interrupt 13 mods for winch */
	int	cylider_number ;	/* (10bits, ==> max 1024 cyls) */
	int	bits ;

	drive register = 0x80 ; 	/* 0x80 or 0x81 */
	CH = cylinder_number & 0xFF
	CL = sector number | ( cylinder_number >> 8 ) ;
	}
#endif

/* Phsically reads or writes disc, returns 0 for no error	 */
	int
low_disc(rd_wr,track,f_sec,l_sec,head,buf,err,quiet)
	char	rd_wr ; /* r = read w = write	*/
	char	err ;	/* if set exit if error, if unset, remain */
	FLAG	quiet ; /* if set be quiet on errors, else complain on errors */
	unsigned track, f_sec, l_sec, head ;
	char	*buf ;
	{
	static FLAG gbl_first_write = 1 ;
	int	rslt = 0 ;
	off_t	count ;

#ifdef MSDOS
	if (!ms_device)
		{
#endif
		if (!rslt)
			{
			count = ( ( track * seek_sec * seek_head ) +
				(head * seek_sec) +
				(f_sec - 1) ) * bytes_per_sector ;
			if ( lseek(floppy_fd, count , 0) != count )
				rslt = SEEK_ERR ;
			}
		if (!rslt)
			{
			count = bytes_per_sector * (l_sec - f_sec + 1) ;
			if ( rd_wr == 'r' )
				{
				if ( read(floppy_fd, buf, count ) != count )
					rslt = SEC_ERR ;
				}
			else
				{
				if ( write(floppy_fd, buf, count ) != count )
					rslt = SEC_ERR ;
				}
			}

#ifdef MSDOS	/* ( */
		}
	else	{
		/*
		Toshiba T1000 as owned by Joe McBeth, fails first read every 
		time because motor is not up to speed, so he is forced to say
		-R2, No such problem with my T1100+ though.

		From Ray Duncan "Advanced MS-DOS Programming" (Second Edition) P 539.
		Note to Int 13H Function 02H Read sector:
			"On floppy disk drives, an error may result from the
			motor being off at the time of the request. The ROM BIOS
			does not automatically wait for the drive to come up to
			speed before attempting the read operation. The requesting
			program should reset the floppy disk system
			(Int 13H Function 00H) and retry the operation three times
			before assuming that the error results from some other cause."
		*/
		if ( rd_wr == 'r' ) in.h.ah = 2 ; else in.h.ah = 3 ;
		in.h.al = l_sec - f_sec + 1 ;	/* number of sectors	*/
		in.x.bx = (int)buf ;		/* dont seem to need extended segment */
		in.h.ch = track  ;
		in.h.cl = f_sec  ;
		in.h.dh = head ;
		in.h.dl = drive_no  ;
#ifdef VSL_SJA	/* { */
		/* this bit is to access a fixed disk */
		/* Not quite the same as JMB said, but the thought was there */
		if (drive_no > 127) {
		in.h.ch = track & 0xFF;
		in.h.cl = ((track >> 2) & 0x300) | (f_sec & 0x3F);
		}
#endif		/* } */
		int86(0x13, &in, &out) ;
		rslt = out.h.ah ;
		}
#endif	/* ) */
	/* Now dela with error codes */
#ifdef VSL_SJA	/* { */
		/* According to the spec CF is set if an error occurred.
		   If CF=0, ah should also be 0, but stick to definition just in case.
		   The following bit set up in this way to facilitate inclusion of
		   error handling routines for the various errors.
		   See cludge under error 2 for my buggered disk 
		*/
		if (out.x.cflag & 0x01) 
			{      /* check CF */
			switch (rslt) 
			{
			case 1:
				printf("Bad command.\n");
				break;
			case 2:
				printf("Address mark not found.\n");
				rslt=0;
				break;
			case 3:
				printf("Diskette is write protected.\n");
				break;
			case 4:
				printf("Sector not found.\n");
				break;
			case 5:
				printf("Reset failed (Fixed disk).\n");
				break;
			case 6:
				printf("Diskette removed.\n");
				break;
			case 7:
				printf("Bad parameter table (Fixed disk).\n");
				break;
			case 8:
				printf("DMA overrun.\n");
				break;
			case 9:
				printf("DMA across 64k boundary.\n");
				break;
			case 10:
				printf("Bad sector flag (Fixed disk).\n");
				break;
			case 11:
				printf("Bad cylinder (Fixed disk).\n");
				break;
			case 12:
				printf("Bad media type (Diskette).\n");
				break;
			case 13:
				printf("Invalid number of sectors.\n");
				break;
			case 0x0e:
				printf("Control data address mark detected.\n");
				break;
			case 0x0f:
				printf("DMA arbitration level out of range.\n");
				break;
			case 0x10:
				printf("Bad CRC or ECC.\n");
				break;
			case 0x11:
				printf("ECC corrected data error.\n");
				break;
			case 0x20:
				printf("Controller failed.\n");
				break;
			case 0x40:
				printf("Seek failed.\n");
				break;
			case 0x80:
				printf("Time out.\n");
				break;
			case 0xaa:
				printf("Drive not ready.\n");
				break;
			case 0xbb:
				printf("Undefined error.\n");
				break;
			case 0xcc:
				printf("Write fault.\n");
				break;
			case 0xe0:
				printf("Status error.\n");
				break;
			case 0xff:
				printf("Sense operation failed.\n");
				break;
			default:
				printf("Unknown error.\n");
				break;
			}
			}
#endif	/* } */
	if (rslt)
		{
#ifndef VSL_SJA
		if (( rd_wr == 'w' ) && gbl_first_write )
			{
			printf(
		"\n%s: %s first write failed - is it write protected,\nor not inserted ?\n",
				*ARGV, txt_error);
			/* JJ later could insert detector for msdos write
				protected discs */
			}
		if (!quiet)
			{
			printf(" %s: ", txt_error) ;
			if (rslt & 1) printf("Illegal driver command.") ;
			if (rslt & 2) printf("Disk write protected.") ;
			if (rslt & SEC_ERR) printf("Sector not found.") ;
			if (rslt & 8) printf("DMA over run.") ;
			if (rslt & 16) printf("CRC read.") ;
			if (rslt & CTL_ERR) printf("Controller %s.", txt_error) ;
			if (rslt & SEEK_ERR) printf("Seek failure.") ;
			if (rslt & 128) printf("Disk timed out.") ;
			putchar('\n') ;
			}
#endif
		if (err != 'i') my_abort(1) ;
		}
	if ( rd_wr == 'w' ) gbl_first_write = 0 ;
	return(rslt) ;
	}

syntax(err_type)
	int err_type ;
	{
	printf(
		"%s, Syntax:\n%s -ddrive [-a/i] [-Rattempts] [-r/w] [-p] [-P]\n\
[-tfirst_track] [-Tlast_track] [-sfirst_sector] [-Slast_sector]\n\
[-hfirst_head] [-Hlast_head] [-e] [-C] [-cinterval] [-f [file_names]]\n\
[-nseekable_sectors_per_track] [-Nseekable_heads] [-0] [-Bbytes_per_sector]\n",
		(err_type) ? "Error" : "Warning", *ARGV ) ;
	if (err_type) my_abort(1) ; else printf("Continuing.\n");
	}

my_abort(x)
	int	x ;
	{
	printf("\nAborting%c.\n",(char)7 /*bell*/ ) ;
	exit(x) ;
	}

/* open, read or write, & close filename appropriate to disc access	*/
rw_file(buf_p,head,track,f_sec,l_sec,ext)
	char	*buf_p	 ;	/* buffer pointer	*/
	unsigned head,track,f_sec,l_sec ;
	char	*ext ;		/* extension name	*/
	{
	int	target_fd ;	/* file descriptor	*/
#define	MAXPATHLEN	1024	/* JJ should really come from <sys/param.h> */
	char	f_name[MAXPATHLEN] ; /* used to be [8 + 1 + 3 + 1] ==
					main + '.' + extension + '\0',
					before path prepend code was added */
	struct stat	stat_buf ;
	int 	length ;
	int	actual ;

#ifdef	DEBUG	/* { */
	printf("rw_file starting with head%d,track%d,f_sec%d,l_sec%d,ext%s\n",
		(int)head,(int)track,(int)f_sec,(int)l_sec,ext);
#endif		/* } */
	(void)sprintf(f_name,CREAT_SKELETON,
		path, (*path == '\0') ? "\0" :"/",
		track, head, n_to_c(f_sec),n_to_c(l_sec),ext) ;
	length = bytes_per_sector * (l_sec - f_sec + 1 ) ;
#ifdef	DEBUG	/* { */
	printf("rw_file length %d, bytes_per_sector %d\n",
		length, bytes_per_sector);
#endif		/* } */
	if (rw_flag == 'r')
		{
		if ((target_fd = open(f_name,O_WRONLY | O_CREAT | O_TRUNC
#ifdef	MSDOS	/* { */
			| O_BINARY , S_IWRITE
#else		/* }{ */
				,0640
#endif		/* } */
				)) == -1)
			{
			fprintf(stderr,txt_failed_to_open,*ARGV,f_name) ;
			perror(*ARGV) ;
			exit(1) ;
			}
		if (write(target_fd,buf_p,length) != length)
			{
			printf( "%s writing file %s\n", txt_error, f_name) ;
			(void) close(target_fd) ; (void)unlink(f_name) ;
			perror(*ARGV) ;
			exit(1) ;
			}
		}
	else	{	/* rw_flag == 'w' */
		/* Check a valid file exists for each iteration.
		| Ensure size of files is correct,
		| ie s19 should be exactly 4608 bytes ((9 - 1) + 1) * 512,
		| ensuring correct size is necessary because:
		|	- kermit will receive fred11 if fred19 already exists,
		|	- if you use transfer in non binary mode, file length
		|	  will most probably be wrong.
		|	- when transfer gets a protocol hicough (with ptp),
		|	  it has been seen to produce ~100 bytes too much in
		|	  direction unix > msdos
		*/
		if (stat(f_name, &stat_buf) != 0)
			{
			printf("%s: stat failed on %s.\n",*ARGV,f_name);
			exit(1) ;
			}
		actual = stat_buf.st_size ;
		if (actual != length )
			{
			printf("%s: %s should be %d bytes, not %d.\n",
				*ARGV, f_name, length, actual) ;
			exit(1) ;
			}
		if ((target_fd = open(f_name,O_RDONLY
#ifdef	MSDOS	/* { */
						| O_BINARY
#endif		/* } */
			)) == -1)
			{
			fprintf(stderr, txt_failed_to_open,*ARGV,f_name) ;
			perror(*ARGV) ;
			exit(1) ;
			}
		if ( read(target_fd,buf_p,length) != length )
			{
			printf("%s reading file\n",txt_error) ;
			perror(*ARGV) ;
			(void) close(target_fd) ;
			exit(1) ;
			}
		}
	(void) close(target_fd) ;
	}

/* ------------------------------  End Of File	---------------------------- */
