/* Appendix 3 MFC Application Source Code Files               */

   /* ---- MFCAUDST ----   HOST DATA steps in this file       */
   /* have been set up for fixed length 80 byte records       */
   /* with the s=72and s2=72 options. If your source is       */
   /* different, adjust the INFILE, FILE, INPUT and PUT       */
   /* INFILE, FILE, INPUT and PUT statements as needed.       */

%put NOTE: MFC PREEDIT has no errors.;

   /* copy the source, place value of full library location   */
   /* location and file name into &MFCSRLBF.                  */
data _null_;
   length dsn176 $176  temp54 $54;
      /* HOST See MFCGETNA code for MVS hosts                 */
      /* regarding using jfcb= option or not.                 */                     
**infile &mfcsrref(&mfcchpgm) jfcb=dsn176; /* HOST            */
   infile &mfcsrref(&mfcchpgm)            ; /* HOST           */
   file &mfcxtref notitles; /* HOST                           */
   if _n_ eq 1 then do;
      **temp54=dsn176; /* HOST MVS                            */
         temp54=symget('mfcsrlib'); /* HOST                   */
         substr(temp54,45)=symget('mfcchpgm');
         %include &mfcsrref(mfcgetna); /* HOST                */
         call symput('mfcsrlbf',temp54);
   end;
      /* make exact copy of DATA step source                  */
   input; put _infile_;
run;

%put NOTE: MFC adding audit trail statements.;

   /* Update DATA step source to be compiled. In this         */
   /* DATA step, if your application source being compiled    */
   /* has a PUT command without a preceding file reference,   */
   /* that is, if you are writing to the SAS LOG, you will    */
   /* have to insert a file statement prior to your PUT. If   */
   /* you do not, your PUT will be to the file you've         */
   /* designated as the audit trail because it will now be    */
   /* the last one mentioned in the DATA step to that point.  */
data _null_;
   length work $200 datapart $12 ;
   length mfcsrlbf $54 mfcrulib $44 time $16 mfcchpgm $8;
   retain mfcsrlbf     mfcrulib     time     mfcchpgm;
   retain sq "'";  /* a single quote '                        */
   retain null b ' '  mfcqtya 0 ;
   retain no 'n' yes 'y';

if _n_ eq 1 then link start;
infile &mfcxtref; /* HOST                                     */
file &mfcsrref(&mfcchpgm) notitles; /* HOST                   */

  /* read and write existing lines exactly                    */
input @1 line $char72.; /* HOST                               */
put   @1 line $char72.;

work=compress(upcase(line));
   /* Try to capture kind of DATA step, so MFCCH variable is  */
   /* not added to user's output data sets. Assumes that DATA */
   /* and _NULL_ are in same DATA statement line.             */
if null eq b then do;
   datapart=left(upcase(line));
   if datapart eq: 'DATA ' then do;
      datapart=left(substr(datapart,6));
      if (datapart eq: '_NULL_ ') or
         (datapart eq: '_NULL_;') then null=yes;
                                  else null=no;
   end;
end;

   /* find macro variable, insert statements                  */
if (index(work,'&MFCCOMPI; ') gt 0) and (null ne b) and
   (mfcqtya eq 0) then do;
   mfcqtya+1;
   set mfctrdat key=mfcchpgm;
   put '   /*  start MFC audit trail  */' ;
   if null eq no then put 'drop mfcch;' ;
   put 'if _n_ eq 1 then do;' ;
   put ' file &mfctrref;' ;  /* HOST                          */

      /* Code below could be simplified via use of the        */
      /* $QUOTEW. format if like.  Audit trail will not print */
      /* for the SOURCED choice if source is executed via     */
      /* %MFCDRIVR macro.                                     */
   put @2 'mfcch=symget('  @15 sq  @16 'mfcch'   @21 sq
          @22 ');'
    /  @2 'if mfcch ne'  @14 sq  @15 'SOURCED'  @22 sq
          @24 'then'
    /  @2  'put / '  @10 sq   @11 mfcsrlbf  sq
    /  @10 sq   @11 'compiled at '  time  sq;

      /* see MFCTRDAT source for logon value               */
   if logon eq: '*' then put @3  '/ '  @7 sq
      @8 'no source file data available' sq;
   else put @3  '/ '  @7 sq  @8 'last mod by' @20 logon
      @29 modymd8  @39 modhhmm  sq;

   put @3 '/ '   @7 sq  @8 'stored pgm to SAS lib:'
       @31 mfcrulib  sq  '/;'
    /  'end;'
    /  '   /*  ends  MFC audit trail  */' ;
