{ $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.7 10/26/2004 9:46:36 PM JPMugaas Updated refs. Rev 1.6 6/8/2004 12:42:22 PM JPMugaas Fixed an Invalid Type Cast problem. Rev 1.5 4/19/2004 5:05:36 PM JPMugaas Class rework Kudzu wanted. Rev 1.4 2004.02.03 5:45:24 PM czhower Name changes Rev 1.3 10/19/2003 3:36:02 PM DSiders Added localization comments. Rev 1.2 4/7/2003 04:03:58 PM JPMugaas User can now descover what output a parser may give. Rev 1.1 2/19/2003 05:53:20 PM JPMugaas Minor restructures to remove duplicate code and save some work with some formats. The Unix parser had a bug that caused it to give a False positive for Xercom MicroRTOS. Rev 1.0 2/19/2003 06:04:36 AM JPMugaas IBM MVS parser has been ported to new design. } unit IdFTPListParseMVS; interface {$i IdCompilerDefines.inc} uses Classes, IdFTPList, IdFTPListParseBase, IdFTPListTypes; { This should work with IBM MVS, OS/390, and z/OS. Note that in z/OS, there is no need for a parser for the HFS (hierarchical file system) because the server would present a Unix-like list for that file system. } type TIdJESJobStatus = (IdJESNotApplicable, IdJESReceived, IdJESHold, IdJESRunning, IdJESOuptutAvailable); TIdMVSFTPListItem = class(TIdRecFTPListItem) protected FBlockSize : Integer; FMigrated : Boolean; FVolume : String; FUnit : String; FOrg : String; //data set organization FMVSNumberExtents: Integer; FMVSNumberTracks: Integer; public constructor Create(AOwner: TCollection); override; property Migrated : Boolean read FMigrated write FMigrated; property BlockSize : Integer read FBlockSize write FBlockSize; property RecLength; property RecFormat; property NumberRecs; property Volume : String read FVolume write FVolume; //can't be unit because that's a reserved word property Units : String read FUnit write FUnit; property Org : String read FOrg write FOrg; //data set organization property NumberExtents: Integer read FMVSNumberExtents write FMVSNumberExtents; property NumberTracks: Integer read FMVSNumberTracks write FMVSNumberTracks; end; TIdMVSJESFTPListItem = class(TIdOwnerFTPListItem) protected FMVSJobStatus : TIdJESJobStatus; FMVSJobSpoolFiles : Integer; public constructor Create(AOwner: TCollection); override; property JobStatus : TIdJESJobStatus read FMVSJobStatus write FMVSJobStatus; property JobSpoolFiles : Integer read FMVSJobSpoolFiles write FMVSJobSpoolFiles; end; TIdMVSJESIntF2FTPListItem = class(TIdOwnerFTPListItem) protected FJobStatus : TIdJESJobStatus; FJobSpoolFiles : Integer; FDetails : TStrings; procedure SetDetails(AValue : TStrings); public constructor Create(AOwner: TCollection); override; destructor Destroy; override; property Details : TStrings read FDetails write SetDetails; property JobStatus : TIdJESJobStatus read FJobStatus write FJobStatus; property JobSpoolFiles : Integer read FJobSpoolFiles write FJobSpoolFiles; end; TIdFTPLPMVS = class(TIdFTPListBaseHeader) protected class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; class function IsHeader(const AData: String): Boolean; override; class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; public class function GetIdent : String; override; end; TIdFTPLPMVSPartitionedDataSet = class(TIdFTPListBaseHeader) protected class function IsHeader(const AData: String): Boolean; override; class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; public class function GetIdent : String; override; end; //Jes queues TIdFTPLPMVSJESInterface1 = class(TIdFTPListBase) protected class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; class function IsMVS_JESNoJobsMsg(const AData: String): Boolean; virtual; class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; public class function GetIdent : String; override; class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; end; TIdFTPLPMVSJESInterface2 = class(TIdFTPListBase) protected class function MakeNewItem(AOwner : TIdFTPListItems) : TIdFTPListItem; override; class function IsMVS_JESIntF2Header(const AData: String): Boolean; class function ParseLine(const AItem : TIdFTPListItem; const APath : String = ''): Boolean; override; public class function GetIdent : String; override; class function CheckListing(AListing : TStrings; const ASysDescript : String = ''; const ADetails : Boolean = True): Boolean; override; class function ParseListing(AListing : TStrings; ADir : TIdFTPListItems) : Boolean; override; end; // RLebeau 2/14/09: this forces C++Builder to link to this unit so // RegisterFTPListParser can be called correctly at program startup... {$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT} {$HPPEMIT LINKUNIT} {$ELSE} {$HPPEMIT '#pragma link "IdFTPListParseMVS"'} {$ENDIF} implementation uses IdException, IdGlobal, IdFTPCommon, IdGlobalProtocols, IdStrings, SysUtils; { TIdFTPLPMVS } class function TIdFTPLPMVS.GetIdent: String; begin Result := 'MVS'; {do not localize} end; class function TIdFTPLPMVS.IsHeader(const AData: String): Boolean; //Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname //Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname //Volume Unit Date Ext Used Recfm Lrecl BlkSz Dsorg Dsname var lvolp, lunp, lrefp, lextp, lusedp, lrecp, lBlkSz, lDsorg, lDsnp : Integer; begin {Note that this one is a little more difficult because I could not find a MVS machine that accepts anonymous FTP. So I have to do the best I can with some old posts where people had posted dir structures they got from FTP.} lvolp := IndyPos('Volume', AData); {Do not translate} lunp := IndyPos('Unit', AData); {Do not translate} lrefp := IndyPos('Referred', AData); {Do not translate} if lrefp = 0 then begin lrefp := IndyPos('Date', AData); {Do not translate} end; lextp := IndyPos('Ext', AData); {Do not translate} lusedp := IndyPos('Used', AData); {Do not translate} lrecp := IndyPos('Lrecl', AData); {Do not translate} lBlkSz := IndyPos('BlkSz', AData); {Do not translate} lDsorg := IndyPos('Dsorg', AData); {Do not translate} lDsnp := IndyPos('Dsname', AData); {Do not translate} Result := (lvolp <> 0) and (lunp > lvolp) and (lrefp > lunp) and (lextp > lrefp) and (lusedp > lextp) and (lrecp > lusedp) and (lBlkSz > lrecp) and (lDsorg > lBlkSz) and (lDsnp > lDsorg); end; class function TIdFTPLPMVS.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; begin Result := TIdMVSFTPListItem.Create(AOwner); end; class function TIdFTPLPMVS.ParseLine(const AItem: TIdFTPListItem; const APath: String): Boolean; {Much of this is based on a thread at: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 and http://www.snee.com/bob/opsys/part6mvs.pdf Note: Thread concerning MVS Data Set Size http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&threadm=jcmorris.767551300%40mwunix&rnum=15&prev=/groups%3Fq%3DMVS%2BRecfm%2BV%26start%3D10%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3Dutf-8%26selm%3Djcmorris.767551300%2540mwunix%26rnum%3D15 http://groups.google.com/groups?q=MVS+Recfm+V&start=10&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=jcmorris.767551300%40mwunix&rnum=15 http://www.isc.ucsb.edu/tsg/ftp-to-mvs.html http://www.lsu.edu/ocs/tsc/os390doc/mvsftp.html } function IsMVSMigrated(const AData : String) : Boolean; begin Result := TextStartsWith(AData, 'Migrated') or TextStartsWith(AData, 'MIGRAT'); {do not localize} end; function IsPseudoDir(const AData : String) : Boolean; begin //In newer implementations, a directory mode is available, see if //item is a Pseudo-directory Result := TextStartsWith(AData, 'Pseudo Directory'); {do not localize} end; function CanGetAttributes(const AData : String) : Boolean; begin Result := (not IsMVSMigrated(AData)) and (not IsPseudoDir(AData)) and (not TextStartsWith(AData, 'Error')); {do not localize} end; var i : Integer; s : TStrings; LI : TIdMVSFTPListItem; begin //NOTE: File Size is not supported at all //because the file size is calculated with something like this: // BlkSz * Blks/Trk * Trks //but you can not get MVS DEVINFO macro so you do not have enough information //to work with. AItem.ModifiedAvail := False; AItem.SizeAvail := False; LI := AItem as TIdMVSFTPListItem; if IsMVSMigrated(AItem.Data) then begin LI.Migrated := True; end; if IsPseudoDir(AItem.Data) then begin LI.ItemType := ditDirectory; end; if CanGetAttributes(AItem.Data) then begin s := TStringList.Create; try SplitDelimitedString(AItem.Data, s, True); if s.Count > 0 then begin LI.Volume := s[0]; end; if s.Count > 1 then begin LI.Units := s[1]; end; if s.Count > 2 then begin //Sometimes, the Referred Column will contain a date. //e.g. **NONE** //Documented in: Communications Server for z/OS V1R2 TCP/IP Implementation Guide Volume 2: UNIX Applications //URL: http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg245228.pdf if IsNumeric(s[2], 1, 1) then begin // If the number of extents is greater than 99 it can run into the date column: // SM6009 3380 2010/03/09123 2415 U 18432 18432 PO LOADLIB if Length(s[2]) > 10 then begin s.Insert(3, Copy(S[2], 11, MaxInt)); s[2] := Copy(S[2], 1, 10); end; LI.ModifiedDate := MVSDate(s[2]); LI.ModifiedAvail := True; end; end; if s.Count > 3 then begin LI.NumberExtents := IndyStrToInt(s[3], 0); end; if s.Count >4 then begin LI.NumberTracks := IndyStrToInt(s[4], 0); end; if s.Count > 5 then begin LI.RecFormat := s[5]; end; if s.Count >6 then begin LI.RecLength := IndyStrToInt(s[6], 0); end; if s.Count > 7 then begin LI.BlockSize := IndyStrToInt(s[7], 0); end; if s.Count > 8 then begin LI.Org := s[8]; // TODO: use PosInStrArray() instead? if (LI.Org = 'PO') or (LI.Org = 'PO-E') then begin {do not localize} LI.ItemType := ditDirectory; end else begin LI.ItemType := ditFile; end; end; finally FreeAndNil(s); end; end; //Note that spaces are illegal in MVS file names (Data set names) //http://www.snee.com/bob/opsys/part6mvs.pdf //but for filenames enclosed in '', we should tolerate spaces. if (AItem.Data <> '') and (TextEndsWith(AItem.Data, '''')) then begin i := IndyPos('''', AItem.Data)+1; AItem.FileName := Copy(AItem.Data, i, Length(AItem.Data)-i-1); end else begin i := RPos(' ', AItem.Data)+1; AItem.FileName := Copy(AItem.Data, i, MaxInt); end; Result := True; end; { TIdFTPLPMVSPartitionedDataSet } class function TIdFTPLPMVSPartitionedDataSet.GetIdent: String; begin Result := 'MVS: Partitioned Data Set'; {do not localize} end; class function TIdFTPLPMVSPartitionedDataSet.IsHeader( const AData: String): Boolean; var LPName, LPSize : Integer; begin //Name VV.MM Created Changed Size Init Mod Id // //or // //Name Size TTR Alias-of AC --------- Attributes --------- Amode Rmode //if there are loaded moduals LPName := IndyPos('Name', AData); {do not localize} LPSize := IndyPos('Size', AData); {do not localize} Result := (LPName > 0) and (LPSize > LPName); end; class function TIdFTPLPMVSPartitionedDataSet.ParseLine(const AItem: TIdFTPListItem; const APath: String): Boolean; var s : TStrings; //MVS Particianed data sets must be treated differently than //the regular MVS catalog. //NOTE: File Size is not supported at all. Size is usually size in records, not bytes // This is based on stuff at: // http://publibz.boulder.ibm.com:80/cgi-bin/bookmgr_OS390/BOOKS/F1AA2032/1.5.15?SHELF=&DT=20001127174124 // and // http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=utf-8&selm=DLspv2.G2w%40epsilon.com&rnum=2 //From Google: http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&threadm=4e7k0p%24t1v%40blackice.winternet.com&rnum=6&prev=/groups%3Fq%3DMVS%2BPartitioned%2Bdata%2Bset%2Bdirectory%26hl%3Den%26lr%3D%26ie%3DUTF-8%26oe%3DUTF-8%26selm%3D4e7k0p%2524t1v%2540blackice.winternet.com%26rnum%3D6 { From: Ralph Goers (rgoer@rgoer.candle.com) Subject: Re: FTP -- VM, MVS, VMS Questions View this article only Newsgroups: comp.os.os2.networking.tcp-ip Date: 1996/01/27 In message <4e7k0p$t1v@blackice.winternet.com> - frickson@gibbon.com (John C. F rickson) writes: :> :>MVS directory lines look like this: :> :>Volume Unit Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname :>OVH025 3380 12/29/95 1 60 FB 80 23200 PO MICSFILE.CLIST :>USS018 3380 12/18/95 1 15 VB 259 8000 PS NFS.DOC :>Migrated OAQPS.INTERIM.CNTYIM.V1.DATA.D052093 :>AIR030 3380 12/13/95 1 18 FB 80 3600 PS OAQPS.INTERIM.PLNTNAME.DATA :> :>1) Ext is number of extents? Yes. :>2) Used is number of tracks? Yes :>3) Is the volume name important or can I ignore it? It is important only if it is MIGRAT or migrated. This indicates that the dataset has not been accessed recently and has been migrated to an HSM volume (DASD or tape). :>4) Is there any way to calculate filesizes? I think I would have to know :> the capacity of the device (i.e. BlkSz * Blks/Trk * Trks) I'm not sure this would do you any good. Just because you know how many bytes are in a track doesn't mean that each track is full. In the case of an FB file you can figure out how many blocks will fit in a track. This may not be true of V type files. :>5) Dsorg -- PO seems to equate to partioned data set. Is that always true? :> Are any values other than PO and PS possible? Yes - you can also have VS files (VSAM) - however I have no idea why anyone would try to FTP one. :>6) What is "Migrated"? Moved to tape? Is the file still available for :> download, or should I simply ignore these lines? If you reference the file it will automatically be brought back to a DASD volume. :>7) After loggin in, the server reports the current directory as "PA2", :> but in response to a "PWD" command, it says "PUBLIC.". ??? Not sure about this. Usually, it defaults to "USERID.". :>8) Is the "CD" command useful for anything other than getting into :> partitioned data sets? Sure. If you have a bunch of sequential datasets starting with A.B then cd'ing to that and then doing MGET * should download all the sequential files. PDSes should not be downloaded but should be treated as a subdirectory. :>9) After CD'ing into a PDS, a directory looks like this: :> Name VV.MM Created Changed Size Init Mod Id :> $README 01.10 89/04/19 94/12/15 18:55 90 1 0 EWZ :> What are the Size, Init, Mod and ID fields? :> Is VV.MM version information? :> Size is the number of records in the file. init is the number of lines that were in the file when statistics were first set and mod is the number of lines modified since then. VV.MM is a version and modification level. These SPF statistics can be reset by going into edit on the member and typing STATS OFF, SAVE, STATS ON, SAVE. They can also be reset via option 3.5 - Member statistics panel. Some programs also modify them for their own use. You should count on these statistics even being present. They are created only by SPF. Other programs use the same area in the directory entry for their own purposes. When you link a load module, for example, information about it is stored there. When you FTP files up to MVS FTP will not create statistics for PDS members. Hope this is helpful. Ralph } begin AItem.ModifiedAvail := False; AItem.SizeAvail := False; s := TStringList.Create; try SplitDelimitedString(AItem.Data, s, True); if s.Count > 0 then begin AItem.FileName := s[0]; //in some particianed data sets, dates are missing. if (s.Count > 7) and (s[3] <> '') and IsNumeric(s[3], 1, 1) and (IndyPos('/', s[3]) > 0) then begin AItem.ModifiedDate := MVSDate(s[3]); { Name VV.MM Created Changed Size Init Mod Id} { $README 01.10 89/04/19 94/12/15 18:55 90 1 0 EWZ } if s.Count > 4 then begin AItem.ModifiedDate := AItem.ModifiedDate + TimeHHMMSS(s[4]); AItem.ModifiedAvail := True; end; end; end; finally FreeAndNil(s); end; Result := True; end; { TIdFTPLPMVSJESInterface1 } class function TIdFTPLPMVSJESInterface1.CheckListing(AListing: TStrings; const ASysDescript: String; const ADetails: Boolean): Boolean; var s : TStrings; begin Result := False; if AListing.Count > 0 then begin s := TStringList.Create; try SplitDelimitedString(AListing[0], s, True); Result := (s.Count > 2) and (PosInStrArray(Trim(s[2]), MVS_JES_Status) > -1); if Result and (s.Count > 3) then begin Result := IsNumeric(s[3]) or CharEquals(s[3], 1, '-'); end; if not Result then begin Result := IsMVS_JESNoJobsMsg(AListing[0]); end; finally FreeAndNil(s); end; end; end; class function TIdFTPLPMVSJESInterface1.GetIdent: String; begin Result := 'MVS: JES Queue Interface 1'; {do not localize} end; class function TIdFTPLPMVSJESInterface1.IsMVS_JESNoJobsMsg(const AData: String): Boolean; begin Result := (AData = 'No jobs found on JES queue'); {do not localize} end; class function TIdFTPLPMVSJESInterface1.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; begin Result := TIdMVSJESFTPListItem.Create(AOwner); end; class function TIdFTPLPMVSJESInterface1.ParseLine(const AItem: TIdFTPListItem; const APath: String): Boolean; var LBuf : String; LI : TIdMVSJESFTPListItem; begin { ALFREDCA JOB03192 OUTPUT 3 Spool Files ALFREDCA JOB03193 OUTPUT 0 Spool Files ALFREDCA JOB03194 INPUT 12345678901234567890123456789012345678901234567890 1 2 3 4 5 #From: IBM Communications Server for OS/390 V2R10 TCP/IP Implementation Guide Volume 2: UNIX Applications #Obtained at: http://www.redbooks.ibm.com/pubs/pdfs/redbooks/sg245228.pdf } AItem.ModifiedAvail := False; AItem.SizeAvail := False; LI := AItem as TIdMVSJESFTPListItem; //owner LBuf := AItem.Data; LI.OwnerName := Fetch(LBuf); LBuf := TrimLeft(LBuf); //filename LI.FileName := Fetch(LBuf); LBuf := TrimLeft(LBuf); case PosInStrArray(Fetch(LBuf), MVS_JES_Status) of 0 : LI.JobStatus := IdJESReceived; // 'INPUT' job received but not run yet 1 : LI.JobStatus := IdJESHold; // 'HELD' job is in hold status 2 : LI.JobStatus := IdJESRunning; // 'ACTIVE' job is running 3 : LI.JobStatus := IdJESOuptutAvailable; // 'OUTPUT' job has finished and has output available end; //spool file output if available LBuf := TrimLeft(LBuf); LI.JobSpoolFiles := IndyStrToInt(Fetch(LBuf), 0); Result := True; end; class function TIdFTPLPMVSJESInterface1.ParseListing(AListing: TStrings; ADir: TIdFTPListItems): Boolean; var LItem : TIdFTPListItem; i : Integer; begin Result := False; if AListing.Count > 0 then begin if not IsMVS_JESNoJobsMsg(Alisting[0]) then begin for i := 0 to AListing.Count -1 do begin LItem := MakeNewItem(ADir); LItem.Data := AListing[i]; ParseLine(LItem); end; end else begin Result := True; end; end; end; { TIdFTPLPMVSJESInterface2 } class function TIdFTPLPMVSJESInterface2.CheckListing(AListing: TStrings; const ASysDescript: String; const ADetails: Boolean): Boolean; begin Result := False; if AListing.Count > 0 then begin Result := IsMVS_JESIntF2Header(AListing[0]); end; end; class function TIdFTPLPMVSJESInterface2.GetIdent: String; begin Result := 'MVS: JES Queue Interface 2'; {do not localize} end; class function TIdFTPLPMVSJESInterface2.IsMVS_JESIntF2Header(const AData: String): Boolean; begin Result := (AData = 'JOBNAME JOBID OWNER STATUS CLASS'); {do not localize} end; class function TIdFTPLPMVSJESInterface2.MakeNewItem(AOwner: TIdFTPListItems): TIdFTPListItem; begin Result := TIdMVSJESIntF2FTPListItem.Create(AOwner); end; class function TIdFTPLPMVSJESInterface2.ParseLine(const AItem: TIdFTPListItem; const APath: String): Boolean; var LBuf, LNo : String; LPos, LPos2 : Integer; LI : TIdMVSJESIntF2FTPListItem; begin { JOBNAME JOBID OWNER STATUS CLASS BPXAS STC02133 ++++++++ OUTPUT STC RC=000 2 spool BPXAS STC00916 ++++++++ OUTPUT STC RC=000 2 spool BPXAS STC02132 ++++++++ ACTIVE STC BPXAS STC02131 ++++++++ ACTIVE STC 123456789012345678901234567890123456789012345678901234567890 1 2 3 4 5 6 } LI := AItem as TIdMVSJESIntF2FTPListItem; LI.ModifiedAvail := False; LI.SizeAvail := False; LI.FileName := Trim(Copy(AItem.Data, 10, 8)); LI.OwnerName := Trim(Copy(AItem.Data, 19, 7)); if IsLineStr(LI.OwnerName) then begin LI.OwnerName := ''; end; case PosInStrArray(Trim(Copy(AItem.Data, 28, 7)), MVS_JES_Status) of 0 : LI.JobStatus := IdJESReceived; // 'INPUT' job received but not run yet 1 : LI.JobStatus := IdJESHold; // 'HELD' job is in hold status 2 : LI.JobStatus := IdJESRunning; // 'ACTIVE' job is running 3 : LI.JobStatus := IdJESOuptutAvailable; // 'OUTPUT' job has finished and has output available end; LBuf := Trim(Copy(AItem.Data, 35, MaxInt)); LPos := IndyPos(' spool', LBuf); {do not localize} if LPos = 0 then begin Result := False; Exit; end; LNo := ''; for LPos2 := LPos-1 downto 1 do begin if LBuf[LPos2] = ' ' then begin Break; end; LNo := LBuf[LPos2] + LNo; end; LI.JobSpoolFiles := IndyStrToInt(LNo, 0); Result := True; end; class function TIdFTPLPMVSJESInterface2.ParseListing(AListing: TStrings; ADir: TIdFTPListItems): Boolean; var LItem : TIdFTPListItem; i : Integer; LStartLine : Integer; LDetailFlag : Boolean; LI : TIdMVSJESIntF2FTPListItem; begin Result := False; if AListing.Count > 0 then begin if IsMVS_JESIntf2Header(AListing[0]) then begin LStartLine := 1; end else begin LStartLine := 0; end; LDetailFlag := False; for i := LStartLine to AListing.Count-1 do begin if LDetailFlag then begin if ADir.Count > 0 then begin LI := ADir.Items[ADir.Count-1] as TIdMVSJESIntF2FTPListItem; LI.Details.Add(AListing[i]); end; end else if AListing[i] = '--------' then begin LDetailFlag := True; end else begin LItem := MakeNewItem(ADir); LItem.Data := AListing[i]; ParseLine(LItem); end; end; Result := True; end; end; { TIdMVSFTPListItem } constructor TIdMVSFTPListItem.Create(AOwner: TCollection); begin inherited Create(AOwner); FSizeAvail := False; //we can't get the file size from a MVS system end; { TIdMVSJESIntFFTPListItem } constructor TIdMVSJESIntF2FTPListItem.Create(AOwner: TCollection); begin inherited Create(AOwner); FDetails := TStringList.Create; end; destructor TIdMVSJESIntF2FTPListItem.Destroy; begin FreeAndNil(FDetails); inherited Destroy; end; procedure TIdMVSJESIntF2FTPListItem.SetDetails(AValue: TStrings); begin FDetails.Assign(AValue); end; { TIdMVSJESFTPListItem } constructor TIdMVSJESFTPListItem.Create(AOwner: TCollection); begin inherited Create(AOwner); JobStatus := IdJESNotApplicable; end; initialization RegisterFTPListParser(TIdFTPLPMVS); RegisterFTPListParser(TIdFTPLPMVSPartitionedDataSet); RegisterFTPListParser(TIdFTPLPMVSJESInterface1); RegisterFTPListParser(TIdFTPLPMVSJESInterface2); finalization UnRegisterFTPListParser(TIdFTPLPMVS); UnRegisterFTPListParser(TIdFTPLPMVSPartitionedDataSet); UnRegisterFTPListParser(TIdFTPLPMVSJESInterface1); UnRegisterFTPListParser(TIdFTPLPMVSJESInterface2); end.