Unit UDir - основа для создания файловых менеджеров

Модуль извлечён из проекта SWE, поэтому в него добавлен ряд функций из модулей утилит и обработки строк



{*******************************************************}
{                                                       }
{         (C) 1991-2003  Sun World Library          }
{         http://uafo.ru/msg/homepage/swe/swe.html      }
{                                                       }
{         Directory Unit              12.2003           }
{                                                       }
{*******************************************************}
unit UDir;

interface

Uses Classes;

type
TDirRec   = class (TObject)
             Attr: word;
             Time: Longint;
             Size: Longint;
             Name: string[255];   (* string[12] *)
           end;

TDirList = class(TList)
  Path       : string;            (* исследуемый каталог       *)
  SDiv       : string;            (* разделить информационной части и ссылки *)
//Count      : integer;           (* общее число имен в списке файлов        *)
  CountDir_s : integer;           (* число директорий в списке *)
(*------------------------------------*)
(* элементы для форматирования вывода *)
(*------------------------------------*)
  LSN        : integer;           (* длина поля имени          *)
  LSNC       : integer;           (* длина поля имени после коррекции *)
  LSE        : integer;           (* длина поля расширения     *)
  LSD        : integer;           (* длина поля даты (=17)     *)
  LSZ        : integer;           (* длина поля размера файла  *)
  LBlank     : integer;           (* пробелов до разделителя   *)
  DirBeg0    : integer;           (* рекомендуемая позиция разделителя *)
                                  (*       копируется из Ed.ctx.DirBeg *)
  DirBeg     : integer;           (* конечная позиция разделителя      *)
(*------------------------------------*)
(* элементарные операции              *)
(*------------------------------------*)
procedure SWAP(I,J:integer);      (* переставить 2 элемента       *)
function Get(I:integer):TDirRec;  (* получить запись нужного типа *)
procedure Done;                   (* освободить память            *)
(*------------------------------------*)
(* ОСНОВНОЕ ДЕЙСТВИЕ ОБЪЕКТА          *)
(*------------------------------------*)
procedure Collect(aPath:string);  (* занести в список файлы, соотв-щие Path *)
(*------------------------------------*)
(* выборка и сортировка               *)
(*------------------------------------*)
procedure GetDir_s;       (* выбрать директории среди файлов и посчитать их *)
procedure LQuickSort(L, R: Integer);
procedure SortFiles(I1,I2:integer);
procedure SortInsideExt;
procedure DefaultSort;            (* по расширениям *)
(*------------------------------------*)
(* подготовка к форматированию строк  *)
(*------------------------------------*)
procedure CalcLen_s;
procedure CorrectLen_s;
procedure FindDirBeg(S:string);
(*------------------------------------*)
(* результаты в строковом виде        *)
(*------------------------------------*)
function GetStr(I:integer):string;(* получить строку, соответствующую записи I*)
function GetStr0:string;          (* получить 0-вую строку             *)
function GetStrPath:string;       (* получить строку с именем каталога *)
function GetStrTotal:string;      (* -''- со статистикой               *)

  end;  (* TDirList *)


(*=====================================================*)
(*             Экспортируемые процедуры                *)
(*  (которые должны входить в модуль обработки строк   *)
(*                     или в модуль утилит)            *)
(*=====================================================*)

//function left(S:string;N:integer):string;    (* аналог ф-ии из Бэйсика *)
//function right(S:string;N:integer):string;   (* аналог ф-ии из Бэйсика *)
function rightfrom(S:string;N:integer):string; (* копировать ОТ позиции N *)
function posR(T,S:string):integer; (* pos справа налево *)
procedure Warn(S:string);
function GetFullCurrentPath:string;


implementation

uses Windows,SysUtils,Forms{,UUtil,swStr,UEd,swFile};

(*----------------------------------------------------------*)
(*  для организации сортировки                              *)
(*----------------------------------------------------------*)
type LessFunc = function(X, Y: pointer): Boolean;
var  Less: LessFunc;

(*----------------------------------------------------------*)
(*  функции со строками                                     *)
(*----------------------------------------------------------*)
const
  MonthStr: array[1..12] of string[3] = (
    'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');

function StrUpCase(S:string):string;
var I:Integer;
begin
  StrUpCase := '';
  If S = '' Then Exit;
  For I := 1 To Length(S) Do S[I] := UpCase(S[I]);
  StrUpCase := S;
end;

function IntStr(I:longint;D:integer):string; (* целое - в сторку (D цифр) *)
  var S : string;
  begin Str(I:D,S); IntStr:=S end;

function NumStr(N, D: integer):string; (* целое - в строку с ведущими нулями *)
begin
  SetLength(Result,D);
  while D > 0 do begin
    NumStr[D] := Chr(N mod 10 + Ord('0'));
    N := N div 10;
    Dec(D);
  end;
end;

(* вписать строку S в поле длинной L1, добавив пробелы справа *)
function JustL(S:string;L1:integer):string;
var L,I : integer;
begin
  L := Length(S);
  if L > L1 then begin S := copy(S,1,L1-1); S := S + '*' end;
  for I := 1 to L1-L do S := S + ' ';
  JustL := S;
end;

(* вписать строку S в поле длинной L1, добавив пробелы слева *)
function JustR(S:string;L1:integer):string;
var L,I : integer;
begin
  L := Length(S);
  if L > L1 then begin S := copy(S,2,L1); S := '*' + S; end;
  for I := 1 to L1-L do S := ' ' + S;
  JustR := S;
end;

(* PosP('1','12345123',3) => 7 *)
function posP(T:string;S:string;P1:integer):integer;  (* pos от позиции P1 *)
var P : integer;
begin
  if P1 < 1 then P1 := 1; (* строка нач-ся с первого, а не с 0-ого символа *)
  P:=System.pos(T,Copy(S,P1,length(S)-P1+1));
  if P>0 then PosP:=P+P1-1 else PosP:=0;
end; (* PosP *)

function posR(T,S:string):integer; (* pos справа налево *)
var P1,P2 : integer;
begin
  P2 := 0; P1 := System.pos(T,S);
  while P1 > 0 do begin
    P2 := P2 + P1; P1 := System.pos(T,copy(S,P2+1,length(S)-P2));
  end;
  Result := P2;
end;

function left(S:string;N:integer):string; (* аналог ф-ии из Бэйсика *)
var L : integer;
begin
  L := length(S);
  if N > L then N := L;
  if N <= 0 then result := '' else result := copy(S,1,N);
end;

function right(S:string;N:integer):string; (* аналог ф-ии из Бэйсика *)
var L : integer;
begin
  L := length(S);
  if N > L then N := L;
  if N <= 0 then result := '' else result := copy(S,L-N+1,N);
end;

function rightfrom(S:string;N:integer):string; (* копировать ОТ позиции N *)
var L : integer;
begin
  L := length(S);
  if N > L then result := '' else result := copy(S,N,L-N+1);
end;

(*----------------------------------------------------------*)
(*  обработка строк с именами или атрибутами файлов         *)
(*----------------------------------------------------------*)

function AddBackSlashForce(Path:string):string;
(* работает для существующих каталогов             *)
(* проверяет, что Path - именно каталог, а не файл *)
var F : file;
 Attr : word;
begin
  if Path[Length(Path)] <> '\' then
  begin
    Assign(F, Path);
    Attr := FileGetAttr(Path);    // ф-я зависит от ПЛАТФОРМЫ
//  GetFAttr(F, Attr);
//  if (DosError = 0) and (Attr and Directory <> 0) then
    if (Attr and faDirectory <> 0) then
      Path := Path + '\';
  end;
  Result := Path;
end;

function GetFullCurrentPath:string;
var S : string;
begin
  S := SysUtils.GetCurrentDir;   //'';     (* текущий каталог *)
  S := ExpandUNCFileName(S);
  Result := AddBackSlashForce(S);
end;

(* разбить имя файла на имя и расширение *)
procedure GetNamExt(const Nam:string;var NS,ES:string);
begin
  if (Nam = '.') or (Nam = '..') then begin
    NS := Nam; ES := '';
  end
  else begin
    ES := ExtractFileExt(Nam);
    NS := Copy(Nam,1,Length(Nam)-Length(ES));
    if ES <> '' then ES := Copy(ES,2,Length(ES)-1);
  end;
end;

(* преобразовать в строку дату/время созданияч файла *)
function FileTimeToS15(fTim:longint):string; (* длина строки 15 символов*)
var T: TDateTime;                            (* "DD-mmm-YY HH:MM" *)
   TT: TSystemTime;
begin
  T := FileDateToDateTime(fTim);
  DateTimeToSystemTime(T,TT);
  Result := IntStr(TT.wDay,2)+'-'+         // 3
            MonthStr[TT.wMonth]+'-'+       // 4
            NumStr(TT.wYear mod 100, 2)+   // 2
            IntStr(TT.wHour,3)+':'+        // 4
            NumStr(TT.wMinute,2);          // 2  =  15
end;

(*----------------------------------------------------------*)
(* Сообщение об ошибке                                      *)
(*----------------------------------------------------------*)
procedure Warn(S:string);
var W : PChar;
begin
  W := StrNew(PChar('W:'+S));
  Application.MessageBox
    (W,'Предупреждение или Напоминание',MB_OK {or MB_TASKMODAL});
  StrDispose(W);
end;

(*----------------------------------------------------------*)
(*  сортировка                                              *)
(*----------------------------------------------------------*)

(* $F+  директива, необходимая в ТурбоПаскале ------*)

function LessName(X, Y: pointer): Boolean;
begin LessName := StrUpCase(TDirRec(X).Name) < StrUpCase(TDirRec(Y).Name); end;

function LessExt(X, Y: pointer): Boolean;
var EX,EY : string;
begin
  EX := ExtractFileExt(TDirRec(X).Name);
  EY := ExtractFileExt(TDirRec(Y).Name);
  LessExt := StrUpCase(EX) < StrUpCase(EY);
end;

function LessSize(X, Y: pointer): Boolean;
begin LessSize := TDirRec(X).Size < TDirRec(Y).Size; end;

function LessTime(X, Y: pointer): Boolean;
begin LessTime := TDirRec(X).Time > TDirRec(Y).Time; end;

(* $F-  --------------------------------------------*)

procedure TDirList.LQuickSort(L, R: Integer);
var
  I, J: Integer;
  X   : TDirRec;
begin
  I := L;
  J := R;
  X := Get((L + R) div 2);
  repeat
    while Less(Get(I), X) do Inc(I);
    while Less(X, Get(J)) do Dec(J);
    if I <= J then
    begin
      SWAP(I,J);
      Inc(I);
      Dec(J);
    end;
  until I > J;
  if L < J then LQuickSort(L, J);
  if I < R then LQuickSort(I, R);
end;


(*------------------------------------*)
(* элементарные операции              *)
(*------------------------------------*)

function TDirList.Get(I:integer):TDirRec;
begin Result := TDirRec(Items[I]); end;

procedure TDirList.SWAP(I,J:integer);
var P : pointer;
begin P := Items[I]; Items[I] := Items[J]; Items[J] := P end;

procedure TDirList.Done;
var D : TDirRec;
    I : integer;
begin
  Path := '';
  for I := Self.Count downto 1 do begin
    D := TDirRec(Items[I-1]);
    D.Name := '';  (* освобождаем память, занятую строкой *)
    D.Free;
  end;
  Self.Free;
end;

(*------------------------------------*)
(* ОСНОВНОЕ ДЕЙСТВИЕ ОБЪЕКТА          *)
(*------------------------------------*)

procedure TDirList.Collect(aPath:string);
var F : TSearchRec;
    D : TDirRec;
    DosError : integer;
    PathTpl : string;
begin
  (* проверить существование каталога *)
  if Not DirectoryExists(aPath) then begin
    WARN('DirList.Collect: Path <'+aPath+'> Not found!');
    Exit;
  end;
  PathTpl := Path + '*.*';  (* шаблон поиска для FindFirst/FindNext *)
  if Count <> 0 then Done;  (* "забыть" предыдущую коллекцию файлов *)
  Path := aPath; SDiv := '!----!';
  DirBeg0 := 59; DirBeg := DirBeg0;
  DosError := FindFirst(PathTpl, faReadOnly + faDirectory + faArchive, F);
  while (DosError = 0) do
  begin
    D := TDirRec.Create;
    D.Attr := F.Attr;
    D.Time := F.Time;
    D.Size := F.Size;
    D.Name := F.Name;
    Add(D);
    DosError := FindNext(F);
  end;
  FindClose(F);
  CalcLen_s;     (* рассчитать длины полей вывода *)
  CorrectLen_s;  (* скорректировать, чтобы всё красиво размещалось *)
end;

(*------------------------------------*)
(* выборка и сортировка               *)
(*------------------------------------*)

procedure TDirList.GetDir_s;(* выбрать директории среди файлов и посчитать их *)
var I : integer;
    D : TDirRec;
begin
  CountDir_s := -1;
  for I:=0 to Count-1 do begin
    D := Get(I);
    if ((D.Attr and faDirectory) <> 0) then begin
      inc(CountDir_s); SWAP(CountDir_s,I);
    end;
  end;
  inc(CountDir_s);
end; (* GetDir_s *)

procedure TDirList.SortFiles(I1,I2:integer);
begin
  if (I1 < I2) and (@Less <> nil) then LQuickSort(I1, I2);
end;

procedure TDirList.SortInsideExt;
  (* в пределах одинаковых расширений имен
     отсортировать файлы по именам *)
var
  P,I1,I2,J1,J2 : integer;
  EX,EY : string; // : ExtStr;
  D1,D2 : TDirRec;
begin
  Less := LessName;
  I1 := CountDir_s;
  I2 := Count-1;             (* последний индекс в массиве Dir *)
  if (I2-I1)>1  then begin
    J1:=I1;
    repeat
      J2:=J1;
      D1 := Get(J1);
      EX := ExtractFileExt(D1.Name);
      repeat
        inc(J2);
        D2 := Get(J2);
        EY := ExtractFileExt(D2.Name);
      until ((EX<>EY) or (J2>=I2));
      SortFiles(J1,J2-1);
      J1:=J2;
    until (J2>=I2);
  end;
end; (* SortInsideExt *)

procedure TDirList.DefaultSort;
begin
  GetDir_s;               (* выбрать директории среди файлов и посчитать их *)
  Less := LessName;
  SortFiles(0,CountDir_s-1); (* отсортировать директории по именам      *)
  Less := LessExt;
  SortFiles(CountDir_s,Count-1); (* отсортировать файлы по расширениям имен *)
  SortInsideExt;                 (* в пределах одинаковых расширений имен
                                              отсортировать файлы по именам *)
end;

(*------------------------------------*)
(* подготовка к форматированию строк  *)
(*------------------------------------*)

procedure TDirList.FindDirBeg(S:string);
var P1,P2 : integer; QDone : boolean;
begin
  P1 := Pos('!',S); QDone := (P1 > 0);
  while Not QDone do begin
    P2 := PosP('!',S,P1+1);
    if P2 = 0 then begin
      WARN('В DIR - файле не найден разделитель "!----!" в строке'+
           chr(10)+S);
      QDone := true;
    end;
    if P2 - P1 = 5 then begin
      DirBeg0 := P1; DirBeg := P1; QDone := true;
    end
    else P1 := P2;
  end;
//WARN('DirBeg='+IntToStr(DirBeg));
end;

procedure TDirList.CalcLen_s;
var
  D : TDirRec;
  I : integer;
  N,E,SZ : string;
function GetSZ:string;
  begin
    if D.Attr and faDirectory <> 0
    then Result :='(DIR)'
    else Result :=IntToStr(D.Size);
  end;
begin
  LSN := 0;
  LSE := 0;
  LSZ := 0;
  for I := 0 to Count-1 do begin (* 1-ый проход - определяем ширины полей *)
    D := Get(I);
    GetNamExt(D.Name,N,E); (* N - имя, E - расширение     *)
    SZ := GetSZ;           (* SZ - размер или слово "DIR" *)
    if LSN < Length(N)  then LSN := Length(N);
    if LSE < Length(E)  then LSE := Length(E);
    if LSZ < Length(SZ) then LSZ := Length(SZ);
  end;
  LSD := 15;
  LSNC := LSN;
  LBlank := 1;   (* LBlank кол-во пробелов от конца записи до !----! *)
end;

procedure TDirList.CorrectLen_s;
begin
  if (Length(Path)+2) > (DirBeg - 1) then DirBeg := Length(Path)+3;

  if LSN + LSE + LSZ + LSD + 5 > (DirBeg - 1)
  then LSNC   := DirBeg + 1 - 1 - LSE - LSZ - LSD - 5
  else LBlank := DirBeg + 1 - (LSN + LSE + LSZ + LSD + 5);
end;

(*------------------------------------*)
(* результаты в строковом виде        *)
(*------------------------------------*)

function TDirList.GetStr(I:integer):string;
var
  D : TDirRec;
  S,N,E,SZ,ST,SLink,PathParent : string;
  J : integer;
begin
  D := Get(I);
  GetNamExt(D.Name,N,E);
  if D.Attr and faDirectory <> 0
    then SZ :='(DIR)'
    else SZ :=IntToStr(D.Size);
  ST := FileTimeToS15(D.Time);

  if D.Attr and faDirectory <> 0 then begin
    if (D.Name='.') then SLink := ''; {,PathNam,'micros.dir');}
    if (D.Name='..') then begin
      J:=length(Path)-1;
      while Path[J]<>'\' do Dec(J);
      PathParent:=Copy(Path,1,J);
      SLink  := PathParent+'micros.dir';
    end;
    if ((D.Name<>'.') and (D.Name<>'..')) then
      SLink := Path+D.Name+'\micros.dir';
  end
  else SLink := Path+D.Name;

  S := JustL(N,LSNC)+' '+JustL(E,LSE)+' '+JustR(SZ,LSZ)+' '
       +JustL(ST,LSD+LBlank)+SDiv
       +SLink;
  Result := S;
end;

function TDirList.GetStr0:string;
var S : string; I : integer;
begin
  S := '';
  for I := 1 to DirBeg-1 do S := S + ':';
  Result := S + SDiv;
end;

function TDirList.GetStrPath:string;
begin Result := '  '+JustL(Path,DirBeg-3)+SDiv; end;

function TDirList.GetStrTotal:string;
var Total : longint; I : integer; S : string;
begin
  Total := 0;
  for I := 0 to Count-1 do Inc(Total, Get(I).Size);
  S := ' '+IntToStr(Count)+' files, '+IntToStr(Total)+' bytes, '+
       IntToStr(DiskFree(Ord(Path[1])-64))+' bytes free';
  Result := JustL(S,DirBeg-1)+SDiv;
end;

end.
Вернуться
(с) Можаровский С.Г. // mailto:mozharovskys@mail.ru // swHome page