end;
return;

   /* first time through                                      */
start:
   time=put(datetime(),datetime16.);
   mfcsrlbf=symget('mfcsrlbf');
   mfcrulib=symget('mfcrulib');
   mfcchpgm=symget('mfcchpgm');
return;
run;


   /* ---- MFCCMPL  ----   Model statements to replace DATA   */
   /* step source in application. Complete the = values.      */
           /* DATA step name    MFC choice                    */
%mfcdrivr(mfcchpgm=        ,mfcch=       ); /* driver macro   */



   /* ---- MFCDRIVR ----   The macro to generate and execute  */
   /* code to perform the compile and run functions of the    */
   /* MFC.                                                    */

%macro mfcdrivr(mfcchpgm=,mfcch=);
   %let mfcchpgm=%upcase(&mfcchpgm);
   %let mfcch=%upcase(&mfcch);
   %let mfctype=%upcase(&mfctype);
   %let mfcerrn=00;
   %let mfcerrch=;
   %let nulvalu=;

%if (&mfctype ne APPLICA) and (&mfctype ne INDCOMP) %then
%do;   /* End SAS for invalid &MFCTYPE                        */
   %let mfcerrn=08;
   options nosource nosource2;
   %include &mfcsrref(mfcrept); /* HOST                       */
%end;

%if &mfcch eq RUNCOMP %then %do;
   %if &mfctype eq APPLICA %then %do;
      %if &mfcredir eq &nulvalu %then %do;
         data pgm=&mfcruref..&mfcchpgm;
         run;
      %end;
      %else %do;
         data pgm=&mfcruref..&mfcchpgm;
            &mfcredir;
         run;
      %end;
   %end;
   %else %do;
         %let mfcerrn=02;
         %let mfcerrch=bad choice;
         options nosource nosource2;
         %include &mfcsrref(mfcrept); /* HOST                 */
         options source;
   %end;
         /* ensure null value after each use                  */
   %let mfcredir=;
%end; /* RUNCOMP section                                      */

%else %if &mfcch eq SOURCED %then %do;
   %if &mfctype eq APPLICA %then %do;
       %let mfccompi=;
       options source source2;
       %include &mfcsrref(&mfcchpgm); /* HOST                 */
       options nosource2;
   %end;
   %else %do;
      %let mfcerrn=02;
      %let mfcerrch=bad choice;
      options nosource nosource2;
      %include &mfcsrref(mfcrept); /* HOST                    */
      options source;
   %end;
%end; /* SOURCED section                                      */

   /* Start CPENDIT CPSTORU processing.                       */
