IdFTPListOutput.pas 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.18 12/10/04 1:13:34 PM RLebeau
  18. FormatDateTime() fixes. Was using 'mm' instead of 'nn' for minutes.
  19. Rev 1.17 10/26/2004 9:36:26 PM JPMugaas
  20. Updated ref.
  21. Rev 1.16 10/26/2004 9:19:14 PM JPMugaas
  22. Fixed references.
  23. Rev 1.15 10/1/2004 6:17:12 AM JPMugaas
  24. Removed some dead code.
  25. Rev 1.14 6/27/2004 1:45:36 AM JPMugaas
  26. Can now optionally support LastAccessTime like Smartftp's FTP Server could.
  27. I also made the MLST listing object and parser support this as well.
  28. Rev 1.13 6/11/2004 9:34:44 AM DSiders
  29. Added "Do not Localize" comments.
  30. Rev 1.12 4/19/2004 5:06:02 PM JPMugaas
  31. Class rework Kudzu wanted.
  32. Rev 1.11 2004.02.03 5:45:34 PM czhower
  33. Name changes
  34. Rev 1.10 24/01/2004 19:18:48 CCostelloe
  35. Cleaned up warnings
  36. Rev 1.9 1/4/2004 12:09:54 AM BGooijen
  37. changed System.Delete to IdDelete
  38. Rev 1.8 11/26/2003 6:23:44 PM JPMugaas
  39. Quite a number of fixes for recursive dirs and a few other things that
  40. slipped my mind.
  41. Rev 1.7 10/19/2003 2:04:02 PM DSiders
  42. Added localization comments.
  43. Rev 1.6 3/11/2003 07:36:00 PM JPMugaas
  44. Now reports permission denied in subdirs when doing recursive listts in Unix
  45. export.
  46. Rev 1.5 3/9/2003 12:01:26 PM JPMugaas
  47. Now can report errors in recursive lists.
  48. Permissions work better.
  49. Rev 1.4 3/3/2003 07:18:34 PM JPMugaas
  50. Now honors the FreeBSD -T flag and parses list output from a program using
  51. it. Minor changes to the File System component.
  52. Rev 1.3 2/26/2003 08:57:10 PM JPMugaas
  53. Bug fix. The owner and group should be left-justified.
  54. Rev 1.2 2/24/2003 07:24:00 AM JPMugaas
  55. Now honors more Unix switches just like the old code and now work with the
  56. NLIST command when emulating Unix. -A switch support added. Switches are
  57. now in constants.
  58. Rev 1.1 2/23/2003 06:19:42 AM JPMugaas
  59. Now uses Classes instead of classes.
  60. Rev 1.0 2/21/2003 06:51:46 PM JPMugaas
  61. FTP Directory list output object for the FTP server.
  62. }
  63. unit IdFTPListOutput;
  64. interface
  65. {$i IdCompilerDefines.inc}
  66. uses
  67. Classes,
  68. IdGlobal,
  69. IdFTPList;
  70. type
  71. // we can't use the standard FTP MLSD option types in the FTP Server
  72. // because we support some minimal things that the user can't set.
  73. // We have the manditory items to make it harder for the user to mess up.
  74. TIdFTPFactOutput = (ItemType, Modify, Size, Perm, Unique, UnixMODE, UnixOwner,
  75. UnixGroup, CreateTime, LastAccessTime, WinAttribs,WinDriveType,WinDriveLabel);
  76. TIdFTPFactOutputs = set of TIdFTPFactOutput;
  77. TIdDirOutputFormat = (doUnix, doWin32, doEPLF);
  78. TIdFTPListOutputItem = class(TIdFTPListItem)
  79. protected
  80. FLinkCount: Integer;
  81. FGroupName: string;
  82. FOwnerName : String;
  83. FLinkedItemName : string;
  84. FNumberBlocks : Integer;
  85. FInode : Integer;
  86. FLastAccessDate: TDateTime;
  87. FLastAccessDateGMT: TDateTime;
  88. FCreationDate: TDateTime;
  89. FCreationDateGMT : TDateTime;
  90. //Unique ID for an item to prevent yourself from downloading something twice
  91. FUniqueID : String;
  92. //MLIST things
  93. FMLISTPermissions : String;
  94. FUnixGroupPermissions: string;
  95. FUnixOwnerPermissions: string;
  96. FUnixOtherPermissions: string;
  97. FUnixinode : Integer;
  98. FWinAttribs : UInt32;
  99. //an error has been reported in the DIR listing itself for an item
  100. FDirError : Boolean;
  101. FWinDriveType : Integer;
  102. FWinDriveLabel : String;
  103. public
  104. constructor Create(AOwner: TCollection); override;
  105. property NumberBlocks : Integer read FNumberBlocks write FNumberBlocks;
  106. property Inode : Integer read FInode write FInode;
  107. //Last Access time values are for MLSD data output and can be returned by the MLST command
  108. property LastAccessDate: TDateTime read FLastAccessDate write FLastAccessDate;
  109. property LastAccessDateGMT : TDateTime read FLastAccessDateGMT write FLastAccessDateGMT;
  110. //Creation time values are for MLSD data output and can be returned by the MLST command
  111. property CreationDate: TDateTime read FCreationDate write FCreationDate;
  112. property CreationDateGMT : TDateTime read FCreationDateGMT write FCreationDateGMT;
  113. // If this is not blank, you can use this as a unique identifier for an item to prevent
  114. // yourself from downloading the same item twice (which is not easy to see with some
  115. // some FTP sites where symbolic links or similar things are used.
  116. //Valid only with EPLF and MLST
  117. property UniqueID : string read FUniqueID write FUniqueID;
  118. //Creation time values are for MLSD data output and can be returned by the
  119. //the MLSD parser in some cases
  120. property ModifiedDateGMT;
  121. //Windows NT File Attributes (just like what is reported by RaidenFTPD
  122. //BlackMoon FTP Server, and Serv-U
  123. //On the server side, you deal with it as a number right from the Win32 FindFirst,
  124. //FindNext functions. Easy
  125. property WinAttribs : UInt32 read FWinAttribs write FWinAttribs;
  126. property WinDriveType : Integer read FWinDriveType write FWinDriveType;
  127. property WinDriveLabel : String read FWinDriveLabel write FWinDriveLabel;
  128. //MLIST Permissions
  129. property MLISTPermissions : string read FMLISTPermissions write FMLISTPermissions;
  130. property UnixOwnerPermissions: string read FUnixOwnerPermissions write FUnixOwnerPermissions;
  131. property UnixGroupPermissions: string read FUnixGroupPermissions write FUnixGroupPermissions;
  132. property UnixOtherPermissions: string read FUnixOtherPermissions write FUnixOtherPermissions;
  133. property LinkCount: Integer read FLinkCount write FLinkCount;
  134. property OwnerName: string read FOwnerName write FOwnerName;
  135. property GroupName: string read FGroupName write FGroupName;
  136. property LinkedItemName : string read FLinkedItemName write FLinkedItemName;
  137. property DirError : Boolean read FDirError write FDirError;
  138. end;
  139. TIdFTPListOutput = class(TCollection)
  140. protected
  141. FSwitches : String;
  142. FOutput : String;
  143. FDirFormat : TIdDirOutputFormat;
  144. FExportTotalLine : Boolean;
  145. function GetLocalModTime(AItem : TIdFTPListOutputItem) : TDateTime; virtual;
  146. function HasSwitch(const ASwitch: String): Boolean;
  147. function UnixItem(AItem : TIdFTPListOutputItem) : String; virtual;
  148. function Win32Item(AItem : TIdFTPListOutputItem) : String; virtual;
  149. function EPLFItem(AItem : TIdFTPListOutputItem) : String; virtual;
  150. function NListItem(AItem : TIdFTPListOutputItem) : String; virtual;
  151. function MListItem(AItem : TIdFTPListOutputItem; AMLstOpts : TIdFTPFactOutputs) : String; virtual;
  152. procedure InternelOutputDir(AOutput : TStrings; ADetails : Boolean = True); virtual;
  153. function UnixINodeOutput(AItem : TIdFTPListOutputItem) : String;
  154. function UnixBlocksOutput(AItem : TIdFTPListOutputItem) : String;
  155. function UnixGetOutputOwner(AItem : TIdFTPListOutputItem) : String;
  156. function UnixGetOutputGroup(AItem : TIdFTPListOutputItem) : String;
  157. function UnixGetOutputOwnerPerms(AItem : TIdFTPListOutputItem) : String;
  158. function UnixGetOutputGroupPerms(AItem : TIdFTPListOutputItem) : String;
  159. function UnixGetOutputOtherPerms(AItem : TIdFTPListOutputItem) : String;
  160. function GetItems(AIndex: Integer): TIdFTPListOutputItem;
  161. procedure SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem);
  162. public
  163. function Add: TIdFTPListOutputItem;
  164. constructor Create; reintroduce;
  165. function IndexOf(AItem: TIdFTPListOutputItem): Integer;
  166. property Items[AIndex: Integer]: TIdFTPListOutputItem read GetItems write SetItems; default;
  167. procedure LISTOutputDir(AOutput : TStrings); virtual;
  168. procedure MLISTOutputDir(AOutput : TStrings; AMLstOpts : TIdFTPFactOutputs); virtual;
  169. procedure NLISTOutputDir(AOutput : TStrings); virtual;
  170. property DirFormat : TIdDirOutputFormat read FDirFormat write FDirFormat;
  171. property Switches : String read FSwitches write FSwitches;
  172. property Output : String read FOutput write FOutput;
  173. property ExportTotalLine : Boolean read FExportTotalLine write FExportTotalLine;
  174. end;
  175. const
  176. DEF_FILE_OWN_PERM = 'rw-'; {do not localize}
  177. DEF_FILE_GRP_PERM = DEF_FILE_OWN_PERM;
  178. DEF_FILE_OTHER_PERM = 'r--'; {do not localize}
  179. DEF_DIR_OWN_PERM = 'rwx'; {do not localize}
  180. DEF_DIR_GRP_PERM = DEF_DIR_OWN_PERM;
  181. DEF_DIR_OTHER_PERM = 'r-x'; {do not localize}
  182. DEF_OWNER = 'root'; {do not localize}
  183. {NLIST and LIST switches - based on /bin/ls }
  184. {
  185. Note that the standard Unix form started simply by Unix
  186. FTP deamons piping output from the /bin/ls program for both
  187. the NLIST and LIST FTP commands. The standard /bin/ls
  188. program has several standard switches that allow the output
  189. to be customized. For our output, we wish to emulate this behavior.
  190. Microsoft IIS even honors a subset of these switches dealing sort order
  191. and recursive listings. It does not honor some sort-by-switches although
  192. we honor those in Win32 (hey, we did MS one better, not that it says much though.
  193. }
  194. const
  195. {format switches - used by Unix mode only}
  196. SWITCH_COLS_ACCROSS = 'x';
  197. SWITCH_COLS_DOWN = 'C';
  198. SWITCH_ONE_COL = '1';
  199. SWITCH_ONE_DIR = 'f';
  200. SWITCH_COMMA_STREAM = 'm';
  201. SWITCH_LONG_FORM = 'l';
  202. {recursive for both Win32 and Unix forms}
  203. SWITCH_RECURSIVE = 'R';
  204. {sort switches - used both by Win32 and Unix forms}
  205. SWITCH_SORT_REVERSE = 'r';
  206. SWITCH_SORTBY_MTIME = 't';
  207. SWITCH_SORTBY_CTIME = 'u';
  208. SWITCH_SORTBY_EXT = 'X';
  209. SWITCH_SORTBY_SIZE = 'S';
  210. {Output switches for Unix mode only}
  211. SWITCH_CLASSIFY = 'F';
  212. //
  213. { -F Put aslash (/) aftereach filename if the file is a directory, an
  214. asterisk (*) if the file is executable, an equal sign(=) if the
  215. file is an AF_UNIX address family socket, andan ampersand (@) if
  216. the file is asymbolic link.Unless the -H option isalso used,
  217. symbolic links are followed to see ifthey might be adirectory; see
  218. above.
  219. From:
  220. http://www.mcsr.olemiss.edu/cgi-bin/man-cgi?ls+1 }
  221. SWITCH_SLASHDIR = 'p';
  222. SWITCH_QUOTEDNAME = 'Q';
  223. SWITCH_PRINT_BLOCKS = 's';
  224. SWITCH_PRINT_INODE = 'i';
  225. SWITCH_SHOW_ALLPERIOD = 'a'; //show all entries even ones with a pariod starting the filename/hidden
  226. //note that anything starting with a period is shown except for the .. and . entries for security reasons
  227. SWITCH_HIDE_DIRPOINT = 'A'; //hide the "." and ".." entries
  228. SWITCH_BOTH_TIME_YEAR = 'T'; //This is used by FTP Voyager with a Serv-U FTP Server to both
  229. //a time and year in the FTP list. Note that this does conflict with a ls -T flag used to specify a column size
  230. //on Linux but in FreeBSD, the -T flag is also honored.
  231. implementation
  232. uses
  233. //facilitate inlining only.
  234. {$IFDEF DOTNET}
  235. {$IFDEF USE_INLINE}
  236. System.IO,
  237. {$ENDIF}
  238. {$ENDIF}
  239. {$IFDEF VCL_XE3_OR_ABOVE}
  240. {$IFNDEF NEXTGEN}
  241. System.Contnrs,
  242. {$ENDIF}
  243. System.Types,
  244. {$ENDIF}
  245. {$IFDEF USE_VCL_POSIX}
  246. Posix.SysTime,
  247. {$ENDIF}
  248. IdContainers, IdFTPCommon, IdGlobalProtocols, SysUtils;
  249. type
  250. {$IFDEF HAS_GENERICS_TObjectList}
  251. TDirEntry = class;
  252. TDirEntryList = TIdObjectList<TDirEntry>;
  253. {$ELSE}
  254. // TODO: flesh out to match TIdObjectList<TDirEntry> for non-Generics compilers
  255. TDirEntryList = TIdObjectList;
  256. {$ENDIF}
  257. TDirEntry = class(TObject)
  258. protected
  259. FPathName : String;
  260. FDirListItem : TIdFTPListOutputItem;
  261. FSubDirs : TDirEntryList;
  262. FFileList : TIdBubbleSortStringList;
  263. public
  264. constructor Create(const APathName : String; ADirListItem : TIdFTPListOutputItem);
  265. destructor Destroy; override;
  266. // procedure Sort(ACompare: TIdSortCompare;const Recurse : Boolean = True);
  267. procedure SortAscendFName;
  268. procedure SortDescendFName;
  269. procedure SortAscendMTime;
  270. procedure SortDescendMTime;
  271. procedure SortAscendSize;
  272. procedure SortDescendSize;
  273. procedure SortAscendFNameExt;
  274. procedure SortDescendFNameExt;
  275. function AddSubDir(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean;
  276. function AddFileName(const APathName : String; ADirEnt : TIdFTPListOutputItem) : Boolean;
  277. property SubDirs : TDirEntryList read FSubDirs;
  278. property FileList : TIdBubbleSortStringList read FFileList;
  279. property PathName : String read FPathName;
  280. property DirListItem : TIdFTPListOutputItem read FDirListItem;
  281. end;
  282. function RawSortAscFName(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
  283. var
  284. {
  285. > 0 (positive) Item1 is less than Item2
  286. = 0 Item1 is equal to Item2
  287. < 0 (negative) Item1 is greater than Item2
  288. }
  289. LTmpPath1, LTmpPath2 : String;
  290. LPath1Dot, LPath2Dot : Boolean;
  291. begin
  292. LTmpPath1 := IndyGetFileName(AItem1.FileName);
  293. LTmpPath2 := IndyGetFileName(AItem2.FileName);
  294. //periods are always greater then letters in dir lists
  295. LPath1Dot := TextStartsWith(LTmpPath1, '.');
  296. LPath2Dot := TextStartsWith(LTmpPath2, '.');
  297. if LPath1Dot and LPath2Dot then begin
  298. if (LTmpPath1 = CUR_DIR) and (LTmpPath2 = PARENT_DIR) then begin
  299. Result := 1;
  300. Exit;
  301. end;
  302. if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = PARENT_DIR) then begin
  303. Result := -1;
  304. Exit;
  305. end;
  306. if (LTmpPath2 = CUR_DIR) and (LTmpPath1 = CUR_DIR) then begin
  307. Result := 0;
  308. Exit;
  309. end;
  310. if (LTmpPath2 = PARENT_DIR) and (LTmpPath1 = PARENT_DIR) then begin
  311. Result := 0;
  312. Exit;
  313. end;
  314. end;
  315. if LPath2Dot and (not LPath1Dot) then begin
  316. Result := -1;
  317. Exit;
  318. end;
  319. if LPath1Dot and (not LPath2Dot) then begin
  320. Result := 1;
  321. Exit;
  322. end;
  323. Result := -IndyCompareStr(LTmpPath1, LTmpPath2);
  324. end;
  325. function RawSortDescFName(AItem1, AItem2: TIdFTPListItem): Integer;
  326. begin
  327. Result := -RawSortAscFName(AItem1, AItem2);
  328. end;
  329. function RawSortAscFNameExt(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
  330. var
  331. {
  332. > 0 (positive) Item1 is less than Item2
  333. = 0 Item1 is equal to Item2
  334. < 0 (negative) Item1 is greater than Item2
  335. }
  336. LTmpPath1, LTmpPath2 : String;
  337. begin
  338. LTmpPath1 := ExtractFileExt(AItem1.FileName);
  339. LTmpPath2 := ExtractFileExt(AItem2.FileName);
  340. Result := -IndyCompareStr(LTmpPath1, LTmpPath2);
  341. if Result = 0 then begin
  342. Result := RawSortAscFName(AItem1, AItem2);
  343. end;
  344. end;
  345. function RawSortDescFNameExt(AItem1, AItem2: TIdFTPListItem): Integer;
  346. begin
  347. Result := -RawSortAscFNameExt(AItem1, AItem2, False);
  348. end;
  349. function RawSortAscMTime(AItem1, AItem2: TIdFTPListItem): Integer;
  350. {
  351. > 0 (positive) Item1 is less than Item2
  352. 0 Item1 is equal to Item2
  353. < 0 (negative) Item1 is greater than Item2
  354. }
  355. begin
  356. if AItem1.ModifiedDate < AItem2.ModifiedDate then begin
  357. Result := -1;
  358. end
  359. else if AItem1.ModifiedDate > AItem2.ModifiedDate then begin
  360. Result := 1;
  361. end
  362. else begin
  363. Result := RawSortAscFName(AItem1, AItem2);
  364. end;
  365. end;
  366. function RawSortDescMTime(AItem1, AItem2: TIdFTPListItem): Integer;
  367. begin
  368. Result := -RawSortAscMTime(AItem1, AItem2);
  369. end;
  370. function RawSortAscSize(AItem1, AItem2: TIdFTPListItem; const ASubDirs : Boolean = True): Integer;
  371. var
  372. LSize1, LSize2 : Int64;
  373. {
  374. > 0 (positive) Item1 is less than Item2
  375. = 0 Item1 is equal to Item2
  376. < 0 (negative) Item1 is greater than Item2
  377. }
  378. begin
  379. LSize1 := AItem1.Size;
  380. LSize2 := AItem2.Size;
  381. if TIdFTPListOutput(AItem1.Collection).DirFormat = doUnix then begin
  382. if AItem1.ItemType = ditDirectory then begin
  383. LSize1 := UNIX_DIR_SIZE;
  384. end;
  385. if AItem2.ItemType = ditDirectory then begin
  386. LSize2 := UNIX_DIR_SIZE;
  387. end;
  388. end;
  389. if LSize1 < LSize2 then begin
  390. Result := -1;
  391. end
  392. else if LSize1 > LSize2 then begin
  393. Result := 1;
  394. end else begin
  395. Result := RawSortAscFName (AItem1, AItem2);
  396. end;
  397. end;
  398. function RawSortDescSize(AItem1, AItem2: TIdFTPListItem): Integer;
  399. begin
  400. Result := -RawSortAscSize(AItem1, AItem2, False);
  401. end;
  402. {DirEntry objects}
  403. function DESortAscFName(AItem1, AItem2: TDirEntry): Integer;
  404. begin
  405. Result := -IndyCompareStr(AItem1.PathName, AItem2.PathName);
  406. end;
  407. function DESortAscMTime(AItem1, AItem2: TDirEntry): Integer;
  408. var
  409. L1, L2 : TIdFTPListItem;
  410. {
  411. > 0 (positive) Item1 is less than Item2
  412. = 0 Item1 is equal to Item2
  413. < 0 (negative) Item1 is greater than Item2
  414. }
  415. begin
  416. L1 := AItem1.DirListItem;
  417. L2 := AItem2.DirListItem;
  418. if L1.ModifiedDate > L2.ModifiedDate then begin
  419. Result := -1;
  420. end
  421. else if L1.ModifiedDate < L2.ModifiedDate then begin
  422. Result := 1;
  423. end else begin
  424. Result := DESortAscFName(AItem1, AItem2);
  425. end;
  426. end;
  427. function DESortDescMTime(AItem1, AItem2: TDirEntry): Integer;
  428. begin
  429. Result := -DESortAscMTime(AItem1, AItem2);
  430. end;
  431. function DESortDescFName(AItem1, AItem2: TDirEntry): Integer;
  432. begin
  433. Result := -DESortAscFName(AItem1, AItem2);
  434. end;
  435. {stringlist objects}
  436. function StrSortAscMTime(List: TStringList; Index1, Index2: Integer): Integer;
  437. begin
  438. Result := RawSortAscMTime(
  439. TIdFTPListItem(List.Objects[Index1]),
  440. TIdFTPListItem(List.Objects[Index2]));
  441. end;
  442. function StrSortDescMTime(List: TStringList; Index1, Index2: Integer): Integer;
  443. begin
  444. Result := RawSortDescMTime(
  445. TIdFTPListItem(List.Objects[Index1]),
  446. TIdFTPListItem(List.Objects[Index2]));
  447. end;
  448. function StrSortAscSize(List: TStringList; Index1, Index2: Integer): Integer;
  449. begin
  450. Result := RawSortAscSize(
  451. TIdFTPListItem(List.Objects[Index1]),
  452. TIdFTPListItem(List.Objects[Index2]));
  453. end;
  454. function StrSortDescSize(List: TStringList; Index1, Index2: Integer): Integer;
  455. begin
  456. Result := RawSortDescSize(
  457. TIdFTPListItem(List.Objects[Index1]),
  458. TIdFTPListItem(List.Objects[Index2]));
  459. end;
  460. function StrSortAscFName(List: TStringList; Index1, Index2: Integer): Integer;
  461. begin
  462. Result := RawSortAscFName(
  463. TIdFTPListItem(List.Objects[Index1]),
  464. TIdFTPListItem(List.Objects[Index2]));
  465. end;
  466. function StrSortDescFName(List: TStringList; Index1, Index2: Integer): Integer;
  467. begin
  468. Result := RawSortDescFName(
  469. TIdFTPListItem(List.Objects[Index1]),
  470. TIdFTPListItem(List.Objects[Index2]));
  471. end;
  472. function StrSortAscFNameExt(List: TStringList; Index1, Index2: Integer): Integer;
  473. begin
  474. Result := RawSortAscFNameExt(
  475. TIdFTPListItem(List.Objects[Index1]),
  476. TIdFTPListItem(List.Objects[Index2]));
  477. end;
  478. function StrSortDescFNameExt(List: TStringList; Index1, Index2: Integer): Integer;
  479. begin
  480. Result := RawSortDescFNameExt(
  481. TIdFTPListItem(List.Objects[Index1]),
  482. TIdFTPListItem(List.Objects[Index2]));
  483. end;
  484. { TIdFTPListOutput }
  485. function TIdFTPListOutput.Add: TIdFTPListOutputItem;
  486. begin
  487. Result := TIdFTPListOutputItem(inherited Add);
  488. end;
  489. constructor TIdFTPListOutput.Create;
  490. begin
  491. inherited Create(TIdFTPListOutputItem);
  492. FDirFormat := doUnix;
  493. end;
  494. function TIdFTPListOutput.EPLFItem(AItem: TIdFTPListOutputItem): String;
  495. var
  496. LFileName : String;
  497. begin
  498. LFileName := IndyGetFileName(AItem.FileName);
  499. if AItem.ModifiedDateGMT > EPLF_BASE_DATE then begin
  500. Result := '+m' + GMTDateTimeToEPLFDate(AItem.ModifiedDateGMT);
  501. end
  502. else if AItem.ModifiedDate > EPLF_BASE_DATE then begin
  503. Result := '+m'+LocalDateTimeToEPLFDate(AItem.ModifiedDate);
  504. end else begin
  505. Result := '';
  506. end;
  507. if AItem.ItemType = ditFile then begin
  508. Result := Result + ',r';
  509. end else begin
  510. Result := Result + ',/';
  511. end;
  512. Result := Result + ',s' + IntToStr(AItem.Size);
  513. Result := Result + #9 + LFileName;
  514. end;
  515. function TIdFTPListOutput.GetItems(AIndex: Integer): TIdFTPListOutputItem;
  516. begin
  517. Result := TIdFTPListOutputItem(inherited GetItem(AIndex));
  518. end;
  519. function TIdFTPListOutput.GetLocalModTime(AItem: TIdFTPListOutputItem): TDateTime;
  520. begin
  521. if AItem.ModifiedDateGMT <> 0 then begin
  522. Result := UTCTimeToLocalTime(AItem.ModifiedDateGMT);
  523. end else begin
  524. Result := AItem.ModifiedDate;
  525. end;
  526. end;
  527. function TIdFTPListOutput.HasSwitch(const ASwitch: String): Boolean;
  528. begin
  529. Result := IndyPos(ASwitch, Switches) > 0;
  530. end;
  531. function TIdFTPListOutput.IndexOf(AItem: TIdFTPListOutputItem): Integer;
  532. var
  533. i : Integer;
  534. begin
  535. Result := -1;
  536. for i := 0 to Count - 1 do begin
  537. if AItem = Items[i] then begin
  538. Result := i;
  539. Exit;
  540. end;
  541. end;
  542. end;
  543. procedure TIdFTPListOutput.InternelOutputDir(AOutput: TStrings; ADetails: Boolean);
  544. type
  545. TIdDirOutputType = (doColsAccross, doColsDown, doOneCol, doOnlyDirs, doComma, doLong);
  546. var
  547. i : Integer;
  548. //note we use this for sorting pathes with recursive dirs
  549. LRootPath : TDirEntry;
  550. LShowNavSym : BOolean;
  551. function DetermineOp : TIdDirOutputType;
  552. //we do things this way because the last switch in a mutually exclusive set
  553. //always takes precidence over the others.
  554. var
  555. LStopScan : Boolean;
  556. li : Integer;
  557. begin
  558. if ADetails then begin
  559. Result := doLong;
  560. end else begin
  561. Result := doOneCol;
  562. end;
  563. if DirFormat <> doUnix then begin
  564. Exit;
  565. end;
  566. LStopScan := False;
  567. for li := Length(Switches) downto 1 do begin
  568. case Switches[li] of
  569. SWITCH_COLS_ACCROSS :
  570. begin
  571. Result := doColsAccross;
  572. LStopScan := True;
  573. end;
  574. SWITCH_COLS_DOWN :
  575. begin
  576. Result := doColsDown;
  577. LStopScan := True;
  578. end;
  579. SWITCH_ONE_COL :
  580. begin
  581. Result := doOneCol;
  582. LStopScan := True;
  583. end;
  584. SWITCH_ONE_DIR :
  585. begin
  586. Result := doOnlyDirs;
  587. LStopScan := True;
  588. end;
  589. SWITCH_COMMA_STREAM :
  590. begin
  591. Result := doComma;
  592. LStopScan := True;
  593. end;
  594. SWITCH_LONG_FORM :
  595. begin
  596. Result := doLong;
  597. LStopScan := True;
  598. end;
  599. end;
  600. if LStopScan then begin
  601. Break;
  602. end;
  603. end;
  604. end;
  605. procedure PrintSubDirHeader(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  606. var
  607. LUnixPrependPath : Boolean;
  608. begin
  609. LUnixPrependPath := HasSwitch(SWITCH_SORT_REVERSE) or HasSwitch(SWITCH_SORTBY_MTIME) or (DetermineOp <> doLong);
  610. if (ACurDir <> ARoot) or LUnixPrependPath then begin
  611. //we don't want an empty line to start the list
  612. if ACurDir <> ARoot then begin
  613. ALOutput.Add('');
  614. end;
  615. if DirFormat = doWin32 then begin
  616. ALOutput.Add(MS_DOS_CURDIR + UnixPathToDOSPath(ACurDir.PathName) + ':');
  617. end
  618. else if LUnixPrependPath then begin
  619. if ACurDir = ARoot then begin
  620. ALOutput.Add(CUR_DIR + ':');
  621. end else begin
  622. ALOutput.Add(UNIX_CURDIR + DOSPathToUnixPath(ACurDir.PathName) + ':');
  623. end;
  624. end else begin
  625. ALOutput.Add(DOSPathToUnixPath(ACurDir.PathName) + ':');
  626. end;
  627. end;
  628. end;
  629. procedure ProcessOnePathCol(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  630. var
  631. li : Integer;
  632. LCurItem : TIdFTPListOutputItem;
  633. begin
  634. if Recurse and Assigned(ACurDir.SubDirs) then begin
  635. if Recurse then begin
  636. PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
  637. end;
  638. end;
  639. for li := 0 to ACurDir.FileList.Count-1 do begin
  640. ALOutput.Add(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])));
  641. end;
  642. if Recurse and Assigned(ACurDir.SubDirs) then begin
  643. for li := 0 to ACurDir.SubDirs.Count-1 do begin
  644. LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
  645. if LCurItem.DirError then begin
  646. if li = 0 then begin
  647. ALOutput.Add('');
  648. end;
  649. ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
  650. end else begin
  651. ProcessOnePathCol(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  652. end;
  653. end;
  654. end;
  655. end;
  656. function CalcMaxLen(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False) : Integer;
  657. var
  658. LEntryMaxLen : Integer;
  659. li : Integer;
  660. begin
  661. Result := 0;
  662. for li := 0 to ACurDir.FileList.Count-1 do begin
  663. LEntryMaxLen := Length(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])));
  664. if LEntryMaxLen > Result then begin
  665. Result := LEntryMaxLen;
  666. end;
  667. end;
  668. if Recurse and Assigned(ACurDir.SubDirs) then begin
  669. for li := 0 to ACurDir.SubDirs.Count-1 do begin
  670. LEntryMaxLen := CalcMaxLen(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  671. if LEntryMaxLen > Result then begin
  672. Result := LEntryMaxLen;
  673. end;
  674. end;
  675. end;
  676. end;
  677. procedure ProcessPathAccross(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  678. var
  679. li, j : Integer;
  680. LTmp : String;
  681. LMaxLen : Integer;
  682. LCols : Integer;
  683. LCurItem : TIdFTPListOutputItem;
  684. begin
  685. if ACurDir.FileList.Count = 0 then begin
  686. Exit;
  687. end;
  688. //Note that we will assume a console width of 80 and we don't want something to wrap
  689. //causing a blank line
  690. LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse);
  691. //if more than 39, we probably are going to exceed the width of the screen,
  692. //just treat as one column
  693. if LMaxLen > 39 then begin
  694. ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse);
  695. Exit;
  696. end;
  697. if Recurse and Assigned(ACurDir.SubDirs) then begin
  698. if Recurse then begin
  699. PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
  700. end;
  701. end;
  702. LCols := 79 div (LMaxLen + 2);//2 spaces between columns
  703. j := 0;
  704. repeat
  705. LTmp := '';
  706. for li := 0 to LCols -1 do begin
  707. LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[j])), LMaxLen, ' ') + ' ';
  708. Inc(j);
  709. if j = ACurDir.FileList.Count then begin
  710. Break;
  711. end;
  712. end;
  713. ALOutput.Add(TrimRight(LTmp));
  714. until j = ACurDir.FileList.Count;
  715. if Recurse and Assigned(ACurDir.SubDirs) then begin
  716. for li := 0 to ACurDir.SubDirs.Count-1 do begin
  717. LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
  718. if LCurItem.DirError then begin
  719. if li = 0 then begin
  720. ALOutput.Add('');
  721. end;
  722. ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
  723. end else begin
  724. ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  725. end;
  726. end;
  727. end;
  728. end;
  729. procedure ProcessPathDown(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  730. var
  731. li, j : Integer;
  732. LTmp : String;
  733. LMaxLen : Integer;
  734. LCols : Integer;
  735. LLines : Integer;
  736. // LFrm : String;
  737. LCurItem : TIdFTPListOutputItem;
  738. begin
  739. if ACurDir.FileList.Count = 0 then begin
  740. Exit;
  741. end;
  742. //Note that we will assume a console width of 80 and we don't want something to wrap
  743. //causing a blank line
  744. LMaxLen := CalcMaxLen(ARoot, ACurDir, ALOutput, Recurse);
  745. //if more than 39, we probably are going to exceed the width of the screen,
  746. //just treat as one column
  747. if LMaxLen > 39 then begin
  748. ProcessOnePathCol(ARoot, ACurDir, ALOutput, Recurse);
  749. Exit;
  750. end;
  751. if Recurse and Assigned(ACurDir.SubDirs) then begin
  752. if Recurse then begin
  753. PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
  754. end;
  755. end;
  756. LCols := 79 div (LMaxLen + 2);//2 spaces between columns
  757. LLines := ACurDir.FileList.COunt div LCols;
  758. //LFrm := '%' + IntToStr(LMaxLen+2) + 's';
  759. if (ACurDir.FileList.COunt mod LCols) > 0 then begin
  760. Inc(LLines);
  761. end;
  762. for li := 1 to LLines do begin
  763. j := 0;
  764. LTmp := '';
  765. repeat
  766. if ((li-1)+(LLInes*j)) < ACurDir.FileList.Count then begin
  767. LTmp := LTmp + PadString(NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[(li-1)+(LLInes*j)])), LMaxLen, ' ') + ' ';
  768. end;
  769. Inc(j);
  770. until (j > LCols);
  771. ALOutput.Add(TrimRight(LTmp));
  772. end;
  773. if Recurse and Assigned(ACurDir.SubDirs) then begin
  774. for li := 0 to ACurDir.SubDirs.Count -1 do begin
  775. LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
  776. if LCurItem.DirError then begin
  777. if li = 0 then begin
  778. ALOutput.Add('');
  779. end;
  780. ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
  781. end else begin
  782. ProcessPathAccross(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  783. end;
  784. end;
  785. end;
  786. end;
  787. procedure ProcessPathComma(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  788. var
  789. li : Integer;
  790. LTmp : String;
  791. LCurItem : TIdFTPListOutputItem;
  792. begin
  793. if Recurse then begin
  794. PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
  795. end;
  796. LTmp := '';
  797. for li := 0 to ACurDir.FileList.Count -1 do begin
  798. LTmp := LTmp + NListItem(TIdFTPListOutputItem(ACurDir.FileList.Objects[li])) + ', ';
  799. end;
  800. IdDelete(LTmp, Length(LTmp)-1, 2);
  801. ALOutput.Text := ALOutput.Text + IndyWrapText(LTmp, EOL + ' ', LWS + ',' , 79); //79 good maxlen for text only terminals
  802. if Recurse and Assigned(ACurDir.SubDirs) then begin
  803. for li := 0 to ACurDir.SubDirs.Count -1 do begin
  804. LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
  805. if LCurItem.DirError then begin
  806. if li = 0 then begin
  807. ALOutput.Add('');
  808. end;
  809. ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
  810. end else begin
  811. ProcessPathComma(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  812. end;
  813. end;
  814. end;
  815. end;
  816. procedure ProcessPathLong(ARoot, ACurDir : TDirEntry; ALOutput : TStrings; const Recurse : Boolean = False);
  817. var
  818. li : Integer;
  819. LBlockCount : Integer;
  820. LCurItem : TIdFTPListOutputItem;
  821. begin
  822. if Recurse then begin
  823. PrintSubDirHeader(ARoot, ACurDir, ALOutput, Recurse);
  824. end;
  825. if (DirFormat = doUnix) and ExportTotalLine then begin
  826. LBlockCount := 0;
  827. for li := 0 to ACurDir.FileList.Count-1 do begin
  828. LBlockCount := LBlockCount + TIdFTPListOutputItem(ACurDir.FileList.Objects[li]).NumberBlocks;
  829. end;
  830. ALOutput.Add(IndyFormat('total %d', [LBlockCount])); {Do not translate}
  831. end;
  832. for li := 0 to ACurDir.FileList.Count-1 do begin
  833. LCurItem := TIdFTPListOutputItem(ACurDir.FileList.Objects[li]);
  834. case DirFormat of
  835. doEPLF : ALOutput.Add(EPLFItem(LCurItem));
  836. doWin32 : ALOutput.Add(Win32Item(LCurItem));
  837. else
  838. ALOutput.Add(UnixItem(LCurItem));
  839. end;
  840. end;
  841. if Recurse and Assigned(ACurDir.SubDirs) then begin
  842. for li := 0 to ACurDir.SubDirs.Count-1 do begin
  843. LCurItem := TDirEntry(ACurDir.SubDirs[li]).DirListItem;
  844. if LCurItem.DirError then begin
  845. if DirFormat = doUnix then begin
  846. if li = 0 then begin
  847. ALOutput.Add('');
  848. end;
  849. ALOutput.Add(IndyFormat('/bin/ls: %s: Permission denied', [LCurItem.FileName])); {do not localize}
  850. end;
  851. end;
  852. ProcessPathLong(ARoot, TDirEntry(ACurDir.SubDirs[li]), ALOutput, Recurse);
  853. end;
  854. end;
  855. end;
  856. procedure DoUnixfParam(ARoot : TDirEntry; ALOutput : TStrings);
  857. var
  858. li : Integer;
  859. LIt : TIdFTPListItem;
  860. begin
  861. for li := 0 to ARoot.FileList.Count -1 do begin
  862. LIt := TIdFTPListItem(ARoot.FileList.Objects[li]);
  863. if LIt.ItemType = ditDirectory then begin
  864. ALOutput.Add(IndyGetFileName(LIt.FileName));
  865. end;
  866. end;
  867. end;
  868. begin
  869. LShowNavSym := (DirFormat = doUnix) and HasSwitch(SWITCH_SHOW_ALLPERIOD);
  870. if LShowNavSym then begin
  871. LShowNavSym := not HasSwitch(SWITCH_HIDE_DIRPOINT);
  872. end;
  873. LRootPath := TDirEntry.Create('', nil);
  874. try
  875. for i := 0 to Count-1 do
  876. begin
  877. if Items[i].ItemType in [ditDirectory, ditSymbolicLinkDir] then begin
  878. if not IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin
  879. LRootPath.AddSubDir(StripInitPathDelim(Items[i].FileName), Items[i]);
  880. end else begin
  881. //if it's a "." or "..", we show it only in Unix mode and only with eht -a switch
  882. if LShowNavSym then begin
  883. LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
  884. end;
  885. end;
  886. end;
  887. end;
  888. //add the file names
  889. for i := 0 to Count-1 do begin
  890. if Items[i].ItemType in [ditFile, ditSymbolicLink] then begin
  891. if IsNavPath(StripInitPathDelim(IndyGetFileName(Items[i].FileName))) then begin
  892. if LShowNavSym then begin
  893. LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
  894. end;
  895. end else begin
  896. LRootPath.AddFileName(StripInitPathDelim(Items[i].FileName), Items[i]);
  897. end;
  898. end;
  899. end;
  900. //Note that Indy does not support a Last Access time in some file systems
  901. //so we use the u parameter to mean the same as the t parameter
  902. if HasSwitch(SWITCH_SORT_REVERSE) then begin
  903. if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin
  904. LRootPath.SortDescendMTime;
  905. end
  906. else if HasSwitch(SWITCH_SORTBY_EXT) then begin
  907. LRootPath.SortDescendFNameExt;
  908. end
  909. else if HasSwitch(SWITCH_SORTBY_SIZE) then begin
  910. LRootPath.SortDescendSize;
  911. end
  912. else begin
  913. LRootPath.SortDescendFName;
  914. end;
  915. end
  916. else if HasSwitch(SWITCH_SORTBY_MTIME) or HasSwitch(SWITCH_SORTBY_CTIME) then begin
  917. LRootPath.SortAscendMTime;
  918. end
  919. else if HasSwitch(SWITCH_SORTBY_EXT) then begin
  920. LRootPath.SortAscendFNameExt;
  921. end
  922. else if HasSwitch(SWITCH_SORTBY_SIZE) then begin
  923. LRootPath.SortAscendSize;
  924. end
  925. else begin
  926. LRootPath.SortAscendFName;
  927. end;
  928. //select the operation
  929. // do the selected output operation
  930. case DetermineOp of
  931. doColsAccross : ProcessPathAccross(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
  932. doColsDown : ProcessPathDown(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
  933. doOneCol : ProcessOnePathCol(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
  934. doOnlyDirs : DoUnixfParam(LRootPath, AOutput);
  935. doComma : ProcessPathComma(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
  936. else
  937. ProcessPathLong(LRootPath, LRootPath, AOutput, HasSwitch(SWITCH_RECURSIVE));
  938. end;
  939. finally
  940. FreeAndNil(LRootPath);
  941. end;
  942. end;
  943. procedure TIdFTPListOutput.LISTOutputDir(AOutput: TStrings);
  944. begin
  945. InternelOutputDir(AOutput, True);
  946. end;
  947. function TIdFTPListOutput.MListItem(AItem: TIdFTPListOutputItem; AMLstOpts: TIdFTPFactOutputs): String;
  948. begin
  949. Result := '';
  950. if AMLstOpts = [] then begin
  951. Result := AItem.FileName;
  952. Exit;
  953. end;
  954. if (Size in AMLstOpts) and AItem.SizeAvail then begin
  955. Result := 'size=' + IntToStr(AItem.Size) + ';'; {do not localize}
  956. end;
  957. if ItemType in AMLstOpts then begin
  958. Result := Result + 'type='; {do not localize}
  959. case AItem.ItemType of
  960. ditFile :
  961. begin
  962. Result := Result + 'file;'; {do not localize}
  963. end;
  964. ditDirectory :
  965. begin
  966. if AItem.FileName = '..' then begin {do not localize}
  967. Result := Result + 'pdir;'; {do not localize}
  968. end
  969. else if AItem.FileName = '.' then begin
  970. Result := Result + 'cdir;'; {do not localize}
  971. end
  972. else begin
  973. Result := Result + 'dir;'; {do not localize}
  974. end;
  975. end;
  976. ditSymbolicLink :
  977. begin
  978. Result := Result + 'OS.unix=slink:' + AItem.FileName + ';'; {do not localize}
  979. end;
  980. end;
  981. end;
  982. if Perm in AMLstOpts then begin
  983. Result := Result + 'perm=' + AItem.MLISTPermissions + ';'; {do not localize}
  984. end;
  985. if (winDriveType in AMLstOpts) and (AItem.WinDriveType<>-1) then begin
  986. Result := Result + 'win32.dt='+IntToStr(AItem.WinDriveType )+';';
  987. end;
  988. if CreateTime in AMLstOpts then begin
  989. if AItem.CreationDateGMT <> 0 then begin
  990. Result := Result + 'create='+ FTPGMTDateTimeToMLS(AItem.CreationDateGMT) + ';'; {do not localize}
  991. end
  992. else if AItem.CreationDate <> 0 then begin
  993. Result := Result + 'create='+ FTPLocalDateTimeToMLS(AItem.CreationDate) + ';'; {do not localize}
  994. end;
  995. end;
  996. if (Modify in AMLstOpts) and AItem.ModifiedAvail then
  997. begin
  998. if AItem.ModifiedDateGMT <> 0 then begin
  999. Result := Result + 'modify='+ FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize}
  1000. end
  1001. else if AItem.ModifiedDate <> 0 then begin
  1002. Result := Result + 'modify='+ FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize}
  1003. end;
  1004. end;
  1005. if UnixMODE in AMLstOpts then begin
  1006. Result := Result + 'UNIX.mode='+ IndyFormat('%.4d', [PermsToChmodNo(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem) )] ) + ';'; {do not localize}
  1007. end;
  1008. if UnixOwner in AMLstOpts then begin
  1009. Result := Result + 'UNIX.owner=' + UnixGetOutputOwner(AItem) + ';'; {do not localize}
  1010. end;
  1011. if UnixGroup in AMLstOpts then begin
  1012. Result := Result + 'UNIX.group=' + UnixGetOutputGroup(AItem) + ';'; {do not localize}
  1013. end;
  1014. if (Unique in AMLstOpts) and (AItem.UniqueID <> '') then begin
  1015. Result := Result + 'unique=' + AItem.UniqueID + ';'; {do not localize}
  1016. end;
  1017. if LastAccessTime in AMLstOpts then begin
  1018. if AItem.ModifiedDateGMT <> 0 then begin
  1019. Result := Result + 'windows.lastaccesstime=' + FTPGMTDateTimeToMLS(AItem.ModifiedDateGMT) + ';'; {do not localize}
  1020. end
  1021. else if AItem.ModifiedDate <> 0 then begin
  1022. Result := Result + 'windows.lastaccesstime=' + FTPLocalDateTimeToMLS(AItem.ModifiedDate) + ';'; {do not localize}
  1023. end;
  1024. end;
  1025. if WinAttribs in AMLstOpts then begin
  1026. Result := Result + 'win32.ea=0x' + IntToHex(AItem.WinAttribs, 8) + ';'; {do not localize}
  1027. end;
  1028. if (AItem.WinDriveType > -1) and (WinDriveType in AMLstOpts) then begin
  1029. Result := Result + 'Win32.dt='+IntToStr( AItem.WinDriveType ) + ';'; {do not localize}
  1030. end;
  1031. if (AItem.WinDriveLabel <> '') and (WinDriveLabel in AMLstOpts) then begin
  1032. Result := Result + 'Win32.dl='+AItem.WinDriveLabel + ';'; {do not localize}
  1033. end;
  1034. Result := Result + ' ' + AItem.FileName; {do not localize}
  1035. end;
  1036. procedure TIdFTPListOutput.MLISTOutputDir(AOutput : TStrings; AMLstOpts: TIdFTPFactOutputs);
  1037. var
  1038. i : Integer;
  1039. begin
  1040. AOutput.BeginUpdate;
  1041. try
  1042. AOutput.Clear;
  1043. for i := 0 to Count-1 do begin
  1044. AOutput.Add(MListItem(Items[i], AMLstOpts));
  1045. end;
  1046. finally
  1047. AOutput.EndUpdate;
  1048. end;
  1049. end;
  1050. function TIdFTPListOutput.NListItem(AItem: TIdFTPListOutputItem): String;
  1051. begin
  1052. Result := IndyGetFileName(AItem.FileName);
  1053. if DirFormat = doUnix then begin
  1054. if HasSwitch(SWITCH_QUOTEDNAME) then begin
  1055. Result := '"' + Result + '"';
  1056. end;
  1057. if HasSwitch(SWITCH_CLASSIFY) or HasSwitch(SWITCH_SLASHDIR) then begin
  1058. case AItem.ItemType of
  1059. ditDirectory :
  1060. Result := Result + PATH_SUBDIR_SEP_UNIX;
  1061. ditSymbolicLink, ditSymbolicLinkDir :
  1062. Result := Result + '@';
  1063. else
  1064. begin
  1065. if IsUnixExec(AItem.UnixOwnerPermissions, AItem.UnixGroupPermissions , AItem.UnixOtherPermissions) then begin
  1066. Result := Result + '*';
  1067. end;
  1068. end;
  1069. end;
  1070. end;
  1071. Result := UnixinodeOutput(AItem)+ UnixBlocksOutput(AItem) + Result;
  1072. end;
  1073. end;
  1074. procedure TIdFTPListOutput.NLISTOutputDir(AOutput: TStrings);
  1075. begin
  1076. InternelOutputDir(AOutput, False);
  1077. end;
  1078. procedure TIdFTPListOutput.SetItems(AIndex: Integer; const AValue: TIdFTPListOutputItem);
  1079. begin
  1080. inherited Items[AIndex] := AValue;
  1081. end;
  1082. function TIdFTPListOutput.UnixBlocksOutput(AItem: TIdFTPListOutputItem): String;
  1083. begin
  1084. if HasSwitch(SWITCH_PRINT_BLOCKS) then begin
  1085. Result := IndyFormat('%4d ', [AItem.NumberBlocks]);
  1086. end else begin
  1087. Result := '';
  1088. end;
  1089. end;
  1090. function TIdFTPListOutput.UnixGetOutputGroup(AItem: TIdFTPListOutputItem): String;
  1091. begin
  1092. if AItem.GroupName = '' then begin
  1093. Result := UnixGetOutputOwner(AItem);
  1094. end else begin
  1095. Result := AItem.GroupName;
  1096. end;
  1097. end;
  1098. function TIdFTPListOutput.UnixGetOutputGroupPerms(AItem: TIdFTPListOutputItem): String;
  1099. begin
  1100. if AItem.UnixOtherPermissions = '' then begin
  1101. if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
  1102. Result := DEF_DIR_GRP_PERM;
  1103. end else begin
  1104. Result := DEF_FILE_GRP_PERM;
  1105. end;
  1106. end else begin
  1107. Result := AItem.UnixOtherPermissions;
  1108. end;
  1109. end;
  1110. function TIdFTPListOutput.UnixGetOutputOtherPerms(AItem: TIdFTPListOutputItem): String;
  1111. begin
  1112. if AItem.UnixOtherPermissions = '' then begin
  1113. if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
  1114. Result := DEF_DIR_OTHER_PERM;
  1115. end else begin
  1116. Result := DEF_FILE_OTHER_PERM;
  1117. end;
  1118. end else begin
  1119. Result := AItem.UnixOtherPermissions;
  1120. end;
  1121. end;
  1122. function TIdFTPListOutput.UnixGetOutputOwner(AItem: TIdFTPListOutputItem): String;
  1123. begin
  1124. if AItem.OwnerName = '' then begin
  1125. Result := DEF_OWNER;
  1126. end else begin
  1127. Result := AItem.OwnerName;
  1128. end;
  1129. end;
  1130. function TIdFTPListOutput.UnixGetOutputOwnerPerms(AItem: TIdFTPListOutputItem): String;
  1131. begin
  1132. if AItem.UnixOwnerPermissions = '' then begin
  1133. if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
  1134. Result := DEF_DIR_OWN_PERM;
  1135. end else begin
  1136. Result := DEF_FILE_OWN_PERM;
  1137. end;
  1138. end else begin
  1139. Result := AItem.UnixOwnerPermissions;
  1140. end;
  1141. end;
  1142. function TIdFTPListOutput.UnixINodeOutput(AItem: TIdFTPListOutputItem): String;
  1143. var
  1144. LInode : String;
  1145. begin
  1146. Result := '';
  1147. if HasSwitch(SWITCH_PRINT_INODE) then begin
  1148. LInode := IntToStr(Abs(AItem.Inode));
  1149. //should be no more than 10 digits
  1150. LInode := Copy(LInode, 1, 10);
  1151. Result := Result + IndyFormat('%10s ', [LInode]);
  1152. end;
  1153. end;
  1154. function TIdFTPListOutput.UnixItem(AItem: TIdFTPListOutputItem): String;
  1155. var
  1156. LSize, LTime: string;
  1157. l, month: Word;
  1158. LLinkNum : Integer;
  1159. LFileName : String;
  1160. LFormat : String;
  1161. LMTime : TDateTime;
  1162. begin
  1163. LFileName := IndyGetFileName(AItem.FileName);
  1164. Result := UnixINodeOutput(AItem) + UnixBlocksOutput(AItem);
  1165. case AItem.ItemType of
  1166. ditDirectory:
  1167. begin
  1168. AItem.Size := UNIX_DIR_SIZE;
  1169. LSize := 'd'; {Do not Localize}
  1170. end;
  1171. ditSymbolicLink:
  1172. begin
  1173. LSize := 'l'; {Do not Localize}
  1174. end;
  1175. else
  1176. begin
  1177. LSize := '-'; {Do not Localize}
  1178. end;
  1179. end;
  1180. if AItem.LinkCount = 0 then begin
  1181. LLinkNum := 1;
  1182. end else begin
  1183. LLinkNum := AItem.LinkCount;
  1184. end;
  1185. LFormat := '%3:3s%4:3s%5:3s %6:3d '; {Do not localize}
  1186. //g - surpress owner
  1187. //lrwxrwxrwx 1 other 7 Nov 16 2001 bin -> usr/bin
  1188. //where it would normally print
  1189. //lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin
  1190. if not HasSwitch('g') then begin
  1191. LFormat := LFormat + '%1:-8s '; {Do not localize}
  1192. end;
  1193. //o - surpress group
  1194. //lrwxrwxrwx 1 root 7 Nov 16 2001 bin -> usr/bin
  1195. //where it would normally print
  1196. //lrwxrwxrwx 1 root other 7 Nov 16 2001 bin -> usr/bin
  1197. if not HasSwitch('o') then begin
  1198. LFormat := LFormat + '%2:-8s '; {Do not localize}
  1199. end;
  1200. LFormat := LFormat + '%0:8d'; {Do not localize}
  1201. LSize := LSize + IndyFormat(LFormat, [AItem.Size, UnixGetOutputOwner(AItem),
  1202. UnixGetOutputGroup(AItem), UnixGetOutputOwnerPerms(AItem),
  1203. UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem), LLinkNum]);
  1204. LMTime := GetLocalModTime(AItem);
  1205. DecodeDate(LMTime, l, month, l);
  1206. LTime := MonthNames[month] + FormatDateTime(' dd', LMTime); {Do not Localize}
  1207. if HasSwitch(SWITCH_BOTH_TIME_YEAR) then begin
  1208. LTime := LTime + FormatDateTime(' hh:nn:ss yyyy', LMTime); {Do not Localize}
  1209. end
  1210. else if IsIn6MonthWindow(LMTime) then begin {Do not Localize}
  1211. LTime := LTime + FormatDateTime(' hh:nn', LMTime); {Do not Localize}
  1212. end
  1213. else begin
  1214. LTime := LTime + FormatDateTime(' yyyy', LMTime); {Do not Localize}
  1215. end;
  1216. // A.Neillans, 20 Apr 2002, Fixed glitch, extra space in front of names.
  1217. // Result := LSize + ' ' + LTime + ' ' + FileName; {Do not Localize}
  1218. Result := Result + LSize + ' ' + LTime + ' ';
  1219. if HasSwitch(SWITCH_QUOTEDNAME) then begin
  1220. Result := Result + '"' + LFileName + '"'; {Do not Localize}
  1221. end else begin
  1222. Result := Result + LFileName;
  1223. end;
  1224. if AItem.ItemType in [ditSymbolicLink, ditSymbolicLinkDir] then begin
  1225. if HasSwitch(SWITCH_QUOTEDNAME) then begin
  1226. Result := Result + UNIX_LINKTO_SYM + '"' + AItem.LinkedItemName + '"'; {Do not Localize}
  1227. end else begin
  1228. Result := Result + UNIX_LINKTO_SYM + AItem.LinkedItemName;
  1229. end;
  1230. end;
  1231. if ((IndyPos(SWITCH_CLASSIFY,Switches)>0) or (IndyPos(SWITCH_SLASHDIR,Switches)>0)) and {Do not translate}
  1232. (AItem.ItemType in [ditDirectory, ditSymbolicLinkDir]) then
  1233. begin
  1234. Result := Result + PATH_SUBDIR_SEP_UNIX;
  1235. end;
  1236. if HasSwitch(SWITCH_CLASSIFY) and (AItem.ItemType = ditFile) and
  1237. IsUnixExec(UnixGetOutputOwnerPerms(AItem), UnixGetOutputGroupPerms(AItem), UnixGetOutputOtherPerms(AItem)) then
  1238. begin
  1239. //star is placed at the end of a file name
  1240. //like this:
  1241. //-r-xr-xr-x 1 0 1 17440 Aug 8 2000 ls*
  1242. Result := Result + '*';
  1243. end;
  1244. end;
  1245. function TIdFTPListOutput.Win32Item(AItem: TIdFTPListOutputItem): String;
  1246. var
  1247. LSize, LFileName : String;
  1248. begin
  1249. LFileName := IndyGetFileName(AItem.FileName);
  1250. if AItem.ItemType = ditDirectory then begin
  1251. LSize := ' <DIR>' + StringOfChar(' ', 9); {Do not Localize}
  1252. end else begin
  1253. LSize := StringOfChar(' ', 20 - Length(IntToStr(AItem.Size))) + IntToStr(AItem.Size); {Do not Localize}
  1254. end;
  1255. Result := FormatDateTime('mm-dd-yy hh:nnAM/PM', GetLocalModTime(AItem)) + ' ' + LSize + ' ' + LFileName; {Do not Localize}
  1256. end;
  1257. { TDirEntry }
  1258. function TDirEntry.AddFileName(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean;
  1259. var
  1260. i : Integer;
  1261. LParentPart : String;
  1262. LDirEnt : TDirEntry;
  1263. begin
  1264. Result := False;
  1265. LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
  1266. if LParentPart = PathName then begin
  1267. if FFileList.IndexOf(APathName) = -1 then begin
  1268. FFileList.AddObject(APathName, ADirEnt);
  1269. end;
  1270. Result := True;
  1271. Exit;
  1272. end;
  1273. if Assigned(SubDirs) then begin
  1274. for i := 0 to SubDirs.Count-1 do begin
  1275. LDirEnt := TDirEntry(SubDirs[i]);
  1276. LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName));
  1277. if TextStartsWith(APathName, LParentPart) then begin
  1278. if TDirEntry(SubDirs[i]).AddFileName(APathName, ADirEnt) then begin
  1279. Result := True;
  1280. Break;
  1281. end;
  1282. end;
  1283. end;
  1284. end;
  1285. end;
  1286. function TDirEntry.AddSubDir(const APathName: String; ADirEnt: TIdFTPListOutputItem) : Boolean;
  1287. var
  1288. LDirEnt : TDirEntry;
  1289. i : Integer;
  1290. LParentPart : String;
  1291. begin
  1292. Result := False;
  1293. LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
  1294. if LParentPart = PathName then begin
  1295. if not Assigned(FSubDirs) then begin
  1296. FSubDirs := TDirEntryList.Create;
  1297. end;
  1298. LParentPart := StripInitPathDelim(IndyGetFilePath(APathName));
  1299. LParentPart := IndyGetFileName(LParentPart);
  1300. LDirEnt := TDirEntry.Create(APathName, ADirEnt);
  1301. try
  1302. FSubDirs.Add(LDirEnt);
  1303. except
  1304. LDirEnt.Free;
  1305. raise;
  1306. end;
  1307. AddFileName(APathName, ADirEnt);
  1308. Result := True;
  1309. Exit;
  1310. end;
  1311. if Assigned(SubDirs) then begin
  1312. for i := 0 to SubDirs.Count-1 do begin
  1313. LDirEnt := TDirEntry(SubDirs[i]);
  1314. LParentPart := StripInitPathDelim(IndyGetFilePath(LDirEnt.FDirListItem.FileName));
  1315. if TextStartsWith(APathName, LParentPart) then begin
  1316. if LDirEnt.AddSubDir(APathName, ADirEnt) then begin
  1317. Result := True;
  1318. Break;
  1319. end;
  1320. end;
  1321. end;
  1322. end;
  1323. end;
  1324. constructor TDirEntry.Create(const APathName : String; ADirListItem : TIdFTPListOutputItem);
  1325. begin
  1326. inherited Create;
  1327. FPathName := APathName;
  1328. FFileList := TIdBubbleSortStringList.Create;
  1329. FDirListItem := ADirListItem;
  1330. //create that only when necessary;
  1331. FSubDirs := TDirEntryList.Create;
  1332. end;
  1333. destructor TDirEntry.Destroy;
  1334. begin
  1335. FreeAndNil(FFileList);
  1336. FreeAndNil(FSubDirs);
  1337. inherited Destroy;
  1338. end;
  1339. procedure TDirEntry.SortAscendFName;
  1340. var
  1341. i : Integer;
  1342. LSubDir: TDirEntry;
  1343. begin
  1344. if Assigned(FFileList) then begin
  1345. FFileList.BubbleSort(StrSortAscFName);
  1346. end;
  1347. if Assigned(FSubDirs) then begin
  1348. FSubDirs.BubbleSort(
  1349. {$IFDEF HAS_GENERICS_TObjectList}
  1350. DESortAscFName
  1351. {$ELSE}
  1352. TIdSortCompare(@DESortAscFName)
  1353. {$ENDIF}
  1354. );
  1355. for i := 0 to FSubDirs.Count-1 do begin
  1356. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1357. LSubDir.SortAscendFName;
  1358. end;
  1359. end;
  1360. end;
  1361. procedure TDirEntry.SortAscendMTime;
  1362. var
  1363. i : Integer;
  1364. LSubDir: TDirEntry;
  1365. begin
  1366. if Assigned(FFileList) then begin
  1367. FFileList.BubbleSort(StrSortAscMTime);
  1368. end;
  1369. if Assigned(FSubDirs) then begin
  1370. FSubDirs.BubbleSort(
  1371. {$IFDEF HAS_GENERICS_TObjectList}
  1372. DESortAscMTime
  1373. {$ELSE}
  1374. TIdSortCompare(@DESortAscMTime)
  1375. {$ENDIF}
  1376. );
  1377. for i := 0 to FSubDirs.Count-1 do begin
  1378. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1379. LSubDir.SortAscendMTime;
  1380. end;
  1381. end;
  1382. end;
  1383. procedure TDirEntry.SortDescendMTime;
  1384. var
  1385. i : Integer;
  1386. LSubDir: TDirEntry;
  1387. begin
  1388. if Assigned(FFileList) then begin
  1389. FFileList.BubbleSort(StrSortDescMTime);
  1390. end;
  1391. if Assigned(FSubDirs) then begin
  1392. FSubDirs.BubbleSort(
  1393. {$IFDEF HAS_GENERICS_TObjectList}
  1394. DESortDescMTime
  1395. {$ELSE}
  1396. TIdSortCompare(@DESortDescMTime)
  1397. {$ENDIF}
  1398. );
  1399. for i := 0 to FSubDirs.Count -1 do begin
  1400. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1401. LSubDir.SortDescendMTime;
  1402. end;
  1403. end;
  1404. end;
  1405. procedure TDirEntry.SortDescendFName;
  1406. var
  1407. i : Integer;
  1408. LSubDir: TDirEntry;
  1409. begin
  1410. if Assigned(FSubDirs) then begin
  1411. FSubDirs.BubbleSort(
  1412. {$IFDEF HAS_GENERICS_TObjectList}
  1413. DESortDescFName
  1414. {$ELSE}
  1415. TIdSortCompare(@DESortDescFName)
  1416. {$ENDIF}
  1417. );
  1418. for i := 0 to FSubDirs.Count-1 do begin
  1419. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1420. LSubDir.SortDescendFName;
  1421. end;
  1422. end;
  1423. if Assigned(FFileList) then begin
  1424. FFileList.BubbleSort(StrSortDescFName);
  1425. end;
  1426. end;
  1427. procedure TDirEntry.SortAscendFNameExt;
  1428. var
  1429. i : Integer;
  1430. LSubDir: TDirEntry;
  1431. begin
  1432. if Assigned(FFileList) then begin
  1433. FFileList.BubbleSort(StrSortAscFNameExt);
  1434. end;
  1435. if Assigned(FSubDirs) then begin
  1436. FSubDirs.BubbleSort(
  1437. {$IFDEF HAS_GENERICS_TObjectList}
  1438. DESortAscFName
  1439. {$ELSE}
  1440. TIdSortCompare(@DESortAscFName)
  1441. {$ENDIF}
  1442. );
  1443. for i := 0 to FSubDirs.Count-1 do begin
  1444. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1445. LSubDir.SortAscendFNameExt;
  1446. end;
  1447. end;
  1448. end;
  1449. procedure TDirEntry.SortDescendFNameExt;
  1450. var
  1451. i : Integer;
  1452. LSubDir: TDirEntry;
  1453. begin
  1454. if Assigned(FFileList) then begin
  1455. FFileList.BubbleSort(StrSortDescFNameExt);
  1456. end;
  1457. if Assigned(FSubDirs) then begin
  1458. FSubDirs.BubbleSort(
  1459. {$IFDEF HAS_GENERICS_TObjectList}
  1460. DESortAscFName
  1461. {$ELSE}
  1462. TIdSortCompare(@DESortAscFName)
  1463. {$ENDIF}
  1464. );
  1465. for i := 0 to FSubDirs.Count-1 do begin
  1466. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1467. LSubDir.SortDescendFNameExt;
  1468. end;
  1469. end;
  1470. end;
  1471. procedure TDirEntry.SortAscendSize;
  1472. var
  1473. i : Integer;
  1474. LSubDir: TDirEntry;
  1475. begin
  1476. if Assigned(FFileList) then begin
  1477. FFileList.BubbleSort(StrSortAscSize);
  1478. end;
  1479. if Assigned(FSubDirs) then begin
  1480. FSubDirs.BubbleSort(
  1481. {$IFDEF HAS_GENERICS_TObjectList}
  1482. DESortAscMTime
  1483. {$ELSE}
  1484. TIdSortCompare(@DESortAscMTime)
  1485. {$ENDIF}
  1486. );
  1487. for i := 0 to FSubDirs.Count-1 do begin
  1488. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1489. LSubDir.SortAscendSize;
  1490. end;
  1491. end;
  1492. end;
  1493. procedure TDirEntry.SortDescendSize;
  1494. var
  1495. i : Integer;
  1496. LSubDir: TDirEntry;
  1497. begin
  1498. if Assigned(FFileList) then begin
  1499. FFileList.BubbleSort(StrSortDescSize);
  1500. end;
  1501. if Assigned(FSubDirs) then begin
  1502. FSubDirs.BubbleSort(
  1503. {$IFDEF HAS_GENERICS_TObjectList}
  1504. DESortDescFName
  1505. {$ELSE}
  1506. TIdSortCompare(@DESortDescFName)
  1507. {$ENDIF}
  1508. );
  1509. for i := 0 to FSubDirs.Count-1 do begin
  1510. LSubDir := {$IFDEF HAS_GENERICS_TObjectList}FSubDirs[i]{$ELSE}TDirEntry(FSubDirs[i]){$ENDIF};
  1511. LSubDir.SortDescendSize;
  1512. end;
  1513. end;
  1514. end;
  1515. { TIdFTPListOutputItem }
  1516. constructor TIdFTPListOutputItem.Create(AOwner: TCollection);
  1517. begin
  1518. inherited Create(AOwner);
  1519. //indicate that this fact is not applicable
  1520. FWinDriveType := -1;
  1521. end;
  1522. end.