home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / dtx9101 / tools / tvision1 / fileman.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1991-03-06  |  26.7 KB  |  724 lines

  1. (* ------------------------------------------------------ *)
  2. (*                      FILEMAN.PAS                       *)
  3. (*   objektorientierte Toolbox zum Zugriff auf Dateien    *)
  4. (*          (c) 1991 Gerd Cebulla & DMV-Verlag            *)
  5. (* ------------------------------------------------------ *)
  6. UNIT FileMan;
  7. {$B-}
  8.  
  9. INTERFACE
  10.  
  11. USES
  12.   Dos, DiskMan;
  13.  
  14. CONST
  15.   ClusListSize = $FFEE DIV 2;
  16.  
  17. TYPE
  18.   FileNameStr  = STRING [12];
  19.  
  20.   PClusList = ^ClusList;
  21.   ClusList  = ARRAY [0..Pred(ClusListSize)] OF WORD;
  22.   ClusTable = ARRAY [0..1] OF PClusList;
  23.  
  24.   PSecBuf = ^SecBuf;
  25.   SecBuf  = RECORD   { Verwaltungs-Record für Dateipuffer }
  26.     BufPtr : PDiskSector;  { Zeiger auf den Sektor-Puffer }
  27.     Prev,                  { vorhergehender Sektor        }
  28.     Next   : PSecBuf;      { nächster Sektor              }
  29.     Check  : LONGINT;      { Prüfsumme                    }
  30.   END; { SecBuf }
  31.  
  32.   PFileManager = ^FileManager;
  33.   FileManager  = OBJECT(DiskInfo)
  34.     Disk            : PDiskManager;
  35.     FileMask        : DosNameStr;                  { Suchmaske          }
  36.     FileInfo        : DirEntry;                    { Verzeichniseintrag }
  37.     DirBuf          : PDirSector;
  38.                   { Zeiger auf Verzeichnis-Puffer                       }
  39.     DirCluster      : WORD;
  40.                   { aktueller Verzeichnis-Cluster                       }
  41.     DirSector       : LONGINT;
  42.                   { absolute Nummer des aktuellen Verzeichnis-Sektors   }
  43.     DirOffset       : BYTE;                      { Offset des aktuellen }
  44.                   { Verzeichnis-Sektors zum Beginn des Clusters         }
  45.     DirIndex        : INTEGER;
  46.                   { Index des aktuellen Verzeichnis-Eintrags            }
  47.     ClusTab         : ClusTable;
  48.                   { Tabelle für sämtliche Clusternummern einer Datei    }
  49.     FileClusters    : WORD;          { Dateigröße in Clusters           }
  50.     FileSectors     : LONGINT;       { Dateigröße in Sektoren           }
  51.     CurrPtr         : PSecBuf;
  52.                                      { Zeiger auf aktuellen Dateisektor }
  53.     FirstSector,                     { erster Sektor im Dateipuffer     }
  54.     LastSector,                      { letzter Sektor im Dateipuffer    }
  55.     CurrSector      : LONGINT;       { aktueller Dateisektor            }
  56.     BufferedSectors : WORD;          { Größe des Dateipuffers           }
  57.  
  58.     CONSTRUCTOR Init(PDisk : PDiskManager);
  59.     DESTRUCTOR  Done;                               VIRTUAL;
  60.     PROCEDURE   ParseFileName(    Dateiname : FileNameStr;
  61.                               VAR DosName   : DosNameStr);
  62.                                                     VIRTUAL;
  63.     FUNCTION    Matching(Name,
  64.                          Suchmaske : DosNameStr) : BOOLEAN;
  65.                                                     VIRTUAL;
  66.     FUNCTION    GetFirstEntry(Dateiname : PathStr) :
  67.                                          PDirEntry; VIRTUAL;
  68.     FUNCTION    GetNextEntry : PDirEntry;           VIRTUAL;
  69.     PROCEDURE   Load(MinAvail : LONGINT);           VIRTUAL;
  70.     PROCEDURE   Unload;                             VIRTUAL;
  71.     FUNCTION    GetCheckSum(BufPtr : PDiskSector) : LONGINT;
  72.                                                     VIRTUAL;
  73.     FUNCTION    TestCheckSum(Check  : LONGINT;
  74.                              BufPtr : PDiskSector) :
  75.                                            BOOLEAN; VIRTUAL;
  76.     FUNCTION    GetFileSize : LONGINT;
  77.     FUNCTION    GetBufSize : WORD;
  78.     PROCEDURE   WriteFileBuffer;                   VIRTUAL;
  79.     PROCEDURE   ReadFileSector(    LogSektor : LONGINT;
  80.                                VAR Buffer);        VIRTUAL;
  81.     PROCEDURE   WriteFileSector(    LogSektor : LONGINT;
  82.                                 VAR Buffer);       VIRTUAL;
  83.     FUNCTION    GetFileSector(LogSektor : LONGINT) :
  84.                               PDiskSector;         VIRTUAL;
  85.     FUNCTION    SaveFileSector(LogSektor : LONGINT) :
  86.                                           BOOLEAN; VIRTUAL;
  87.     FUNCTION    AbsSector(LogSektor : LONGINT) : LONGINT;
  88.                                                    VIRTUAL;
  89.   END; { FileManager }
  90.  
  91. IMPLEMENTATION
  92.  
  93. CONST
  94.   Root = '\          ';
  95.  
  96.   CONSTRUCTOR FileManager.Init(PDisk : PDiskManager);
  97.     { Initialisiert Variablen und legt einen                 }
  98.     { Verzeichnispuffer an.                                  }
  99.   BEGIN
  100.     CurrPtr         := NIL;
  101.     ClusTab[0]      := NIL;
  102.     DirSector       := 0;
  103.     FileSectors     := 0;
  104.     BufferedSectors := 0;
  105.     Disk            := PDisk;
  106.     Disk^.GetDiskInfo(Self);
  107.     GetMem(DirBuf, BytesPerSector);
  108.     IF DirBuf = NIL THEN BEGIN
  109.       DiskError := dskNoMemory;
  110.       Done;
  111.       Fail;
  112.     END;
  113.   END; { FileManager.Init }
  114.  
  115.   DESTRUCTOR FileManager.Done;
  116.     { Schließt eine evtl. geöffnete Datei und gibt alle      }
  117.     { Puffer frei.                                           }
  118.   BEGIN
  119.     Unload;
  120.     IF DirBuf <> NIL THEN BEGIN
  121.       FreeMem(DirBuf, BytesPerSector);
  122.       DirBuf := NIL;
  123.     END;
  124.   END; { FileManager.Done }
  125.  
  126.   PROCEDURE FileManager.ParseFileName(Dateiname : FileNameStr;
  127.                                      VAR DosName : DosNameStr);
  128.     { Wandelt einen Dateinamen in der üblichen Form          }
  129.     { 'Filename.Ext' in das intern von DOS verwendete Format }
  130.     { um: ein 11-elementiges Char-Array, bei dem die ersten  }
  131.     { 8 Byte für den Dateinamen, die letzten 3 Byte für die  }
  132.     { Erweiterung vorgesehen sind (beide jeweils mit         }
  133.     { Leerzeichen aufgefüllt). Jokerzeichen ('?' und '*')    }
  134.     { werden unterstützt.                                    }
  135.   VAR
  136.     Punkt, Stelle : BYTE;
  137.   BEGIN
  138.     FillChar(DosName, SizeOf(DosNameStr), #32);
  139.     Punkt := Pos('.', Dateiname);
  140.     IF Punkt = 0 THEN
  141.       Move(Dateiname[1], DosName[1], Length(Dateiname))
  142.     ELSE BEGIN
  143.       Move(Dateiname[1], DosName[1], Pred(Punkt));
  144.       Move(Dateiname[Succ(Punkt)], DosName[9],
  145.            Length(Dateiname)-Punkt);
  146.     END;
  147.     FOR Stelle := 1 TO SizeOf(DosNameStr) DO BEGIN
  148.       IF DosName[Stelle] = '*' THEN BEGIN
  149.         IF Stelle <= 8 THEN
  150.           FillChar(DosName[Stelle], 9-Stelle, '?')
  151.         ELSE
  152.           FillChar(DosName[Stelle], 12-Stelle, '?');
  153.       END;
  154.     END;
  155.   END; { FileManager.ParseFileName }
  156.  
  157.   FUNCTION FileManager.Matching(Name, Suchmaske : DosNameStr) : BOOLEAN;
  158.     { Vergleicht einen Dateinamen im internen DOS-Format mit }
  159.     { einer Suchmaske. Liefert True zurück, wenn der         }
  160.     { Vergleich positiv ausfällt.                            }
  161.   VAR
  162.     Stelle : BYTE;
  163.     Gleich : BOOLEAN;
  164.   BEGIN
  165.     Stelle := 1;
  166.     REPEAT
  167.       Gleich := (Suchmaske[Stelle] = '?') OR
  168.                 (Suchmaske[Stelle] = Name[Stelle]);
  169.       INC(Stelle);
  170.     UNTIL NOT Gleich OR (Stelle > SizeOf(DosNameStr));
  171.     Matching := Gleich;
  172.   END; { FileManager.Matching }
  173.  
  174.   FUNCTION FileManager.GetFirstEntry(Dateiname : PathStr) :
  175.                                                     PDirEntry;
  176.     { Sucht nach dem ersten Auftreten des angegebenen        }
  177.     { Dateinamens, der auch Jokerzeichen enthalten darf.     }
  178.     { Liefert im Erfolgsfall einen Zeiger auf den            }
  179.     { zugehörigen Verzeichniseintrag zurück, ansonsten den   }
  180.     { Wert nil. Entspricht in etwa Dos.FindFirst, bezieht    }
  181.     { sich aber immer auf das Stammverzeichnis (der          }
  182.     { Dateiname muß daher den vollständigen Suchpfad         }
  183.     { enthalten) und erwartet den Dateinamen in genau der    }
  184.     { Schreibweise, wie sie auch im Verzeichnis verwendet    }
  185.     { wird, d.h. in der Regel: in Großbuchstaben. Findet     }
  186.     { auch gelöschte Dateien; diese sind daran kenntlich,    }
  187.     { daß das erste Zeichen des Dateinamens Chr ($E5) ist.   }
  188.   VAR
  189.     Suchmaske : DosNameStr;
  190.     Stelle1,
  191.     Stelle2   : BYTE;
  192.     Gefunden  : BOOLEAN;
  193.   BEGIN
  194.     DiskError := dskOk;
  195.     Stelle1   := Succ(Pos(':', Dateiname));
  196.     IF Dateiname[Stelle1] = '\' THEN
  197.       INC(Stelle1);
  198.     IF Stelle1 > Length(Dateiname) THEN BEGIN
  199.       FileMask := Root;
  200.       WITH FileInfo DO BEGIN
  201.         Name      := Root;
  202.         Attribute := Directory;
  203.         FillChar(Fill, SizeOf(FileInfo)-SizeOf(Name)-
  204.                        SizeOf(Attribute), 0);
  205.       END;
  206.     END ELSE BEGIN
  207.       Stelle2 := Succ(Stelle1);
  208.       WHILE (Dateiname[Stelle2] <> '\') AND
  209.             (Stelle2 <= Length(Dateiname)) DO
  210.         INC(Stelle2);
  211.       ParseFileName(Copy(Dateiname, Stelle1,
  212.                          Stelle2-Stelle1), Suchmaske);
  213.       IF DirSector <> RootDirStart THEN BEGIN
  214.         DirSector := RootDirStart;
  215.         Disk^.ReadSector(DirBuf^, DirSector, 1);
  216.       END;
  217.       DirCluster := 0;
  218.       DirIndex   := -1;
  219.       REPEAT
  220.         INC(DirIndex);
  221.         IF DirIndex = DirEntriesPerSector THEN BEGIN
  222.           INC(DirSector);
  223.           IF DirSector-RootDirStart = RootDirSectors THEN BEGIN
  224.             IF Stelle2 <= LENGTH(Dateiname) THEN
  225.               DiskError := dskPathNotFound
  226.             ELSE
  227.               DiskError := dskNoMoreFiles;
  228.           END ELSE BEGIN
  229.             Disk^.ReadSector(DirBuf^, DirSector, 1);
  230.             DirIndex := 0;
  231.           END;
  232.         END;
  233.         WITH DirBuf^[DirIndex] DO BEGIN
  234.           IF Stelle2 <= LENGTH(Dateiname) THEN
  235.             Gefunden := (Attribute AND Directory <> 0) AND
  236.                         (Name = Suchmaske)
  237.           ELSE
  238.             Gefunden := Matching(Name, Suchmaske);
  239.         END;
  240.       UNTIL (DiskError <> dskOk) OR Gefunden;
  241.       WHILE (DiskError = dskOk) AND
  242.             (Stelle2 < Length(Dateiname)) DO BEGIN
  243.         DirCluster := DirBuf^[DirIndex].FirstCluster;
  244.         DirSector  := LONGINT(DirCluster-2)*
  245.                       SectorsPerCluster+ClusterStart;
  246.         DirOffset  := 0;
  247.         Disk^.ReadSector(DirBuf^, DirSector, 1);
  248.         DirIndex   := -1;
  249.         Stelle1    := Succ(Stelle2);
  250.         Stelle2    := Succ(Stelle1);
  251.         WHILE (Dateiname[Stelle2] <> '\') AND
  252.               (Stelle2 <= LENGTH(Dateiname)) DO
  253.           INC(Stelle2);
  254.         ParseFileName(Copy(Dateiname, Stelle1,
  255.                            Stelle2-Stelle1), Suchmaske);
  256.         REPEAT
  257.           INC(DirIndex);
  258.           IF DirIndex = DirEntriesPerSector THEN BEGIN
  259.             INC(DirOffset);
  260.             IF DirOffset = SectorsPerCluster THEN BEGIN
  261.               DirCluster := Disk^.GetFatEntry(DirCluster);
  262.               IF DirCluster >= $FFF0 THEN BEGIN
  263.                 IF Stelle2 <= Length(Dateiname) THEN
  264.                   DiskError := dskPathNotFound
  265.                 ELSE
  266.                   DiskError := dskNoMoreFiles;
  267.               END ELSE BEGIN
  268.                 DirSector := LONGINT(DirCluster-2)*
  269.                              SectorsPerCluster+ClusterStart;
  270.                 DirOffset := 0;
  271.               END;
  272.             END;
  273.             IF DiskError = dskOk THEN BEGIN
  274.               Disk^.ReadSector(DirBuf^,
  275.                                DirSector+DirOffset, 1);
  276.               DirIndex := 0;
  277.             END;
  278.           END;
  279.           WITH DirBuf^[DirIndex] DO BEGIN
  280.             IF Stelle2 <= Length(Dateiname) THEN
  281.               Gefunden := (Attribute AND Directory <> 0) AND
  282.                           (Name = Suchmaske)
  283.             ELSE
  284.               Gefunden := Matching(Name, Suchmaske);
  285.           END;
  286.         UNTIL (DiskError <> dskOk) OR Gefunden;
  287.       END;
  288.       FileMask := Suchmaske;
  289.       FileInfo := DirBuf^[DirIndex];
  290.     END;
  291.     IF DiskError = dskOk THEN
  292.       GetFirstEntry := @FileInfo
  293.     ELSE BEGIN
  294.       DirSector     := 0;
  295.       GetFirstEntry := NIL;
  296.     END;
  297.   END; { FileManager.GetFirstEntry }
  298.  
  299.   FUNCTION FileManager.GetNextEntry : PDirEntry;
  300.     { Ermittelt nach vorhergehendem Aufruf von GetFirstEntry }
  301.     { das nächste Vorkommen des betreffenden Dateinamens.    }
  302.     { Analogon zu Dos.FindNext.                              }
  303.   BEGIN
  304.     DiskError := dskOk;
  305.     IF DirSector = 0 THEN
  306.       DiskError := dskNoMoreFiles
  307.     ELSE IF DirSector-RootDirStart < RootDirSectors THEN BEGIN
  308.       REPEAT
  309.         INC(DirIndex);
  310.         IF DirIndex = DirEntriesPerSector THEN BEGIN
  311.           INC(DirSector);
  312.           IF DirSector-RootDirStart = RootDirSectors THEN
  313.             DiskError := dskNoMoreFiles
  314.           ELSE BEGIN
  315.             Disk^.ReadSector(DirBuf^, DirSector, 1);
  316.             DirIndex := 0;
  317.           END;
  318.         END;
  319.       UNTIL (DiskError <> dskOk) OR
  320.       Matching(DirBuf^[DirIndex].Name, FileMask);
  321.     END ELSE BEGIN
  322.       REPEAT
  323.         INC(DirIndex);
  324.         IF DirIndex = DirEntriesPerSector THEN BEGIN
  325.           INC(DirOffset);
  326.           IF DirOffset = SectorsPerCluster THEN BEGIN
  327.             DirCluster := Disk^.GetFatEntry(DirCluster);
  328.             IF DirCluster >= $FFF0 THEN
  329.               DiskError := dskNoMoreFiles
  330.             ELSE BEGIN
  331.               DirSector := LONGINT(DirCluster-2)*
  332.                            SectorsPerCluster+ClusterStart;
  333.               DirOffset := 0;
  334.             END;
  335.           END;
  336.           IF DiskError = dskOk THEN BEGIN
  337.             Disk^.ReadSector(DirBuf^,
  338.                              DirSector+DirOffset, 1);
  339.             DirIndex := 0;
  340.           END;
  341.         END;
  342.       UNTIL (DiskError <> 0) OR
  343.              Matching(DirBuf^[DirIndex].Name, FileMask);
  344.     END;
  345.     IF DiskError = dskOk THEN BEGIN
  346.       FileInfo     := DirBuf^[DirIndex];
  347.       GetNextEntry := @FileInfo;
  348.     END ELSE BEGIN
  349.       DirSector    := 0;
  350.       GetNextEntry := NIL;
  351.     END;
  352.   END; { FileManager.GetNextEntry }
  353.  
  354.   PROCEDURE FileManager.Load(MinAvail : LONGINT);
  355.     { Liest die zuletzt mit GetFirstEntry/GetNextEntry       }
  356.     { gefundene Datei ein. Diese Datei kann auch ein         }
  357.     { Verzeichnis sein. Über den Parameter MinAvail wird     }
  358.     { angegeben, wieviel zusammenhängender Heap mindestens   }
  359.     { frei bleiben soll; beträgt dieser Wert 0, versucht     }
  360.     { Load, die Datei so vollständig wie möglich in den      }
  361.     { Hauptspeicher zu lesen.                                }
  362.   VAR
  363.     FirstPtr,
  364.     LastPtr : PSecBuf;
  365.     ClusterNr,
  366.     Index   : WORD;
  367.   BEGIN
  368.     Unload;
  369.     IF MinAvail < 8 THEN
  370.       MinAvail := 8;
  371.     IF (DirSector = 0) AND(FileInfo.Name <> Root) THEN
  372.       DiskError := dskFileNotFound
  373.     ELSE BEGIN
  374.       DiskError := dskOk;
  375.       ClusterNr := FileInfo.FirstCluster;
  376.       IF ClusterNr = 0 THEN BEGIN
  377.         IF FileInfo.Name <> Root THEN BEGIN
  378.           FileClusters := 0;
  379.           FileSectors  := 0;
  380.         END ELSE BEGIN
  381.           FileSectors  := RootDirSectors;
  382.           FileClusters := Succ(Pred(FileSectors) DIV
  383.                                SectorsPerCluster);
  384.           IF MaxAvail-SizeOf(WORD)-SizeOf(SecBuf)-
  385.           BytesPerSector < MinAvail THEN
  386.             DiskError := dskNoMemory
  387.           ELSE BEGIN
  388.             GetMem(ClusTab[0], SizeOf(WORD));
  389.             ClusTab[0]^[0] := 0;
  390.             FirstSector    := 0;
  391.             NEW(FirstPtr);
  392.             GetMem(FirstPtr^.BufPtr, BytesPerSector);
  393.             ReadFileSector(FirstSector, FirstPtr^.BufPtr^);
  394.             FirstPtr^.Check := GetCheckSum(FirstPtr^.BufPtr);
  395.             LastSector      := 1;
  396.             LastPtr         := FirstPtr;
  397.             WHILE (DiskError = dskOk) AND
  398.                   (LastSector < RootDirSectors) AND
  399.                   (MaxAvail-SizeOf(SecBuf)-
  400.                   BytesPerSector >= MinAvail) DO BEGIN
  401.               NEW(LastPtr^.Next);
  402.               LastPtr^.Next^.Prev := LastPtr;
  403.               LastPtr             := LastPtr^.Next;
  404.               GetMem(LastPtr^.BufPtr, BytesPerSector);
  405.               ReadFileSector(LastSector, LastPtr^.BufPtr^);
  406.               LastPtr^.Check := GetCheckSum(LastPtr^.BufPtr);
  407.               INC(LastSector);
  408.             END;
  409.             LastPtr^.Next   := FirstPtr;
  410.             FirstPtr^.Prev  := LastPtr;
  411.             CurrPtr         := FirstPtr;
  412.             CurrSector      := 0;
  413.             BufferedSectors := LastSector;
  414.             DEC(LastSector);
  415.           END;
  416.         END;
  417.       END ELSE BEGIN
  418.         FileClusters := 0;
  419.         REPEAT
  420.           INC(FileClusters);
  421.           ClusterNr := Disk^.GetFatEntry(ClusterNr);
  422.         UNTIL (DiskError <> dskOk) OR
  423.               (ClusterNr = 0) OR(ClusterNr >= $FFF0);
  424.         IF DiskError = dskOk THEN BEGIN
  425.           FileSectors := LONGINT(FileClusters)*
  426.                          SectorsPerCluster;
  427.           IF MaxAvail-LONGINT(FileClusters)*
  428.                       SizeOf(WORD)-SizeOf(SecBuf)-
  429.                       BytesPerSector < MinAvail THEN
  430.             DiskError := dskNoMemory
  431.           ELSE BEGIN
  432.             IF FileClusters <= ClusListSize THEN
  433.               GetMem(ClusTab[0], FileClusters*SizeOf(WORD))
  434.             ELSE BEGIN
  435.               NEW(ClusTab[0]);
  436.               GetMem(ClusTab[1], FileClusters MOD
  437.                      ClusListSize*SizeOf(WORD));
  438.             END;
  439.             ClusterNr      := FileInfo.FirstCluster;
  440.             ClusTab[0]^[0] := ClusterNr;
  441.             Index          := 1;
  442.             WHILE (DiskError = dskOk) AND
  443.                   (Index < FileClusters) DO BEGIN
  444.               ClusterNr := Disk^.GetFatEntry(ClusterNr);
  445.               ClusTab[Index DIV ClusListSize]^
  446.                      [Index MOD ClusListSize] := ClusterNr;
  447.               INC(Index);
  448.             END;
  449.             FirstSector := 0;
  450.             NEW(FirstPtr);
  451.             GetMem(FirstPtr^.BufPtr, BytesPerSector);
  452.             ReadFileSector(FirstSector, FirstPtr^.BufPtr^);
  453.             FirstPtr^.Check := GetCheckSum(FirstPtr^.BufPtr);
  454.             LastSector      := 1;
  455.             LastPtr         := FirstPtr;
  456.             WHILE (DiskError = dskOk) AND
  457.                   (LastSector < FileSectors) AND
  458.                   (MaxAvail-SizeOf(SecBuf)-
  459.                   BytesPerSector >= MinAvail) DO BEGIN
  460.               NEW(LastPtr^.Next);
  461.               LastPtr^.Next^.Prev := LastPtr;
  462.               LastPtr             := LastPtr^.Next;
  463.               GetMem(LastPtr^.BufPtr, BytesPerSector);
  464.               ReadFileSector(LastSector, LastPtr^.BufPtr^);
  465.               LastPtr^.Check := GetCheckSum(LastPtr^.BufPtr);
  466.               INC(LastSector);
  467.             END;
  468.             LastPtr^.Next   := FirstPtr;
  469.             FirstPtr^.Prev  := LastPtr;
  470.             CurrPtr         := FirstPtr;
  471.             CurrSector      := 0;
  472.             BufferedSectors := LastSector;
  473.             DEC(LastSector);
  474.           END;
  475.         END;
  476.       END;
  477.       IF DiskError <> dskOk THEN Unload;
  478.     END;
  479.   END; { FileManager.Load }
  480.  
  481.   PROCEDURE FileManager.Unload;
  482.     { Gibt sämtliche Dateipuffer frei und schreibt den       }
  483.     { Inhalt veränderter Sektoren auf den Datenträger.       }
  484.   VAR
  485.     NextPtr : PSecBuf;
  486.     Zaehler : LONGINT;
  487.   BEGIN
  488.     IF CurrPtr <> NIL THEN BEGIN
  489.       WriteFileBuffer;
  490.       FOR Zaehler := FirstSector TO LastSector DO BEGIN
  491.         NextPtr := CurrPtr^.Next;
  492.         FreeMem(CurrPtr^.BufPtr, BytesPerSector);
  493.         Dispose(CurrPtr);
  494.         CurrPtr := NextPtr;
  495.       END;
  496.       CurrPtr := NIL;
  497.     END;
  498.     IF ClusTab[0] <> NIL THEN BEGIN
  499.       IF ClusTab[0]^[0] = 0 THEN
  500.         FreeMem(ClusTab[0], SizeOf(WORD))
  501.       ELSE IF FileClusters <= ClusListSize THEN
  502.         FreeMem(ClusTab[0], FileClusters*SizeOf(WORD))
  503.       ELSE BEGIN
  504.         Dispose(ClusTab[0]);
  505.         FreeMem(ClusTab[1], FileClusters MOD
  506.                 ClusListSize*SizeOf(WORD));
  507.       END;
  508.       ClusTab[0] := NIL;
  509.     END;
  510.     FileSectors     := 0;
  511.     BufferedSectors := 0;
  512.   END; { FileManager.Unload }
  513.  
  514.   FUNCTION FileManager.GetCheckSum(BufPtr : PDiskSector) : LONGINT;
  515.     { Berechnet eine Prüfsumme für den Inhalt eines Sektors. }
  516.   VAR
  517.     CheckSum : LONGINT;
  518.     Index    : WORD;
  519.   BEGIN
  520.     CheckSum := 0;
  521.     FOR Index := 0 TO Pred(BytesPerSector) DO
  522.       INC(CheckSum, Succ(WORD(BufPtr^[Index]))*
  523.                     (Succ(LONGINT(Index))*127-Index));
  524.     GetCheckSum := CheckSum;
  525.   END; { FileManager.GetCheckSum }
  526.  
  527.   FUNCTION FileManager.TestCheckSum(Check  : LONGINT;
  528.                                     BufPtr : PDiskSector) : BOOLEAN;
  529.     { Überprüft, ob die angegebene Prüfsumme mit dem Inhalt  }
  530.     { des Sektors übereinstimmt. Falls ja, wird der Wert     }
  531.     { True zurückgegeben; ist das Funktionsergebnis False,   }
  532.     { wurde der Inhalt des Sektors zwischenzeitlich          }
  533.     { verändert.                                             }
  534.   BEGIN
  535.     TestCheckSum := (Check = GetCheckSum(BufPtr));
  536.   END; { FileManager.TestCheckSum }
  537.  
  538.   FUNCTION FileManager.GetFileSize : LONGINT;
  539.     { Liefert die Dateigröße in Sektoren zurück.             }
  540.   BEGIN
  541.     GetFileSize := FileSectors;
  542.   END; { FileManager.GetFileSize }
  543.  
  544.   FUNCTION FileManager.GetBufSize : WORD;
  545.     { Gibt an, wie viele Sektoren der Datei im Puffer        }
  546.     { enthalten sind.                                        }
  547.   BEGIN
  548.     GetBufSize := BufferedSectors;
  549.   END; { FileManager.GetBufSize }
  550.  
  551.   PROCEDURE FileManager.WriteFileBuffer;
  552.     { Schreibt alle geänderten Sektoren der mit Load         }
  553.     { geladenen Datei auf den Datenträger.                   }
  554.   VAR
  555.     Zaehler : LONGINT;
  556.   BEGIN
  557.     DiskError := dskOk;
  558.     IF CurrPtr <> NIL THEN BEGIN
  559.       Zaehler := 0;
  560.       REPEAT
  561.         IF NOT TestCheckSum(CurrPtr^.Check, CurrPtr^.BufPtr) AND
  562.                SaveFileSector(CurrSector) THEN BEGIN
  563.           WriteFileSector(CurrSector, CurrPtr^.BufPtr^);
  564.           IF DiskError = dskOk THEN
  565.             CurrPtr^.Check := GetCheckSum(CurrPtr^.BufPtr);
  566.         END;
  567.         IF DiskError = dskOk THEN BEGIN
  568.           IF CurrSector = LastSector THEN
  569.             CurrSector := FirstSector
  570.           ELSE
  571.             INC(CurrSector);
  572.           CurrPtr := CurrPtr^.Next;
  573.           INC(Zaehler);
  574.         END;
  575.       UNTIL (DiskError <> dskOk) OR (Zaehler = BufferedSectors);
  576.     END;
  577.   END; { FileManager.WriteFileBuffer }
  578.  
  579.   PROCEDURE FileManager.ReadFileSector(LogSektor : LONGINT;
  580.                                        VAR Buffer);
  581.     { Liest einen Sektor einer Datei in den Puffer ein.      }
  582.   VAR
  583.     SektorNr : LONGINT;
  584.   BEGIN
  585.     SektorNr := AbsSector(LogSektor);
  586.     IF DiskError = dskOk THEN
  587.       Disk^.ReadSector(Buffer, SektorNr, 1);
  588.   END; { FileManager.ReadFileSector }
  589.  
  590.   PROCEDURE FileManager.WriteFileSector(LogSektor : LONGINT;
  591.                                         VAR Buffer);
  592.     { Schreibt einen Sektor aus dem Puffer auf den           }
  593.     { Datenträger.                                           }
  594.   VAR
  595.     SektorNr : LONGINT;
  596.   BEGIN
  597.     SektorNr := AbsSector(LogSektor);
  598.     IF DiskError = dskOk THEN
  599.       Disk^.WriteSector(Buffer, SektorNr, 1);
  600.   END; { FileManager.WriteFileSector }
  601.  
  602.   FUNCTION FileManager.GetFileSector(LogSektor : LONGINT) : PDiskSector;
  603.     { Liefert einen Zeiger auf den angegebenen Sektor der    }
  604.     { mit Load geladenen Datei zurück; die Zählung beginnt   }
  605.     { bei Null. Falls sich dieser Sektor nicht im Puffer     }
  606.     { befindet, wird er eingelesen.                          }
  607.  
  608.     PROCEDURE SektorSuchen(LogSektor : LONGINT);
  609.     BEGIN
  610.       WHILE LogSektor < CurrSector DO BEGIN
  611.         DEC(CurrSector);
  612.         CurrPtr := CurrPtr^.Prev;
  613.       END;
  614.       WHILE LogSektor > CurrSector DO BEGIN
  615.         INC(CurrSector);
  616.         CurrPtr := CurrPtr^.Next;
  617.       END;
  618.     END; { SektorSuchen }
  619.  
  620.   VAR
  621.     Offset,
  622.     NewFirst,
  623.     NewLast,
  624.     NewCurr,
  625.     LastToRead : LONGINT;
  626.   BEGIN { FileManager.GetFileSector }
  627.     IF (LogSektor < 0) OR(LogSektor >= FileSectors) THEN
  628.       DiskError := dskAccessDenied
  629.     ELSE BEGIN
  630.       DiskError := dskOk;
  631.       IF (LogSektor < FirstSector) OR (LogSektor > LastSector) THEN BEGIN
  632.         WriteFileBuffer;
  633.         IF DiskError = dskOk THEN BEGIN
  634.           Offset := PRED(SUCC(BufferedSectors) DIV 2);
  635.           IF Offset > LogSektor THEN
  636.             NewFirst := 0
  637.           ELSE BEGIN
  638.             NewFirst := LogSektor-Offset;
  639.             IF NewFirst > FileSectors-BufferedSectors THEN
  640.               NewFirst := FileSectors-BufferedSectors;
  641.           END;
  642.           NewLast := NewFirst+PRED(BufferedSectors);
  643.           IF (NewFirst > FirstSector) AND
  644.              (NewFirst <= LastSector) THEN BEGIN
  645.             SektorSuchen(LastSector);
  646.             NewCurr    := LastSector;
  647.             LastToRead := NewLast;
  648.           END ELSE IF (NewFirst < FirstSector) AND
  649.                       (NewLast >= FirstSector) THEN BEGIN
  650.             SektorSuchen(NewLast);
  651.             NewCurr    := Pred(NewFirst);
  652.             LastToRead := Pred(FirstSector);
  653.           END ELSE BEGIN
  654.             NewCurr    := Pred(NewFirst);
  655.             LastToRead := NewLast;
  656.           END;
  657.           REPEAT
  658.             IF CurrSector = LastSector THEN
  659.               CurrSector := FirstSector
  660.             ELSE
  661.               INC(CurrSector);
  662.             INC(NewCurr);
  663.             CurrPtr := CurrPtr^.Next;
  664.             ReadFileSector(NewCurr, CurrPtr^.BufPtr^);
  665.             CurrPtr^.Check := GetCheckSum(CurrPtr^.BufPtr);
  666.           UNTIL (DiskError <> dskOk) OR
  667.                 (NewCurr = LastToRead);
  668.           IF DiskError = dskOk THEN BEGIN
  669.             CurrSector  := NewCurr;
  670.             FirstSector := NewFirst;
  671.             LastSector  := NewLast;
  672.           END ELSE
  673.             Unload;
  674.         END;
  675.       END;
  676.     END;
  677.     IF DiskError <> dskOk THEN
  678.       GetFileSector := NIL
  679.     ELSE BEGIN
  680.       SektorSuchen(LogSektor);
  681.       GetFileSector := CurrPtr^.BufPtr;
  682.     END;
  683.   END; { FileManager.GetFileSector }
  684.  
  685.   FUNCTION FileManager.SaveFileSector(LogSektor : LONGINT) : BOOLEAN;
  686.     { Wird von WriteFileBuffer aufgerufen. Wenn diese        }
  687.     { Funktion den Wert True zurückliefert, wird der         }
  688.     { angegebene Sektor auf den Datenträger geschrieben,     }
  689.     { ansonsten unterbleibt die Speicherung.                 }
  690.   BEGIN
  691.     SaveFileSector := TRUE;
  692.   END; { FileManager.SaveFileSector }
  693.  
  694.   FUNCTION FileManager.AbsSector(LogSektor : LONGINT) : LONGINT;
  695.     { Berechnet für einen Sektor der mit Load geladenen      }
  696.     { Datei die tatsächliche Position auf dem Datenträger.   }
  697.   VAR
  698.     ClusterNr,
  699.     SektorOffset : WORD;
  700.   BEGIN
  701.     DiskError := dskOk;
  702.     IF ClusTab[0] = NIL THEN
  703.       DiskError := dskFileNotFound
  704.     ELSE IF (LogSektor < 0) OR (LogSektor >= FileSectors) THEN
  705.       DiskError := dskAccessDenied
  706.     ELSE IF ClusTab[0]^[0] = 0 THEN
  707.       AbsSector := RootDirStart+LogSektor
  708.     ELSE BEGIN
  709.       ClusterNr    := LogSektor DIV SectorsPerCluster;
  710.       SektorOffset := LogSektor MOD SectorsPerCluster;
  711.       AbsSector    := ClusterStart+
  712.                       (ClusTab[ClusterNr DIV ClusListSize]^
  713.                       [ClusterNr MOD ClusListSize]-2)*
  714.                       LONGINT(SectorsPerCluster)+
  715.                       SektorOffset;
  716.     END;
  717.     IF DiskError <> dskOk THEN
  718.       AbsSector := 0;
  719.   END; { FileManager.AbsSector }
  720.  
  721. END.
  722. (* ------------------------------------------------------ *)
  723. (*              Ende von FILEMAN.PAS                      *)
  724.