home *** CD-ROM | disk | FTP | other *** search
/ Power Programming / powerprogramming1994.iso / progtool / dskutl / reform12.arc / REFORM12.PAS < prev    next >
Pascal/Delphi Source File  |  1987-05-23  |  42KB  |  1,255 lines

  1. PROGRAM reformat;
  2. {
  3. Program to reformat any disk attached to a Olivetti PC or compatible.
  4. The progam will probably work well on any MS/PC-DOS machine running under
  5. DOS 2.xx.  Fixed disks of all sizes.  [Toad Hall note:  not correct.]
  6.  
  7. Global types }
  8.  
  9. TYPE
  10.  
  11.   Regpack    = RECORD CASE INTEGER OF
  12.                1: (ax, bx, cx, dx, bp, si, di, ds, es, flags : INTEGER);
  13.                2: (al, ah, bl, bh, cl, ch, dl, dh            : Byte);
  14.                END;
  15.  
  16.   Boot       = RECORD
  17.                  Jump:                  ARRAY[0..2] OF Byte;
  18.                  OEM :                  ARRAY[0..7] OF CHAR;
  19.                  sectorSize:            INTEGER;
  20.                  clusterSize:           Byte;
  21.                  reservedSectors:       INTEGER;
  22.                  numberOfFats:          Byte;
  23.                  rootDirSize,
  24.                  totalSectors:          INTEGER;
  25.                  mediaDescriptor:       Byte;
  26.                  fatSize,
  27.                  trackSize,
  28.                  numberOfHeads,
  29.                  numberOfHiddenSectors: INTEGER;
  30.                END;
  31.  
  32.   IntArray   = ARRAY[0..32766] OF INTEGER;
  33.  
  34.   Buffer     = ARRAY[0..32766] OF Byte;
  35.  
  36.   longInteger      = ARRAY[0..1] OF INTEGER;
  37.  
  38.   DirectoryPointer = ^DirectoryEntry;
  39.  
  40.   DirectoryEntry   = RECORD
  41.                        EntryName:          ARRAY[0..10] OF CHAR;
  42.                        attribute:          Byte;
  43.                        Reserved:           ARRAY[1..10] OF Byte;
  44.                        timeLastUpdated:    INTEGER;
  45.                        dateLastUpdated:    INTEGER;
  46.                        startingCluster:    INTEGER;
  47.                        fileSize:           longInteger;
  48.                        newStartingCluster: INTEGER;
  49.                        Next,
  50.                        SubDirectory:       DirectoryPointer;
  51.                      END;
  52.  
  53.   WorkString       = STRING[255];
  54.  
  55. CONST
  56.  
  57.   READONLY        = $01;
  58.   HIDDENFILE      = $02;
  59.   SYSTEMFILE      = $04;
  60.   VOLUMELABEL     = $08;
  61.   SUBDIRECTORY    = $10;
  62.   ARCHIVE         = $20;
  63.  
  64.   NEVERUSED       = $00;
  65.   ERASED          = $E5;
  66.  
  67.   FIXEDDISK       = $F8;
  68.   DUAL8SECTOR     = $FF;
  69.   SINGLE8SECTOR   = $FE;
  70.   DUAL9SECTOR     = $FD;
  71.   SINGLE9SECTOR   = $FC;
  72.  
  73.   Unused:          INTEGER = $0000;
  74.   ReservedMinimum: INTEGER = $0FF0;
  75.   ReservedMaximum: INTEGER = $0FF6;
  76.   BadCluster:      INTEGER = $0FF7;
  77.   LastMinimum:     INTEGER = $0FF8;
  78.   LastMaximum:     INTEGER = $0FFF;
  79.   lastNormal:      INTEGER = $0FFF;
  80.  
  81. VAR
  82.  
  83. { Drive characteristics and constants communications block }
  84.  
  85.   DriveLetter:          CHAR;
  86.   numberOfFats,
  87.   media,
  88.   defaultDrive,
  89.   driveNumber:          Byte;
  90.   freeClusters,
  91.   totalDataClusters,
  92.   firstDataSector,
  93.   fatSize,
  94.   firstFATsector,
  95.   rootDirSize,
  96.   directorySectors,
  97.   firstDirectorySector,
  98.   sectorSize,
  99.   clusterSize:          INTEGER;
  100.  
  101. { Global variables }
  102.  
  103.   Registers:            Regpack;
  104.   oldFATindex,
  105.   newFATindex,
  106.   errors,
  107.   lostClusters,
  108.   totalFiles,
  109.   hiddenFiles,
  110.   inRootDirectory,
  111.   inSubdirectories,
  112.   nonContiguousFiles,
  113.   subdirectories,
  114.   movedClusters,
  115.   clustersToMove,
  116.   count:                INTEGER;
  117.   SAVEaddress,
  118.   DTAddress:           ^Buffer;
  119.   PermutationAddress,
  120.   NewFATAddress,
  121.   OldFATAddress:       ^IntArray;
  122.   RootDir:              DirectoryPointer;
  123.   movedField,
  124.   inputField,
  125.   logField,
  126.   warningField,
  127.   errorField,
  128.   disasterField:        longInteger;
  129.   Anything,
  130.   Instr:                CHAR;
  131.   AlreadyWritten:       BOOLEAN;
  132.   DiskLabel:            ARRAY[0..10] OF CHAR;
  133.  
  134. {$I REFORMAT.INC    Toad Hall Turbo Inline disk procedure Int2526}
  135.  
  136. PROCEDURE Beep;
  137.   BEGIN
  138.     WRITE(CHR(7));
  139.   END;
  140.  
  141. PROCEDURE WriteLog(S: WorkString);
  142.   VAR
  143.     count: INTEGER;
  144.   BEGIN
  145.     GotoXY(logField[0], logField[1]);
  146.     FOR count := logField[0] TO 79 DO WRITE(' ');
  147.     GotoXY(logField[0], logField[1]);
  148.     WRITE(S);
  149.   END;  {of WriteLog}
  150.  
  151.  
  152. PROCEDURE WriteWarning(S: WorkString);
  153.   VAR
  154.     count: INTEGER;
  155.   BEGIN
  156.     GotoXY(warningField[0], warningField[1]);
  157.     FOR count := warningField[0] TO 79 DO WRITE(' ');
  158.     GotoXY(warningField[0], warningField[1]);
  159.     WRITE(S);
  160.   END;  {of WriteWarning}
  161.  
  162.  
  163. PROCEDURE WriteError(S: WorkString);
  164.   VAR
  165.     count: INTEGER;
  166.   BEGIN
  167.     GotoXY(errorField[0], errorField[1]);
  168.     FOR count := errorField[0] TO 79 DO WRITE(' ');
  169.     GotoXY(errorField[0], errorField[1]);
  170.     WRITE(S);
  171.   END;  {of WriteError}
  172.  
  173.  
  174. PROCEDURE WriteDisaster(S: WorkString);
  175.   VAR
  176.     count: INTEGER;
  177.   BEGIN
  178.     GotoXY(disasterField[0], disasterField[1]);
  179.     FOR count := disasterField[0] TO 79 DO WRITE(' ');
  180.     GotoXY(disasterField[0], disasterField[1]);
  181.     WRITE(S);
  182.   END;  {of WriteDisaster}
  183.  
  184.  
  185. PROCEDURE GetInput(VAR Instr: CHAR);
  186.   VAR
  187.     count: INTEGER;
  188.   BEGIN
  189.     GotoXY(inputField[0], inputField[1]);
  190.     FOR count := inputField[0] TO 79 DO WRITE(' ');
  191.     GotoXY(inputField[0], inputField[1]);
  192.     Beep;
  193.     READLN(Instr);
  194.     Instr := Upcase(Instr);
  195.   END;  {of GetInput}
  196.  
  197.  
  198. PROCEDURE GetInformation;
  199. { Ask DOS for information about the specified or default disk.
  200.   If we have an error return code from DOS we assume that the disk
  201.   specified was invalid. }
  202.   VAR
  203.     ValidDrive:  BOOLEAN;
  204.     InLetter:    CHAR;
  205.     Instr:       CHAR;
  206.   BEGIN
  207. { get current disk: MS-DOS function call 19h
  208.   information is returned in AL: 0 = A, 1 = B, etc.}
  209.  
  210.     WriteLog('Reading Disk Information');
  211.     Registers.ah := $19;
  212.     MSDos(Registers);
  213.     defaultDrive := Registers.al;
  214.  
  215.     IF paramcount = 0
  216.     THEN Instr   := CHR(65 + defaultDrive)
  217.     ELSE Instr   := COPY(paramstr(1), 1, 1);
  218.  
  219.     ValidDrive   := FALSE;
  220.     WITH Registers DO REPEAT
  221.       IF ORD(Instr) < 64 THEN Instr := CHR($FF);
  222.       DriveLetter := UpCase(Instr);
  223.       driveNumber := ORD(DriveLetter) - 64;
  224.       ah := $36;
  225.       dl := driveNumber;
  226.       MSDos(Registers);
  227.       IF ax <> $ffff
  228.       THEN BEGIN
  229.         driveNumber          := PRED(driveNumber);
  230.         freeClusters         := bx;
  231.         totalDataClusters    := dx;
  232.         sectorSize           := cx;
  233.         clusterSize          := ax;
  234.         firstFATsector       := 1;
  235.         count                := ( totalDataClusters + 2 ) * 3 ;
  236.         IF count MOD ( sectorSize ShR 1 ) = 0
  237.         THEN fatSize         := count DIV ( sectorSize ShL 1 )
  238.         ELSE fatSize         := count DIV ( sectorSize ShL 1 ) + 1;
  239.         firstDirectorySector := SUCC(fatSize ShL 1);
  240.         ValidDrive           := TRUE;
  241.       END
  242.       ELSE BEGIN
  243.         WriteWarning('Invalid driveletter, enter new letter!');
  244.         GetInput(Instr);
  245.         WriteWarning(' ');
  246.       END;
  247.     UNTIL ValidDrive;
  248.   END;  {of GetInformation}
  249.  
  250.  
  251. FUNCTION CarryFlag: BOOLEAN;
  252.   BEGIN
  253.     CarryFlag := ( Registers.Flags AND $01 ) <> 0 ;
  254.   END;  {of CarryFlag}
  255.  
  256.  
  257. PROCEDURE ResetDisk;
  258.   BEGIN
  259.     Registers.ah := $0D;
  260.     MSDos(Registers);
  261.   END;  {of ResetDisk}
  262.  
  263.  
  264. PROCEDURE ReadSectors(sectorNumber, numberOfSectors: INTEGER);
  265.   BEGIN
  266.     WITH Registers DO REPEAT
  267.       al := driveNumber;
  268.       cx := numberOfSectors;
  269.       dx := sectorNumber;
  270.       ds := Seg(DTAddress^);
  271.       bx := Ofs(DTAddress^);
  272.       Int2526($25);  {Toad Hall disk read}
  273.       IF CarryFlag THEN BEGIN
  274.         IF NOT AlreadyWritten
  275.         THEN BEGIN
  276.           WriteWarning('No data lost!');
  277.           WriteError('Disk read error, enter A (abort), R (retry)?');
  278.         END
  279.         ELSE BEGIN
  280.           WriteError('Probably loss of data!');
  281.           WriteDisaster('Disk read error A(bort), R(etry), I(gnore)?');
  282.         END;
  283.         Instr := '?';
  284.         REPEAT
  285.           Getinput(Instr);
  286.         UNTIL ( Instr IN ['A', 'R'] )
  287.         OR (( Instr = 'I' ) AND AlreadyWritten );
  288.         IF Instr = 'A'
  289.         THEN BEGIN
  290.           ClrScr;
  291.           HALT;
  292.         END
  293.         ELSE BEGIN
  294.           WriteError(' ');
  295.           WriteWarning(' ');
  296.           WriteDisaster(' ');
  297.           IF Instr = 'I' THEN flags := 0;
  298.        END; END;
  299.     UNTIL NOT CarryFlag;
  300.   END;  {of ReadSectors}
  301.  
  302.  
  303. PROCEDURE WriteSectors(sectorNumber, numberOfSectors: INTEGER);
  304.   BEGIN
  305.     WITH Registers DO REPEAT
  306.       al := driveNumber;
  307.       cx := numberOfSectors;
  308.       dx := sectorNumber;
  309.       ds := Seg(DTAddress^);
  310.       bx := Ofs(DTAddress^);
  311.       int2526($26);  {Toad Hall write}
  312.       IF CarryFlag
  313.       THEN BEGIN
  314.         IF NOT AlreadyWritten
  315.         THEN BEGIN
  316.           WriteWarning('No data lost!');
  317.           WriteError('Disk write error, enter A (abort), R (retry)?');
  318.         END
  319.         ELSE BEGIN
  320.           WriteError('Probably data lost!');
  321.           WriteDisaster('Disk write error A(bort), R(etry), I(gnore)?');
  322.         END;
  323.         REPEAT
  324.           Getinput(Instr);
  325.         UNTIL ( Instr IN ['A', 'R'] )
  326.         OR (( Instr = 'I' ) AND AlreadyWritten );
  327.         IF Instr = 'A' THEN BEGIN
  328.           ClrScr;
  329.           HALT;
  330.         END
  331.         ELSE BEGIN
  332.           WriteError(' ');
  333.           WriteWarning(' ');
  334.           WriteDisaster(' ');
  335.           IF Instr = 'I' THEN flags := 0;
  336.        END; END;
  337.     UNTIL NOT CarryFlag;
  338.     AlreadyWritten := TRUE;
  339.   END;  {of WriteSectors}
  340.  
  341.  
  342. PROCEDURE ReadCluster(clusterNumber: INTEGER);
  343.   VAR
  344.     sectorNumber: INTEGER;
  345.   BEGIN
  346. { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
  347.   sectorNumber is greater than 32767) we split the following formula:
  348.  
  349.      sectorNumber := clusterSize * ( clusterNumber - 2 ) + firstDataSector;
  350.  
  351.   Multiplication does not return a correct value when sectorNumber becomes
  352.   greater than maxint. Addition returns a word value (16 bits) that is the
  353.   correct sectorNumber if interpreted as a non-signed integer.
  354.   Since clusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
  355.   boot record layout) a power of 2, we may divide it by 2. }
  356.  
  357.     IF clusterSize < 2
  358.     THEN sectorNumber := clusterNumber - 2 + firstDataSector
  359.     ELSE sectorNumber := ( clusterSize ShR 1 ) * ( clusterNumber - 2 ) +
  360.                          ( clusterSize ShR 1 ) * ( clusterNumber - 2 ) +
  361.                          firstDataSector;
  362.     ReadSectors(sectorNumber, clusterSize);
  363.   END;  {of ReadCluster}
  364.  
  365.  
  366. PROCEDURE WriteCluster(clusterNumber: INTEGER);
  367.   VAR
  368.     sectorNumber: INTEGER;
  369.   BEGIN
  370. { To get around Turbo's maxint, (in case of fixed disks of 20 MB the largest
  371.   sectorNumber is greater than 32767) we split the following formula:
  372.  
  373.      sectorNumber := clusterSize * ( clusterNumber - 2 ) + firstDataSector;
  374.  
  375.   Multiplication does not return a correct value when sectorNumber becomes
  376.   greater than maxint. Addition returns a word value (16 bits) that is the
  377.   correct sectorNumber if interpreted as a non-signed integer.
  378.   Since clusterSize is ALWAYS (PC-DOS TECH REF: chap Device Drivers,
  379.   boot record layout) a power of 2, we may divide it by 2. }
  380.  
  381.     IF clusterSize < 2
  382.     THEN sectorNumber := clusterNumber - 2 + firstDataSector
  383.     ELSE sectorNumber := ( clusterSize ShR 1 ) * ( clusterNumber - 2 ) +
  384.                          ( clusterSize ShR 1 ) * ( clusterNumber - 2 ) +
  385.                          firstDataSector;
  386.     WriteSectors(sectorNumber, clusterSize);
  387.   END;  {of WriteCluster}
  388.  
  389.  
  390. PROCEDURE ReadBootSector(VAR DTArea: Buffer);
  391. { Read the bootsector from disk. Use the information we find in it
  392.   to set a number of variables in the communication block. If the
  393.   information in the bootsector is inconsistent with the story DOS
  394.   told us (GetInformation) we use the FAT identification byte for
  395.   the setting of the variables. This will probably only occur in
  396.   case we have a disk that was formatted under a pre DOS 2.0 version.}
  397.  
  398.   VAR
  399.     FATidentification: Byte;
  400.     Instr:             CHAR;
  401.     BootInfo:          Boot Absolute DTArea;
  402.  
  403.   BEGIN
  404.     WriteLog('Reading Bootsector.');
  405.     ReadSectors(0, 1);
  406.     IF        ( BootInfo.sectorSize      <> sectorSize )
  407.        OR     ( BootInfo.clusterSize     <> clusterSize )
  408.        OR     ( BootInfo.numberOfFats    =  0 )
  409.        OR     ( BootInfo.rootDirSize     =  0 )
  410.        OR     ( BootInfo.totalSectors    <  totalDataClusters * clusterSize )
  411.        OR NOT ( BootInfo.mediaDescriptor IN [$F0..$FF] )
  412.        OR     ( BootInfo.fatSize         <> fatSize )
  413.     THEN BEGIN
  414.       WriteWarning('Pre DOS 2.0 formatted disk, or incomplete bootsector.');
  415.       ReadSectors(firstFATsector, 1);
  416.       FATidentification := DTArea[0];
  417.       numberOfFats      := 2;
  418.       IF ( FATidentification = SINGLE8SECTOR )
  419.       OR ( FATidentification = SINGLE9SECTOR )
  420.       THEN rootDirSize  := 64
  421.       { Not Single Sided }
  422.       ELSE IF ( FATidentification = DUAL8SECTOR )
  423.       OR ( FATidentification = DUAL9SECTOR )
  424.       THEN rootDirSize  := 112
  425.       ELSE IF FATidentification = FIXEDDISK
  426.       THEN BEGIN  {Fixed Disk}
  427.         WriteError('Fixed Disk: cannot compute size.');
  428.         WriteDisaster('Press enter to return to DOS.');
  429.         GetInput(Instr);
  430.         ClrScr;
  431.         HALT;
  432.       END
  433.       ELSE BEGIN
  434.         WriteError('Unknown Disk Type (FAT id byte).');
  435.         WriteDisaster('Press enter to return to DOS.');
  436.         GetInput(Instr);
  437.         ClrScr;
  438.         HALT;
  439.       END;
  440.       firstDataSector   := numberOfFats * fatSize +
  441.                            rootDirSize  * 32 DIV sectorSize + 1;
  442.       media             := FATidentification;
  443.  
  444.     END
  445.     ELSE BEGIN
  446.       numberOfFats    := BootInfo.numberOfFats;
  447.       IF numberOfFats <> 2
  448.       THEN firstDirectorySector := SUCC(fatSize * numberOfFats);
  449.       rootDirSize     := BootInfo.rootDirSize;
  450.       firstDataSector := numberOfFats * fatSize +
  451.                          rootDirSize  * 32 DIV sectorSize + 1;
  452.       media           := BootInfo.mediaDescriptor;
  453.     END;
  454.   END;  {of ReadBootSector}
  455.  
  456.  
  457. PROCEDURE ReadFat(VAR unscrambledFAT: IntArray; VAR scrambledFAT: Buffer);
  458.   { Read and unscramble the FAT. Only the first FAT is processed.}
  459.   VAR
  460.     i, temp:     INTEGER;
  461.   BEGIN
  462.     WriteLog('Reading and unscrambling FAT.');
  463.     ReadSectors(firstFATsector, fatSize);
  464.     FOR i := 0 TO SUCC(totalDataClusters) DO BEGIN
  465.       Move( scrambledFAT[3 * i ShR 1], temp, 2);
  466.       IF ODD(i) THEN temp := temp ShR 4 ELSE temp := temp AND $0FFF;
  467.       unscrambledFAT[i] := temp;
  468.     END;
  469.   END;  {of ReadFat}
  470.  
  471.  
  472. PROCEDURE WriteFat(VAR unscrambledFAT: IntArray; VAR scrambledFAT: Buffer);
  473. { Write the FAT back to the disk. The FAT has to be scrambled before
  474.   writing. FAT entries on disk are 12 bits long. Because there are mostly
  475.   2 versions of the fat on disk, we write both fats simultaneously.}
  476.   VAR
  477.     i,
  478.     temp1,
  479.     temp2:          INTEGER;
  480.   BEGIN
  481.     WriteLog('Writing FAT.');
  482.     FOR i := 0 TO totalDataClusters + 1 DO BEGIN
  483.       temp1 := unscrambledFAT[i];
  484.       Move( scrambledFAT[3 * i ShR 1], temp2, 2);
  485.       IF ODD(i) THEN temp1 := (temp2 AND $000F) OR (temp1 ShL 4)
  486.                 ELSE temp1 := (temp2 AND $F000) OR temp1;
  487.       Move( temp1, scrambledFAT[3 * i ShR 1], 2);
  488.     END;
  489.     WriteSectors(firstFATsector, fatSize);
  490.     WriteSectors(firstFATsector + fatSize, fatSize);
  491.   END;  {of WriteFat}
  492.  
  493.  
  494. PROCEDURE ReadSubdirectory(VAR DTArea:      Buffer;
  495.                            VAR FATarea:     INTArray;
  496.                            VAR SubRoot:     DirectoryPointer;
  497.                            startingCluster: INTEGER);
  498. { Link subdirectory entries in a list. Build a tree (by calling this
  499.   routine recursively) if a subdirectory is found.}
  500.   VAR
  501.     clusterNumber,
  502.     dirIndex:      INTEGER;
  503.     Present:       DirectoryPointer;
  504.     EndSearch:     BOOLEAN;
  505.   BEGIN
  506.     subdirectories := SUCC(subdirectories);
  507.     clusterNumber  := startingCluster;
  508.     SubRoot        := NIL;
  509.     EndSearch      := FALSE;
  510.     REPEAT
  511.       ReadCluster(clusterNumber);
  512.       dirIndex     := 0;
  513.       REPEAT
  514.         IF NOT ( DTArea[dirIndex] IN [NEVERUSED, ERASED] )
  515.         THEN BEGIN
  516.           IF SubRoot = NIL THEN BEGIN
  517.             NEW(SubRoot);
  518.             Present := SubRoot;
  519.           END
  520.           ELSE BEGIN
  521.             NEW(Present^.Next);
  522.             Present := Present^.Next;
  523.           END;
  524.           Move(DTArea[dirIndex], Present^, 32);
  525.           IF ( Present^.attribute = SUBDIRECTORY ) AND
  526.              ( Present^.EntryName[0] <> '.' )
  527.           THEN BEGIN
  528.             ReadSubdirectory(DTArea, FATarea, Present^.SubDirectory,
  529.                              Present^.startingCluster);
  530.             Readcluster(clusterNumber);
  531.           END
  532.           ELSE BEGIN
  533.             Present^.SubDirectory := NIL;
  534.             IF Present^.Entryname[0] <> '.'
  535.             THEN BEGIN
  536.               totalFiles            := SUCC(totalFiles);
  537.               inSubdirectories      := SUCC(inSubdirectories);
  538.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  539.               THEN hiddenFiles := SUCC(hiddenFiles);
  540.             END;
  541.           END;
  542.         END
  543.         ELSE IF DTArea[dirIndex] = NEVERUSED
  544.         THEN EndSearch := TRUE;
  545.         dirIndex := dirIndex + 32;
  546.       UNTIL    ( dirIndex >= sectorSize * clusterSize)
  547.       OR ( EndSearch );
  548.       clusterNumber :=  FATarea[clusterNumber];
  549.     UNTIL ( clusterNumber >= ReservedMinimum ) OR EndSearch;
  550.     IF Present <> NIL THEN Present^.Next := NIL;
  551.   END;  {of ReadSubdirectory}
  552.  
  553.  
  554. PROCEDURE ReadDirectories(VAR DTArea: Buffer);
  555. { Read the Rootdirectory and whenever an entry for a subdirectory is
  556.   found call ReadSubdirectory. Link all directory entries dynamically
  557.   in a linked list. This list is actually a tree, because the lists
  558.   for subdirectories are linked to this list.}
  559.  
  560.   VAR
  561.     EndSearch:     BOOLEAN;
  562.     sectorNumber,
  563.     dirIndex:      INTEGER;
  564.     Present:       DirectoryPointer;
  565.   BEGIN
  566.     WriteLog('Reading Directory and Subdirectories.');
  567.     sectorNumber := firstDirectorySector;
  568.     RootDir      := NIL;
  569.     EndSearch    := FALSE;
  570.     REPEAT
  571.       dirIndex := 0;
  572.       ReadSectors(sectorNumber, 1);
  573.       REPEAT
  574.         IF NOT ( DTArea[dirIndex] IN [NEVERUSED, ERASED] )
  575.         THEN BEGIN
  576.           IF RootDir = NIL THEN BEGIN
  577.             NEW(RootDir);
  578.             Present := RootDir;
  579.           END
  580.           ELSE BEGIN
  581.             NEW(Present^.Next);
  582.             Present := Present^.Next;
  583.           END;
  584.           Move(DTArea[dirIndex], Present^, 32);
  585.           IF ( Present^.attribute = SUBDIRECTORY ) AND
  586.              ( Present^.EntryName[0] <> '.' )
  587.           THEN BEGIN
  588.             ReadSubdirectory(DTArea, OldFATaddress^,
  589.                              Present^.SubDirectory,
  590.                              Present^.startingCluster);
  591.             ReadSectors(sectorNumber, 1);
  592.           END
  593.           ELSE BEGIN
  594.             Present^.SubDirectory := NIL;
  595.             IF ( Present^.attribute    <> VOLUMELABEL ) AND
  596.                ( Present^.Entryname[0] <> '.'         )
  597.             THEN BEGIN
  598.               totalFiles            := SUCC(totalFiles);
  599.               inRootDirectory       := SUCC(inRootDirectory);
  600.               IF ( Present^.attribute AND HIDDENFILE ) <> 0
  601.               THEN hiddenFiles := SUCC(hiddenFiles);
  602.             END;
  603.           END;
  604.         END
  605.         ELSE IF DTArea[dirIndex] = NEVERUSED
  606.         THEN EndSearch := TRUE;
  607.         dirIndex := dirIndex + 32;
  608.       UNTIL ( dirIndex >= sectorSize ) OR EndSearch;
  609.       sectorNumber := SUCC(sectorNumber);
  610.     UNTIL ( sectorNumber = firstDataSector ) OR EndSearch;
  611.     IF Present <> NIL THEN Present^.Next := NIL;
  612.   END;  {of ReadDirectories}
  613.  
  614.  
  615. PROCEDURE RemakeFAT(VAR oldFATarea, newFATarea, permutation: IntArray;
  616.                     Root: DirectoryPointer; parent, thisDir: INTEGER);
  617.  
  618. { This procedure is called recursively.
  619.   From the OldFAT and the directory entries we construct a NewFAT and
  620.   a permutation. The permutation is used by DoIt for moving the
  621.   clusters. This routine is called one extra time for the chain of
  622.   the empty clusters by LinkFreeDataClusters.
  623.   Recursion is used whenever we find an entry for a subdirectory, in
  624.   the following way: first call this routine for the remainder of the
  625.   current directory, second for the subdirectory.
  626.   The function newFATindex is used to prevent accidental use of clusters
  627.   that were marked as bad or reserved clusters.}
  628.  
  629.   FUNCTION nextFATindex: INTEGER;
  630.     VAR
  631.       temp: INTEGER;
  632.     BEGIN
  633.       temp := SUCC(newFATindex);
  634.       WHILE ( oldFATarea[temp] >= ReservedMinimum ) AND
  635.             ( oldFATarea[temp] <= BadCluster      ) AND
  636.             ( temp <= SUCC(totalDataClusters) )
  637.       DO BEGIN
  638.         newFATarea[temp] := oldFATarea[temp];
  639.         temp             := SUCC(temp);
  640.       END;
  641.       nextFATindex := temp;
  642.     END;  {of nextFATindex}
  643.  
  644.   VAR
  645.     Present: DirectoryPointer;
  646.     Split:   BOOLEAN;
  647.     temp:    INTEGER;
  648.   BEGIN
  649.     IF newFATindex = 1 THEN newFATindex := nextFATindex;
  650.     Present := Root;
  651.     Split   := FALSE;
  652.     WHILE ( Present <> NIL ) AND NOT Split DO BEGIN
  653.       IF ( Present^.attribute <> VOLUMELABEL ) AND
  654.          ( Present^.startingCluster <> 0 ) AND
  655.          ( Present^.Entryname[0] <> '.')
  656.       THEN BEGIN
  657.         IF Present^.SubDirectory <> NIL
  658.         THEN BEGIN
  659.           Split := TRUE;
  660.           RemakeFAT(oldFATarea, newFATarea, permutation,
  661.                     Present^.Next, parent, thisDir);
  662.         END;
  663.         oldFATindex                 := Present^.startingCluster;
  664.         Present^.newStartingCluster := newFATindex;
  665.         permutation[newFATindex]    := oldFATindex;
  666.         WHILE oldFATarea[oldFATindex] < LastMinimum DO BEGIN
  667.           temp                     := nextFATindex;
  668.           newFATarea[newFATindex]  := temp;
  669.           newFATindex              := temp;
  670.           oldFATindex              := oldFATarea[oldFATindex];
  671.           permutation[newFATindex] := oldFATindex;
  672.         END;
  673.         newFATarea[newFATindex] := lastNormal;
  674.         newFATindex             := nextFATindex;
  675.         IF Split THEN
  676.           RemakeFAT(oldFATarea, newFATarea, permutation,
  677.                     Present^.SubDirectory, thisDir,
  678.                     Present^.newStartingCluster);
  679.      END
  680.      ELSE BEGIN
  681.        IF ( Present^.EntryName[0] = '.'  ) AND
  682.           ( Present^.EntryName[1] = '.'  )
  683.        THEN Present^.newStartingCluster := parent
  684.        ELSE IF Present^.EntryName[0] = '.'
  685.        THEN Present^.newStartingCluster := thisDir
  686.        ELSE BEGIN
  687.          Present^.newStartingCluster := 0;
  688.          IF Present^.attribute = VOLUMELABEL
  689.          THEN FOR count := 0 TO 10 DO
  690.            DiskLabel[count] := Present^.EntryName[count];
  691.        END;
  692.      END;
  693.      Present := Present^.Next;
  694.    END;
  695.  END;  {of RemakeFAT}
  696.  
  697.  
  698. PROCEDURE LinkFreeClusters(VAR oldFATarea, newFATarea: IntArray);
  699. { Link Free clusters in a chain, pointed to by Empty^.
  700.   Use RemakeFAT to fill permutation, but clean NewFAT after
  701.   this. This procedure will ensure that permutation is a
  702.   proper permutation, without double entries which might
  703.   cause DoIt to loop indefinitely or destroy our disk. }
  704.  
  705.   VAR
  706.     count,
  707.     next,
  708.     previous: INTEGER;
  709.     Empty:    DirectoryPointer;
  710.  
  711.   BEGIN
  712.     NEW(Empty);
  713.     Empty^.next            := NIL;
  714.     Empty^.subDirectory    := NIL;
  715.     Empty^.Entryname[0]    := 'X';
  716.     Empty^.attribute       := HIDDENFILE;
  717.     Empty^.startingCluster := 0;
  718.     count                  := 2;
  719.     WHILE ( count <= totalDataClusters + 1 ) AND
  720.           ( oldFATarea[count] <> 0         )
  721.     DO count := SUCC(count);
  722.     IF count <= SUCC(totalDataClusters)
  723.     THEN BEGIN
  724.       Empty^.startingCluster := count;
  725.       previous               := count;
  726.       WHILE count < SUCC(totalDataClusters)
  727.       DO BEGIN
  728.         count := SUCC(count);
  729.         IF oldFATarea[count] = 0
  730.         THEN BEGIN
  731.           oldFATarea[previous] := count;
  732.           previous             := count;
  733.         END;
  734.       END;
  735.       oldFATarea[previous] := lastNormal;
  736.     END;
  737.     IF Empty^.startingCluster <> 0
  738.     THEN BEGIN
  739.       RemakeFAT(oldFATarea, newFATarea,
  740.                 PermutationAddress^, Empty, 0, 0);
  741.       Next := Empty^.newStartingCluster;
  742.       WHILE next <> lastNormal
  743.       DO BEGIN
  744.         previous             := next;
  745.         next                 := newFATarea[previous];
  746.         newFATarea[previous] := 0;
  747.       END;
  748.     END;
  749.   END;  {of LinkFreeClusters}
  750.  
  751.  
  752. PROCEDURE  WriteSubdirectory(VAR DTArea: Buffer; VAR oldFATarea: IntArray;
  753.                              Root: DirectoryPointer; start: INTEGER);
  754.  
  755. { Write subdirectories back to disk. Erased entries are removed
  756.   from the subdirectories. The subdirectories are written to their
  757.   old locations, because DoIt will take care of moving the clusters
  758.   to their new places. No effort is done to truncate a subdirectory
  759.   which would be longer than needed after removal of erased entries.
  760.   We will however set all remaining entries to 'NEVERUSED'.
  761.   This routine is used recursively.}
  762.  
  763.   VAR
  764.     start1,
  765.     clusterNumber,
  766.     dirIndex:      INTEGER;
  767.     Present:       DirectoryPointer;
  768.  
  769.   BEGIN
  770.     Present       := Root;
  771.     clusterNumber := start;
  772.     WHILE Present <> NIL
  773.     DO BEGIN
  774.       dirIndex := 0;
  775.       FillChar(DTArea, clusterSize * sectorSize, $00);
  776.       REPEAT
  777.         start1 := Present^.startingCluster;
  778.         Present^.startingCluster := Present^.newStartingCluster;
  779.         Move(Present^, DTArea[dirIndex], 32);
  780.         IF ( Present^.attribute = SUBDIRECTORY ) AND
  781.            ( Present^.EntryName[0] <> '.' )
  782.         THEN BEGIN
  783.           WriteCluster(clusterNumber);
  784.           WriteSubdirectory(DTArea, oldFATarea,
  785.                             Present^.SubDirectory, start1);
  786.           ReadCluster(clusterNumber);
  787.         END;
  788.         Present  := Present^.Next;
  789.         dirIndex := dirIndex + 32;
  790.       UNTIL ( dirIndex >= clusterSize * sectorSize ) OR ( Present = NIL );
  791.       WriteCluster(clusterNumber);
  792.       clusterNumber := oldFATarea[clusterNumber];
  793.     END;
  794.     IF clusterNumber < LastMinimum
  795.     THEN BEGIN
  796.       FillChar(DTArea, sectorSize * clusterSize, $00);
  797.       WHILE clusterNumber < LastMinimum
  798.       DO BEGIN
  799.         WriteCluster(clusterNumber);
  800.         clusterNumber := oldFATarea[clusterNumber];
  801.       END;
  802.     END;
  803.   END;  {of WriteSubdirectories}
  804.  
  805.  
  806. PROCEDURE WriteDirectories(VAR DTArea: Buffer);
  807.  
  808. { Write rootdirectory back to disk. Erased entries are removed
  809.   from the directory. When we find a subdirectory entry, we first
  810.   process this subdirectory by calling WriteSubdirectories,
  811.   before we proceed with the root. All entries that are no in use
  812.   are set to 'NEVERUSED'.}
  813.  
  814.   VAR
  815.     start,
  816.     sectorNumber,
  817.     dirIndex:      INTEGER;
  818.     Present:       DirectoryPointer;
  819.   BEGIN
  820.     WriteLog('Writing new Directory and Subdirectories.');
  821.     sectorNumber := firstDirectorySector;
  822.     Present      := RootDir;
  823.     WHILE Present <> NIL
  824.     DO BEGIN
  825.       dirIndex := 0;
  826.       FillChar(DTArea, sectorSize, $00);
  827.       REPEAT
  828.         start := Present^.startingCluster;
  829.         Present^.startingCluster := Present^.newStartingCluster;
  830.         Move(Present^, DTArea[dirIndex], 32);
  831.         IF ( Present^.attribute = SUBDIRECTORY ) AND
  832.            ( Present^.EntryName[0] <> '.' )
  833.         THEN BEGIN
  834.           WriteSectors(sectorNumber, 1);
  835.           WriteSubdirectory(DTArea, OldFATaddress^,
  836.                             Present^.SubDirectory, start);
  837.           ReadSectors(sectorNumber, 1);
  838.         END;
  839.         Present  := Present^.Next;
  840.         dirIndex := dirIndex + 32;
  841.       UNTIL ( dirIndex >= sectorSize ) OR ( Present = NIL );
  842.       WriteSectors(sectorNumber, 1);
  843.       sectorNumber := SUCC(sectorNumber);
  844.     END;
  845.     IF sectorNumber < firstDataSector
  846.     THEN BEGIN
  847.       FillChar(DTArea, sectorSize, $00);
  848.       WHILE sectorNumber < firstDataSector
  849.       DO BEGIN
  850.         WriteSectors(sectorNumber, 1);
  851.         sectorNumber := SUCC(sectorNumber);
  852.       END;
  853.     END;
  854.   END;  {of WriteDirectories}
  855.  
  856.  
  857. PROCEDURE DoIt(VAR permutation: IntArray; VAR DTArea, SaveArea: Buffer);
  858.  
  859. { DoIt. This routine performs the actual reformating of the disk.
  860.   The array permutation contains in every location  [i] (starting
  861.   from 2) which cluster has to be moved to cluster location i.
  862.   Because we have a real permutation, this permutation can be
  863.   parsed into a number of cyclical permutations. We start at the
  864.   first cyclic permutation that is not identity. We save the first
  865.   cluster of this cyclical permutation, proceed through the cyclical
  866.   permutation, moving one cluster at a time, until we finish the
  867.   cycle. We than write the saved cluster to disk.}
  868.  
  869.   VAR
  870.     prior,
  871.     next,
  872.     lastStart: INTEGER;
  873.   BEGIN
  874.     WriteLog('Reformatting......');
  875.     lastStart := 2;
  876.     WHILE lastStart <= SUCC(totalDataClusters)
  877.     DO BEGIN
  878.       IF lastStart = permutation[lastStart]
  879.       THEN lastStart := SUCC(lastStart)
  880.       ELSE BEGIN
  881.         ReadCluster(lastStart);
  882.         Move(DTArea, SaveArea, sectorSize * clusterSize);
  883.         prior := lastStart;
  884.         next  := permutation[lastStart];
  885.         REPEAT
  886.           ReadCluster(next);
  887.           WriteCluster(prior);
  888.           movedClusters      := SUCC(movedClusters);
  889.           GotoXY(movedField[0], movedField[1]);
  890.           WRITE(movedClusters:10);
  891.           permutation[prior] := prior;
  892.           prior              := next;
  893.           next               := permutation[next];
  894.         UNTIL next = lastStart;
  895.         Move(SaveArea, DTArea, sectorSize * clusterSize);
  896.         WriteCluster(prior);
  897.         movedClusters      := SUCC(movedClusters);
  898.         GotoXY(movedField[0], movedField[1]);
  899.         WRITE(movedClusters:10);
  900.         permutation[prior] := prior;
  901.       END;
  902.     END;
  903.     WriteLog(' ');
  904.   END;  {of Doit}
  905.  
  906.  
  907. PROCEDURE InitScreen;
  908.   VAR
  909.     row,
  910.     column: INTEGER;
  911.     S : STRING[80];
  912.   BEGIN
  913.     NormVideo;
  914.     ClrScr;
  915.     S[0] := #77;             {force length}
  916.     FillChar(S[1],77,#205);  {horizontal line}
  917.     row := 2;
  918.     WRITE(#201, S, #187);
  919.     WRITE(#186); GotoXY(80, row);  WRITE(#186);
  920.     GotoXY(17, row); WRITE('REFORMAT: an original JOS disk tool. Ver: 1.21TH');
  921.     FillChar(S[1],77,#196);  {horizontal line}
  922.  
  923.     row := SUCC(row); GotoXY(1, row);
  924.     WRITE(#199, S, #182);
  925.     FOR row := 4 TO 15 DO BEGIN
  926.       WRITE(#186); GotoXY(80, row);  WRITE(#186);
  927.     END;
  928.     WRITE(#199, S, #182);
  929.     WRITE(#186);  GotoXY(80, 17);  WRITE(#186);
  930.     WRITE(#199, S, #182);
  931.     FOR row := 19 TO 23 DO BEGIN
  932.       WRITE(#186);  GotoXY(80, row);  WRITE(#186);
  933.     END;
  934.     WRITE(#200, S, #188);
  935.     GotoXY(05, 19); WRITE('User Input Field :');
  936.     GotoXY(05, 20); WRITE('Activity Logging :');
  937.     GotoXY(05, 21); WRITE('Warning  Messages:');
  938.     GotoXY(05, 22); WRITE('Error    Messages:');
  939.     GotoXY(05, 23); WRITE('Disaster Messages:');
  940.     inputField[0]    := 24;
  941.     inputField[1]    := 19;
  942.     logField[0]      := 24;
  943.     logField[1]      := 20;
  944.     warningField[0]  := 24;
  945.     warningField[1]  := 21;
  946.     errorField[0]    := 24;
  947.     errorField[1]    := 22;
  948.     disasterField[0] := 24;
  949.     disasterField[1] := 23;
  950.   END;  {of InitScreen}
  951.  
  952.  
  953. PROCEDURE CheckSubdirectory(VAR FAT: IntArray;
  954.                             Root: DirectoryPointer; parent, thisDir: INTEGER);
  955.  
  956. { This procedure is called recursively.
  957.   The SubDirectories are checked here. No attempt is made
  958.   to correct any errors found. If any errors are found, a message
  959.   is issued and the program stops. The users must first run CHKDSK from
  960.   DOS before we accept the disk. }
  961.  
  962.   VAR
  963.     Present: DirectoryPointer;
  964.     prior,
  965.     next:    INTEGER;
  966.   BEGIN
  967.     Present := Root;
  968.     WHILE ( Present <> NIL ) AND ( errors = 0 ) BEGIN
  969.       IF ( Present^.attribute <> VOLUMELABEL ) AND
  970.          ( Present^.startingCluster <> 0 ) AND
  971.          ( Present^.Entryname[0] <> '.')
  972.       THEN BEGIN
  973.         next  := Present^.startingCluster;
  974.         count := 0;
  975.         REPEAT;
  976.           IF ( next > SUCC(totalDataClusters) )
  977.           OR ( next < 1 )
  978.           THEN errors := SUCC(errors)
  979.           ELSE BEGIN
  980.             prior      := next;
  981.             next       := FAT[prior];
  982.             FAT[prior] := 0;
  983.             IF next <> SUCC(prior) THEN count := SUCC(count);
  984.           END;
  985.         UNTIL ( next >= LastMinimum ) OR ( errors <> 0 );
  986.         IF count > 1 THEN nonContiguousFiles := SUCC(nonContiguousFiles);
  987.         IF Present^.SubDirectory <> NIL
  988.         THEN CheckSubdirectory(FAT, Present^.SubDirectory,
  989.                                thisDir, Present^.startingCluster);
  990.       END
  991.       ELSE BEGIN
  992.         IF ( Present^.EntryName[0] = '.' ) AND
  993.            ( Present^.EntryName[1] = '.' )
  994.         THEN IF Present^.startingCluster <> parent
  995.              THEN errors := SUCC(errors)
  996.              ELSE
  997.         ELSE IF Present^.EntryName[0] = '.'
  998.         THEN IF Present^.startingCluster <> thisDir
  999.              THEN errors := SUCC(errors)
  1000.              ELSE
  1001.         ELSE IF Present^.startingCluster <> 0
  1002.         THEN errors := SUCC(errors);
  1003.       END;
  1004.       Present := Present^.next;
  1005.     END;
  1006.   END;  {of CheckSubdirectory}
  1007.  
  1008.  
  1009. PROCEDURE CheckDisk(VAR FAT: IntArray; Root: DirectoryPointer);
  1010.  
  1011. { The FAT and the Directories are checked here. No attempt is made
  1012.   to correct any errors found. If any errors are found, a message
  1013.   is issued and the program stops. The users must first run CHKDSK from
  1014.   DOS before we accept the disk.                                        }
  1015.  
  1016.   BEGIN
  1017.     WriteLog('Checking FAT....');
  1018.     CheckSubdirectory(FAT, Root, 0, 0);
  1019.     FOR count := 2 TO totalDataClusters + 1 DO
  1020.       IF ( FAT[count] <> 0 ) AND
  1021.          ( ( FAT[count] < ReservedMinimum ) OR
  1022.            ( FAT[count] > BadCluster      ) )
  1023.       THEN lostClusters := SUCC(lostClusters);
  1024.     IF errors <> 0
  1025.     THEN BEGIN
  1026.       WriteError('Crosslinked clusters found. Run CHKDSK first.');
  1027.       WriteWarning('Press Enter to return to DOS.');
  1028.       GetInput(Instr);
  1029.       ClrScr;
  1030.       HALT;
  1031.     END
  1032.     ELSE IF lostClusters <> 0
  1033.     THEN BEGIN
  1034.       WriteError('Lost clusters found. Run CHKDSK first.');
  1035.       WriteWarning('Press Enter to return to DOS.');
  1036.       GetInput(Instr);
  1037.       ClrScr;
  1038.       HALT;
  1039.     END;
  1040.   END;  {of CheckDisk}
  1041.  
  1042.  
  1043. PROCEDURE CountClustersToMove(VAR permutation: IntArray);
  1044.   BEGIN
  1045.     FOR count := 2 TO SUCC(totalDataClusters)
  1046.     DO IF permutation[count] <> count
  1047.     THEN clustersToMove := SUCC(clustersToMove);
  1048.   END;  {of CountClustersToMove}
  1049.  
  1050.  
  1051. PROCEDURE InitCounters;
  1052.   BEGIN
  1053.     oldFATindex         := 0;
  1054.     newFATindex         := 1;
  1055.     errors              := 0;
  1056.     lostClusters        := 0;
  1057.     totalFiles          := 0;
  1058.     hiddenFiles         := 0;
  1059.     inRootDirectory     := 0;
  1060.     inSubdirectories    := 0;
  1061.     nonContiguousFiles  := 0;
  1062.     subdirectories      := 0;
  1063.     movedClusters       := 0;
  1064.     clustersToMove      := 0;
  1065.     count               := 0;
  1066.     AlreadyWritten      := FALSE;
  1067.     DiskLabel           := '           ';
  1068.   END;  {of InitCounters}
  1069.  
  1070.  
  1071. PROCEDURE WriteStatistics;
  1072.   VAR
  1073.     row: INTEGER;
  1074.   BEGIN
  1075.     IF nonContiguousFiles = 0 THEN clustersToMove := 0;
  1076.     row := 5;
  1077.     IF DiskLabel <> '           '
  1078.     THEN BEGIN
  1079.       GotoXY(18, row); WRITE('Volume Label is . . . . . :   ', DiskLabel);
  1080.       row := SUCC(row);
  1081.     END;
  1082.     GotoXY(18, row); WRITE(     'Total # of files. . . . . :', totalFiles:10);
  1083.     IF hiddenFiles <> 0
  1084.     THEN             WRITE(' (hidden:', hiddenFiles:3,')');
  1085.     row := SUCC(row);
  1086.     IF subdirectories = 0
  1087.     THEN BEGIN
  1088.       GotoXY(18, row); WRITE('All files in Rootdirectory.');
  1089.     END
  1090.     ELSE BEGIN
  1091.       GotoXY(18, row); WRITE('  in Root directory . . . :',
  1092.                                 inRootDirectory:10);
  1093.       row := SUCC(row);
  1094.       GotoXY(18, row); WRITE('  in ', subdirectories:3, ' Subdirectories . :',
  1095.                                 inSubDirectories:10);
  1096.     END;
  1097.     row := SUCC(row);
  1098.     GotoXY(18, row);      WRITE('# of noncontiguous files. :',
  1099.                                  nonContiguousFiles:10);
  1100.     row := SUCC(row);
  1101.     GotoXY(18, row);      WRITE('# of clusters to be moved :',
  1102.                                  clustersToMove:10);
  1103.     row := SUCC(row);
  1104.     GotoXY(18, row);      WRITE('# of clusters moved . . . :',
  1105.                                  movedClusters:10);
  1106.     movedField[0] := 45;
  1107.     movedField[1] := row;
  1108.     row := row + 2;
  1109.     GotoXY(05, row);      WRITE('clusterSize . . :', clusterSize:06,
  1110.                                 ' sectors.');
  1111.     GotoXY(45, row);      WRITE('sectorSize. . . :', sectorSize:06,
  1112.                                 ' bytes.');
  1113.     row := SUCC(row);
  1114.     GotoXY(05, row);      WRITE('Total data space:', totalDataClusters:6,
  1115.                                 ' clusters.');
  1116.     GotoXY(45, row);      WRITE('DOS space . . . :', firstDataSector:6,
  1117.                                 ' sectors.');
  1118.     row := SUCC(row);
  1119.     GotoXY(05, row);      WRITE('Free data space :', freeClusters:6,
  1120.                                 ' clusters.');
  1121.     GotoXY(45, row);      WRITE('Disk type . . . :');
  1122.     CASE media OF
  1123.       $F8:   { FIXEDDISK    }   WRITE(' Fixed Disk');
  1124.       $FE:   { SINGLE8SECTOR}   WRITE(' 1 sided / 8 sect');
  1125.       $FF:   { DUAL8SECTOR  }   WRITE(' 2 sided / 8 sect');
  1126.       $FC:   { SINGLE9SECTOR}   WRITE(' 1 sided / 9 sect');
  1127.       $FD:   { DUAL9SECTOR  }   WRITE(' 2 sided / 9 sect');
  1128.     END;  {case}
  1129.   END;  {of WriteStatistics}
  1130.  
  1131.  
  1132. PROCEDURE WriteDoc;
  1133.   BEGIN
  1134.     ClrScr;
  1135.     WRITELN;
  1136.     WRITELN('           REFORMAT: an original JOS disk tool.');
  1137.     WRITELN;
  1138.     WRITELN('                Public Domain Software.');
  1139.     WRITELN;
  1140.     WRITELN('Makes all files  on a floppy or  fixed disk contiguous  again,');
  1141.     WRITELN('improving  disk  performance  dramatically. Either fixed disks');
  1142.     WRITELN('or diskettes.                               Requires DOS 2.xx.');
  1143.     WRITELN('Register at the following address to be on my mailing list for');
  1144.     WRITELN('updates:');
  1145.     WRITELN;
  1146.     WRITELN('                   Jos Wennmacker');
  1147.     WRITELN('                   Universitair Rekencentrum');
  1148.     WRITELN('                   Geert Grooteplein Zuid 41');
  1149.     WRITELN('                   NL-6525 GA Nijmegen');
  1150.     WRITELN('                   The Netherlands');
  1151.     WRITELN;
  1152.     WRITELN;
  1153.     WRITELN;
  1154.     WRITELN('Also comments, bugs etc are expected at one of these addresses.');
  1155.     WRITELN;
  1156.     WRITELN('       Press enter to see next page');
  1157.     READLN;
  1158.     ClrScr;
  1159.     WRITELN;
  1160.     WRITELN('           REFORMAT: an original JOS disk tool.');
  1161.     WRITELN('                Version 1.21TH, 860502');
  1162.     WRITELN('                Public Domain Software.');
  1163.     WRITELN;
  1164.     WRITELN;
  1165.     WRITELN('Use: Reformat [d:]');
  1166.     WRITELN;
  1167.     WRITELN('where d: is an optional driveletter. Ommiting d: will select the');
  1168.     WRITELN('default  drive.  This  program  works for  both fixed  disks and');
  1169.     WRITELN('floppies.');
  1170.     WRITELN;
  1171.     WRITELN('*  WARNING * WARNING * WARNING * WARNING * WARNING * WARNING  **');
  1172.     WRITELN;
  1173.     WRITELN('NEVER use  this  program  on a disk that  contains * PROTECTED *');
  1174.     WRITELN('software. You might find these  programs turned into an  illegal');
  1175.     WRITELN('copy or even end up with a scrambled disk!!!!!!');
  1176.     WRITELN('Always *UNINSTALL* this kind of software before using REFORMAT!!');
  1177.     WRITELN('The program will  prompt you to confirm  this in case of a fixed');
  1178.     WRITELN('disk.');
  1179.     WRITELN;
  1180.   END;  {of WriteDoc}
  1181.  
  1182.  
  1183. BEGIN  {main}
  1184.   IF paramcount <> 0
  1185.   THEN IF COPY(paramstr(1), 1, 1) = '?'
  1186.        THEN BEGIN
  1187.             WriteDoc;
  1188.             HALT;
  1189.        END
  1190.        ELSE BEGIN
  1191.             IF ( paramcount > 1 )
  1192.             OR ( LENGTH(paramstr(1)) > 2 )
  1193.             OR ( (LENGTH(paramstr(1)) = 2 ) AND
  1194.                  (COPY(paramstr(1), 2, 1) <> ':') )
  1195.             THEN BEGIN
  1196.                  WRITELN;
  1197.                  WRITELN('Invalid parameter: REFORMAT [d:] or ?.');
  1198.                  HALT;
  1199.        END; END;
  1200.   InitCounters;
  1201.   InitScreen;
  1202.   GetInformation;
  1203.   IF clusterSize < fatSize
  1204.   THEN GetMem(DTAddress, sectorSize * fatSize)
  1205.   ELSE GetMem(DTAddress, sectorSize * clusterSize);
  1206.   GetMem(SAVEaddress, sectorSize * clusterSize);
  1207.   GetMem(PermutationAddress, totalDataClusters ShL 1 + 4);
  1208.   GetMem(OldFATaddress, totalDataClusters ShL 1 + 4);
  1209.   GetMem(NewFATaddress, totalDataClusters ShL 1 + 4);
  1210.   ReadBootSector(DTAddress^);
  1211.   ReadFat(OldFATaddress^, DTAddress^);
  1212.   ReadDirectories(DTAddress^);
  1213.   Move(OldFATaddress^, NewFATaddress^, totalDataClusters ShL 1 + 4);
  1214.   CheckDisk(NewFATaddress^, RootDir);
  1215.   FillChar(NewFATaddress^, totalDataClusters ShL 1 + 4, 0);
  1216.   FOR count := 0 TO SUCC(totalDataClusters) DO
  1217.   PermutationAddress^[count] := count;
  1218.   Move(OldFATaddress^, NewFATaddress^, 4);
  1219.   RemakeFAT(OldFATaddress^, NewFATaddress^,
  1220.             PermutationAddress^, RootDir, 0, 0);
  1221.   LinkFreeClusters(OldFATaddress^, NewFATaddress^);
  1222.   CountClustersToMove(PermutationAddress^);
  1223.   WriteStatistics;
  1224.   IF nonContiguousFiles <> 0
  1225.   THEN BEGIN
  1226.     IF media = FIXEDDISK
  1227.     THEN BEGIN
  1228.       GotoXY(05, 17);
  1229.       WRITE ('Fixed disk: did you uninstall all protected software? ',
  1230.              'Continue (Y/N)?');
  1231.       Instr := 'Q';
  1232.       WHILE NOT ( Instr IN ['Y', 'N'] )
  1233.       DO GetInput(Instr);
  1234.       IF Instr = 'N' THEN BEGIN
  1235.         WriteWarning('Press Enter to return to DOS.');
  1236.         GetInput(Instr);
  1237.         ClrScr;
  1238.         HALT;
  1239.       END;
  1240.     END;
  1241.     ResetDisk;
  1242.     WriteFAT(NewFATaddress^, DTAddress^);
  1243.     WriteDirectories(DTAddress^);
  1244.     DoIt(PermutationAddress^, DTAddress^, SAVEaddress^);
  1245.     ResetDisk;
  1246.     WriteLog('Done ! Press Enter-Key to return to DOS.');
  1247.   END
  1248.   ELSE BEGIN
  1249.     WriteWarning('All files are contiguous. Nothing to be done!');
  1250.     WriteLog('Press Enter-Key to return to DOS.');
  1251.   END;
  1252.   GetInput(Anything);
  1253.   ClrScr;
  1254. END.
  1255.