restemplate/indy/Protocols/IdFTPListOutput.pas

1657 lines
54 KiB
Plaintext
Raw Normal View History

{
$Project$
$Workfile$
$Revision$
$DateUTC$
$Id$
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
$Log$
}
{
Rev 1.18 12/10/04 1:13:34 PM RLebeau
FormatDateTime() fixes. Was using 'mm' instead of 'nn' for minutes.
Rev 1.17 10/26/2004 9:36:26 PM JPMugaas
Updated ref.
Rev 1.16 10/26/2004 9:19:14 PM JPMugaas
Fixed references.
Rev 1.15 10/1/2004 6:17:12 AM JPMugaas
Removed some dead code.
Rev 1.14 6/27/2004 1:45:36 AM JPMugaas
Can now optionally support LastAccessTime like Smartftp's FTP Server could.
I also made the MLST listing object and parser support this as well.
Rev 1.13 6/11/2004 9:34:44 AM DSiders
Added "Do not Localize" comments.
Rev 1.12 4/19/2004 5:06:02 PM JPMugaas
Class rework Kudzu wanted.
Rev 1.11 2004.02.03 5:45:34 PM czhower
Name changes
Rev 1.10 24/01/2004 19:18:48 CCostelloe
Cleaned up warnings
Rev 1.9 1/4/2004 12:09:54 AM BGooijen
changed System.Delete to IdDelete
Rev 1.8 11/26/2003 6:23:44 PM JPMugaas
Quite a number of fixes for recursive dirs and a few other things that
slipped my mind.
Rev 1.7 10/19/2003 2:04:02 PM DSiders
Added localization comments.
Rev 1.6 3/11/2003 07:36:00 PM JPMugaas
Now reports permission denied in subdirs when doing recursive listts in Unix
export.
Rev 1.5 3/9/2003 12:01:26 PM JPMugaas
Now can report errors in recursive lists.
Permissions work better.
Rev 1.4 3/3/2003 07:18:34 PM JPMugaas
Now honors the FreeBSD -T flag and parses list output from a program using
it. Minor changes to the File System component.
Rev 1.3 2/26/2003 08:57:10 PM JPMugaas
Bug fix. The owner and group should be left-justified.
Rev 1.2 2/24/2003 07:24:00 AM JPMugaas
Now honors more Unix switches just like the old code and now work with the
NLIST command when emulating Unix. -A switch support added. Switches are
now in constants.
Rev 1.1 2/23/2003 06:19:42 AM JPMugaas
Now uses Classes instead of classes.
Rev 1.0 2/21/2003 06:51:46 PM JPMugaas
FTP Directory list output object for the FTP server.
}
unit IdFTPListOutput;
interface
{$i IdCompilerDefines.inc}
uses
Classes,
IdGlobal,
IdFTPList;
type
// we can't use the standard FTP MLSD option types in the FTP Server
// because we support some minimal things that the user can't set.
// We have the manditory items to make it harder for the user to mess up.
TIdFTPFactOutput = (ItemType, Modify, Size, Perm, Unique, UnixMODE, UnixOwner,
UnixGroup, CreateTime, LastAccessTime, WinAttribs,WinDriveType,WinDriveLabel);
TIdFTPFactOutputs = set of TIdFTPFactOutput;
TIdDirOutputFormat = (doUnix, doWin32, doEPLF);
TIdFTPListOutputItem = class(TIdFTPListItem)
protected
FLinkCount: Integer;
FGroupName: string;
FOwnerName : String;
FLinkedItemName : string;
FNumberBlocks : Integer;
FInode : Integer;
FLastAccessDate: TDateTime;
FLastAccessDateGMT: TDateTime;
FCreationDate: TDateTime;
FCreationDateGMT : TDateTime;
//Unique ID for an item to prevent yourself from downloading something twice
FUniqueID : String;
//MLIST things
FMLISTPermissions : String;
FUnixGroupPermissions: string;
FUnixOwnerPermissions: string;
FUnixOtherPermissions: string;
FUnixinode : Integer;
FWinAttribs : UInt32;
//an error has been reported in the DIR listing itself for an item
FDirError : Boolean;
FWinDriveType : Integer;
FWinDriveLabel : String;
public
constructor Create(AOwner: TCollection); override;
property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks;
property Inode : Integer read FInode write FInode;
//Last Access time values are for MLSD data output and can be returned by the MLST command
property LastAccessDate: TDateTime read FLastAccessDate write FLastAccessDate;
property LastAccessDateGMT : TDateTime read FLastAccessDateGMT write FLastAccessDateGMT;
//Creation time values are for MLSD data output and can be returned by the MLST command
property CreationDate: TDateTime read FCreationDate write FCreationDate;
property CreationDateGMT : TDateTime read FCreationDateGMT write FCreationDateGMT;
// If this is not blank, you can use this as a unique identifier for an item to prevent
// yourself from downloading the same item twice (which is not easy to see with some
// some FTP sites where symbolic links or similar things are used.
//Valid only with EPLF and MLST
property UniqueID : string read FUniqueID write FUniqueID;
//Creation time values are for MLSD data output and can be returned by the
//the MLSD parser in some cases
property ModifiedDateGMT;
//Windows NT File Attributes (just like what is reported by RaidenFTPD
//BlackMoon FTP Server, and Serv-U
//On the server side, you deal with it as a number right from the Win32 FindFirst,
//FindNext functions. Easy
property WinAttribs : UInt32 read FWinAttribs write FWinAttribs;
property WinDriveType : Integer read FWinDriveType write FWinDriveType;
property WinDriveLabel : String read FWinDriveLabel write FWinDriveLabel;
//MLIST Permissions
property MLISTPermissions : string read FMLISTPermissions write FMLISTPermissions;
property UnixOwnerPermissions: string read FUnixOwnerPermissions write FUnixOwnerPermissions;
property UnixGroupPermissions: string read FUnixGroupPermissions write FUnixGroupPermissions;
property UnixOtherPermissions: string read FUnixOtherPermissions write FUnixOtherPermissions;
property LinkCount: Integer read FLinkCount write FLinkCount;
property OwnerName: string read FOwnerName write FOwnerName;
property GroupName: string read FGroupName write FGroupName;
property LinkedItemName : string read FLinkedItemName write FLinkedItemName;
property DirError : Boolean read FDirError write FDirError;
end;
TIdFTPListOutput = class(TCollection)
protected
FSwitches : String;
FOutput : String;
FDirFormat : TIdDirOutputFormat;
FExportTotalLine : Boolean;
function GetLocalModTime(AItem : TIdFTPListOutputItem) : TDateTime; virtual;
function HasSwitch(const ASwitch: String): Boolean;
function UnixItem(AItem : TIdFTPListOutputItem) : String; virtual;
function Win32Item(AItem : TIdFTPListOutputItem) : String; virtual;
function EPLFItem(AItem : TIdFTPListOutputItem) : String; virtual;
function NListItem(AItem : TIdFTPListOutputItem) : String; virtual;
function MListItem(AItem : TIdFTPListOutputItem; AMLstOpts : TIdFTPFactOutputs) : String; virtual;
procedure InternelOutputDir(AOutput : TStrings; ADetails : Boolean = True); virtual;
function UnixINodeOutput(AItem : TIdFTPListOutputItem) : String;
function UnixBlocksOutput(AItem : TIdFTPListOutputItem) : String;
function UnixGetOutputOwner(AItem : TIdFTPListOutputItem) : String;
function UnixGetOutputGroup(AItem : TIdFTPListOutputItem) : String;
function UnixGetOutputOwnerPerms(AItem : TIdFTPListOutputItem) : String;
function UnixGetOutputGroupPerms(AItem : TIdFTPListOutputItem) : String;
function UnixGetOutputOtherPerms(AItem : TIdFTPListOutputItem) : String;
function GetItems(AIndex: Integer): TIdFTPListOutputItem;
procedure SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem);
public
function Add: TIdFTPListOutputItem;
constructor Create; reintroduce;
function IndexOf(AItem: TIdFTPListOutputItem): Integer;
property Items[AIndex: Integer]: TIdFTPListOutputItem read GetItems write SetItems; default;
procedure LISTOutputDir(AOutput : TStrings); virtual;
procedure MLISTOutputDir(AOutput : TStrings; AMLstOpts : TIdFTPFactOutputs); virtual;
procedure NLISTOutputDir(AOutput : TStrings); virtual;
property DirFormat : TIdDirOutputFormat read FDirFormat write FDirFormat;
property Switches : String read FSwitches write FSwitches;
property Output : String read FOutput write FOutput;
property ExportTotalLine : Boolean read FExportTotalLine write FExportTotalLine;
end;
const
DEF_FILE_OWN_PERM = 'rw-'; {do not localize}
DEF_FILE_GRP_PERM = DEF_FILE_OWN_PERM;
DEF_FILE_OTHER_PERM = 'r--'; {do not localize}
DEF_DIR_OWN_PERM = 'rwx'; {do not localize}
DEF_DIR_GRP_PERM = DEF_DIR_OWN_PERM;
DEF_DIR_OTHER_PERM = 'r-x'; {do not localize}
DEF_OWNER = 'root'; {do not localize}
{NLIST and LIST switches - based on /bin/ls }
{
Note that the standard Unix form started simply by Unix
FTP deamons piping output from the /bin/ls program for both
the NLIST and LIST FTP commands. The standard /bin/ls
program has several standard switches that allow the output
to be customized. For our output, we wish to emulate this behavior.
Microsoft IIS even honors a subset of these switches dealing sort order
and recursive listings. It does not honor some sort-by-switches although
we honor those in Win32 (hey, we did MS one better, not that it says much though.
}
const
{format switches - used by Unix mode only}
SWITCH_COLS_ACCROSS = 'x';
SWITCH_COLS_DOWN = 'C';
SWITCH_ONE_COL = '1';
SWITCH_ONE_DIR = 'f';
SWITCH_COMMA_STREAM = 'm';
SWITCH_LONG_FORM = 'l';
{recursive for both Win32 and Unix forms}
SWITCH_RECURSIVE = 'R';
{sort switches - used both by Win32 and Unix forms}
SWITCH_SORT_REVERSE = 'r';
SWITCH_SORTBY_MTIME = 't';
SWITCH_SORTBY_CTIME = 'u';
SWITCH_SORTBY_EXT = 'X';
SWITCH_SORTBY_SIZE = 'S';
{Output switches for Unix mode only}
SWITCH_CLASSIFY = 'F';
//
{ -F Put aslash (/) aftereach filename if the file is a directory, an
asterisk (*) if the file is executable, an equal sign(=) if the
file is an AF_UNIX address family socket, andan ampersand (@) if
the file is asymbolic link.Unless the -H option isalso used,
symbolic links are followed to see ifthey might be adirectory; see
above.
From:
http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?ls+1 }
SWITCH_SLASHDIR = 'p';
SWITCH_QUOTEDNAME = 'Q';
SWITCH_PRINT_BLOCKS = 's';
SWITCH_PRINT_INODE = 'i';
SWITCH_SHOW_ALLPERIOD = 'a'; //show all entries even ones with a pariod starting the filename/hidden
//note that anything starting with a period is shown except for the .. and . entries for security reasons
SWITCH_HIDE_DIRPOINT = 'A'; //hide the "." and ".." entries
SWITCH_BOTH_TIME_YEAR = 'T'; //This is used by FTP Voyager with a Serv-U FTP Server to both
//a time and year in the FTP list. Note that this does conflict with a ls -T flag used to specify a column size
//on Linux but in FreeBSD, the -T flag is also honored.
implementation
uses
//facilitate inlining only.
IdException,
{$IFDEF DOTNET}
{$IFDEF USE_INLINE}
System.IO,
{$ENDIF}
{$ENDIF}
{$IFDEF VCL_XE3_OR_ABOVE}
{$IFNDEF NEXTGEN}
System.Contnrs,
{$ENDIF}
System.Types,
{$ENDIF}
{$IFDEF USE_VCL_POSIX}
Posix.SysTime,
{$ENDIF}
IdContainers, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils;
type
{$IFDEF HAS_GENERICS_TObjectList}
TDirEntry = class;
TDirEntryList = TIdObjectList<TDirEntry>;
{$ELSE}
// TODO: flesh out to match TIdObjectList<TDirEntry> for non-Generics compilers
TDirEntryList = TIdObjectList;
{$ENDIF}
TDirEntry = class(TObject)
protected
FPathName : String;
FDirListItem : TIdFTPListOutputItem;
FSubDirs : TDirEntryList;
FFileList : TIdBubbleSortStringList;
public
constructor Create(const APathName : String; ADirListItem : TIdFTPListOutputItem);
destructor Destroy; override;
// procedure Sort(ACompare: TIdSortCompare;const Recurse : Boolean = True);
procedure SortAscendFName;
procedure SortDescendFName;
procedure SortAscendMTime;
procedure SortDescendMTime;
procedure SortAscendSize;
procedure SortDescendSize;
procedure SortAscendFNameExt;
procedure SortDescendFNameExt;
function AddSubDir(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean;
function AddFileName(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean;
property SubDirs : TDirEntryList read FSubDirs;
property FileList : TIdBubbleSortStringList read FFileList;
property PathName : String read FPathName;
property DirListItem : TIdFTPListOutputItem read FDirListItem;
end;
function RawSortAscFName(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
var
{
> 0 (positive) Item1 is less than Item2
= 0 Item1 is equal to Item2
< 0 (negative) Item1 is greater than Item2
}
LTmpPath1, LTmpPath2 : String;
LPath1Dot, LPath2Dot : Boolean;
begin
LTmpPath1 := IndyGetFileName(AItem1.FileName);
LTmpPath2 := IndyGetFileName(AItem2.FileName);
//periods are always greater then letters in dir lists
LPath1Dot := TextStartsWith(LTmpPath1, '.');
LPath2Dot := TextStartsWith(LTmpPath2, '.');
if LPath1Dot and LPath2Dot then begin
if (LTmpPath1 = CUR_DIR) and (LTmpPath2 = PARENT_DIR) then begin
Result := 1;
Exit;
end;
if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = PARENT_DIR) then begin
Result := -1;
Exit;
end;
if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = CUR_DIR) then begin
Result := 0;
Exit;
end;
if (LTmpPath2 = PARENT_DIR) and (LTmpPath1 = PARENT_DIR) then begin
Result := 0;
Exit;
end;
end;
if LPath2Dot and (not LPath1Dot) then begin
Result := -1;
Exit;
end;
if LPath1Dot and (not LPath2Dot) then begin
Result := 1;
Exit;
end;
Result := -IndyCompareStr(LTmpPath1, LTmpPath2);
end;
function RawSortDescFName(AItem1, AItem2: TIdFTPListItem): Integer;
begin
Result := -RawSortAscFName(AItem1, AItem2);
end;
function RawSortAscFNameExt(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
var
{
> 0 (positive) Item1 is less than Item2
= 0 Item1 is equal to Item2
< 0 (negative) Item1 is greater than Item2
}
LTmpPath1, LTmpPath2 : String;
begin
LTmpPath1 := ExtractFileExt(AItem1.FileName);
LTmpPath2 := ExtractFileExt(AItem2.FileName);
Result := -IndyCompareStr(LTmpPath1, LTmpPath2);
if Result = 0 then begin
Result := RawSortAscFName(AItem1, AItem2);
end;
end;
function RawSortDescFNameExt(AItem1, AItem2: TIdFTPListItem): Integer;
begin
Result := -RawSortAscFNameExt(AItem1, AItem2, False);
end;
function RawSortAscMTime(AItem1, AItem2: TIdFTPListItem): Integer;
{
> 0 (positive) Item1 is less than Item2
0 Item1 is equal to Item2
< 0 (negative) Item1 is greater than Item2
}
begin
if AItem1.ModifiedDate < AItem2.ModifiedDate then begin
Result := -1;
end
else if AItem1.ModifiedDate > AItem2.ModifiedDate then begin
Result := 1;
end
else begin
Result := RawSortAscFName(AItem1, AItem2);
end;
end;
function RawSortDescMTime(AItem1, AItem2: TIdFTPListItem): Integer;
begin
Result := -RawSortAscMTime(AItem1, AItem2);
end;
function RawSortAscSize(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
var
LSize1, LSize2 : Int64;
{
> 0 (positive) Item1 is less than Item2
= 0 Item1 is equal to Item2
< 0 (negative) Item1 is greater than Item2
}
begin
LSize1 := AItem1.Size;
LSize2 := AItem2.Size;
if TIdFTPListOutput(AItem1.Collection).DirFormat = doUnix then begin
if AItem1.ItemType = ditDirectory then begin
LSize1 := UNIX_DIR_SIZE;
end;
if AItem2.ItemType = ditDirectory then begin
LSize2 := UNIX_DIR_SIZE;
end;
end;
if LSize1 < LSize2 then begin
Result := -1;
end
else if LSize1 > LSize2 then begin
Result := 1;
end else begin
Result := RawSortAscFName (AItem1, AItem2);
end;
end;
function RawSortDescSize(AItem1, AItem2: TIdFTPListItem): Integer;
begin
Result := -RawSortAscSize(AItem1, AItem2, False);
end;
{DirEntry objects}
function DESortAscFName(AItem1, AItem2: TDirEntry): Integer;
begin
Result := -IndyCompareStr(AItem1.PathName, AItem2.PathName);
end;
function DESortAscMTime(AItem1, AItem2: TDirEntry): Integer;
var
L1, L2 : TIdFTPListItem;
{
> 0 (positive) Item1 is less than Item2
= 0 Item1 is equal to Item2
< 0 (negative) Item1 is greater than Item2
}
begin
L1 := AItem1.DirListItem;
L2 := AItem2.DirListItem;
if L1.ModifiedDate > L2.ModifiedDate then begin
Result := -1;
end
else if L1.ModifiedDate < L2.ModifiedDate then begin
Result := 1;
end else begin
Result := DESortAscFName(AItem1, AItem2);
end;
end;
function DESortDescMTime(AItem1, AItem2: TDirEntry): Integer;
begin
Result := -DESortAscMTime(AItem1, AItem2);
end;
function DESortDescFName(AItem1, AItem2: TDirEntry): Integer;
begin
Result := -DESortAscFName(AItem1, AItem2);
end;
{stringlist objects}
function StrSortAscMTime(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortAscMTime(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortDescMTime(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortDescMTime(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortAscSize(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortAscSize(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortDescSize(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortDescSize(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortAscFName(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortAscFName(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortDescFName(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortDescFName(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortAscFNameExt(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortAscFNameExt(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
function StrSortDescFNameExt(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := RawSortDescFNameExt(
TIdFTPListItem(List.Objects[Index1]),
TIdFTPListItem(List.Objects[Index2]));
end;
{ TIdFTPListOutput }
function TIdFTPListOutput.Add: TIdFTPListOutputItem;
begin
Result := TIdFTPListOutputItem(inherited Add);
end;
constructor TIdFTPListOutput.Create;
begin
inherited Create(TIdFTPListOutputItem);
FDirFormat := doUnix;
end;
function TIdFTPListOutput.EPLFItem(AItem: TIdFTPListOutputItem): String;
var
LFileName : String;
begin
LFileName := IndyGetFileName(AItem.FileName);
if AItem.ModifiedDateGMT > EPLF_BASE_DATE then begin
Result := '+m' + GMTDateTimeToEPLFDate(AItem.ModifiedDateGMT);
end
else if AItem.ModifiedDate > EPLF_BASE_DATE then begin
Result := '+m'+LocalDateTimeToEPLFDate(AItem.ModifiedDate);
end else begin
Result := '';
end;
if AItem.ItemType = ditFile then begin
Result := Result + ',r';
end else begin
Result := Result + ',/';
end;
Result := Result + ',s' + IntToStr(AItem.Size);
Result := Result + #9 + LFileName;
end;
function TIdFTPListOutput.GetItems(AIndex: Integer): TIdFTPListOutputItem;
begin
Result := TIdFTPListOutputItem(inherited GetItem(AIndex));
end;
function TIdFTPListOutput.GetLocalModTime(AItem: TIdFTPListOutputItem): TDateTime;
begin
if AItem.ModifiedDateGMT <> 0 then begin
Result := AItem.ModifiedDateGMT - TimeZoneBias;
end else begin
Result := AItem.ModifiedDate;
end;
end;
function TIdFTPListOutput.HasSwitch(const ASwitch: String): Boolean;
begin
Result := IndyPos(ASwitch, Switches) > 0;
end;
function TIdFTPListOutput.IndexOf(AItem: TIdFTPListOutputItem): Integer;
var
i : Integer;
begin
Result := -1;
for i := 0 to Count - 1 do begin
if AItem = Items[i] then begin
Result := i;
Exit;
end;
end;
end;
procedure TIdFTPListOutput.InternelOutputDir(AOutput: TStrings; ADetails: Boolean);
type
TIdDirOutputType = (doColsAccross, doColsDown, doOneCol, doOnlyDirs, doComma, doLong);
var
i : Integer;
//note we use this for sorting pathes with recursive dirs
LRootPath : TDirEntry;
LShowNavSym : BOolean;
function DetermineOp : TIdDirOutputType;
//we do things this way because the last switch in a mutually exclusive set
//always takes precidence over the others.
var
LStopScan : Boolean;
li : Integer;
begin
if ADetails then begin
Result := doLong;
end else begin
Result := doOneCol;
end;
if DirFormat <> doUnix then begin
Exit;
end;
LStopScan := False;
for li := Length(Switches) downto 1 do begin
case Switches[li] of
SWITCH_COLS_ACCROSS :
begin
Result := doColsAccross;
LStopScan := True;
end;
SWITCH_COLS_DOWN :
begin
Result := doColsDown;
LStopScan := True;
end;
SWITCH_ONE_COL :
begin
Result := doOneCol;
LStopScan := True;
end;
SWITCH_ONE_DIR :
begin
Result := doOnlyDirs;
LStopScan := True;
end;
SWITCH_COMMA_STREAM :
begin
Result := doComma;
LStopScan := True;
end;
SWITCH_LONG_FORM :
begin
Result := doLong;
LStopScan := True;
end;
end;
if LStopScan then begin
Break;
end;
end;
end;
procedure PrintSubDirHeader(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
LUnixPrependPath : Boolean;
begin
LUnixPrependPath := HasSwitch(SWITCH_SORT_REVERSE) or HasSwitch(SWITCH_SORTBY_MTIME) or (DetermineOp <> doLong);
if (ACurDir <> ARoot) or LUnixPrependPath then begin
//we don't want an empty line to start the list
if ACurDir <> ARoot then begin
ALOutput.Add('');
end;
if DirFormat = doWin32 then begin
ALOutput.Add(MS_DOS_CURDIR + UnixPathToDOSPath(ACurDir.PathName) + ':');
end
else if LUnixPrependPath then begin
if ACurDir = ARoot then begin
ALOutput.Add(CUR_DIR + ':');
end else begin
ALOutput.Add(UNIX_CURDIR + DOSPathToUnixPath(ACurDir.PathName) + ':');
end;
end else begin
ALOutput.Add(DOSPathToUnixPath(ACurDir.PathName) + ':');
end;
end;
end;
procedure ProcessOnePathCol(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
li : Integer;
LCurItem : TIdFTPListOutputItem;
begin
if Recurse and Assigned(ACurDir.SubDirs) then begin
if Recurse then begin
PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
end;
end;
for li := 0 to ACurDir.FileList.Count-1 do begin
ALOutput.Add(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])));
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count-1 do begin
LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
if LCurItem.DirError then begin
if li = 0 then begin
ALOutput.Add('');
end;
ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
end else begin
ProcessOnePathCol(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
end;
end;
end;
end;
function CalcMaxLen(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False) : Integer;
var
LEntryMaxLen : Integer;
li : Integer;
begin
Result := 0;
for li := 0 to ACurDir.FileList.Count-1 do begin
LEntryMaxLen := Length(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])));
if LEntryMaxLen > Result then begin
Result := LEntryMaxLen;
end;
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count-1 do begin
LEntryMaxLen := CalcMaxLen(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
if LEntryMaxLen > Result then begin
Result := LEntryMaxLen;
end;
end;
end;
end;
procedure ProcessPathAccross(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
li, j : Integer;
LTmp : String;
LMaxLen : Integer;
LCols : Integer;
LCurItem : TIdFTPListOutputItem;
begin
if ACurDir.FileList.Count = 0 then begin
Exit;
end;
//Note that we will assume a console width of 80 and we don't want something to wrap
//causing a blank line
LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse);
//if more than 39, we probably are going to exceed the width of the screen,
//just treat as one column
if LMaxLen > 39 then begin
ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse);
Exit;
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
if Recurse then begin
PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
end;
end;
LCols := 79 div (LMaxLen + 2);//2 spaces between columns
j := 0;
repeat
LTmp := '';
for li := 0 to LCols -1 do begin
LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[j])), LMaxLen, ' ') + ' ';
Inc(j);
if j = ACurDir.FileList.Count then begin
Break;
end;
end;
ALOutput.Add(TrimRight(LTmp));
until j = ACurDir.FileList.Count;
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count-1 do begin
LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
if LCurItem.DirError then begin
if li = 0 then begin
ALOutput.Add('');
end;
ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
end else begin
ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
end;
end;
end;
end;
procedure ProcessPathDown(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
li, j : Integer;
LTmp : String;
LMaxLen : Integer;
LCols : Integer;
LLines : Integer;
// LFrm : String;
LCurItem : TIdFTPListOutputItem;
begin
if ACurDir.FileList.Count = 0 then begin
Exit;
end;
//Note that we will assume a console width of 80 and we don't want something to wrap
//causing a blank line
LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse);
//if more than 39, we probably are going to exceed the width of the screen,
//just treat as one column
if LMaxLen > 39 then begin
ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse);
Exit;
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
if Recurse then begin
PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
end;
end;
LCols := 79 div (LMaxLen + 2);//2 spaces between columns
LLines := ACurDir.FileList.COunt div LCols;
//LFrm := '%' + IntToStr(LMaxLen+2) + 's';
if (ACurDir.FileList.COunt mod LCols) > 0 then begin
Inc(LLines);
end;
for li := 1 to LLines do begin
j := 0;
LTmp := '';
repeat
if ((li-1)+(LLInes*j)) < ACurDir.FileList.Count then begin
LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[(li-1)+(LLInes*j)])), LMaxLen, ' ') + ' ';
end;
Inc(j);
until (j > LCols);
ALOutput.Add(TrimRight(LTmp));
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count -1 do begin
LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
if LCurItem.DirError then begin
if li = 0 then begin
ALOutput.Add('');
end;
ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
end else begin
ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
end;
end;
end;
end;
procedure ProcessPathComma(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
li : Integer;
LTmp : String;
LCurItem : TIdFTPListOutputItem;
begin
if Recurse then begin
PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
end;
LTmp := '';
for li := 0 to ACurDir.FileList.Count -1 do begin
LTmp := LTmp + NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])) + ', ';
end;
IdDelete(LTmp, Length(LTmp)-1, 2);
ALOutput.Text := ALOutput.Text + IndyWrapText(LTmp, EOL + ' ', LWS + ',' , 79); //79 good maxlen for text only terminals
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count -1 do begin
LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
if LCurItem.DirError then begin
if li = 0 then begin
ALOutput.Add('');
end;
ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
end else begin
ProcessPathComma(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
end;
end;
end;
end;
procedure ProcessPathLong(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
var
li : Integer;
LBlockCount : Integer;
LCurItem : TIdFTPListOutputItem;
begin
if Recurse then begin
PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
end;
if (DirFormat = doUnix) and ExportTotalLine then begin
LBlockCount := 0;
for li := 0 to ACurDir.FileList.Count-1 do begin
LBlockCount := LBlockCount + TIdFTPListOutputItem(ACurDir.FileList.Objects[li]).NumberBlocks;
end;
ALOutput.Add(IndyFormat('total %d', [LBlockCount])); {Do not translate}
end;
for li := 0 to ACurDir.FileList.Count-1 do begin
LCurItem := TIdFTPListOutputItem(ACurDir.FileList.Objects[li]);
case DirFormat of
doEPLF : ALOutput.Add(EPLFItem(LCurItem));
doWin32 : ALOutput.Add(Win32Item(LCurItem));
else
ALOutput.Add(UnixItem(LCurItem));
end;
end;
if Recurse and Assigned(ACurDir.SubDirs) then begin
for li := 0 to ACurDir.SubDirs.Count-1 do begin
LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
if LCurItem.DirError then begin
if DirFormat = doUnix then begin
if li = 0 then begin
ALOutput.Add('');
end;
ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
end;
end;
ProcessPathLong(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
end;
end;
end;
procedure DoUnixfParam(ARoot : TDirEntry; ALOutput : TStrings);
var
li : Integer;
LIt : TIdFTPListItem;
begin
for li := 0 to ARoot.FileList.Count -1 do begin
LIt := TIdFTPListItem(ARoot.FileList.Objects[li]);
if LIt.ItemType = ditDirectory then begin
ALOutput.Add(IndyGetFileName(LIt.FileName));
end;
end;
end;
begin
LShowNavSym := (DirFormat = doUnix) and HasSwitch(SWITCH_SHOW_ALLPERIOD);
if LShowNavSym then begin
LShowNavSym := not HasSwitch(SWITCH_HIDE_DIRPOINT);
end;
LRootPath := TDirEntry.Create('', nil);
try
for i := 0 to Count-1 do
begin
if Items[i].ItemType in [ditDirectory, ditSymbolicLinkDir] then begin
if not IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin
LRootPath.AddSubDir(StripInitPathDelim(Items[i].FileName), Items[i]);
end else begin
//if it's a "." or "..", we show it only in Unix mode and only with eht -a switch
if LShowNavSym then begin
LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
end;
end;
end;
end;
//add the file names
for i := 0 to Count-1 do begin
if Items[i].ItemType in [ditFile, ditSymbolicLink] then begin
if IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin
if LShowNavSym then begin
LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
end;
end else begin
LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
end;
end;
end;
//Note that Indy does not support a Last Access time in some file systems
//so we use the u parameter to mean the same as the t parameter
if HasSwitch(SWITCH_SORT_REVERSE) then begin
if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin
LRootPath.SortDescendMTime;
end
else if HasSwitch(SWITCH_SORTBY_EXT) then begin
LRootPath.SortDescendFNameExt;
end
else if HasSwitch(SWITCH_SORTBY_SIZE) then begin
LRootPath.SortDescendSize;
end
else begin
LRootPath.SortDescendFName;
end;
end
else if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin
LRootPath.SortAscendMTime;
end
else if HasSwitch(SWITCH_SORTBY_EXT) then begin
LRootPath.SortAscendFNameExt;
end
else if HasSwitch(SWITCH_SORTBY_SIZE) then begin
LRootPath.SortAscendSize;
end
else begin
LRootPath.SortAscendFName;
end;
//select the operation
// do the selected output operation
case DetermineOp of
doColsAccross : ProcessPathAccross(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
doColsDown : ProcessPathDown(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
doOneCol : ProcessOnePathCol(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
doOnlyDirs : DoUnixfParam(LRootPath, AOutput);
doComma : ProcessPathComma(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
else
ProcessPathLong(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
end;
finally
FreeAndNil(LRootPath);
end;
end;
procedure TIdFTPListOutput.LISTOutputDir(AOutput: TStrings);
begin
InternelOutputDir(AOutput, True);
end;
function TIdFTPListOutput.MListItem(AItem: TIdFTPListOutputItem; AMLstOpts: TIdFTPFactOutputs): String;
begin
Result := '';
if AMLstOpts = [] then begin
Result := AItem.FileName;
Exit;
end;
if (Size in AMLstOpts) and AItem.SizeAvail then begin
Result := 'size=' + IntToStr(AItem.Size) + ';'; {do not localize}
end;
if ItemType in AMLstOpts then begin
Result := Result + 'type='; {do not localize}
case AItem.ItemType of
ditFile :
begin
Result := Result + 'file;'; {do not localize}
end;
ditDirectory :
begin
if AItem.FileName = '..' then begin {do not localize}
Result := Result + 'pdir;'; {do not localize}
end
else if AItem.FileName = '.' then begin
Result := Result + 'cdir;'; {do not localize}
end
else begin
Result := Result + 'dir;'; {do not localize}
end;
end;
ditSymbolicLink :
begin
Result := Result + 'OS.unix=slink:' + AItem.FileName + ';'; {do not localize}
end;
end;
end;
if Perm in AMLstOpts then begin
Result := Result + 'perm=' + AItem.MLISTPermissions + ';'; {do not localize}
end;
if (winDriveType in AMLstOpts) and (AItem.WinDriveType<>-1) then begin
Result := Result + 'win32.dt='+IntToStr(AItem.WinDriveType )+';';
end;
if CreateTime in AMLstOpts then begin
if AItem.CreationDateGMT <> 0 then begin
Result := Result + 'create='+ FTPGMTDateTimeToMLS(AItem.CreationDateGMT) + ';'; {do not localize}
end
else if AItem.CreationDate <> 0 then begin
Result := Result + 'create='+ FTPLocalDateTimeToMLS(AItem.CreationDate) + ';'; {do not localize}
end;
end;
if (Modify in AMLstOpts) and AItem.ModifiedAvail then
begin
if AItem.ModifiedDateGMT <> 0 then begin
Result := Result + 'modify='+ FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize}
end
else if AItem.ModifiedDate <> 0 then begin
Result := Result + 'modify='+ FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize}
end;
end;
if UnixMODE in AMLstOpts then begin
Result := Result + 'UNIX.mode='+ IndyFormat('%.4d', [PermsToChmodNo(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem) )] ) + ';'; {do not localize}
end;
if UnixOwner in AMLstOpts then begin
Result := Result + 'UNIX.owner=' + UnixGetOutputOwner(AItem) + ';'; {do not localize}
end;
if UnixGroup in AMLstOpts then begin
Result := Result + 'UNIX.group=' + UnixGetOutputGroup(AItem) + ';'; {do not localize}
end;
if (Unique in AMLstOpts) and (AItem.UniqueID <> '') then begin
Result := Result + 'unique=' + AItem.UniqueID + ';'; {do not localize}
end;
if LastAccessTime in AMLstOpts then begin
if AItem.ModifiedDateGMT <> 0 then begin
Result := Result + 'windows.lastaccesstime=' + FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize}
end
else if AItem.ModifiedDate <> 0 then begin
Result := Result + 'windows.lastaccesstime=' + FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize}
end;
end;
if WinAttribs in AMLstOpts then begin
Result := Result + 'win32.ea=0x' + IntToHex(AItem.WinAttribs, 8) + ';'; {do not localize}
end;
if (AItem.WinDriveType > -1) and (WinDriveType in AMLstOpts) then begin
Result := Result + 'Win32.dt='+IntToStr( AItem.WinDriveType ) + ';';
end;
if (AItem.WinDriveLabel <> '') and (WinDriveLabel in AMLstOpts) then begin
Result := Result + 'Win32.dl='+AItem.WinDriveLabel;
end;
Result := Result + ' ' + AItem.FileName;
end;
procedure TIdFTPListOutput.MLISTOutputDir(AOutput : TStrings; AMLstOpts: TIdFTPFactOutputs);
var
i : Integer;
begin
AOutput.Clear;
for i := 0 to Count-1 do begin
AOutput.Add(MListItem(Items[i], AMLstOpts));
end;
end;
function TIdFTPListOutput.NListItem(AItem: TIdFTPListOutputItem): String;
begin
Result := IndyGetFileName(AItem.FileName);
if DirFormat = doUnix then begin
if HasSwitch(SWITCH_QUOTEDNAME) then begin
Result := '"' + Result + '"';
end;
if HasSwitch(SWITCH_CLASSIFY) or HasSwitch(SWITCH_SLASHDIR) then begin
case AItem.ItemType of
ditDirectory :
Result := Result + PATH_SUBDIR_SEP_UNIX;
ditSymbolicLink, ditSymbolicLinkDir :
Result := Result + '@';
else
begin
if IsUnixExec(AItem.UnixOwnerPermissions, AItem.UnixGroupPermissions , AItem.UnixOtherPermissions) then begin
Result := Result + '*';
end;
end;
end;
end;
Result := UnixinodeOutput(AItem)+ UnixBlocksOutput(AItem) + Result;
end;
end;
procedure TIdFTPListOutput.NLISTOutputDir(AOutput: TStrings);
begin
InternelOutputDir(AOutput, False);
end;
procedure TIdFTPListOutput.SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem);
begin
inherited Items[AIndex] := AValue;
end;
function TIdFTPListOutput.UnixBlocksOutput(AItem: TIdFTPListOutputItem): String;
begin
if HasSwitch(SWITCH_PRINT_BLOCKS) then begin
Result := IndyFormat('%4d ', [AItem.NumberBlocks]);
end else begin
Result := '';
end;
end;
function TIdFTPListOutput.UnixGetOutputGroup(AItem: TIdFTPListOutputItem): String;
begin
if AItem.GroupName = '' then begin
Result := UnixGetOutputOwner(AItem);
end else begin
Result := AItem.GroupName;
end;
end;
function TIdFTPListOutput.UnixGetOutputGroupPerms(AItem: TIdFTPListOutputItem): String;
begin
if AItem.UnixOtherPermissions = '' then begin
if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
Result := DEF_DIR_GRP_PERM;
end else begin
Result := DEF_FILE_GRP_PERM;
end;
end else begin
Result := AItem.UnixOtherPermissions;
end;
end;
function TIdFTPListOutput.UnixGetOutputOtherPerms(AItem: TIdFTPListOutputItem): String;
begin
if AItem.UnixOtherPermissions = '' then begin
if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
Result := DEF_DIR_OTHER_PERM;
end else begin
Result := DEF_FILE_OTHER_PERM;
end;
end else begin
Result := AItem.UnixOtherPermissions;
end;
end;
function TIdFTPListOutput.UnixGetOutputOwner(AItem: TIdFTPListOutputItem): String;
begin
if AItem.OwnerName = '' then begin
Result := DEF_OWNER;
end else begin
Result := AItem.OwnerName;
end;
end;
function TIdFTPListOutput.UnixGetOutputOwnerPerms(AItem: TIdFTPListOutputItem): String;
begin
if AItem.UnixOwnerPermissions = '' then begin
if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
Result := DEF_DIR_OWN_PERM;
end else begin
Result := DEF_FILE_OWN_PERM;
end;
end else begin
Result := AItem.UnixOwnerPermissions;
end;
end;
function TIdFTPListOutput.UnixINodeOutput(AItem: TIdFTPListOutputItem): String;
var
LInode : String;
begin
Result := '';
if HasSwitch(SWITCH_PRINT_INODE) then begin
LInode := IntToStr(Abs(AItem.Inode));
//should be no more than 10 digits
LInode := Copy(LInode, 1, 10);
Result := Result + IndyFormat('%10s ', [LInode]);
end;
end;
function TIdFTPListOutput.UnixItem(AItem: TIdFTPListOutputItem): String;
var
LSize, LTime: string;
l, month: Word;
LLinkNum : Integer;
LFileName : String;
LFormat : String;
LMTime : TDateTime;
begin
LFileName := IndyGetFileName(AItem.FileName);
Result := UnixINodeOutput(AItem) + UnixBlocksOutput(AItem);
case AItem.ItemType of
ditDirectory:
begin
AItem.Size := UNIX_DIR_SIZE;
LSize := 'd'; {Do not Localize}
end;
ditSymbolicLink:
begin
LSize := 'l'; {Do not Localize}
end;
else
begin
LSize := '-'; {Do not Localize}
end;
end;
if AItem.LinkCount = 0 then begin
LLinkNum := 1;
end else begin
LLinkNum := AItem.LinkCount;
end;
LFormat := '%3:3s%4:3s%5:3s %6:3d '; {Do not localize}
//g - surpress owner
//lrwxrwxrwx 1 other 7 Nov 16 2001 bin -> usr/bin
//where it would normally print
//lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin
if not HasSwitch('g') then begin
LFormat := LFormat + '%1:-8s '; {Do not localize}
end;
//o - surpress group
//lrwxrwxrwx 1 root 7 Nov 16 2001 bin -> usr/bin
//where it would normally print
//lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin
if not HasSwitch('o') then begin
LFormat := LFormat + '%2:-8s '; {Do not localize}
end;
LFormat := LFormat + '%0:8d'; {Do not localize}
LSize := LSize + IndyFormat(LFormat, [AItem.Size, UnixGetOutputOwner(AItem),
UnixGetOutputGroup(AItem), UnixGetOutputOwnerPerms(AItem),
UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem), LLinkNum]);
LMTime := GetLocalModTime(AItem);
DecodeDate(LMTime, l, month, l);
LTime := MonthNames[month] + FormatDateTime(' dd', LMTime); {Do not Localize}
if HasSwitch(SWITCH_BOTH_TIME_YEAR) then begin
LTime := LTime + FormatDateTime(' hh:nn:ss yyyy', LMTime); {Do not Localize}
end
else if IsIn6MonthWindow(LMTime) then begin {Do not Localize}
LTime := LTime + FormatDateTime(' hh:nn', LMTime); {Do not Localize}
end
else begin
LTime := LTime + FormatDateTime(' yyyy', LMTime); {Do not Localize}
end;
// A.Neillans, 20 Apr 2002, Fixed glitch, extra space in front of names.
// Result := LSize + ' ' + LTime + ' ' + FileName; {Do not Localize}
Result := Result + LSize + ' ' + LTime + ' ';
if HasSwitch(SWITCH_QUOTEDNAME) then begin
Result := Result + '"' + LFileName + '"'; {Do not Localize}
end else begin
Result := Result + LFileName;
end;
if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
if HasSwitch(SWITCH_QUOTEDNAME) then begin
Result := Result + UNIX_LINKTO_SYM + '"' + AItem.LinkedItemName + '"'; {Do not Localize}
end else begin
Result := Result + UNIX_LINKTO_SYM + AItem.LinkedItemName;
end;
end;
if ((IndyPos(SWITCH_CLASSIFY,Switches)>0) or (IndyPos(SWITCH_SLASHDIR,Switches)>0)) and {Do not translate}
(AItem.ItemType in [ditDirectory, ditSymbolicLinkDir]) then
begin
Result := Result + PATH_SUBDIR_SEP_UNIX;
end;
if HasSwitch(SWITCH_CLASSIFY) and (AItem.ItemType = ditFile) and
IsUnixExec(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem)) then
begin
//star is placed at the end of a file name
//like this:
//-r-xr-xr-x 1 0 1 17440 Aug 8 2000 ls*
Result := Result + '*';
end;
end;
function TIdFTPListOutput.Win32Item(AItem: TIdFTPListOutputItem): String;
var
LSize, LFileName : String;
begin
LFileName := IndyGetFileName(AItem.FileName);
if AItem.ItemType = ditDirectory then begin
LSize := ' <DIR>' + StringOfChar(' ', 9); {Do not Localize}
end else begin
LSize := StringOfChar(' ', 20 - Length(IntToStr(AItem.Size))) + IntToStr(AItem.Size); {Do not Localize}
end;
Result := FormatDateTime('mm-dd-yy hh:nnAM/PM', GetLocalModTime(AItem)) + ' ' + LSize + ' ' + LFileName; {Do not Localize}
end;
{ TDirEntry }
function TDirEntry.AddFileName(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean;
var
i : Integer;
LParentPart : String;
LDirEnt : TDirEntry;
begin
Result := False;
LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
if LParentPart = PathName then begin
if FFileList.IndexOf(APathName) = -1 then begin
FFileList.AddObject(APathName, ADirEnt);
end;
Result := True;
Exit;
end;
if Assigned(SubDirs) then begin
for i := 0 to SubDirs.Count-1 do begin
LDirEnt := TDirEntry(SubDirs[i]);
LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName));
if TextStartsWith(APathName, LParentPart) then begin
if TDirEntry(SubDirs[i]).AddFileName(APathName, ADirEnt) then begin
Result := True;
Break;
end;
end;
end;
end;
end;
function TDirEntry.AddSubDir(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean;
var
LDirEnt : TDirEntry;
i : Integer;
LParentPart : String;
begin
Result := False;
LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
if LParentPart = PathName then begin
if not Assigned(FSubDirs) then begin
FSubDirs := TDirEntryList.Create;
end;
LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
LParentPart := IndyGetFileName(LParentPart);
LDirEnt := TDirEntry.Create(APathName, ADirEnt);
try
FSubDirs.Add(LDirEnt);
except
LDirEnt.Free;
raise;
end;
AddFileName(APathName, ADirEnt);
Result := True;
Exit;
end;
if Assigned(SubDirs) then begin
for i := 0 to SubDirs.Count-1 do begin
LDirEnt := TDirEntry(SubDirs[i]);
LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName));
if TextStartsWith(APathName, LParentPart) then begin
if LDirEnt.AddSubDir(APathName, ADirEnt) then begin
Result := True;
Break;
end;
end;
end;
end;
end;
constructor TDirEntry.Create(const APathName : String; ADirListItem : TIdFTPListOutputItem);
begin
inherited Create;
FPathName := APathName;
FFileList := TIdBubbleSortStringList.Create;
FDirListItem := ADirListItem;
//create that only when necessary;
FSubDirs := TDirEntryList.Create;
end;
destructor TDirEntry.Destroy;
begin
FreeAndNil(FFileList);
FreeAndNil(FSubDirs);
inherited Destroy;
end;
procedure TDirEntry.SortAscendFName;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortAscFName);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortAscFName
{$ELSE}
TIdSortCompare(@DESortAscFName)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortAscendFName;
end;
end;
end;
procedure TDirEntry.SortAscendMTime;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortAscMTime);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortAscMTime
{$ELSE}
TIdSortCompare(@DESortAscMTime)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortAscendMTime;
end;
end;
end;
procedure TDirEntry.SortDescendMTime;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortDescMTime);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortDescMTime
{$ELSE}
TIdSortCompare(@DESortDescMTime)
{$ENDIF}
);
for i := 0 to FSubDirs.Count -1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortDescendMTime;
end;
end;
end;
procedure TDirEntry.SortDescendFName;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortDescFName
{$ELSE}
TIdSortCompare(@DESortDescFName)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortDescendFName;
end;
end;
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortDescFName);
end;
end;
procedure TDirEntry.SortAscendFNameExt;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortAscFNameExt);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortAscFName
{$ELSE}
TIdSortCompare(@DESortAscFName)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortAscendFNameExt;
end;
end;
end;
procedure TDirEntry.SortDescendFNameExt;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortDescFNameExt);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortAscFName
{$ELSE}
TIdSortCompare(@DESortAscFName)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortDescendFNameExt;
end;
end;
end;
procedure TDirEntry.SortAscendSize;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortAscSize);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortAscMTime
{$ELSE}
TIdSortCompare(@DESortAscMTime)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortAscendSize;
end;
end;
end;
procedure TDirEntry.SortDescendSize;
var
i : Integer;
LSubDir: TDirEntry;
begin
if Assigned(FFileList) then begin
FFileList.BubbleSort(StrSortDescSize);
end;
if Assigned(FSubDirs) then begin
FSubDirs.BubbleSort(
{$IFDEF HAS_GENERICS_TObjectList}
DESortDescFName
{$ELSE}
TIdSortCompare(@DESortDescFName)
{$ENDIF}
);
for i := 0 to FSubDirs.Count-1 do begin
LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
LSubDir.SortDescendSize;
end;
end;
end;
{ TIdFTPListOutputItem }
constructor TIdFTPListOutputItem.Create(AOwner: TCollection);
begin
inherited Create(AOwner);
//indicate that this fact is not applicable
FWinDriveType := -1;
end;
end.