/* 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 #endif /* } */ #include #ifndef MSDOS /* { unix */ #include #endif /* } */ #include #include #include #include #include #ifdef MSDOS /* { */ #include #endif /* } */ #ifdef i386 /* { */ #include #endif /* } */ #include 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 */ 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 ---------------------------- */