home *** CD-ROM | disk | FTP | other *** search
- /*--------------------------------------*
- | File: DT.c - Rev. 1.17 911202 |
- +--------------------------------------+
- | DT: disk test, a la Norton Utilities |
- +--------------------------------------+------------------*
- | Author: Maurizio Loreti, aka MLO or I3NOO. |
- | Address: University of Padova - Department 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; or |
- | LORETI@PADOVA.INFN.IT (INTERNET). |
- | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
- *---------------------------------------------------------*/
-
- /**
- | #include's
- **/
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <stdarg.h>
- #include <exec/types.h>
- #include <exec/memory.h>
- #include <libraries/dos.h>
- #include <devices/trackdisk.h>
- #include <intuition/intuitionbase.h>
- #include <proto/dos.h>
- #include <proto/exec.h>
- #include <proto/intuition.h>
- #include "mlo.h"
- #include "proto.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
-
- #define BRK_DETECTED 0x1
- #define WARN_PRINTED 0x10
- #define INTERNAL_ERR (BRK_DETECTED | WARN_PRINTED)
-
- /**
- | A structure definition to store directory entries, when recursively
- | checking all files.
- **/
-
- typedef struct sdirEntry {
- struct sdirEntry *next;
- char name[1];
- } dirEntry;
-
- /**
- | Global variables
- **/
-
- struct IntuitionBase *IntuitionBase = NULL; /* Pointer to Int. library */
-
- /**
- | 'Local' global variables
- **/
-
- static struct MsgPort *diskPort = NULL; /* Various Intuition pointers */
- static struct IOExtTD *diskReq = NULL; /* for disk input */
- static BYTE *diskBuffer = NULL; /* Disk input buffer */
- static struct FileInfoBlock *pFIB = NULL; /* Buffer for directory scan */
- static ULONG diskChangeCount; /* Disk change count */
- static int nErFil = 0; /* Total number of errors */
- static int nDirs = 0; /* Nr. of checked directories */
- static int nFiles = 0; /* Nr. of checked files */
- static Boolean fromWorkBench; /* Scheduled from WB or CLI ? */
- static unsigned abortDT = 0; /* True when CTRL-C hit */
- static int maxDrive = NUMUNITS - 1; /* Highest possible drive */
-
- /**
- | Local procedures
- **/
-
- static unsigned checkBreak(void);
- static void checkDir(char *path, const Boolean root);
- static void checkFile(char *name);
- static void motor(const ULONG action);
- static void pcl(int before, int after, char *fmt, ...);
- static void readCyl(const int cyl, const int hd);
- static void seekFullRange(const SHORT howmany);
- static void syntax(void);
-
- 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 umain().
- |
- | 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...
- **/
-
- if (fromWorkBench = !argc) {
- do {
- fprintf(stdout, "\nDrive to test (DF0-DF%d) ? ", maxDrive);
- (void) fgets(driveName, sizeof(driveName), stdin);
- } while (strnicmp(driveName, "df", 2) ||
- (drive = atoi(driveName+2)) < 0 ||
- drive > maxDrive);
- } else {
- if (argc != 2 ||
- strnicmp(*++argv, "df", 2) ||
- (drive = atoi(*argv+2)) < 0 ||
- drive > maxDrive) syntax();
- }
-
- /**
- | Now, obtain memory for our internal buffers.
- **/
-
- if ((diskBuffer = (BYTE *) AllocMem(TD_CYL, MEMF_CHIP)) == NULL ||
- (pFIB = (struct FileInfoBlock *)
- AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
- fprintf(stderr, "Can't allocate internal buffers ...\n");
- cleanup(SYS_ABORT_CODE);
- }
-
- /**
- | Pass 1
- **/
-
- if (!(diskPort = 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, (struct IORequest *) 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;
- (void) DoIO((struct IORequest *) diskReq);
- fprintf(stdout, "\nChange number for drive %s is %d;\n",
- driveName, (diskChangeCount = diskReq->iotd_Req.io_Actual));
-
- motor(ON);
- seekFullRange(1);
-
- /**
- | Pass 2
- **/
-
- fprintf(stdout, "Checking all disk tracks:\n");
- for (cyl=0; cyl<NUMCYLS; cyl++) {
- for (head=0; head<NUMHEADS; head++) {
- pcl(0, 0, " reading cylinder %d, head %d ...", cyl, head);
- if (checkBreak()) {
- motor(OFF);
- cleanup(SYS_NORMAL_CODE);
- }
- readCyl(cyl, head);
- if (error = diskReq->iotd_Req.io_Error) {
- pcl(0, 1, "* Error 0x%X detected for cylinder %d, head %d",
- error, cyl, head);
- nErFil++;
- }
- }
- }
- motor(OFF);
-
- if (nErFil) {
- pcl(0, 1, "* %d hard errors detected reading drive %s.",
- nErFil, driveName);
- cleanup(SYS_ABORT_CODE);
- } else {
- pcl(0, 1, " no errors detected reading drive %s.", driveName);
- }
-
- /**
- | Pass 3
- **/
-
- pcl(0, 1, "Checking all files in drive %s", driveName);
- checkDir(driveName, True);
- pcl(0, 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);
- }
-
- static unsigned checkBreak(void)
- {
- if (!abortDT &&
- (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C))
- abortDT |= BRK_DETECTED;
- if (abortDT && !(abortDT & WARN_PRINTED)) {
- pcl(1, 1, "*** DT: BREAK ***");
- abortDT |= WARN_PRINTED;
- }
- return abortDT;
- }
-
- static void checkDir(
- char *path,
- const Boolean root
- ){
- BPTR dlock;
- char fileName[FILENAME_MAX];
- char *pc;
- dirEntry *rdE = NULL;
- dirEntry *pdE;
-
- /**
- | This procedure checks (recursively) a directory.
- | "path" contains the full directory name, and "root" is non-zero the
- | first time that checkDir() is called - i.e. for the root directory.
- | checkDir() scans the wanted directory, checking immediately the 'true'
- | files; the subdirectories (if any) are checked recursively at the
- | end, one by one.
- | If an error is detected from checkDir(), the directory/file test is
- | stopped and the global flag "abortDT" is set; this makes possible, in
- | the further steps, to recursively free() all the memory that has been
- | allocated in order to store subdirectory names.
- |
- | First: obtain a lock on the wanted directory, and Examine() the lock;
- | since only one directory is being scanned at a time, we can use a
- | single FileInfoBlock buffer.
- **/
-
- if ((dlock = Lock(path, ACCESS_READ)) == NULL) {
- pcl(0, 1, "* Can't access directory %s !", path);
- abortDT = INTERNAL_ERR;
- nErFil++;
- } else {
- if (!Examine(dlock, pFIB)) {
- pcl(0, 1, "* Error return from Examine(), directory %s", path);
- abortDT = INTERNAL_ERR;
- nErFil++;
- } else {
-
- /**
- | Prepare in "fileName" the full directory name - to which local
- | filenames will be appended.
- **/
-
- if (root) {
- pc = strcpy(fileName, pFIB->fib_FileName);
- pc += strlen(fileName);
- *pc++ = ':';
- *pc = NIHIL;
- pcl(0, 1, " checking files in root directory %s ...", fileName);
- } else {
- pc = strcpy(fileName, path);
- pc += strlen(fileName);
- pcl(0, 1, " checking files in directory %s ...", fileName);
- *pc++ = '/';
- }
-
- nDirs++;
-
- /**
- | Now, loop over all directory entries. As already said, all the
- | 'real' files are immediately checked; the subdirectory names are
- | stored in a linked list to be examined at the end. This list is
- | implemented as a LIFO tree (the simplest type).
- **/
-
- while (ExNext(dlock, pFIB)) {
- (void) strcpy(pc, pFIB->fib_FileName);
- if (pFIB->fib_DirEntryType < 0) {
- checkFile(fileName);
- } else {
-
- /**
- | If a memory allocation error is detected when asking space for
- | our linked list, we exit the "while" loop; setting the "abortDT"
- | flag before exiting, ensures that all the memory we had from
- | these malloc()'s will later be free()-ed recursively.
- **/
-
- if ((pdE = malloc(sizeof(dirEntry) + strlen(fileName))) == NULL) {
- pcl(1, 1, "* Can't allocate heap memory!");
- abortDT = INTERNAL_ERR;
- break;
- }
-
- (void) strcpy(pdE->name, fileName);
- pdE->next = rdE;
- rdE = pdE;
- }
-
- if (checkBreak()) break;
- }
-
- /**
- | We should check if ExNext() has failed, or if the last
- | entry has been found.
- **/
-
- if (!abortDT && IoErr() != ERROR_NO_MORE_ENTRIES) {
- pcl(1, 1, "* Error reading directory %s !", path);
- nErFil++;
- }
- }
- UnLock(dlock);
- }
-
- /**
- | Now, loop over all detected subdirectories (if any);
- | freeing in the same time the memory used to store their names.
- **/
-
- while (rdE != NULL) {
- if (!abortDT) checkDir(rdE->name, False);
-
- pdE = rdE->next;
- free(rdE);
- rdE = pdE;
- }
- }
-
- static void checkFile(
- char *name
- ){
- BPTR pFH;
- long ier;
-
- /**
- | Check a file, opening and reading it record by record.
- **/
-
- nFiles++;
-
- pcl(0, 0, " file %s ...", name);
- if ((pFH = Open(name, MODE_OLDFILE)) == NULL) {
- pcl(0, 1, "* Error %d opening file \"%s\".", IoErr(), name);
- nErFil++;
- } else {
- while (!abortDT && (ier = Read(pFH, diskBuffer, TD_CYL)) > 0) {
- (void) checkBreak();
- }
- Close(pFH);
-
- if (ier < 0) {
- pcl(0, 1, "* Error %d reading file \"%s\".", IoErr(), name);
- nErFil++;
- }
- }
- }
-
- void cleanup(
- const int code
- ){
-
- /**
- | Releases all global resources, then exit to the operating system.
- **/
-
- if (diskBuffer != NULL) FreeMem(diskBuffer, TD_CYL);
- if (pFIB != NULL) FreeMem(pFIB, sizeof(struct FileInfoBlock));
-
- if (diskReq != NULL) {
- CloseDevice((struct IORequest *) diskReq);
- DeleteExtIO((struct IORequest *) diskReq);
- }
- if (diskPort != NULL) DeletePort(diskPort);
-
- if (fromWorkBench) {
- int i;
-
- fprintf(stdout, "Strike <CR> to continue ... ");
- while ( (i = getchar()) != '\n' && i != EOF) { }
- }
-
- if (IntuitionBase != NULL) CloseLibrary((struct Library *) IntuitionBase);
-
- exit(code);
- }
-
- int CXBRK(void)
- {
- /**
- | If a CTRL-C is detected from the operating system,
- | we silently defer all handling until checkBreak() is called.
- **/
-
- abortDT |= BRK_DETECTED;
- return 0;
- }
-
- static void motor(
- const ULONG action
- ){
- diskReq->iotd_Req.io_Length = action;
- diskReq->iotd_Req.io_Command = TD_MOTOR;
- (void) DoIO((struct IORequest *) diskReq);
- }
-
- static void pcl(
- int before,
- int after,
- 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 <ESC>[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 length of the old one - if not, we output some trailing blanks.
- | "before" and "after" are the number of newlines to be printed before
- | and after this line; if "after" is 0 no newline but a carriage return
- | is output.
- **/
-
- if (before) {
- while (before--) puts("");
- length = 0;
- }
-
- va_start(vl, fmt);
- nc = vfprintf(stdout, fmt, vl);
- va_end(vl);
-
- length -= nc;
- if (length > 0) fprintf(stdout, "%*s", length, " ");
-
- if (after) {
- while (after--) puts("");
- length = 0;
- } else {
- fprintf(stdout, "%c", '\r');
- length = nc;
- }
- }
-
- static void readCyl(
- const int cyl,
- const int hd
- ){
- 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;
- diskReq->iotd_Req.io_Offset =
- TD_SECTOR * (NUMSECS * (hd + NUMHEADS * cyl));
- (void) DoIO((struct IORequest *) diskReq);
- }
-
- static void seekFullRange(
- const 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;
- (void) DoIO((struct IORequest *) diskReq);
- if (error = diskReq -> iotd_Req.io_Error) {
- fprintf(stdout, "* Seek 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;
- (void) DoIO((struct IORequest *) diskReq);
- if (error = diskReq->iotd_Req.io_Error) {
- fprintf(stdout, "* Seek cycle %d, error 0x%X ...\n", i, error);
- cleanup(SYS_ABORT_CODE);
- }
- }
- fprintf(stdout, " no errors detected seeking over full disk range.\n");
- }
-
- static void syntax(void)
- {
- fprintf(stdout,
- "\n\tUsage:\t\tDT DFn, where 'n' (0-%d) is the drive number.\n",
- maxDrive);
- fprintf(stdout,
- "\tPurpose:\tDisk test.\n\n");
- cleanup(SYS_NORMAL_CODE);
- }
-