home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / utils / dskutl / swp-ms10.ark / TRAMDF.IF3 < prev    next >
Text File  |  1989-09-27  |  22KB  |  573 lines

  1.  
  2. {* -------------------------------------------------------------------------
  3.  *                    M S - D O S   F I L E   S Y S T E M
  4.  * ------------------------------------------------------------------------- *}
  5.  
  6. {*
  7.  * The MS-DOS 'file system' contains the basic directory manipulations, that
  8.  * is searching through the directory, and the basic file manipulation, such
  9.  * as writing, reading and deleting an MS-DOS file.
  10.  *}
  11.  
  12. function GetMsdosFileName : FullFileNames ;
  13. {*
  14.  * Build the file name with the '.' separator before the extension field.
  15.  *}
  16. begin
  17.    GetMsdosFileName:= Copy( MsdosFcb^.FileName, 1, 8 ) + '.' +
  18.                       Copy( MsdosFcb^.FileName, 9, 3 ) ;
  19. end ;  { of GetMsdosFileName }
  20.  
  21. procedure GetNextDirEntry( var Status: EntryTypes ) ;
  22. {*
  23.  * Locate the next file entry in the (sub)directory of an MS-DOS file
  24.  * system.  The entry of the directory is made accessible via MsdosFcb.
  25.  * In case of an error, an EndOfDirectory status will be returned.
  26.  *}
  27. begin
  28.    Status:= EndOfDirectory ;  { Set default status }
  29.    if DirectorySearchPos=AfterLastEntry then  Exit ;
  30. {*
  31.  * If at the beginning of a search through the (sub)directory, preset the
  32.  * variables such that the first sector of the (sub)directory will be read.
  33.  *}
  34.    if DirectorySearchPos=BeforeFirstEntry then
  35.     begin
  36.      DirectoryCluster:= DirectoryStartCls ;
  37.      if DirectoryStartCls=0 then  { search the root directory }
  38.       begin
  39.        DirectoryCluster :=               $FFF ;  { For EndOfDirectory test }
  40.        DirectoryStartSct:= RootDirectoryStart ;
  41.        DirectorySize    := RootDirectorySize  ;
  42.       end
  43.      else { if not in root directory then }
  44.       begin
  45.        DirectoryStartSct:= FirstDataSector +
  46.                            (DirectoryCluster-2)*SectorsPerCluster ;
  47.        DirectorySize    := SectorsPerCluster ;
  48.       end ;  { of else }
  49.      DirectoryOrdinal  := FcbsPerSector ;
  50.      DirectorySector   := Pred( DirectoryStartSct ) ;
  51.      DirectorySearchPos:= InSubDirectory ;
  52.     end ;  { of if }
  53. {*
  54.  * Fetch the next entry from the (sub) directory.  First of all, determine
  55.  * the ordinal, the sector and the cluster of the next directory entry.
  56.  *}
  57.    DirectoryOrdinal:= Succ( DirectoryOrdinal ) ;
  58.    if DirectoryOrdinal>=FcbsPerSector then
  59.     begin
  60.      DirectorySector:= Succ( DirectorySector ) ;
  61.      if DirectorySector>=(DirectoryStartSct+DirectorySize) then
  62. {*
  63.  * The end of a set of directory sectors has been reached. If in the root
  64.  * directory, this means that the end of the (root)directory is reached.
  65.  * However, if in a subdirectory, there might be another cluster with
  66.  * directory information. The availability of additional information is
  67.  * set in variable DirectoryCluster.
  68.  *}
  69.       begin
  70.        if DirectoryStartCls<>0 then
  71.          DirectoryCluster:= GetFatEntry( DirectoryCluster ) ;
  72.        if DirectoryCluster>ClustersPerDisk then
  73.         begin
  74.          DirectorySearchPos:= AfterLastEntry ;
  75.          Exit ;
  76.         end
  77.        else  { there is another cluster in the subdirectory }
  78.         begin
  79.          DirectoryStartSct:= FirstDataSector +
  80.                              (DirectoryCluster-2)*SectorsPerCluster ;
  81.          DirectorySize    := SectorsPerCluster ;
  82.         end ;  { of else }
  83.       end ;  { of if }
  84. {*
  85.  * The address of the next (sub)directory sector is determined.  Go read it.
  86.  *}
  87.      ReadSector( DirectorySector, Addr(DirBuffer) ) ;
  88.      if ErrorDetected then
  89.       begin
  90.        BuildErrorTrace( 'GetNDE_' ) ;
  91.        Exit ;
  92.       end ;  { of if }
  93.      DirectoryOrdinal:= 0 ;
  94.     end ;  { of if }
  95. {*
  96.  * The sector with the next directory entry is in the directory buffer.
  97.  * Determine its address and its attributes (status).
  98.  *}
  99.    MsdosFcb:= Ptr( Addr(DirBuffer) + BytesPerFcb*DirectoryOrdinal ) ;
  100.    case MsdosFcb^.FileName[1] of
  101.      #$00 : Status:= UnusedEntry ;
  102.      #$E5 ,
  103.      #$F6 : Status:= FreeEntry ;
  104.    else
  105.      Status:= FileNameEntry ;
  106.      if SubDirectory in MsdosFcb^.Attribute then
  107.        Status:= SubDirectoryNameEntry ;
  108.      if Volume in MsdosFcb^.Attribute then
  109.        Status:= VolumeNameEntry ;
  110.    end ;  { of cases}
  111. end ;  { of GetNextDirEntry }
  112.  
  113. function LocateMsdosDirectory: Boolean ;
  114. {*
  115.  * Search the current MS-DOS (sub)directory for a subdirectory. The name of
  116.  * the subdirectory is supplied in the current file entry.  If found,
  117.  * the returned value is True and the global variable MsdosFcb is set.  In
  118.  * case of an error, a False value will be returned.
  119.  *}
  120. var
  121.    EntryType: EntryTypes ;  { Type of located file-entry }
  122.    Found    :    Boolean ;  { Result of this function so far }
  123. begin
  124.    DirectorySearchPos:= BeforeFirstEntry ;
  125.    Found:= False ;  { Preset result of search }
  126.    repeat
  127.      GetNextDirEntry( EntryType ) ;
  128.      if EntryType=SubDirectoryNameEntry then
  129.        Found:= ( FileEntry^.Name = MsdosFcb^.FileName ) ;
  130.    until Found or (EntryType in [UnusedEntry,EndOfDirectory]) ;
  131.    if ErrorDetected then
  132.      BuildErrorTrace( 'LocMD_' ) ;
  133.    LocateMsdosDirectory:= Found ;
  134. end ;  { of LocateMsdosDirectory }
  135.  
  136. function LocateMsdosFile: Boolean ;
  137. {*
  138.  * Search the current MS-DOS (sub)directory for a file; The name of the
  139.  * file is supplied in the current file entry.  If found,
  140.  * the returned value is True and the global variable MsdosFcb is set.  In
  141.  * case of an error, a False value will be returned.
  142.  *}
  143. var
  144.    EntryType: EntryTypes ;  { Type of located file-entry }
  145.    Found    :    Boolean ;  { Result of this function so far }
  146. begin
  147.    DirectorySearchPos:= BeforeFirstEntry ;
  148.    Found:= False ;  { Preset result of search }
  149.    repeat
  150.      GetNextDirEntry( EntryType ) ;
  151.      if EntryType=FileNameEntry then
  152.        Found:= ( FileEntry^.Name = MsdosFcb^.FileName ) ;
  153.    until Found or (EntryType in [UnusedEntry,EndOfDirectory]) ;
  154.    if ErrorDetected then
  155.      BuildErrorTrace( 'LocMF_' ) ;
  156.    LocateMsdosFile:= Found ;
  157. end ;  { of LocateMsdosFile }
  158.  
  159. procedure DeleteMsdosFile ;
  160. {*
  161.  * Delete the file, associated with the current filelist entry, from the
  162.  * MS-DOS directory.  The directory and the FAT('s) on the disk are updated.
  163.  *}
  164. var
  165.    NextName : FileDescriptors ;  { File name from directory entry }
  166.    ThisEntry:         Integer ;  { Ordinal of cluster to be released }
  167.    NextEntry:         Integer ;  { Ordinal of next cluster in chain }
  168. begin
  169.    SplitFileName( AMsdosFileName, GetFileEntryName ) ;
  170. {*
  171.  * Check the current MS-DOS file entry for a match.  This could save some
  172.  * searching through the MS-DOS directory.
  173.  *}
  174.    SplitFileName( NextName, GetMsdosFileName ) ;
  175.    if not SameName( NextName, AMsdosFileName, NE_Format ) then
  176. {*
  177.  * Search the current directory for the given file name.  If it is not
  178.  * found, raise an error condition: according to the calling procedure
  179.  * it should be there!
  180.  *}
  181.      if not LocateMsdosFile then
  182.       begin
  183.        if ErrorDetected then
  184.          BuildErrorTrace( 'DelMF_' )
  185.        else
  186.          FlagError( 'DelMF: File not found : ' +
  187.                     ExpandFileName(AMsdosFileName,NE_Format) ) ;
  188.        Exit ;
  189.       end ;  { of if/if }
  190. {*
  191.  * Free the entry in the directory and rewrite the directory.
  192.  *}
  193.    MsdosFcb^.FileName[1]:= #$E5 ;  { Indicate deleted file }
  194. {*
  195.  * Free the clusters allocated to the deleted file.
  196.  *}
  197.    ThisEntry:= MsdosFcb^.Cluster ;  { First cluster allocated to deleted file }
  198.    while (ThisEntry>1) and (ThisEntry<=ClustersPerDisk) do
  199.     begin
  200.      NextEntry:= GetFatEntry( ThisEntry ) ;  { Next cluster in chain }
  201.      PutFatEntry( ThisEntry, 0 ) ;           { Free cluster in FAT }
  202.      ThisEntry:= NextEntry ;
  203.     end ;  { of while }
  204.  
  205.    WriteSector( DirectorySector, Addr(DirBuffer) ) ;
  206.    if not ErrorDetected then
  207.      for ThisEntry:= 0 to Pred(FatsPerDisk) do
  208.        WriteFat( ThisEntry ) ;
  209.    if ErrorDetected then
  210.      BuildErrorTrace( 'DelMF_' ) ;
  211.    FlushCache ;
  212. end ;  { of DeleteMsdosFile }
  213.  
  214. function GetMsdosFileSize : Integer ;
  215. {*
  216.  * Return the length of an MS-DOS file, expressed as the number of CP/M
  217.  * records of 128 bytes.  Note that the size of the CP/M record size is
  218.  * hardcoded into this function.  Moreover, it is assumed (again) that
  219.  * the maximum file size is 4 Megabytes, giving a 15-bit result.
  220.  *}
  221. var
  222.    Result: Integer ;  { Intermediate result of function }
  223. begin
  224.    with MsdosFcb^ do
  225.     begin
  226.      Result:= (Size[0] shr 7) + (Size[1] shl 1) + ((Size[2] and $3F) shl 9) ;
  227.      if (Size[0] and $7F)>0 then
  228.        Result:= Succ( Result ) ;
  229.     end ;  { of with }
  230.    GetMsdosFileSize:= Result ;
  231. end ;  { of GetMsdosFileSize }
  232.  
  233. procedure ReadMsdosFile ;
  234. {*
  235.  * Read the contents of the MS-DOS file associated with the current filelist
  236.  * entry and write it to a new CP/M file with the same name.  The CP/M file
  237.  * will inherit the attributes READONLY and SYSTEM.
  238.  *}
  239. var
  240.    CurrentCluster: Integer ;  { Cluster to be read }
  241.    RecordsToDo   : Integer ;  { Number of CP/M records (still) to copy }
  242.    FirstFreeByte : Integer ;  { Ordinal of first unused byte in last recprd }
  243.    TransferCount : Integer ;  { Number of records to write in BlockWrite }
  244.    RecordsWritten: Integer ;  { Number of records written in BlockWrite }
  245.    PaddingChar   :    Byte ;  { Padding character }
  246.  
  247.    CpmFib: CpmFibs absolute ACpmFile ;  { Type casting: file -> FIB }
  248. begin
  249. {*
  250.  * Create a new CP/M file with the same name as the MS-DOS file.
  251.  *}
  252.    SplitFileName( ACpmFileName, GetFileEntryName ) ;
  253.    ACpmFileName.Drive:= CpmDriveName.Drive ;
  254.    ACpmFileName.User := CpmDriveName.User  ;
  255.    RegisterFile( ACpmFileName, ACpmFile ) ;
  256.    Assign ( ACpmFile, ExpandFileName(ACpmFileName,DNE_Format) ) ;
  257.    ReWrite( ACpmFile ) ;
  258. {*
  259.  * Open the MS-DOS file and setup the copy control variables.  If the file
  260.  * is not found, the value of the variables will be unpredictable, but they
  261.  * will not be used in that case.
  262.  *}
  263.    if not LocateMsdosFile then
  264.      FlagError( ^H': File not found : ' +
  265.                 ExpandFileName(ACpmFileName,NE_Format) ) ;
  266.    CurrentCluster:= MsdosFcb^.Cluster ;  { First cluster of MS-DOS file }
  267.    RecordsToDo   := GetMsdosFileSize  ;  { File size in CP/M records }
  268.    FirstFreeByte := MsdosFcb^.Size[0] and $7F ;  { EOF pos. in last record }
  269.  
  270.    while (CurrentCluster<ClustersPerDisk) and (not ErrorDetected) do
  271.     begin
  272.      ReadCluster( CurrentCluster ) ;
  273.      if not ErrorDetected then
  274. {*
  275.  * Copy the cluster to the CP/M file.  However, some extra care is needed in
  276.  * case of the last cluster:
  277.  * - The number of records to write may be smaller than RecordsPerCluster,
  278.  * - The unused part of the last record should be filled up.
  279.  *}
  280.       begin
  281.        TransferCount:= RecordsPerCluster ;  { Default number of records }
  282.        if RecordsToDo<=RecordsPerCluster then
  283.         begin
  284.          TransferCount:= RecordsToDo ;
  285.          if FirstFreeByte>0 then
  286.           begin
  287.            if FileClass=TextFile then  PaddingChar:= CpmTxtFillChar
  288.                                  else  PaddingChar:= CpmBinFillChar ;
  289.            FillChar( ClusterBuffer[Pred(TransferCount)*128+FirstFreeByte],
  290.                      128-FirstFreeByte, PaddingChar ) ;
  291.           end ;  { of if }
  292.         end ;  { of if }
  293.  
  294.        BlockWrite( ACpmFile, ClusterBuffer, TransferCount, RecordsWritten ) ;
  295.        if TransferCount=RecordsWritten then
  296.         begin
  297.          RecordsToDo   := RecordsToDo - TransferCount ;
  298.          CurrentCluster:= GetFatEntry( CurrentCluster ) ;
  299.         end
  300.        else  { there is an error detected }
  301.          FlagError( ^H': CP/M disk write error' ) ;
  302.       end ;  { of if }
  303.     end ;  { of while }
  304.    Close( ACpmFile ) ;
  305. {*
  306.  * The file is copied to the CP/M file system.  If an error is encountered,
  307.  * the CP/M file is destroyed.  If no errors are found, copy some of the
  308.  * MS-DOS file attributes to the CP/M file.
  309.  *}
  310.    if ErrorDetected then
  311.     begin
  312.      BuildErrorTrace( 'ReaMF_' ) ;
  313.      Erase( ACpmFile ) ;
  314.     end
  315.    else
  316.      if CopyMsdosFileAttr then
  317.       begin
  318.        if ReadOnly in MsdosFcb^.Attribute then
  319.          CpmFib.Fcb[09]:= CpmFib.Fcb[09] + $80 ;
  320.        if [Hidden,System]*MsdosFcb^.Attribute <> [] then
  321.          CpmFib.Fcb[10]:= CpmFib.Fcb[10] + $80 ;
  322.        Bdos( SetFileAttributes, Addr(CpmFib.Fcb) ) ;
  323.       end ;  { of if/else }
  324. end ;  { of ReadMsdosFile }
  325.  
  326. procedure SetTodaysDate( NewDate: DateStrings ) ;
  327. {*
  328.  * Save the current date, both in text format and in the compressed MS-DOS
  329.  * format.  If an obvious error is found in the supplied date, which must
  330.  * be of the form 'yyyymmdd', the date is not changed and the global error
  331.  * flag is set.
  332.  *}
  333. var
  334.    Year  : Integer ;  { Year number from NewDate }
  335.    Month : Integer ;  { Month number from NewDate }
  336.    Day   : Integer ;  { Day number from NewDate }
  337.    Status: Integer ;  { Status of string to number conversion }
  338. begin
  339.    FlagError( 'SetTD: Illegal date : ' + NewDate ) ;  { Assume an error }
  340.  
  341.    Val( Copy(NewDate,1,4), Year , Status ) ;  { Convert year number }
  342.    if Status<>0 then  Exit ;
  343.    Val( Copy(NewDate,5,2), Month, Status ) ;  { Convert month number }
  344.    if Status<>0 then  Exit ;
  345.    Val( Copy(NewDate,7,2), Day  , Status ) ;  { Convert day number }
  346.    if Status<>0 then  Exit ;
  347.  
  348.    Year:= Year - 1980 ;
  349.    if (Year <0) or (Year >32) then  Exit ;
  350.    if (Month=0) or (Month>12) then  Exit ;
  351.    if (Day  =0) or (Day  >31) then  Exit ;
  352.  
  353.    ClearError ;  { There is no error after all }
  354.    TodaysDate:= NewDate ;
  355.    MsdosDate := (Year shl 9) + (Month shl 5) + Day ;
  356. end ;  { of SetTodaysDate }
  357.  
  358. procedure WriteMsdosFile ;
  359. {*
  360.  * Copy (write) the contents of a CP/M file into a new MS-DOS file.
  361.  * The MS-DOS file will inherit both the name and the status flags
  362.  * from the CP/M file.
  363.  *
  364.  * Note that it is assumed in this procedure that the maximum file size
  365.  * is less than 4 Megabytes, thus the CP/M file size is always a
  366.  * non-negative number.
  367.  *}
  368. var
  369.    CpmFileSize     : Integer ;  { Size of file to copy [records] }
  370.    LengthLastRecord: Integer ;  { Length of last, partial record }
  371.    RecordsToDo     : Integer ;  { Loop control variable }
  372.    TransferCount   : Integer ;  { Number of records read }
  373.    CurrentCluster  : Integer ;  { Cluster currently being written }
  374.    NextCluster     : Integer ;  { Next cluster in chain }
  375.  
  376.  procedure CreateMsdosFile ;
  377.  {*
  378.   * Find a free entry in the directory and enter the name of the file in it.
  379.   *}
  380.  var
  381.     Found    :    Boolean ;  { Result of search for a free directory entry }
  382.     EntryType: EntryTypes ;  { Type of directory entry }
  383.  begin
  384.     Found:= False ;  { Preset result of search }
  385.     DirectorySearchPos:= BeforeFirstEntry ;
  386.     repeat
  387.       GetNextDirEntry( EntryType ) ;
  388.       Found:= EntryType in [FreeEntry,UnusedEntry] ;
  389.     until Found or (EntryType=EndOfDirectory) ;
  390.  
  391.     if Found then
  392. {*
  393.  * Initialise the FCB for this file.
  394.  *}
  395.      begin
  396.       FillChar( MsdosFcb^, SizeOf(MsdosFcbs), 0 ) ;
  397.       MsdosFcb^.FileName:= FileEntry^.Name ;
  398.       if CopyCpmFileAttr then
  399.         MsdosFcb^.Attribute:= FileEntry^.Attr + [Archive] ;
  400.       MsdosFcb^.Date:= MsdosDate ;
  401.      end
  402.     else
  403. {*
  404.  * The end of the directory is hit, thus the directory is full.  However,
  405.  * if GetNextDirEntry encounters an error it will fake EndOfDirectory and
  406.  * set the flobal error flag.
  407.  *}
  408.       if ErrorDetected then
  409.         BuildErrorTrace( 'CreMF_' )
  410.       else
  411.         FlagError( 'CreMF: Directory is full' ) ;
  412.  end ;  { of CreateMsdosFile }
  413.  
  414.  procedure CloseMsdosFile ;
  415.  {*
  416.   * Write the directory entry of a new MS-DOS file onto the disk as well as
  417.   * the updated FAT.
  418.   *}
  419.  var
  420.     I: Integer ;  { Loop control variable }
  421.  begin
  422.     PushErrorMessage ;  { Save current errormessage, clear error status }
  423.  
  424.     if CpmFileSize<>0 then
  425.      begin
  426. {*
  427.  * The file is not empty: register the length of the file in the directory
  428.  * entry and write it together with the modified FAT's to disk.
  429.  *}
  430.       with MsdosFcb^ do
  431.        begin
  432.         Size[0]:= Lo( CpmFileSize shl 7 ) + LengthLastRecord ;
  433.         Size[1]:= Lo( CpmFileSize shr 1 ) ;
  434.         Size[2]:= Lo( CpmFileSize shr 9 ) ;
  435.        end ;  { of with }
  436.       for I:= 0 to Pred(FatsPerDisk) do
  437.         WriteFat( I ) ;
  438.      end ;  { of if }
  439. {*
  440.  * Write the modified directory entry to disk.  Even if during writing the
  441.  * FAT an error is found, the directory should be rewritten to keep the
  442.  * file system as consistent as possible.
  443.  *}
  444.     WriteSector( DirectorySector, Addr(DirBuffer) ) ;
  445.     if ErrorDetected then
  446.       BuildErrorTrace( 'CloMF_' ) ;
  447.     FlushCache ;
  448.  
  449.     PopErrorMessage ;  { Restore original error status }
  450.  end ;  { of CloseMsdosFile }
  451.  
  452.  procedure LocateEofPosition ;
  453.  {*
  454.   * Determine the precise length of the CP/M file: for text files, a Ctrl-Z
  455.   * in the last record of the file indicates the actual EndOfFile.  For
  456.   * other types of files it is not possible to give a full-proof, better
  457.   * length.
  458.   *
  459.   * Given the precise length, CpmFileSize and LengthLastRecord are adjusted
  460.   * and the unused part of the cluster is preset.
  461.   *}
  462.  var
  463.     LastRecordPtr: ^Char ;  { Pointer somewhere in last record }
  464.  begin
  465.     if FileClass=TextFile then
  466.      begin
  467.       LastRecordPtr:= Ptr( Addr(ClusterBuffer) +
  468.                            Pred(TransferCount)*BytesPerRecord ) ;
  469.       while (LastRecordPtr^<>^Z) and (LengthLastRecord<BytesPerRecord) do
  470.        begin
  471.         LastRecordPtr   := Ptr( Succ(Ord(LastRecordPtr)) ) ;
  472.         LengthLastRecord:= Succ( LengthLastRecord ) ;
  473.        end ;  { of while }
  474. {*
  475.  * If an EndOfFile marker has been found, at least one byte and at most
  476.  * BytesPerRecord bytes still need to be preset.  Moreover, the number of
  477.  * complete CP/M records needs to be adjusted.
  478.  *}
  479.       if LastRecordPtr^=^Z then
  480.        begin
  481.         CpmFileSize:= Pred( CpmFileSize ) ;
  482.         FillChar( LastRecordPtr^, BytesPerRecord-LengthLastRecord,
  483.                   MsdosFillChar ) ;
  484.        end  { of if }
  485.  {*
  486.   * No EndOfFile Marker is found.  Restore the length of the last record.
  487.   *}
  488.       else
  489.         LengthLastRecord:= 0 ;
  490.      end ;  { of if }
  491.  end ;  { of LocateEofPosition }
  492.  
  493. begin  { of WriteMsdosFile }
  494. {*
  495.  * Build the CP/M (and MS-DOS) file name.  The drive and user area are set
  496.  * to their 'current' values.
  497.  *}
  498.    SplitFileName( ACpmFileName, GetFileEntryName ) ;
  499.    ACpmFileName.Drive:= CpmDriveName.Drive ;
  500.    ACpmFileName.User := CpmDriveName.User  ;
  501.  
  502.    CpmFileSize     := 0 ;  { For CloseMsdosFile in order to handle empty }
  503.    LengthLastRecord:= 0 ;  {  files and error cases correctly }
  504.    CreateMsdosFile ;  { Allocate an entry in the MS-DOS file system }
  505.    if not ErrorDetected then
  506. {*
  507.  * If the file to be copied is empty, the file needs to be closed only: the
  508.  * input file is not read at all in such a case.
  509.  *}
  510.     begin
  511.      if FileEntry^.Size>0 then
  512.       begin
  513. {*
  514.  * Allocate the first cluster of the new MS-DOS file and open the CP/M file
  515.  * for read access.
  516.  *}
  517.        CurrentCluster   := GetFreeFatEntry( 1 ) ;
  518.        MsdosFcb^.Cluster:= CurrentCluster ;
  519.  
  520.        RegisterFile( ACpmFileName, ACpmFile ) ;
  521.        Assign( ACpmFile, ExpandFileName(ACpmFileName,DNE_Format) ) ;
  522.        Reset ( ACpmFile ) ;
  523.        CpmFileSize:= FileSize( ACpmFile ) ;
  524.        RecordsToDo:= CpmFileSize ;
  525. {*
  526.  * Copy the CP/M file to the MS-DOS file, one cluster per pass through
  527.  * this loop.  Note that CP/M supplies a file size, which is a multiple
  528.  * of the record size.  Therefore, the precise location of the EndOfFile
  529.  * must be determined in another way.
  530.  *}
  531.        while (RecordsToDo>0) and (not ErrorDetected) do
  532.         begin
  533.          if RecordsToDo<RecordsPerCluster then
  534.            FillChar( ClusterBuffer, SizeOf(ClusterBuffer), MsdosFillChar ) ;
  535.          BlockRead( ACpmFile, ClusterBuffer, RecordsPerCluster, TransferCount ) ;
  536.          RecordsToDo:= RecordsToDo - TransferCount ;
  537.          if RecordsToDo<=0 then  { the last record of the file is read }
  538.            LocateEofPosition ;   { For text files only! }
  539.          WriteCluster( CurrentCluster ) ;
  540. {*
  541.  * Determine the ordinal of the next cluster in the chain and update the
  542.  * FileAssignmentTable.
  543.  *}
  544.          if RecordsToDo<=0 then
  545.            NextCluster:= $0FFF
  546.          else
  547.            NextCluster:= GetFreeFatEntry( CurrentCluster ) ;
  548.          PutFatEntry( CurrentCluster, NextCluster ) ;
  549.          CurrentCluster:= NextCluster ;
  550.  
  551.          if ErrorDetected then  { by WriteCluster or GetFreeFatEntry }
  552.            BuildErrorTrace( 'WriMF_' ) ;
  553.         end ;  { of while }
  554.        Close( ACpmFile ) ;
  555.        UnregisterFile( ACpmFile ) ;
  556.       end ;  { of if }
  557.     end ;  { of if }
  558. {*
  559.  * Perform unconditionally a close of the MS-DOS file: it causes the directory
  560.  * to reflect the actual status.  In case of an error, DeleteMsdosFile then
  561.  * will be able to locate the file!
  562.  *}
  563.    CloseMsdosFile ;  { Rewrite the MS-DOS directory and FAT(s) }
  564.  
  565.    if ErrorDetected then
  566.     begin
  567.      PushErrorMessage ;  { Save error status and clear it }
  568.      DeleteMsdosFile  ;  { Remove file with error }
  569.      PopErrorMessage  ;  { Restore error status }
  570.     end ;  { of if }
  571. end ;  { of WriteMsdosFile }
  572.  
  573.