home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 620.lha / DT_v1.17 / Source / dt.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-02  |  13.7 KB  |  518 lines

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