%else %if (&mfcch eq CPSTORU) or
(&mfcch eq CPENDIT) %then %do;

      /* CPSTORY,CPENDIT choices are only valid when used in  */
      /* an application stream.                               */
   %if &mfctype eq APPLICA %then %do;

            /* don't show source for MFCEDITS step in the     */
            /* SAS LOG                                        */
         options nosource nosource2;

            /* Bring in MFCEDITS to review source. At the end */
            /* of MFCEDITS DATA step a SYMPUT routine is used */
            /* to place a value into the &MFCNEXTS macvar as  */
            /* follows:                                       */
            /*    call symput('mfcnexts', ARGUMENT );         */
            /* The value of argument is either a blank or a   */
            /* %INCLUDE stmt for MFCREPT or MFCAUDST          */
            /* processing. The last line in the MFCEDITS file */
            /* is &mfcnexts; a resolved value of blank        */
            /* returns processing to this section of the      */
            /* macro; a %INCLUDE brings in the next MFC file  */
            /* of code. The final MFC DATA step returns       */
            /* control to the macro.                          */
         %include &mfcsrref(mfcedits); /* HOST                */

            /* Establish the values of the stored program     */
            /* libref and program name for the compile.       */
         %let mfccompi=/pgm=&mfcruref..&mfcchpgm;

            /* Leave a message in the SAS LOG.                */
         %put NOTE: MFC/SAS compiling &mfcchpgm.;

            /* Make all source visible in the SAS LOG for the */
            /* upcoming compile.                              */
         options source source2;

            /* Bring in DATA step source to compile.          */
         %include &mfcsrref(&mfcchpgm); /* HOST               */

            /* Save the value of the automatic MACVAR which   */
            /* contains the status of the compile attempt.    */
         %let mfcerrch=&syserr;

            /* Don't show source for MFC processing.          */
         options nosource nosource2;

            /* Assign error values for MFCREPT code.          */
         %if &mfcch eq CPENDIT %then %let mfcerrn=06;
                               %else %let mfcerrn=07;

            /* HOST on &MFCERRCH=&SYSERR values. The compile  */
            /* was clean or had warnings. Run the stored pgm. */
            /* If there was a compile warning, make mention   */
            /* by including the MFCREPT step.                 */
         %if (&mfcerrch lt 5) %then %do;
            data pgm=&mfcruref..&mfcchpgm;
            run;
            %if &mfcerrch gt 3 %then %do;
                  %include &mfcsrref(mfcrept); /* HOST        */
            %end;
         %end;

            /* The compile was bad. MFC code will run to      */
            /* restore the source to its original condition   */
            /* and a report will be produced. MFCREPT will    */
            /* end SAS.                                       */
         %else %if &mfcch eq CPENDIT %then %do;
            options nosource nosource2;
            %include &mfcsrref(mfcrstrs,mfcrept); /* HOST     */
         %end;
                                                             
            /* Similar processing to CPENDIT above, but       */
            /* MFCREPTwill not end SAS. This section will run */
            /* a prior compiled version of the source.        */
         %else %if &mfcch eq CPSTORU %then %do;
            options nosource nosource2;
            %include &mfcsrref(mfcrstrs,mfcrept); /* HOST     */  
            options source;
            data pgm=&mfcruref..&mfcchpgm;
            run;
         %end;
   %end;

            /* CPSTORU, CPENDIT are not valid for INDCOMP.    */
            /* Assign values to MACVARS and bring in MFCREPT  */
            /* processing.                                    */
   %else %do;
      %let mfcerrn=02;
      %let mfcerrch=bad choice;
      options nosource nosource2;
      %include &mfcsrref(mfcrept); /* HOST                    */
      options source;
   %end;
%end; /* CPSTORU CPENDIT section                              */

%else %if &mfcch eq COMPILE %then %do;
   %if &mfctype eq INDCOMP %then %do;
      options nosource nosource2;
      %include &mfcsrref(mfcedits); /* HOST                   */
         /* preedit must have no MFC errors to allow a        */
         /* compile attempt. MFCEDITS and MFCREPT end         */
         /* CPSTORU and CPENDIT                               */
      %if &mfcerrn eq 00 %then %do;
         %let mfccompi=/pgm=&mfcruref..&mfcchpgm;
         options source source2;
         %include &mfcsrref(&mfcchpgm); /* HOST               */
         %let mfcerrch=&syserr;
         options nosource nosource2;
         %if &mfcerrch ge 5 %then %do;
            %let mfcerrn=05;
            %include &mfcsrref(mfcrstrs,mfcrept); /* HOST     */
         %end;
      %end;
      options source;
   %end;
   %else %do;
      %let mfcerrn=03;
      %let mfcerrch=bad choice;
      options nosource nosource2;
      %include &mfcsrref(mfcrept); /* HOST                    */
      options source;
   %end;
