home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_progs / diskutil / disktest.lha / source / dt.c next >
Encoding:
C/C++ Source or Header  |  1992-05-21  |  14.6 KB  |  541 lines

  1. /*--------------------------------------*
  2.  | File: DT.c - Rev. 1.18 920229        |
  3.  +--------------------------------------+
  4.  | DT: disk test, a la Norton Utilities |
  5.  +--------------------------------------+------------------*
  6.  | Author:  Maurizio Loreti, aka MLO or I3NOO.             |
  7.  | Address: University of Padova - Department of Physics   |
  8.  |          Via F. Marzolo, 8 - 35131 PADOVA - Italy       |
  9.  | Phone:   (39)(49) 844-313         FAX: (39)(49) 844-245 |
  10.  | E-Mail:  LORETI at IPDINFN (BITNET); or VAXFPD::LORETI  |
  11.  |         (DECnet) - VAXFPD is node 38.257 i.e. 39169; or |
  12.  |          LORETI@VAXFPD.PD.INFN.IT (INTERNET).           |
  13.  | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
  14.  *---------------------------------------------------------*/
  15.  
  16. /**
  17.  | #include's
  18. **/
  19.  
  20. #include <stdio.h>
  21. #include <string.h>
  22. #include <stdlib.h>
  23. #include <stdarg.h>
  24. #include <exec/types.h>
  25. #include <exec/memory.h>
  26. #include <libraries/dos.h>
  27. #include <devices/trackdisk.h>
  28. #include <intuition/intuitionbase.h>
  29. #include <proto/dos.h>
  30. #include <proto/exec.h>
  31. #include <proto/intuition.h>
  32. #include "mlo.h"
  33. #include "proto.h"
  34.  
  35. /**
  36.  | #define's.
  37.  |
  38.  | FILENAME_MAX was supposed to be in <stdio.h> for ANSI
  39.  | C compilers, but is not there in SAS-C 5.10.
  40.  | NUMHEADS is the number of heads in the drive: it could be obtained
  41.  | with a TD_GETGEOMETRY call, but this is v2.0 specific; for AmigaDOS
  42.  | v1.3 there is no command to obtain this parameter but (RKM devices,
  43.  | page 313, under TD_GETNUMTRACKS) "... the standard 3.5" Amiga drive
  44.  | has two heads". So this program will not work for non-standard drives.
  45.  | TD_CYL is the number of bytes per cylinder.
  46. **/
  47.  
  48. #define FILENAME_MAX  108
  49.  
  50. #define NUMHEADS      2
  51. #define TD_CYL        (TD_SECTOR * NUMSECS)
  52.  
  53. #define ON            1
  54. #define OFF           0
  55.  
  56. #define BRK_DETECTED  0x1
  57. #define WARN_PRINTED  0x10
  58. #define INTERNAL_ERR  (BRK_DETECTED | WARN_PRINTED)
  59.  
  60. /**
  61.  | A structure definition to store directory entries, when recursively
  62.  | checking all files.
  63. **/
  64.  
  65. typedef struct sdirEntry {
  66.   struct sdirEntry *next;
  67.   char name[1];
  68. } dirEntry;
  69.  
  70. /**
  71.  | Global variables
  72. **/
  73.  
  74. struct IntuitionBase *IntuitionBase = NULL; /* Pointer to Int. library */
  75.  
  76. /**
  77.  | 'Local' global variables
  78. **/
  79.  
  80. static struct MsgPort *diskPort = NULL;   /* Various Intuition pointers */
  81. static struct IOExtTD *diskReq = NULL;    /*   for disk input */
  82. static BYTE *diskBuffer = NULL;           /* Disk input buffer */
  83. static struct FileInfoBlock *pFIB = NULL; /* Buffer for directory scan */
  84. static ULONG diskChangeCount;             /* Disk change count */
  85. static int nErFil = 0;                    /* Total number of errors */
  86. static int nDirs = 0;                     /* Nr. of checked directories */
  87. static int nFiles = 0;                    /* Nr. of checked files */
  88. static Boolean fromWorkBench;             /* Scheduled from WB or CLI ? */
  89. static unsigned abortDT = 0;              /* True when CTRL-C hit */
  90. static int maxDrive = NUMUNITS - 1;       /* Highest possible drive */
  91. static int numCyls;                       /* Number of cylinders on drive */
  92.  
  93. /**
  94.  | Local procedures
  95. **/
  96.  
  97. static unsigned checkBreak(void);
  98. static   void   checkDir(char *path, const Boolean root);
  99. static   void   checkFile(char *name);
  100. static   void   motor(const ULONG action);
  101. static   void   pcl(int before, int after, char *fmt, ...);
  102. static   void   readCyl(const int cyl, const int hd);
  103. static   void   seekFullRange(const SHORT howmany);
  104. static   void   syntax(void);
  105.  
  106. void main(
  107.   int argc,
  108.   char **argv
  109. ){
  110.   int drive, cyl, head;
  111.   SHORT error;
  112.   char driveName[5];
  113.  
  114. /**
  115.  | To be called from CLI, with DT DFx[:] ; if called from the
  116.  | Workbench, a prompt for the floppy unit is sent to the console
  117.  | window---created from the Lattice initialisation routine umain(),
  118.  | gently hacked for this program.
  119.  |
  120.  |  Pass 1: a seek over full range;
  121.  |  Pass 2: read all cylinders;
  122.  |  Pass 3: read all files record by record.
  123.  |
  124.  | But first, check the input arguments...
  125. **/
  126.  
  127.   if (fromWorkBench = !argc) {
  128.     do {
  129.       fprintf(stdout, "\nDrive to test (DF0-DF%d) ? ", maxDrive);
  130.       (void) fgets(driveName, sizeof(driveName), stdin);
  131.     } while (strnicmp(driveName, "df", 2)     ||
  132.              (drive = atoi(driveName+2)) < 0  ||
  133.              drive > maxDrive);
  134.   } else {
  135.     if (argc != 2                     ||
  136.         strnicmp(*++argv, "df", 2)    ||
  137.         (drive = atoi(*argv+2)) < 0   ||
  138.         drive > maxDrive)                   syntax();
  139.   }
  140.  
  141. /**
  142.  | Open properly the device, check for a disk in drive,
  143.  | obtain disk parameters from various status commands
  144.  | and obtain memory for our internal buffers.
  145. **/
  146.  
  147.   if (!(diskPort = CreatePort(NULL, 0L))) {
  148.     fprintf(stderr, "Can't create I/O port!\n");
  149.     cleanup(SYS_ABORT_CODE);
  150.   }
  151.  
  152.   if (!(diskReq = (struct IOExtTD *)
  153.       CreateExtIO(diskPort, sizeof(struct IOExtTD))) ) {
  154.     fprintf(stderr, "Can't obtain I/O request block!\n");
  155.     cleanup(SYS_ABORT_CODE);
  156.   }
  157.  
  158.   sprintf(driveName, "DF%d:", drive);
  159.  
  160.   if (error =
  161.       OpenDevice(TD_NAME, drive, (struct IORequest *) diskReq, 0L)) {
  162.         fprintf(stderr,
  163.                 "Error 0x%X returned by OpenDevice for drive %s ...\n",
  164.                 error, driveName);
  165.     cleanup(SYS_ABORT_CODE);
  166.   }
  167.  
  168.   if ((diskBuffer = (BYTE *) AllocMem(TD_CYL, MEMF_CHIP)) == NULL   ||
  169.       (pFIB = (struct FileInfoBlock *)
  170.             AllocMem(sizeof(struct FileInfoBlock), MEMF_CLEAR)) == NULL) {
  171.     fprintf(stderr, "Can't allocate internal buffers ...\n");
  172.     cleanup(SYS_ABORT_CODE);
  173.   }
  174.  
  175.   diskReq->iotd_Req.io_Command = TD_GETNUMTRACKS;
  176.   (void) DoIO((struct IORequest *) diskReq);
  177.   numCyls = diskReq->iotd_Req.io_Actual / NUMHEADS;
  178.  
  179.   diskReq->iotd_Req.io_Command = TD_CHANGESTATE;
  180.   (void) DoIO((struct IORequest *) diskReq);
  181.   if (diskReq->iotd_Req.io_Actual) {
  182.     fprintf(stdout, "No disk present in drive %d ...\n", drive);
  183.     cleanup(SYS_ABORT_CODE);
  184.   }
  185.  
  186.   diskReq->iotd_Req.io_Command = TD_CHANGENUM;
  187.   (void) DoIO((struct IORequest *) diskReq);
  188.   fprintf(stdout, "Change number for drive %s is %d;\n",
  189.           driveName, (diskChangeCount = diskReq->iotd_Req.io_Actual));
  190.  
  191. /**
  192.  | Pass 1
  193. **/
  194.  
  195.   motor(ON);
  196.   seekFullRange(1);
  197.  
  198. /**
  199.  | Pass 2
  200. **/
  201.  
  202.   fprintf(stdout, "Checking all disk tracks:\n");
  203.   for (cyl=0; cyl<numCyls; cyl++) {
  204.     for (head=0; head<NUMHEADS; head++) {
  205.       pcl(0, 0, "  reading cylinder %d, head %d ...", cyl, head);
  206.       if (checkBreak()) {
  207.         motor(OFF);
  208.         cleanup(SYS_NORMAL_CODE);
  209.       }
  210.       readCyl(cyl, head);
  211.       if (error = diskReq->iotd_Req.io_Error) {
  212.         pcl(0, 1, "* Error 0x%X detected for cylinder %d, head %d",
  213.             error, cyl, head);
  214.         nErFil++;
  215.       }
  216.     }
  217.   }
  218.   motor(OFF);
  219.  
  220.   if (nErFil) {
  221.     pcl(0, 1, "* %d hard errors detected reading drive %s.",
  222.         nErFil, driveName);
  223.     cleanup(SYS_ABORT_CODE);
  224.   } else {
  225.     pcl(0, 1, "  no errors detected reading drive %s.", driveName);
  226.   }
  227.  
  228. /**
  229.  | Pass 3
  230. **/
  231.  
  232.   pcl(0, 1, "Checking all files in drive %s", driveName);
  233.   checkDir(driveName, True);
  234.   pcl(0, 2, "%d director%s and %d file%s checked: %d error%s detected.",
  235.       nDirs,  (nDirs  == 1 ? "y" : "ies"),
  236.       nFiles, (nFiles == 1 ? ""  : "s"),
  237.       nErFil, (nErFil == 1 ? ""  : "s"));
  238.  
  239.   cleanup(SYS_NORMAL_CODE);
  240. }
  241.  
  242. static unsigned checkBreak(void)
  243. {
  244.   if (!abortDT  && 
  245.     (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C))
  246.       abortDT |= BRK_DETECTED;
  247.   if (abortDT  &&  !(abortDT & WARN_PRINTED)) {
  248.     pcl(1, 1, "*** DT: BREAK ***");
  249.     abortDT |= WARN_PRINTED;
  250.   }
  251.   return abortDT;
  252. }
  253.  
  254. static void checkDir(
  255.   char *path,
  256.   const Boolean root
  257. ){
  258.   BPTR dlock;
  259.   char fileName[FILENAME_MAX];
  260.   char *pc;
  261.   dirEntry *rdE = NULL;
  262.   dirEntry *pdE;
  263.  
  264. /**
  265.  | This procedure checks (recursively) a directory.
  266.  | "path" contains the full directory name, and "root" is non-zero the
  267.  | first time that checkDir() is called - i.e. for the root directory.
  268.  | checkDir() scans the wanted directory, checking immediately the 'true'
  269.  | files; the subdirectories (if any) are checked recursively at the
  270.  | end, one by one.
  271.  | If an error is detected from checkDir(), the directory/file test is
  272.  | stopped and the global flag "abortDT" is set; this makes possible, in
  273.  | the further steps, to recursively free() all the memory that has been
  274.  | allocated in order to store subdirectory names.
  275.  |
  276.  | First: obtain a lock on the wanted directory, and Examine() the lock;
  277.  | since only one directory is being scanned at a time, we can use a
  278.  | single FileInfoBlock buffer.
  279. **/
  280.  
  281.   if ((dlock = Lock(path, ACCESS_READ)) == NULL) {
  282.     pcl(0, 1, "* Can't access directory %s !", path);
  283.     abortDT = INTERNAL_ERR;
  284.     nErFil++;
  285.   } else {
  286.     if (!Examine(dlock, pFIB)) {
  287.       pcl(0, 1, "* Error return from Examine(), directory %s", path);
  288.       abortDT = INTERNAL_ERR;
  289.       nErFil++;
  290.     } else {
  291.  
  292. /**
  293.  | Prepare in "fileName" the full directory name - to which local
  294.  | filenames will be appended.
  295. **/
  296.  
  297.       if (root) {
  298.         pc = strcpy(fileName, pFIB->fib_FileName);
  299.         pc += strlen(fileName);
  300.         *pc++ = ':';
  301.         *pc = NIHIL;
  302.         pcl(0, 1, "  checking files in root directory %s ...", fileName);
  303.       } else {
  304.         pc = strcpy(fileName, path);
  305.         pc += strlen(fileName);
  306.         pcl(0, 1, "  checking files in directory %s ...", fileName);
  307.         *pc++ = '/';
  308.       }
  309.  
  310.       nDirs++;
  311.  
  312. /**
  313.  | Now, loop over all directory entries. As already said, all the
  314.  | 'real' files are immediately checked; the subdirectory names are
  315.  | stored in a linked list to be examined at the end. This list is
  316.  | implemented as a LIFO tree (the simplest type).
  317. **/
  318.  
  319.       while (ExNext(dlock, pFIB)) {
  320.         (void) strcpy(pc, pFIB->fib_FileName);
  321.         if (pFIB->fib_DirEntryType < 0) {
  322.           checkFile(fileName);
  323.         } else {
  324.  
  325. /**
  326.  | If a memory allocation error is detected when asking space for
  327.  | our linked list, we exit the "while" loop; setting the "abortDT"
  328.  | flag before exiting, ensures that all the memory we had from
  329.  | these malloc()'s will later be free()-ed recursively.
  330. **/
  331.  
  332.           if ((pdE = malloc(sizeof(dirEntry) + strlen(fileName))) == NULL) {
  333.             pcl(1, 1, "* Can't allocate heap memory!");
  334.             abortDT = INTERNAL_ERR;
  335.             break;
  336.           }
  337.  
  338.           (void) strcpy(pdE->name, fileName);
  339.           pdE->next = rdE;
  340.           rdE = pdE;
  341.         }
  342.  
  343.         if (checkBreak()) break;
  344.       }
  345.  
  346. /**
  347.  | We should check if ExNext() has failed, or if the last
  348.  | entry has been found.
  349. **/
  350.  
  351.       if (!abortDT   &&   IoErr() != ERROR_NO_MORE_ENTRIES) {
  352.         pcl(1, 1, "* Error reading directory %s !", path);
  353.         nErFil++;
  354.       }
  355.     }
  356.     UnLock(dlock);
  357.   }
  358.  
  359. /**
  360.  | Now, loop over all detected subdirectories (if any);
  361.  | freeing in the same time the memory used to store their names.
  362. **/
  363.  
  364.   while (rdE != NULL) {
  365.     if (!abortDT) checkDir(rdE->name, False);
  366.  
  367.     pdE = rdE->next;
  368.     free(rdE);
  369.     rdE = pdE;
  370.   }
  371. }
  372.  
  373. static void checkFile(
  374.   char *name
  375. ){
  376.   BPTR pFH;
  377.   long ier;
  378.  
  379. /**
  380.  | Check a file, opening and reading it record by record.
  381. **/
  382.  
  383.   nFiles++;
  384.  
  385.   pcl(0, 0, "    file %s ...", name);
  386.   if ((pFH = Open(name, MODE_OLDFILE)) == NULL) {
  387.     pcl(0, 1, "* Error %d opening file \"%s\".", IoErr(), name);
  388.     nErFil++;
  389.   } else {
  390.     while (!abortDT  &&  (ier = Read(pFH, diskBuffer, TD_CYL)) > 0) {
  391.       (void) checkBreak();
  392.     }
  393.     Close(pFH);
  394.  
  395.     if (ier < 0) {
  396.       pcl(0, 1, "* Error %d reading file \"%s\".", IoErr(), name);
  397.       nErFil++;
  398.     }
  399.   }
  400. }
  401.  
  402. void cleanup(
  403.   const int code
  404. ){
  405.  
  406. /**
  407.  | Releases all global resources, then exit to the operating system.
  408. **/
  409.  
  410.   if (diskBuffer != NULL) FreeMem(diskBuffer, TD_CYL);
  411.   if (pFIB != NULL)       FreeMem(pFIB, sizeof(struct FileInfoBlock));
  412.  
  413.   if (diskReq != NULL) {
  414.     CloseDevice((struct IORequest *) diskReq);
  415.     DeleteExtIO((struct IORequest *) diskReq);
  416.   }
  417.   if (diskPort != NULL)   DeletePort(diskPort);
  418.  
  419.   if (fromWorkBench) {
  420.     int i;
  421.  
  422.     fprintf(stdout, "Strike <CR> to continue ... ");
  423.     while ( (i = getchar()) != '\n'   &&   i != EOF)  { }
  424.   }
  425.  
  426.   if (IntuitionBase != NULL) CloseLibrary((struct Library *) IntuitionBase);
  427.  
  428.   exit(code);
  429. }
  430.  
  431. int CXBRK(void)
  432. {
  433. /**
  434.  | If a CTRL-C is detected from the operating system,
  435.  | we silently defer all handling until checkBreak() is called.
  436. **/
  437.  
  438.   abortDT |= BRK_DETECTED;
  439.   return 0;
  440. }
  441.  
  442. static void motor(
  443.   const ULONG action
  444. ){
  445.   diskReq->iotd_Req.io_Length = action;
  446.   diskReq->iotd_Req.io_Command = TD_MOTOR;
  447.   (void) DoIO((struct IORequest *) diskReq);
  448. }
  449.  
  450. static void pcl(
  451.   int before,
  452.   int after,
  453.   char *fmt,
  454.   ...
  455. ){
  456.   va_list vl;
  457.   static length = 0;
  458.   int nc;
  459.  
  460. /**
  461.  | What the hell is the delete-to-end-of-line sequence on the Amiga?
  462.  | The AmigaDOS manual refers to the ANSI sequence <ESC>[1K - that do
  463.  | not work in my NewCon windows; so I wrote this simple interface. When
  464.  | overprinting, we check if the length of the new line is greater than
  465.  | the length of the old one - if not, we output some trailing blanks.
  466.  | "before" and "after" are the number of newlines to be printed before
  467.  | and after this line; if "after" is 0 no newline but a carriage return
  468.  | is output.
  469. **/
  470.  
  471.   if (before) {
  472.     while (before--) puts("");
  473.     length = 0;
  474.   }
  475.  
  476.   va_start(vl, fmt);
  477.   nc = vfprintf(stdout, fmt, vl);
  478.   va_end(vl);
  479.  
  480.   length -= nc;
  481.   if (length > 0) fprintf(stdout, "%*s", length, " ");
  482.  
  483.   if (after) {
  484.     while (after--) puts("");
  485.     length = 0;
  486.   } else {
  487.     fprintf(stdout, "%c", '\r');
  488.     length = nc;
  489.   }
  490. }
  491.  
  492. static void readCyl(
  493.   const int cyl,
  494.   const int hd
  495. ){
  496.   diskReq->iotd_Req.io_Length = TD_CYL;
  497.   diskReq->iotd_Req.io_Data = (APTR) diskBuffer;
  498.   diskReq->iotd_Req.io_Command = ETD_READ;
  499.   diskReq->iotd_Count = diskChangeCount;
  500.   diskReq->iotd_Req.io_Offset =
  501.            TD_SECTOR * (NUMSECS * (hd + NUMHEADS * cyl));
  502.   (void) DoIO((struct IORequest *) diskReq);
  503. }
  504.  
  505. static void seekFullRange(
  506.   const SHORT howmany
  507. ){
  508.   int i;
  509.   SHORT error;
  510.  
  511.   for (i=1; i<=howmany; i++) {
  512.     diskReq->iotd_Req.io_Offset =
  513.           ((numCyls - 1) * NUMSECS * NUMHEADS - 1) * TD_SECTOR;
  514.     diskReq->iotd_Req.io_Command = TD_SEEK;
  515.     (void) DoIO((struct IORequest *) diskReq);
  516.     if (error = diskReq -> iotd_Req.io_Error) {
  517.       fprintf(stdout, "* Seek cycle %d, error 0x%X ...\n", i, error);
  518.       cleanup(SYS_ABORT_CODE);
  519.     }
  520.  
  521.     diskReq->iotd_Req.io_Offset = 0;
  522.     diskReq->iotd_Req.io_Command = TD_SEEK;
  523.     (void) DoIO((struct IORequest *) diskReq);
  524.     if (error = diskReq->iotd_Req.io_Error) {
  525.       fprintf(stdout, "* Seek cycle %d, error 0x%X ...\n", i, error);
  526.       cleanup(SYS_ABORT_CODE);
  527.     }
  528.   }
  529.   fprintf(stdout, "  no errors detected seeking over full disk range.\n");
  530. }
  531.  
  532. static void syntax(void)
  533. {
  534.   fprintf(stdout,
  535.         "\n\tUsage:\t\tDT DFn, where 'n' (0-%d) is the drive number.\n",
  536.         maxDrive);
  537.   fprintf(stdout,
  538.         "\tPurpose:\tDisk test.\n\n");
  539.   cleanup(SYS_NORMAL_CODE);
  540. }
  541.