/********************************************************************/
/* S A S S A M P L E L I B R A R Y */
/* */
/* NAME: TLKTLOAD */
/* TITLE: TLKTDUMP executable module loading program */
/* PRODUCT: BASE */
/* SYSTEM: ALL */
/* KEYS: */
/* PROCS: PDSCOPY (MVS only) */
/* DATA: NONE */
/* */
/* SUPPORT: Richard D. Langston UPDATE: JUN94 */
/* REF: TLKTLOAD/TLKTDUMP User Documentation */
/* MISC: Companion to the TLKTDUMP program */
/* */
/********************************************************************/
/* TLKTLOAD is a SAS program that reads in a text file generated by */
/* the TLKTDUMP program. It validates the file and ensures that the */
/* file has been tranferred correctly. If all is valid, it creates */
/* the executable(s) dumped by the TLKTDUMP program. */
/* */
/* First %INCLUDE this TLKTLOAD program, then use the %TLKTLOAD */
/* macro to invoke the program: */
/* */
/* %TLKTLOAD(INFILE=dumpfile,OUTFILE=executable); */
/* */
/* The INFILE= option refers to a fileref or a quoted string giving */
/* the name of the input dump file. This file is one that has been */
/* created by the TLKTDUMP program. The INFILE= option must be */
/* given. */
/* */
/* The OUTFILE= option refers to a fileref or a quoted string giving*/
/* the name of the executable module or library. On MVS, this will */
/* be a load module library. All restored modules will be added to */
/* (or replaced in) the library. On CMS, this will be a LOADLIB. */
/* All restored modules will be added to (or replaced in) the */
/* library. On VMS platforms, this will be a specific .EXE file. */
/* It will be replaced. On UNIX platforms, this will be a specific */
/* executable file, with no extension. It will be replaced. On PC */
/* platforms, this will be a specific .DLL file. */
/* */
/* The dump file can consist of several pieces that have been */
/* downloaded separately and "glued" back together, even if they're */
/* not in the right order. As long as each section is separated by */
/* the part delimiter record, any data */
/* between the delimiter records (such as EMAIL address information)*/
/* will be ignored. If any text has been altered, or any data */
/* modified in any way, or if a section appears more than once or */
/* not at all, the TLKTLOAD program will detect it and alert you */
/* in the PRINT file. */
/* */
/* Note that on CMS, it is required that a writeable A disk */
/* be present, since our temporary files are hard-coded to use */
/* a filemode of A. */
/*------------------------------------------------------------------*/
%MACRO TLKTLOAD(INFILE=,OUTFILE=);
%LET VERSION = 1.02;
/*==================================================================*/
/* In this section, we define the temporary files for the REORDER */
/* and SASCODE filedefs. These files will be used in creating the */
/* intermediate dump file with all parts in the right order and */
/* without any fence information. Also at this point we will try */
/* to determine what the correct DCB info will be for the executable*/
/* if possible. */
/*==================================================================*/
/*------------------------------------------------------------------*/
/* We may need to perform an explicit OS command to delete the */
/* REORDER and SASCODE temporary files used during our execution. */
/* To ensure we can simply reference the macro variable to invoke */
/* the command, we create dummy values for the macro variables that */
/* are simply comments. */
/*------------------------------------------------------------------*/
%LET DELREORD= * No deletion of REORDER necessary;
%LET DELSASCD= * No deletion of SASCODE necessary;
/*------------------------------------------------------------------*/
/* MVS-type operating systems simply use &&-prefixed data set names */
/* which will be deleted when the SAS job terminates. We defer */
/* setting our file DCB until we determine if the dump represents */
/* an IEBCOPY dump file or a PROC PDSCOPY dump file. */
/*------------------------------------------------------------------*/
%IF "&SYSSCP."="OS" %THEN %DO;
%LET REORDER= '&&REORDER';
%LET SASCODE= '&&SASCODE';
%END;
/*------------------------------------------------------------------*/
/* CMS uses real file names, and therefore the DELREORD and DELSASCD*/
/* macro variables are set to the proper ERASE commands. Note that */
/* we also set the DCB of the LOADLIB file. */
/*------------------------------------------------------------------*/
%ELSE %IF "&SYSSCP."="CMS" %THEN %DO;
%LET DCB=RECFM=V LRECL=32760;
%LET REORDER= 'TEMP REORDER A';
%LET SASCODE= 'TEMP SASCODE A';
%LET DELREORD= X "ERASE TEMP REORDER A";
%LET DELSASCD= X "ERASE TEMP SASCODE A";
%END;
/*------------------------------------------------------------------*/
/* For VMS, we can place the files in SAS$WORKLIB, which gets */
/* cleared when the SAS job terminates. We can also set the DCB */
/* at this time. */
/*------------------------------------------------------------------*/
%ELSE %IF "&SYSSCP."="VMS" OR "&SYSSCP."="VMS_AXP" %THEN %DO;
*-----obtain VMS DCB for EXE file-----*;
%LET DCB=RECFM=F LRECL=512;
%LET REORDER= 'SAS$WORKLIB:REORDER.DAT';
%LET SASCODE= 'SAS$WORKLIB:SASCODE.SAS';
%END;
/*------------------------------------------------------------------*/
/* For PC platforms, we use explicit file names and set the */
/* DEL commands to delete the files. Note that we use OPTIONS */
/* NOXWAIT to ensure that the user doesn't have to terminate the */
/* shell session. */
/*------------------------------------------------------------------*/
%ELSE %IF "&SYSSCP."="OS2" %OR "&SYSSCP."="WIN" %THEN %DO;
%LET DCB=RECFM=N;
OPTIONS NOXWAIT;
%LET REORDER= 'REORDER.TMP';
%LET SASCODE= 'SASCODE.TMP';
%LET DELREORD= X "DEL REORDER.TMP";
%LET DELSASCD= X "DEL SASCODE.TMP";
%END;
/*------------------------------------------------------------------*/
/* For UNIX platforms, we use explicit file names in /tmp, but */
/* we also set up rm commands to ensure that the files are cleaned */
/* up. */
/*------------------------------------------------------------------*/
%ELSE %DO;
%LET DCB=RECFM=N;
%LET REORDER= "/tmp/reorder.tmp.&sysjobid.";
%LET SASCODE= "/tmp/sascode.tmp.&sysjobid.";
%LET DELREORD= x "rm /tmp/reorder.tmp.&sysjobid.";
%LET DELSASCD= x "rm /tmp/sascode.tmp.&sysjobid.";
%END;
*------print initial information in print file-----------------------*;
DATA _NULL_; FILE PRINT;
PUT 'TLKTLOAD: A SAS program to create SAS/TOOLKIT(TM)'
'executables from downloaded TLKTDUMP files';
PUT "Version &VERSION.";
PUT "Requested INPUT file: &INFILE";
PUT "Requested OUTPUT file: &OUTFILE";
RUN;
/*------------------------------------------------------------------*/
/* Since we allow either a fileref or an explicit file name for */
/* both INFILE= and OUTFILE=, we must ensure that we have a fileref */
/* for each, since some code later on may require a fileref and not */
/* permit an explicit file name. We do this by verifying if the */
/* first character of the INFILE and OUTFILE macro variables is a */
/* quote (single or double). If either, then we consider the string */
/* to be an explicit name, in which case a FILENAME statement is */
/* generated in a macro variable (FILEST1 or FILEST2). If the name */
/* is already a fileref, then FILESTx is a comment statement. */
/* We also create the macros INFILEX and OUTFILEX which will always */
/* contain filerefs, either provided by the user via INFILE= and */
/* OUTFILE= or produced via our generated FILENAME statements. */
/*------------------------------------------------------------------*/
DATA _NULL_;
LENGTH WHICH $8;
WHICH = 'INFILE'; NUMBER=1; LINK DOIT;
WHICH = 'OUTFILE'; NUMBER=2; LINK DOIT;
RETURN;
DOIT:;
XFILE=LEFT(SYMGET(WHICH));
IF SUBSTR(XFILE,1,1)='"' OR SUBSTR(XFILE,1,1)="'" THEN DO;
FILENAME='RDL'!!PUT(NUMBER,Z5.);
STMT='FILENAME '!!TRIM(FILENAME)!!' '!!XFILE;
END;
ELSE DO;
FILENAME=XFILE;
STMT='* No FILENAME statement necessary for '!!WHICH;
END;
CALL SYMPUT(TRIM(WHICH)!!'X',TRIM(FILENAME));
CALL SYMPUT('FILEST'!!PUT(NUMBER,1.),STMT);
RETURN;
RUN;
*-----invoke the possible FILENAME statements------------------------*;
&FILEST1;
&FILEST2;
/*==================================================================*/
/* In this section, we read through the dump file and determine */
/* the start and end locations for each part of the dump. This is */
/* necessary because the parts may not be in the right order, and */
/* also because we permit extraneous records (such as mail headers) */
/* to appear between the parts. With this first pass of the dump */
/* file, the only error checking done is to ensure that each IDENT */
/* indicator is the same and that we encounter and EOF record */
/* somewhere in the dump. */
/* Note that each part is delineated by a header and trailer record */
/* that starts with ****PART in column 1. The part number follows, */
/* followed then by some identifier string. That string can be */
/* supplied by the dump creator, but the string always has the */
/* creation datetime appended to it. */
/*==================================================================*/
DATA PARTS; INFILE &INFILEX LENGTH=L END=EOF;
RETAIN MAXPARTN FOUNDEOF 0 SEQUENCE START IDENT_S;
LENGTH IDENT $80;
*-----read a record of the dump---------------------------------*;
INPUT @; INPUT @1 RECORD $VARYING80. L;
*-----if the record is a part header, obtain more info----------*;
IF RECORD=:'****PART ' THEN DO;
*-----get the IDENT info from the record---------------------*;
I=INDEX(RECORD,'IDENT = ');
IF I THEN DO;
I+8;
I+(SUBSTR(RECORD,I,1)=':');
RRECORD=REVERSE(RECORD);
J=81-VERIFY(RRECORD,'* ');
IDENT=SUBSTR(RECORD,I,J-I+1);
END;
*-----get the part number from the record--------------------*;
WHICH=SCAN(SUBSTR(RECORD,13),1,': ');
NUM=SCAN(RECORD,2,' ');
IF VERIFY(NUM,'0123456789 ')=0 THEN DO;
SEQUENCE=INPUT(NUM,BEST12.);
MAXPARTN=MAX(MAXPARTN,SEQUENCE);
END;
*-----part number XXX means we found the last section--------*;
ELSE IF NUM='XXX'
THEN FOUNDEOF=1;
*-----record the record number of start of data--------------*;
IF WHICH='START' THEN DO;
START=_N_+1;
IDENT_S=IDENT;
END;
*-----record the record number of end of data----------------*;
ELSE DO;
END=_N_-1;
*-----ensure IDENT values match between start and end-----*;
IF IDENT_S NE IDENT THEN DO;
FILE PRINT;
PUT 'ERROR: IDENT labels for start and end do not '
'match for part ' sequence;
ABORT;
END;
OUTPUT;
END;
END;
*-----if at end of file, ensure we found an EOF record----------*;
IF EOF THEN DO;
IF NOT FOUNDEOF THEN DO;
PUT 'ERROR: An EOF was not found in the dump file, '
'indicating at least the last section was omitted.';
ABORT;
END;
*-----save part count for later use--------------------------*;
CALL SYMPUT('MAXPARTN',PUT(MAXPARTN,10.));
END;
KEEP SEQUENCE START END IDENT;
RUN;
/*------------------------------------------------------------------*/
/* Verify that all the IDENT values are the same for all parts. If */
/* not, we have an error condition. */
/*------------------------------------------------------------------*/
DATA _NULL_; SET PARTS END=EOF; BY IDENT NOTSORTED;
NDIFF+FIRST.IDENT;
IF EOF AND NDIFF NE 1;
FILE PRINT;
PUT 'ERROR: Identifying string is not the same for all parts '
'of the dump';
ABORT;
RUN;
/*------------------------------------------------------------------*/
/* Sort the section indicators into the correct sequence. We also */
/* create a set of observations to merge together to ensure that */
/* there is one and only one section per sequence number. */
/*------------------------------------------------------------------*/
PROC SORT DATA=PARTS(DROP=IDENT); BY SEQUENCE;
DATA MATCHUP;
DO SEQUENCE=0 TO &MAXPARTN;
OUTPUT;
END;
RUN;
/*------------------------------------------------------------------*/
/* Now merge the two together and ensure that 1) all sections are */
/* present and 2) no section is repeated. */
/*------------------------------------------------------------------*/
DATA _NULL_; MERGE PARTS(IN=HAVE) MATCHUP END=EOF; BY SEQUENCE;
FILE PRINT;
RETAIN ERROR 0;
IF NOT HAVE THEN DO;
PUT 'ERROR: Part ' sequence ' of the dump file is not '
'present. Processing cannot continue.';
ERROR = 1;
END;
ELSE IF NOT (FIRST.SEQUENCE AND LAST.SEQUENCE) THEN DO;
PUT 'ERROR: Part ' sequence ' of the dump file appears '
'multiple times. Processing cannot continue.';
ERROR = 1;
END;
IF EOF AND ERROR
THEN ABORT;
RUN;
/*------------------------------------------------------------------*/
/* At this point, we know that the dump contains all the sections */
/* we need, and we know the locations of each section. We generate */
/* a SAS program into the SASCODE file that will be of the form */
/* */
/* DATA _NULL_; INFILE &INFILEX */
/* LENGTH=L FIRSTOBS=x OBS=y; */
/* FILE REORDER ; */
/* INPUT @; INPUT @1 LINE $VARYING200. L; */
/* IF LINE NE ' ' THEN PUT _INFILE_; */
/* RUN; */
/* */
/* This code will write into the REORDER file only the dump records */
/* we want, eliminating headers/trailers and any other superfluous */
/* text, including blank lines. Note that the MOD option is not */
/* used for the first DATA step. Also, blank lines are preserved */
/* for the first part of the dump, which is textual in nature. */
/*------------------------------------------------------------------*/
*-----ensure we have FILEREFs for REORDER and SASCODe----------------*;
FILENAME REORDER &REORDER;
FILENAME SASCODE &SASCODE;
*-----Generate the SAS code described above--------------------------*;
DATA _NULL_; SET PARTS(RENAME=(START=FIRSTOBS END=OBS)); FILE SASCODE;
LENGTH MOD $3;
RETAIN OBS MOD;
IF SEQUENCE=0
THEN CALL SYMPUT('STARTREC',PUT(OBS-FIRSTOBS+2,BEST12.));
PUT "DATA _NULL_; INFILE &INFILEX LENGTH=L " FIRSTOBS= OBS=
'; FILE REORDER ' MOD ';';
PUT 'INPUT @; INPUT @1 LINE $VARYING200. L; ';
IF SEQUENCE NE 0 THEN PUT "IF LINE NE ' ' THEN ";
PUT "PUT _INFILE_;";
PUT 'RUN;';
MOD='MOD';
RUN;
*-----Invoke the SAS code that has just been generated---------------*;
%INC SASCODE; RUN;
*-----Clear out the SASCODE file since it is no longer needed--------*;
FILENAME SASCODE CLEAR;
&DELSASCD;
*------get information from the header portion-----------------------*;
DATA _NULL_; INFILE REORDER LENGTH=L END=EOF;
*-----get ident, length, version, host-----*;
LENGTH LRECL $8 TLKTDMPV $4 HOST IEBCOPY $8 RECFM $8 BLKSIZE $8;
INPUT LRECL= HOST= & TLKTDMPV= IEBCOPY= ;
FILELREC=LRECL;
*-----get IEBCOPY information if necessary-----*;
if IEBCOPY='YES' THEN DO;
INPUT @19 RECFM= LRECL= BLKSIZE=;
CALL SYMPUT('RECFM',TRIM(RECFM));
CALL SYMPUT('IEBLRECL',TRIM(LRECL));
CALL SYMPUT('BLKSIZE',TRIM(BLKSIZE));
END;
ELSE IEBCOPY='NO';
*-----make macros out of ident and lrecl for later use-----*;
CALL SYMPUT('LRECL',TRIM(FILELREC));
CALL SYMPUT('IEBCOPY',TRIM(IEBCOPY));
*-----verify we are on the right host-----*;
IF HOST NE "&SYSSCP." THEN DO;
FILE PRINT;
PUT 'ERROR: The TLKTDUMP file is meant for the ' HOST
' operating system but this SAS program is being '
"run on the &SYSSCP. operating system.";
ABORT;
END;
*-----warn if the version number does not match-----*;
IF TLKTDMPV NE "&VERSION" THEN DO;
FILE PRINT;
PUT 'WARNING: The TLKTDUMP file was generated with TLKTDUMP'
' Version ' TLKTDMPV ' but this TLKTLOAD is '
" Version &VERSION.. Beware of incompatibilities...";
END;
STOP;
RUN;
/*------------------------------------------------------------------*/
/* Here we have to do some host-specific things. For MVS, we must */
/* now deal with IEBCOPY information. The IEBCOPY utility is very */
/* sensitive about correct DCB information for its unload file. */
/* Therefore, when TLKTDUMP creates a dump file, it records the DCB */
/* of the unload file so that we will be able to recreate the */
/* file with correct DCBs. If instead we're using the PDSCOPY */
/* unload file, we can always use DCB=(RECFM=VB,LRECL=32756, */
/* BLKSIZE=32760). And note that if we are not recreating an */
/* IEBCOPY unload file, we're recreating a PDSCOPY unload file, */
/* which must be a temporary. Therefore, we redefine the output */
/* file to be SEQFILE. Note that we used OUTFILEX earlier to hold */
/* the output fileref. We can redefine OUTFILEX to SEQFILE for */
/* PDSCOPY unload files. For CMS, we have to deal with the */
/* peculiarity that the DATA step does not permit you to use a */
/* LOADLIB file as an output file. This is because it expects you */
/* to treat the LOADLIB file as a library, not as a sequential */
/* file. To overcome this, we use a temporary file TEMP DATA A and */
/* have the fileref RDL00003 refer to it. We reset OUTFILEX */
/* accordingly. Once we populate TEMP DATA A, we'll copy it to the */
/* desired LOADLIB file. */
/*------------------------------------------------------------------*/
%IF "&SYSSCP."="OS" %THEN %DO;
%IF "&IEBCOPY." NE "YES" %THEN %DO;
%LET DCB=RECFM=VB LRECL=32756 BLKSIZE=32760;
FILENAME SEQFILE '&&OUT';
%LET OUTFILEX=SEQFILE;
%END;
%ELSE %LET DCB=RECFM=&RECFM LRECL=&IEBLRECL BLKSIZE=&BLKSIZE;
%END;
%IF "&SYSSCP."="CMS" %THEN %DO;
FILENAME RDL00003 'TEMP DATA A';
%LET OUTFILEX=RDL00003;
%END;
/*------------------------------------------------------------------*/
/* Finally, we are at the point where we can read through the dump */
/* file and create our local binary file from the dump. */
/*------------------------------------------------------------------*/
DATA _NULL_; INFILE REORDER FIRSTOBS=&STARTREC END=EOF LENGTH=INRECL;
LENGTH STG $160 SRECLEN $20;
RETAIN PRECNUM RECNUM PARTNUM 0;
*-----not getting hex data (getting length value)-----*;
HEX=0;
*-----get length of next restore record-----*;
LINK GETLEN;
IF LENGTH=0 /* EOF */
THEN STOP;
*-----read the hex data, computing checksum and making binary---*;
CHECKSUM=0;
I=1;
HEX=1;
DO WHILE(I<=LENGTH);
*-----extract a piece-----*;
LINK GETCOMP;
*-----accumulate checksum-----*;
DO K=1 TO L;
CHECKSUM=MOD(CHECKSUM+INPUT(SUBSTR(STG,K,1),PIB1.)+1,32768);
END;
*-----write out the piece-----*;
LINK PUTDATA;
I+L;
END;
*-----not in hex mode (getting checksum)-----*;
HEX=0;
*-----flush the binary record-----*;
L=0; LINK PUTDATA;
*-----absorb comma-----*;
L=1; LINK GETDATA;
*-----get the checksum and validate against computed one-----*;
LINK GETLEN;
IF LENGTH NE CHECKSUM THEN DO;
FILE PRINT;
PUT 'ERROR: For restore file record number ' RECNUM
'(input file record ' NRECS ')'
' the provided checksum and the computed checksum '
' did not match. there is a data integrity problem.';
ABORT;
END;
*-----on to next restore file record-----*;
RETURN;
*-----routine to get recnum and length from input file-----*;
GETLEN:;
J=1;
*-----read string of format xxxx:yyyy,-----*;
DO UNTIL(STG=',');
*-----get a single byte-----*;
L=1;
LINK GETDATA;
*-----concatenate in-----*;
SUBSTR(SRECLEN,J,1)=STG;
J+1;
END;
*-----read recnum before colon, length after-----*;
RECNUM=INPUT(SCAN(SRECLEN,1,':,'),5.);
LENGTH=INPUT(SCAN(SRECLEN,2,':,'),5.);
RETURN;
*-----routine to generate a stream of uncompressed data-----*;
GETCOMP:;
*-----read the indicator byte first-----*;
L=1;
LINK GETDATA;
COMPBYTE=INPUT(SUBSTR(STG,1,1),PIB1.);
*-----if over 127, this indicates compression-----*;
IF COMPBYTE>127 THEN DO;
*-----get rid of high-order bit to get length-----*;
COMPBYTE=COMPBYTE-128;
*-----read the repeating byte-----*;
L=1;
LINK GETDATA;
*-----generate the complete string-----*;
STG=REPEAT(SUBSTR(STG,1,1),COMPBYTE-1);
L=COMPBYTE;
END;
*-----otherwise we get the specified number of bytes-----*;
ELSE DO;
L=COMPBYTE;
LINK GETDATA;
END;
RETURN;
*-----read L bytes of binary data from the hex dump-----*;
GETDATA:;
RETAIN INCOL 1;
LENGTH PART $&LRECL;
*-----determine actual bytes to read, depending on hex mode-----*;
LL=L*(1+HEX);
II=1; STG=' ';
*-----read that many bytes-----*;
DO WHILE(II<=LL);
*-----read remaining bytes, or all we need-----*;
LLL=MIN(LL-II+1,&LRECL-INCOL+1);
INPUT @INCOL PART $VARYING&LRECL.. LLL @@;
*-----concatenate that into STG-----*;
SUBSTR(STG,II,LLL)=PART;
INCOL+LLL;
*-----if at end of record, see if next record is a fence-----*;
IF INCOL>&LRECL THEN DO;
INPUT;
NRECS+1;
INCOL=1;
END;
*-----increment data-read count and continue-----*;
II+LLL;
END;
*-----if the data are supposed to be in hex...-----*;
IF HEX THEN DO;
*-----verify only hex characters used-----*;
IF VERIFY(SUBSTR(STG,1,LL),'0123456789ABCDEF') THEN DO;
FILE PRINT;
PUT 'ERROR: invalid hex characters in string at about '
'input record ' NRECS ':' STG;
ABORT;
END;
*-----informat the string using $HEX-----*;
STG=INPUT(SUBSTR(STG,1,LL),$HEX160.);
END;
RETURN;
*-----write out the resulting string to the output file-----*;
PUTDATA:;
FILE &OUTFILEX &DCB;
IF L=0 THEN PUT;
ELSE PUT STG $VARYING80. L @@;
FILE LOG;
RETURN;
RUN;
*-----we are done with the dump file and can clear it now------------*;
FILENAME REORDER CLEAR;
&DELREORD;
/*------------------------------------------------------------------*/
/* We have to do some final things on a host-specific basis. */
/* For MVS, if we are dealing with a PDSCOPY unload file, we need */
/* to run PROC PDSCOPY to convert the unload file into a real */
/* load module PDS. For CMS, we must rename the TEMP DATA A file */
/* to the desired LOADLIB file name. This is somewhat tricky on */
/* CMS because if we don't have the fileid, we have to obtain it */
/* by redirecting the SAS log of a DATA step that accesses the */
/* fileref, since the DATA step reports the fileid. */
/* To deal with all this mess, we create a fileref of RDL00004 to */
/* refer to a temporary file RDL00004 RDLLOG A. We run PROC PRINTTO */
/* to redirect the SAS log to that file, then run a DATA step that */
/* references the output file. We then run PROC PRINTTO to stop the */
/* redirection, then read RDL00004 RDLLOG A to get the fileid as */
/* specified by the DATA step. We then can issue a COPY command to */
/* copy TEMP DATA A to the proper fileid, then we ERASE TEMP DATA */
/* A. Note that we can't issue a RENAME because the final LOADLIB */
/* file may reside on a different disk. */
/*------------------------------------------------------------------*/
%IF "&SYSSCP."="OS" & "&IEBCOPY." NE "YES" %THEN %DO;
*-----for MVS, convert to a load module library via PDSCOPY----------*;
PROC PDSCOPY IN=SEQFILE OUT=&OUTFILE INTAPE; RUN;
FILENAME SEQFILE CLEAR;
%END;
%IF "&SYSSCP."="CMS" %THEN %DO;
*-----for CMS, get fileid and issue COPY and ERASE-------------------*;
FILENAME RDL00004 'RDL00004 RDLLOG A';
PROC PRINTTO LOG=RDL00004 NEW; RUN;
DATA _NULL_; INFILE &OUTFILE; STOP; RUN;
PROC PRINTTO; RUN;
DATA _NULL_; INFILE RDL00004 LENGTH=L;
INPUT @; INPUT @1 LINE $VARYING133. L;
LINE=UPCASE(LINE);
IF INDEX(LINE,'FILENAME=');
FILENAME=SCAN(LINE,2,'=,');
RC = CMS('COPY TEMP DATA A '!!FILENAME!!'(REPLACE');
RC = CMS('ERASE TEMP DATA A');
STOP;
RUN;
FILENAME RDL00003 CLEAR;
FILENAME RDL00004 CLEAR;
X 'ERASE RDL00004 RDLLOG A';
%END;
*------put trailer with support information-----*;
DATA _NULL_; FILE PRINT;
PUT 'End of TLKTLOAD processing';
PUT //;
PUT 'For questions/comments about this SAS program,';
PUT 'contact:';
PUT // 'Rick Langston';
PUT 'Product Manager, SAS/TOOLKIT Software';
PUT 'SAS Institute Inc.';
PUT 'SAS Campus Drive';
PUT 'Cary, NC 27513';
PUT '919/677-8000 X7613';
PUT 'EMAIL: sasrdl@unx.sas.com';
%MEND TLKTLOAD;