%end; /* end COMPILE section                                  */

%else %if &mfcch eq PREEDIT %then %do;
   %if &mfctype eq INDCOMP %then %do;
      options nosource nosource2;
      %include &mfcsrref(mfcedits); /* HOST                   */
      options source;
   %end;
   %else %do;
      %let mfcerrn=03;
      %let mfcerrch=bad choice;
      options nosource nosource2;
      %include &mfcsrref(mfcrept); /* HOST                    */
      options source;
   %end;
%end; /* end PREEDIT section                                  */

   /* MFCCH=INVALID function choice                           */
%else %do;
   %let mfcerrn=01;
   %let mfcerrch=bad choice;
   options nosource nosource2;
   %include &mfcsrref(mfcrept); /* HOST                       */
   options source;
%end;
%mend mfcdrivr;

   /* ---- MFCEDITS ----   Edit source for &MFCCOMPI macro    */
   /* placement and other MFC rules. Ensure that the mfctrdat */
   /* indexed data set can be accessed.                       */

   /* HOST- This MFC DATA step has been set up for source     */
   /* files where the S=72 and S2=72 SAS options are used. If */
   /* your source is different, adjust the $CHAR72. format to */
   /* your requirements. MVS HOST Further, if you use         */
   /* line-numbered members in your source PDS, you will have */ 
   /* to make sure the code below does not include them in    */
   /* the testing for blanks after finding the &MFCCOMPI      */
   /* macro variable via the INDEX statement and the RUN      */
   /* statement at the end of your DATA step.                 */

   /* &MFCCOMPI placement is critical to auto insertion of    */
   /* audit trail statements. The other warnings are some     */
   /* items on which SAS compilation logic is silent. It      */
   /* would be duplicating SAS's effort to build in more      */
   /* 'hard' errors to prevent a compile from being           */
   /* attempted.                                              */


data _null_;
  infile &mfcsrref(&mfcchpgm) end=eof; /* HOST                */
  file &mfctrref; /* HOST                                     */
  length work $200;
  length mfcchpgm $8 mfcch $7;
  retain mfcchpgm    mfcch;
  retain qtycompi qtyfatal qtyerr qtydatst 0;
  retain dataon no 'n'   yes 'y';

if _n_ eq 1 then do;
      /* MFCCHPGM is 8 character source file name             */
   mfcchpgm=symget('mfcchpgm');
   put / @3 'Source preedit --------'  @28 mfcchpgm
         @38 20*'-';
   set mfctrdat key=mfcchpgm;
   if _iorc_ ne 0 then do;
      qtyfatal+1;
      put / 'ERROR, mfctrdat audit trail data has NO RECORD'
         ' for source file ' mfcchpgm '.' /
         'This is a technical error.  User will have'
         ' to figure out' /
         'why all files in source library did not get to'
         ' the indexed data set.'  /
         'You may use if _iorc_ eq %sysrc( mnenomic ) method'
         ' here to get '  /
         'failed i/o reason value. See Tech Report P-222'
         ' page 318. '  ;
      _error_+ -1; /* avoid dump for THIS ERROR               */
   end;
   mfcch=symget('mfcch');
end;

input @1 line $char72.  @1 prtline $char72.; /* HOST          */
line=left(upcase(line));
work=compress(line);
if (qtydatst eq 0) and (line eq: 'DATA ') then dataon=yes;

