%macro pageof( /* Specify a file or fileref. */ /* Physical file names should be in quotes. */ /* Filerefs/DDnames should not be in quotes. */ pgm =, /* program file to execute */ output =, /* output file */ /* placement of page number text on print page */ just = center, /* left, center, or right */ topbot = bottom, /* top or bottom */ psadd = 2, /* number of lines from text */ pagetext = Page PAGEX of PAGEN, /* page number text */ cc = ! /* character to use as form feed */ ); /* Initialize global system options. */ options number pageno=1 formdlim="&cc"; /* Initialize column to begin reading */ /* original output. */ %let strt_col = 1; /* Initialize system-specific files and options. */ /* Determine the operating system with automatic */ /* macro variable SYSSCP. */ %if &sysscp=HP 800 %then %do; filename temp1 'potemp1.txt'; filename temp2 'potemp2.txt'; %end; %else %if &sysscp=VMS or &sysscp=OS2 or &sysscp=WIN %then %do; /* Do not require the user to press enter to */ /* leave the X window brought up by an X */ /* statement. */ options noxwait; filename temp1 'potemp1.txt'; filename temp2 'potemp2.txt'; %end; %else %if &sysscp=CMS %then %do; filename temp1 'potemp1 txt a'; filename temp2 'potemp2 txt a'; %let strt_col = %eval(&strt_col + 1); %let psadd=%eval(&psadd + 2); %end; %else %if &sysscp=OS %then %do; filename temp1 '&temp'; /* Cause a short time delay between */ /* allocations because the time value is used */ /* in the name of the temporary file. */ data _null_; run; filename temp2 '&temp'; %let strt_col = %eval(&strt_col + 1); %let psadd=%eval(&psadd + 2); %end; /* Execute the user's program in a PRINTTO block */ /* to save the output for post processing. */ proc printto print=temp1 new; %include &pgm; proc printto; run; /* Set page eject back to the system default. */ options formdlim=''; /* Put the original LINESIZE= and PAGESIZE= */ /* settings into macro variables for later use */ /* by reading SASHELP.VOPTION and writing the */ /* values into macro variables. */ data _null_; set sashelp.voption; where optname='LINESIZE' or optname='PAGESIZE'; call symput(optname,setting); run; /* Route the output of a dummy PROC PRINT to the */ /* TEMP2 file to generate the next page number. */ proc printto print=temp2 new; data _temp_; x=1; proc print; options number; proc printto; run; /* Read the first line of output to get the page */ /* number. Store the number in a macro variable. */ data _null_; infile temp2 truncover; input record $ 1-200; call symput('lastpage',trim(left(put(input(reverse (scan(reverse(record),1)),6.)-1,6.)))); stop; run; /* Adjust options. */ %let pagesize=%eval(&pagesize+&psadd); options nodate nonumber ps=&pagesize; /* Use macro logic to parse the text parameter */ /* and create the appropriate DATA step syntax */ /* to print the page number. */ /* Find the position of PAGEX in the string. */ %let xpos=%index(%nrbquote(&pagetext),PAGEX); /* If PAGEX occurs at the beginning of the */ /* string, substitute this: */ %if &xpos=1 %then %let maketext = trim(left(put(pagenum,6.))) || "%substr(&pagetext,6)"; %else %do; /* If PAGEX is not at the beginning of the */ /* string, substitute this: */ %let maketext = "%substr(&pagetext,1,&xpos-1)" || trim(left(put(pagenum,6.))); /* If PAGEX is not at the end of the string, add */ /* the remainder of the string. */ %if &xpos+4 le %length(&pagetext) %then %let maketext = &maketext || "%substr(&pagetext,&xpos+5)"; %end; /* Find the position of PAGEN. */ %let npos = %index(&maketext,PAGEN); /* Substitute the total page count for PAGEN. */ %let maketext = %qsubstr(&maketext,1, &npos-1)%nrstr(&lastpage)%qsubstr(&maketext, &npos+5); /* Read in TEMP1 and check for carriage control. */ /* Each time carriage control is encountered, */ /* read the current page number and then rewrite */ /* it at the desired location along with the */ /* total page count. Otherwise write the original*/ /* record. */ data _null_; infile temp1 length=len truncover eof=eof noprint; length text $ 200 next_cc $ 10; retain pagenum; /* Use the option NOTITLES. Since the input */ /* file already has titles, do not repeat */ /* printing of the titles. */ file &output notitles n=ps line=cur_line /* If the user does not want output to go to */ /* the OUTPUT window, then use these options: */ /* PRINT and OLD. */ %if "%upcase(&output)" ne "PRINT" %then %do; print old %end; ; /* Read a line of output. Scan for carriage */ /* control and page number. */ input @&strt_col record $varying200. len ; /* If the line begins with carriage control, */ /* read the page number and replace it */ /* with blanks. */ if substr(record,2,10) eq "&cc&cc&cc&cc&cc&cc&cc&cc&cc&cc" then do; /* Page eject detected. */ /* If the line is a carriage control */ /* line, read in the next (blank) line. */ input; /* Read a line of output. */ input @&strt_col record $varying200. len ; /* Scan for the current page number. */ pagenum=reverse(scan(reverse(record),1)); /* Replace the original page number with*/ /* blanks because the page number will */ /* be printed in a different place. */ /* To do this, use the SUBSTR function */ /* on the left side of the equal sign. */ /* In this case SUBSTR works as a text */ /* replacement function. So, replace the*/ /* text from the first character of the */ /* page number to the end of the line. */ substr(record, length(trim(record)) - length(pagenum) +1, length(pagenum)) = ' '; /* If user requests page number text at */ /* top of page and requests to increase */ /* page size, then add &PSADD blank */ /* lines to the top of each page. */ if (&psadd gt 0) and ("%upcase(%substr(&topbot,1,1))" eq "T") then do; /* Add blank lines to top of page. */ do line_num = 1 to %eval(&psadd); put #line_num @1 ' '; end; end; /* Add blank lines to top of page. */ end; /* Page eject detected. */ /* Write the record back to the file. */ if _n_ gt 1 then do; /* Write regular record back to file.*/ put @1 record $varying200. len @; if cur_line lt &pagesize then put; end; /* Write regular record back to file.*/ /* Look ahead to the next input line to */ /* determine if it has a carriage control */ /* character. If it does, write the page */ /* number on the current page. */ input @2 next_cc $10. @@; /* If look-ahead detects a page eject, write */ /* page number text. */ if next_cc eq "&cc&cc&cc&cc&cc&cc&cc&cc&cc&cc" and pagenum ge 1 then do; /* Write page number text. */ /* Use macro logic to set the line and */ /* column placement for the page-numbering */ /* text. */ /* Place text at top or bottom of the page.*/ /* If T, place text on line 1. */ %if %substr(%upcase(&topbot),1,1) eq T %then %let line=1; /* Otherwise place text on last line */ /* (same as page size). */ %else %let line=&pagesize; /* Place text at left, center, or right. */ %if %substr(%upcase(&just),1,1) eq L %then %let col=1; %else %if %substr(%upcase(&just),1,1) eq C %then %let col=(floor((&linesize-length(text))/2)); %else %if %substr(%upcase(&just),1,1) eq R %then %let col=(&linesize-length(text)-1); /* Concatenate text for page numbering. */ text=%unquote(&maketext); put #&line @&col text; put _page_; end; /* Write page number text. */ return; /* End of main part of DATA step */ /* To avoid a lost card problem, jump to end */ /* of data when eof is read in input file. */ eof: /* Concatenate text for page numbering. */ text=%unquote(&maketext); put #&line @&col text; run; /* End of entire DATA step */ /* Delete temporary system files. */ %if &sysscp=HP 800 %then %do; X 'rm potemp1.txt potemp2.txt'; %end; %else %if &sysscp=OS2 or &sysscp=WIN %then %do; X 'del potemp1.txt'; X 'del potemp2.txt'; %end; %else %if &sysscp=VMS %then %do; X 'del potemp1.txt;*'; X 'del potemp2.txt;*'; %end; %else %if &sysscp=CMS %then %do; X 'erase potemp1 txt a'; X 'erase potemp2 txt a'; %end; /* No action is needed for &SYSSCP=OS. Files are */ /* deleted automatically when the SAS session */ /* ends. */ /* Set the page size and page number to the */ /* original settings. */ options ps=%eval(&pagesize-&psadd) pageno=%eval(&lastpage+1); %mend pageof;