#define VERSION 1.12 #define LAST_CHANGED 901212 /*--------------------------------------* | File: DT.c - Rev. 1.12 901212 | +--------------------------------------+ | DT: disk test, a la Norton Utilities | +--------------------------------------+---------------* | Author: Maurizio Loreti, aka MLO or I3NOO. | | Address: University of Padova - Deparment of Physics | | Via F. Marzolo, 8 - 35131 PADOVA - Italy | | Phone: (39)(49) 844-313 FAX: (39)(49) 844-245 | | E-Mail: LORETI at IPDINFN (BITNET) or VAXFPD::LORETI | | (DECnet). VAXFPD is node 38.257, i.e. 39169. | | Home: Via G. Donizetti, 6 - 35010 CADONEGHE (Padova) | *------------------------------------------------------*/ /** | #include's **/ #include #include #include #include #include #include #include #include #include "mlo.h" /** | #define's. The first two should be in TRACKDISK.H, the fourth in | STDIO.H for ANSI compilers - but are not there in the Lattice | Include files. TD_CYL is the number of bytes per cylinder. **/ #define NUMCYLS 80 #define NUMHEADS 2 #define TD_CYL (TD_SECTOR * NUMSECS) #define FILENAME_MAX 108 #define ON 1 #define OFF 0 /** | A structure definition for directory entries; for Lattice C, only one | call to dfind/dnext can be active at a time: so, scanning directories, | we check all files first and, only then, all subdirectories one by one. **/ typedef struct sdirEntry { char *name; struct sdirEntry *next; } dirEntry; /** | Global variables **/ struct MsgPort *diskPort = NULL; /* Various Intuition pointers */ struct IOExtTD *diskReq = NULL; /* for disk input */ BYTE *diskBuffer = NULL; /* Disk input buffer */ ULONG diskChangeCount; /* Disk change count */ int nErFil = 0; /* Total number of errors */ int nDirs = 0; /* Nr. of checked directories */ int nFiles = 0; /* Nr. of checked files */ Boolean fromWorkBench; /* Scheduled from WB or CLI ? */ Boolean abortDT = False; /* True when CTRL-C hit */ /** | The ANSI procedure prototypes **/ Boolean checkBreak(void); void checkDir(char *path, Boolean root); void checkFile(char *name); void cleanup(int code); void motor(int action); void pcl(int n, char *fmt, ...); void readCyl(int cyl, int hd); void seekFullRange(SHORT howmany); void syntax(void); main( int argc, char **argv ){ int drive, cyl, head; SHORT error; char driveName[5]; /** | To be called from CLI, with DT DFx[:] ; if called from the | Workbench, a prompt for the floppy unit is sent to the console | window (created from the Lattice initialisation routine). | Pass 1: a seek over full range; | Pass 2: read all cylinders; | Pass 3: read all files record by record. | But first, check the input arguments and open Intuition. **/ fprintf(stdout, "\n\t\"DT\" - v%.2f - MLO %d\n", VERSION, LAST_CHANGED); if (argc == 0) { do { fprintf(stdout, "\nDrive to test (DF0-DF4) ? "); fgets(driveName, sizeof(driveName), stdin); } while (strnicmp(driveName, "df", 2) || (drive = atoi(driveName + 2)) < 0 || drive > 4); fromWorkBench = True; } else { fromWorkBench = False; if (argc != 2 || strnicmp(*++argv, "df", 2)) syntax(); if ((drive = atoi(*argv+2)) < 0 || drive > 4) syntax(); } if ((diskBuffer = (BYTE *) AllocMem(TD_CYL, MEMF_CHIP)) == NULL) { fprintf(stderr, "Can't allocate chip memory ...\n"); cleanup(SYS_ABORT_CODE); } /** | Pass 1 **/ if (!(diskPort = (struct MsgPort *) CreatePort(0, 0))) { fprintf(stderr, "Can't create I/O port ...\n"); cleanup(SYS_ABORT_CODE); } if (!(diskReq = (struct IOExtTD *) CreateExtIO(diskPort, sizeof(struct IOExtTD))) ) { fprintf(stderr, "Can't obtain I/O request block ...\n"); cleanup(SYS_ABORT_CODE); } sprintf(driveName, "DF%d:", drive); if (error = OpenDevice(TD_NAME, drive, diskReq, 0)) { fprintf(stderr, "Error 0x%X returned by OpenDevice for drive %s ...\n", error, driveName); cleanup(SYS_ABORT_CODE); } diskReq->iotd_Req.io_Command = TD_CHANGENUM; DoIO(diskReq); diskChangeCount = diskReq->iotd_Req.io_Actual; fprintf(stdout, "\nChange number for drive %s is %d;\n", driveName, diskChangeCount); motor(ON); seekFullRange(1); /** | Pass 2 **/ fprintf(stdout, "Checking all disk tracks:\n"); for (cyl=0; cyliotd_Req.io_Error) { fprintf(stdout, " Error 0x%X detected for cylinder %d, head %d\n", error, cyl, head); nErFil++; } } /** | Test for CTRL-C **/ if (checkBreak()) { fprintf(stdout, "\n*** DT: BREAK ***\n"); motor(OFF); cleanup(SYS_NORMAL_CODE); } } motor(OFF); if (nErFil) { fprintf(stdout, "%d hard errors detected reading drive %s.\n", nErFil, driveName); cleanup(SYS_ABORT_CODE); } else { fprintf(stdout, " no errors detected reading drive %s.\n", driveName); nErFil = 0; } /** | Pass 3 **/ fprintf(stdout, "Checking all files in drive %s\n", driveName); checkDir(driveName, True); pcl(2, "%d director%s and %d file%s checked: %d error%s detected.", nDirs, (nDirs == 1 ? "y" : "ies"), nFiles, (nFiles == 1 ? "" : "s"), nErFil, (nErFil == 1 ? "" : "s")); cleanup(SYS_NORMAL_CODE); } Boolean checkBreak(void) { long SetSignal(); if (SetSignal(0L, 0x1000L) & 0x1000L) return True; else return False; } void checkDir( char *path, Boolean root ){ struct FILEINFO info; char fileName[FILENAME_MAX]; int error; dirEntry *rdE = NULL; dirEntry *pdE; /** | Check a directory; path contains the full directory name, and root is | non zero for the root directory. We scan all directory files, checking | 'true' files at first and all subdirectories at the end, one by one. **/ nDirs++; pcl(1, " checking files in%s directory %s ...", (root ? " root" : ""), path); strcpy(fileName, path); strcat(fileName, (root ? "#?" : "/#?")); error = dfind(&info, fileName, True); while (!error && !abortDT) { strcpy(fileName, path); if (!root) strcat(fileName, "/"); strcat(fileName, info.fib_FileName); if (info.fib_DirEntryType < 0) { checkFile(fileName); } else { if ((pdE = malloc(sizeof(dirEntry))) == NULL) { fprintf(stderr, "Can't allocate heap memory ...\n"); cleanup(SYS_ABORT_CODE); } if ((pdE->name = malloc(strlen(fileName) + 1)) == NULL) { fprintf(stderr, "Can't allocate heap memory ...\n"); cleanup(SYS_ABORT_CODE); } strcpy(pdE->name, fileName); pdE->next = rdE; rdE = pdE; } error = dnext(&info); if (abortDT = checkBreak()) { pcl(1, "*** DT: BREAK ***"); } } while (rdE != NULL) { if (!abortDT) checkDir(rdE->name, False); free(rdE->name); pdE = rdE->next; free(rdE); rdE = pdE; } } void checkFile( char *name ){ struct FileHandle *pFH; long ier; /** | Check a file, opening and reading record by record; this procedure | is performed using the standard AmigaDOS disk file interface. **/ nFiles++; pcl(0, " file %s ...", name); if ((pFH = (struct FileHandle *) Open(name, MODE_OLDFILE)) == NULL) { pcl(1, "* Error opening file \"%s\": file not found.", name); nErFil++; } else { while ((ier = Read(pFH, diskBuffer, TD_CYL)) > 0) { } if (ier < 0) { pcl(1, "* AmigaDOS error %d reading file \"%s\".", IoErr(), name); nErFil++; } } Close(pFH); } void cleanup( int code ){ if (diskBuffer != NULL) { FreeMem(diskBuffer, TD_CYL); } if (diskReq != NULL) { CloseDevice(diskReq); DeleteExtIO(diskReq, sizeof(struct IOExtTD)); } if (diskPort != NULL) DeletePort(diskPort); if (fromWorkBench) { int i; fprintf(stdout, "Strike to continue ..."); while ( (i = getchar()) != '\n' && i != EOF) { } } exit(code); } void motor( int action ){ diskReq->iotd_Req.io_Length = action; diskReq->iotd_Req.io_Command = TD_MOTOR; DoIO(diskReq); } void pcl( int n, char *fmt, ... ){ va_list vl; static length = 0; int nc; /** | What the hell is the delete-to-end-of-line sequence on the Amiga? | The AmigaDOS manual refers to the ANSI sequence [1K - that do | not work in my NewCon windows; so I wrote this simple interface. When | overprinting, we check if the length of the new line is greater than | the old one - if not, we output some blanks. "n" is the number of | newlines at the end, or zero for no newline but carriage return. **/ va_start(vl, fmt); nc = vfprintf(stdout, fmt, vl); va_end(vl); length -= nc; if (length > 0) fprintf(stdout, "%*s", length, " "); if (n) { while (n--) fprintf(stdout, "\n"); length = 0; } else { fprintf(stdout, "\r"); length = nc; } } void readCyl( int cyl, int hd ){ LONG offset; diskReq->iotd_Req.io_Length = TD_CYL; diskReq->iotd_Req.io_Data = (APTR) diskBuffer; diskReq->iotd_Req.io_Command = ETD_READ; diskReq->iotd_Count = diskChangeCount; offset = TD_SECTOR * (NUMSECS * (hd + NUMHEADS * cyl)); diskReq->iotd_Req.io_Offset = offset; DoIO(diskReq); } void seekFullRange( SHORT howmany ){ int i; SHORT error; for (i=1; i<=howmany; i++) { diskReq->iotd_Req.io_Offset = ((NUMCYLS - 1) * NUMSECS * NUMHEADS - 1) * TD_SECTOR; diskReq->iotd_Req.io_Command = TD_SEEK; DoIO(diskReq); if (error = diskReq -> iotd_Req.io_Error) { fprintf(stdout, "\nSeek cycle %d, error 0x%X ...\n", i, error); cleanup(SYS_ABORT_CODE); } diskReq->iotd_Req.io_Offset = 0; diskReq->iotd_Req.io_Command = TD_SEEK; DoIO(diskReq); if (error = diskReq->iotd_Req.io_Error) { fprintf(stdout, "\nSeek cycle %d, error 0x%X ...\n", i, error); cleanup(SYS_ABORT_CODE); } } fprintf(stdout, "no errors detected seeking over full disk range.\n"); } void syntax(void) { fprintf(stdout, "\n\tUsage:\t\tDT DFn, where 'n' (0-4) is the drive number.\n"); fprintf(stdout, "\tPurpose:\tDisk test.\n\n"); cleanup(SYS_NORMAL_CODE); }