if dataon eq yes then do;
   qtydatst+1; /* quantity data statement lines               */
   bytemfc=index(work,'&MFCCOMPI;');
   if bytemfc gt 0 then do;
      qtycompi+1;
         /* warn if &vars are to left of &mfccompi            */
         if bytemfc gt 2 then
            if index(substr(work,1,bytemfc-1),'&') gt 0 then
               link ampermsg;
         if work ne: '&MFCCOMPI; ' then do;
            byte=bytemfc+10;
            link cheksemi;
            if work ne: ' ' then do;
               qtyerr+1;
               put / 'ERROR, &mfccompi; can have nothing to '
                  'its right on the same line.' /
                  'Line=' prtline ;
            end;
         end;
   end; /* &MFCCOMPI found                                    */
         else if index(work,'&') gt 0 then link ampermsg;
   if substr(line,length(line),1) eq ';' then dataon=no;
end;  /* 1st DATA statement                                   */

else do;
   if index(line,'&') gt 0 then link ampermsg;

   if (index(line,'SET ') gt 0) or
      (index(line,'MERGE ') gt 0) or
      (index(line,'MODIFY ') gt 0) or
      (index(line,'UPDATE ') gt 0) then put /
      'Warning, when compiling a data step all '
      'input SAS data sets must' / ' be available.' /
      'Line=' prtline ;

   if line eq 'DATA ' then put /
      'Warning, you can compile only a single data step '
      'if the word data'  /
      ' starts a second data step.' / 'Line=' prtline;
end;  /* not the 1st DATA statement                           */

if eof then do;
   if work ne: 'RUN; ' then do;
      qtyerr+1;
      put / 'ERROR, RUN; must be the only text in last'
         ' line of data step.' / 'Last line=' prtline;
   end;
   if qtydatst lt 1 then do;
      qtyerr+1;
      put / 'ERROR, could not find start of DATA statement.'
         ' Remember MFC rule' /
         ' about no text being to the left of the DATA'
         ' statement''s start.' ;
   end;
   if qtycompi eq 0 then put / 'ERROR, &mfccompi missing'
      ' from data statement.' ;
   if _error_ gt 0 then do;
      qtyfatal+1;
      put / 'MFCEDITS step has failed in processing data '
         'step source.' / 'Turn on source source2 options'
         ' to debug. Sorry.' /
         'SAS _error_ value=' _error_ /;
   end;

   /* decide final action on edit results                     */
   if (qtycompi eq 1) and (qtyfatal eq 0) and (qtyerr eq 0)
   then do;
      put / 'NO MFC errors.';
      if mfcch eq 'PREEDIT'
         then call symput('mfcnexts',' ');
         else call symput('mfcnexts',
            '%include &mfcsrref(mfcaudst);'); /*HOST*/
   end;
   else do;
      call symput('mfcerrn','04');
      call symput('mfcerrch','bad edit');
      call symput('mfcnexts',
         '%include &mfcsrref(mfcrept);'); /*HOST*/
   end;
   if mfcch eq 'PREEDIT' then put 'MFC Function Choice: '
      'mfcch=PREEDIT';
   put 25*'*' @28 mfcchpgm @37 'ended' @44 14*'*' /;
   stop;
end; /* end of file logic                                     */
return;

ampermsg:   put /
   'Warning, if & is macro variable text in a data step '
   'it''s value will' /
   ' be CONSTANT in a stored program. Assess impact.' /
   'Line=' prtline;
return;

   /* remove semicolons, keep rest of line                    */
cheksemi:
   substr(work,byte)=translate(substr(work,byte),' ',';');
   work=compress(substr(work,byte));
return;
run;
&mfcnexts;

                                                              
   /* ---- MFCGETNA ----   HOST Formats a flat MVS external   */
   /* file, or partitioned library and (file/member) name     */
   /* into a consistently 'printable' variable. TEMP54 comes  */
   /* into this code with bytes:                              */
   /*    1-44 full data set name                              */
   /*    45-52 pds member file name (if not = HEX low values) */
   /*    53-54 ignorable data                                 */
   /* TEMP54 is returned in print form.                       */
   /*           ---------------------------------------       */
   /* To use JFCB= option in INFILE statement for MVS HOST    */
   /* where DDNAME, DSN= JCL is used instead of filename and  */
   /* libname statments for source library and SAS library    */
   /* containing the stored pgms.                             */
   /* Set $54 length for TEMP54 in mainline. To pick up full  */
   /* external file name, place a TEMP54=VAR statement after  */
   /* your INFILE statement with the JFCB=VAR option in the   */
   /* mainline. Then include this code in the general form:   */
   /* %include fileref(mfcgetna);                             */

