740 lines
25 KiB
Plaintext
740 lines
25 KiB
Plaintext
|
{
|
||
|
$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.
|