if substr(temp54,45,1) eq '00'x then substr(temp54,45)=' ';
else do;
   _8ch_=substr(temp54,45,8);
   substr(temp54,45,10)='(        )';
   substr(temp54,46,08)=_8ch_;
   temp54=compress(temp54);
end;


   /* ---- MFCINIT  ----   Executes initial processing for    */
   /* the MFC which is common to application streams and the  */
   /* optional independent compile job.                       */

%global mfcch    mfcerrch mfcredir mfcsrlbf mfctrref
        mfcchpgm mfcerrn  mfcrulib mfcsrlib mfctype
        mfccompi mfcnexts mfcruref mfcsrref mfcxtref ;

%let mfctrref=AUDITCMP; /* HOST audit trail fileref           */
filename &mfctrref sysout=*; /* HOST                          */

%let mfcxtref=TEMP80; /* HOST external work fileref           */
filename &mfcxtref '&TEMP80' disp=new unit=sysda
            space=(trk,(5,1),rlse)
            recfm=fb lrecl=80 blksize=6160;

%let mfcredir=;  /* default value, optional redirect          */
   /* initial compile of macro                                */
%include &mfcsrref(mfcdrivr); /* HOST                         */



   /* ---- MFCINITA ----   Executes initial processing to     */
   /* compile DATA steps in a stream. Gets source file data   */
   /* for audit trail statement creation and compiles the     */
   /* %MFCDRIVR macro. Any DATA step compiles in your stream  */
   /* using the MFC must follow the inclusion of this code.   */

%let mfctype=APPLICA; /* required for application stream      */

   /* HOST values for MFCRUREF, MFCRULIB                      */
%let mfcruref=runpgms; /* SAS library libref                  */
%let mfcrulib=hilevel.sas.runpgms; /* HOST libr. location     */

   /* MVS HOST must read comments in MFCLIBNA                 */
* %include &mfcsrref(mfclibna);
libname &mfcruref "&mfcrulib";

   /* common initialization with independent compile job      */
%include &mfcsrref(mfcinit);

   /* In a production environment, RUNCOMP is the only        */
   /* choice used. As there is no need to develop audit trail */
   /* data, CPU can be saved by commenting out the include    */
   /* for MFCTRDAT processing.                                */
%include &mfcsrref(mfctrdat); /* HOST                         */



   /* ---- MFCLIBNA ----   Get location/library names via     */
   /* jfcb= infile option for the source library and the SAS  */
   /* library containing the stored programs so MFCAUDST can  */
   /* insert audit trail statements with their names. In a    */
   /* production environment where only RUNCOMP is used, this */
   /* code codedoes not have to be included. There are        */
   /* choices for development:                                */
   /*    NOT-MVS HOST--You will not need this code since you  */
   /* already assigned a value to &MFCSRLIB and &MFCRULIB. Do */
   /* not %INCLUDE MFCLIBNA into MFCINITA, MFCINITC.          */
   /*    MVS HOST--If you have added NO DDNAME JCL for the    */
   /* source library and the SAS library for stored programs, */
   /* follow the NOT-MVS HOST instructions. If you have added */
   /* DDNAME JCL for these two libraries, you can include     */
   /* MFCLIBNA to automatically assign values for &MFCSRLIB   */
   /* &MFCRULIB. You must delete these statements-            */
   /*    In application stream initialization:                */
   /*       %let mfcsrlib=....                                */
   /*       filename &mfcsrref ....                           */
   /*    In MFCINITA code:                                    */
   /*       %let mfcrulib=....                                */
   /*       libname &mfcruref ....                            */

data _null_;
   /* mvs data set name in bytes 1-44 of dsn176 var.          */
infile &mfcruref jfcb=dsn176;
call symput('mfcrulib',substr(dsn176,1,44));

infile &mfcsrref(mfcdrivr) jfcb=dsn176a;
call symput('mfcsrlib',substr(dsn176a,1,44));
run;


   /* ---- MFCREPT  ----   Write messages, decide next        */
   /* actions, and determine the return code. (HOST for       */
   /* return code). The instructions which put the value of   */
   /* 'endsas' into the &mfcnexts macro variable are based on */
   /* the function choices for &mfcch. If you add logic to    */
   /* this DATA step, the choice for the &mfcnexts value is   */
   /* yours.                                                  */


data _null_;
   file &mfctrref; /* HOST                                    */
   length mfcerrch $8 mfcerrn $2 mfcch $7
      prtfnc $20 mfcsrlib $44;
   mfcchpgm=symget('mfcchpgm');
   mfcsrlib=symget('mfcsrlib');
   mfcerrch=symget('mfcerrch');
   mfcerrn=symget('mfcerrn');
   mfcch=symget('mfcch');
   mfcruref=symget('mfcruref');
   prtfnc=symget('mfcch');
   %let mfcnexts=;
put 'Source lib: ' mfcsrlib ' file: ' mfcchpgm
 /  'MFC Function Choice: ' mfcch= ;

if (mfcerrn ge '01') and (mfcerrn le '03') then do;
   if mfcerrn eq '01' then put prtfnc 'is an invalid choice'
      ' for mfcch= .' /
      'Choices are: SOURCED CPSTORU CPENDIT RUNCOMP PREEDIT '
      'COMPILE.';
   else if mfcerrn eq '02' then put mfcch 'is an invalid '
      'choice for mfcch= in the independent compile job.' /
      'PREEDIT and COMPILE are valid.';
   else if mfcerrn eq '03' then put mfcch 'is an invalid'
      ' choice for mfcch= in an application stream.' /
      'Valid choices are SOURCED CPSTORU CPENDIT RUNCOMP.';
   put 'SAS will end.' /;
   call symput('mfcnexts','endsas');
   abort return 8; /* HOST                                    */
end;

else if mfcerrn eq '04' then do;
      /* MFCEDITS has found hard preedit errors               */
   put mfcchpgm 'Data step source fails MFC '
      'PREEDIT compile rules.' / 'ERRORS  M U S T  '
      'be fixed prior to compiling.';
   if (mfcch eq 'PREEDIT') or (mfcch eq 'COMPILE') then do;
      put 'SAS will NOT end as there may be other '
         'PREEDITS or COMPILEs.';
      put /;
   end;
   else do;  /* SAS ends on errors for CPSTORU CPENDIT        */
      call symput('mfcnexts','endsas');
         /* HOST code 8                                       */
      put 'forcing a return code of 8, SAS will end.' /;
      abort return 8; /* HOST                                 */
   end;
end;

   /* simplify &syserr value for compile status               */
if (length(mfcerrch) gt 1) or (mfcerrch eq: '8')
   then number=8;
   else if mfcerrch eq: '4' then number=4;

if mfcerrn eq '05' then do;
   put 'Data step has not=0 return code from'
      ' SAS compile, value was ' mfcerrch '.'  /
      'SAS will not end. Additional files may '
      'need PREEDIT COMPILE choices.' /;
end;

else if mfcerrn eq '06' then do;
   put 'Data step has not=0 return code from'
      ' SAS compile, value was ' mfcerrch '.';
   if number eq 4
      then put 'CPENDIT will not end SAS for warnings.'/;
      else if number eq 8 then do;
         put 'mfcch=CPENDIT specifies that SAS  will end.' /;
         call symput('mfcnexts','endsas');
         abort return 8; /* HOST                              */
      end;
end;

else if mfcerrn eq '07' then do;
   put 'Data step has not=0 return code from'
      ' SAS compile, value was ' mfcerrch '.';
   if number eq 4
      then put 'CPSTORU will not end SAS for warnings.'/;
      else if number eq 8 then do;
         put 'CPSTORU runs a prior-compiled ' mfcchpgm
            'if it''s in ' mfcruref 'SAS library.' / ;
      end;
end;

else if mfcerrn eq '08' then do;
   mfctype=symget('mfctype');
   put MFCTYPE= ' is an invalid value. Source lib file '
      'MFCINITA requires' / ' APPLICA and INDCOMP source lib'
      ' file MFCINITC requires INDCOMP. SAS will end.';
   call symput('mfcnexts','endsas');
   abort return 8; /* HOST                                    */
end;
run;
&mfcnexts;



   /* ---- MFCRSTRS ----   Restore source from copy after     */
   /* bad compile to remove MFC added audit trail statements. */
   /* HOST This DATA step has been setup for MVS source files */
   /* where the s=72 and s2=72 SAS options are used for fixed */
   /* length 80 byte source lines. If your source is          */
   /* different you may have to adjust the code for your      */
   /* needs. You may need other variable length record kinds  */
   /* of changes.                                             */

%put NOTE: MFC is restoring &mfcsrref &mfcchpgm.;

data _null_;
   infile &mfcxtref; /* HOST                                  */
   file &mfcsrref(&mfcchpgm) notitles; /* HOST                */
   input; put _infile_;     /* HOST                           */
run;


   /* ---- MFCTRDAT ----   Create mfctrdat indexed data set   */
   /* containing information about files in the source        */
   /* library.                                                */
%put NOTE: MFC creating audit trail data.;

   /* HOST You will have to create a workaround for the PROC  */
   /* SOURCE below to create the &MFCXTREF external file for  */
   /* non MVS hosts. You should try to modify the DATA step   */
   /* which produces the MFCTRDAT data set to read the source */
   /* library directory in a more direct manner.              */

   /* make external file of directory data                    */
proc source indd=&mfcsrref dirdd=&mfcxtref
        nodata noprint null nosummary;

   /* Make MFCTRDAT indexed SAS data set by source file       */
   /* member name. The DATA step below is run from source. It */
   /* took more cpu cycles to execute a compiled version      */
   /* followed by a PROC DATASETS to create the index, versus */
   /* running the DATA step from source and creating the      */
   /* index at the same time. SAS does not allow a stored     */
   /* program to create an index.                             */

data mfctrdat (keep= mfcchpgm dtchange modymd8 modhhmm logon
index=(mfcchpgm=(mfcchpgm)/unique));
   length b8 modymd8 $8 modhhmm $5;
   infile &mfcxtref; /* HOST                                  */
   /* HOST layout of directory data will be different         */
input @1  mfcchpgm       $char08.
      @22 dtchange ??    pd3.
      @25 hour     ??    pk1.
      @26 min      ??    pk1.
      @33 logon          $char08.;
   /* MVS if logon is blank, file has no data                 */
if logon ne b8 then do;
   dtchange=datejul(dtchange);
   modymd8=put(dtchange,yymmdd8.);
   modhhmm='  :  ';
   substr(modhhmm,1,2)=put(hour,z2.);
   substr(modhhmm,4)=put(min,z2.);
   modymd8=translate(modymd8,'/','-');
end;
else logon='*nostats';
return;
run;


/* Sample DATA Steps                                          */

   /* ---- MFCSAMP1 ----   no error no warn source            */
data _null_ &mfccompi;
   do x=1 to 3; put 'MFCSAMP1 TEST COMPILE';  end;
run;



   /* ---- MFCSAMP2 ----   source with errors and warns       */
*test;  data
   a
&mfccompi;  /* test comment                                   */
      /* array forces compile error for testing               */
   array testay (4) var1 var2 var3;
   set intosamp;
   if &macvar ne: 'TESTIT' then
      do x=1 to dim(testay);
         if testay(x) lt 0 then output;
      end;
delete; run;