/*
/ Program : scanfile.sas
/ Version : 3.1
/ Author : Roland Rashleigh-Berry
/ Date : 19-Sep-2011
/ Purpose : Counts the number of lines of text in a file that contain the
/ string or the regular expression you specify within the line limit
/ you choose and optionally writes the line or blocks of lines to
/ the log.
/ SubMacros : none
/ Notes : This macro is useful for scanning LST files to ensure they contain
/ strings such as population set identifiers and subgroup
/ identifiers. The search can be case sensitive or not as you wish.
/ If you set a low line limit then you could aim to just search the
/ titles on the first page of .LST output.
/
/ Setting prx=yes allows you to use the more powerful Perl Regular
/ Expressions to search on in which case you can use the "or" (¦)
/ symbol to search on multiple terms.
/
/ The result of the number of lines matching the pattern you specify
/ will be written to the global macro variable _lines_ .
/
/ If you specify a file that does not exist then no error message
/ will be put out by this macro. Instead, _lines_ will be set to
/ DNE and it is up to the user to take action based on that.
/
/ If an error message is issued then _lines_ will be null (i.e. it
/ will be blank).
/
/ If the file exists but is empty then _lines_ will be set to
/ EMPTY .
/
/ You can write blocks of lines to the log using the printmore=
/ parameter to state a specific number of lines to print after the
/ matching line or using the untilstr= parameter to signal a line
/ match to stop writing more lines.
/
/ Usage : %scanfile(C:\temp\myfile.lst,Treated,3,casesens=no)
/
/ *-- Complex example of scanning all the sas programs --;
/ *-- in a library and printing the "proc format" steps. --;
/ %doallitem(%qreadpipe(dir /B C:\Mylib\*.sas),
/ '%scanfile(C:\Mylib\&item,proc format,
/ untilstr=run,notstr=cntlin,casesens=no)');
/===============================================================================
/ PARAMETERS:
/-------name------- -------------------------description------------------------
/ (note that enclosing quotes will be ignored for "file" and "str")
/ file (pos) Full file path of file you wish to search
/ str (pos) String or regular expression you wish to search on
/ limit (pos) Number of lines limit to search
/ casesens=yes By default, search is case sensitive
/ prx=no By default, the string is not a perl regular expression
/ print=no By default, do not print the matching lines
/ printmore=0 By default, do not print this extra number of lines after
/ finding a match.
/ untilstr String or regular expression to signal the last of the extra
/ lines to print.
/ notstr String or regular expression to exclude a match on str
/ _n_=no By default, do not display the line numbers
/ silent=no By default, put out a message to the log for all the files
/ being scanned whether the string was found or not.
/===============================================================================
/ AMENDMENT HISTORY:
/ init --date-- mod-id ----------------------description------------------------
/ rrb 08Sep11 new (v1.0)
/ rrb 12Sep11 existerr= parameter added (v1.1)
/ rrb 13Sep11 print=, printmore=, notstr= and untilstr= parameters
/ added (v2.0)
/ rrb 15Sep11 _n_= and silent= parameters added (v2.1)
/ rrb 17Sep11 existerr= processing removed so that this macro will not
/ issue an error message if a file does not exist but will
/ instead set _lines_ to DNE (v3.0)
/ rrb 19Sep11 _lines_ now set to EMPTY for an empty file so that it
/ works the same way as the %gettitles macro (v3.1)
/===============================================================================
/ This is public domain software. No guarantee as to suitability or accuracy is
/ given or implied. User uses this code entirely at their own risk.
/=============================================================================*/
%put MACRO CALLED: scanfile v3.1;
%macro scanfile(file,str,limit,
untilstr=,
notstr=,
print=no,
printmore=0,
prx=no,
casesens=yes,
_n_=no,
silent=no);
%local err errflag savopts;
%let savopts=%sysfunc(getoption(notes));
options nonotes;
%let err=ERR%str(OR);
%let errflag=0;
%global _lines_;
%let _lines_=;
%if not %length(&silent) %then %let silent=no;
%let silent=%upcase(%substr(&silent,1,1));
%if not %length(&printmore) %then %let printmore=0;
%if %length(&untilstr) %then %let printmore=99;
%if &printmore GT 0 %then %let print=yes;
%if not %length(&_n_) %then %let _n_=no;
%let _n_=%upcase(%substr(&_n_,1,1));
%if &_n_ EQ Y %then %let _n_=_n_=;
%else %let _n_=;
%if not %length(&print) %then %let print=no;
%let print=%upcase(%substr(&print,1,1));
%if not %length(&file) %then %do;
%let errflag=1;
%put &err: (scanfile) No file specified to the first positional parameter;
%end;
%else %do;
%let file=%sysfunc(dequote(&file));
%if not %sysfunc(fileexist(&file)) %then %do;
%let _lines_=DNE;
%goto skip;
%end;
%end;
%if not %length(&str) %then %do;
%let errflag=1;
%put &err: (scanfile) No search string specified to the second positional parameter;
%end;
%if %length(&limit) %then %do;
%if %length(%sysfunc(compress(&limit,0123456789))) %then %do;
%let errflag=1;
%put &err: (scanfile) Third positional parameter must be a positive integer limit=&limit;
%end;
%end;
%if &errflag %then %goto exit;
%if not %length(&casesens) %then %let casesens=yes;
%let casesens=%upcase(%substr(&casesens,1,1));
%if not %length(&prx) %then %let prx=no;
%let prx=%upcase(%substr(&prx,1,1));
%if &prx EQ Y %then %do;
%if &casesens EQ Y %then %let casesens=;
%else %let casesens=i;
%end;
%let _lines_=0;
data _null_;
retain printmore . gotit 0;
infile "&file" eof=eof;
input;
%if &print EQ Y and &silent NE Y %then %do;
if _n_=1 then put / ">>>>>>>>>>>>>>>>>>> scanning file &file";
%end;
%if %length(&limit) %then %do;
if _n_>&limit then goto eof;
%end;
%if &prx EQ Y %then %do;
if prxmatch("/%sysfunc(dequote(&str))/&casesens",_infile_)
%if %length(¬str) %then %do;
and not prxmatch("/%sysfunc(dequote(¬str))/&casesens",_infile_)
%end;
then do;
numlines+1;
printmore=&printmore;
%if &print EQ Y %then %do;
%if &silent EQ Y %then %do;
if gotit eq 0 then put / ">>>>>>>>>>>>>>>>>>> scanning file &file";
%end;
put &_n_ _infile_;
%end;
gotit=1;
end;
%if %length(&untilstr) %then %do;
else if printmore>0 and
prxmatch("/%sysfunc(dequote(&untilstr))/&casesens",_infile_) then do;
printmore=0;
put &_n_ _infile_;
end;
%end;
else do;
if printmore>0 then do;
put &_n_ _infile_;
printmore=printmore-1;
end;
end;
%end;
%else %do;
%if &casesens EQ N %then %do;
if index(upcase(_infile_),%upcase("%sysfunc(dequote(&str))"))
%if %length(¬str) %then %do;
and not index(upcase(_infile_),%upcase("%sysfunc(dequote(¬str))"))
%end;
then do;
numlines+1;
printmore=&printmore;
%if &print EQ Y %then %do;
%if &silent EQ Y %then %do;
if gotit eq 0 then put / ">>>>>>>>>>>>>>>>>>> scanning file &file";
%end;
put &_n_ _infile_;
%end;
gotit=1;
end;
%if %length(&untilstr) %then %do;
else if printmore>0 and
index(upcase(_infile_),%upcase("%sysfunc(dequote(&untilstr))")) then do;
printmore=0;
put &_n_ _infile_;
end;
%end;
else do;
if printmore>0 then do;
put &_n_ _infile_;
printmore=printmore-1;
end;
end;
%end;
%else %do;
if index(_infile_,"%sysfunc(dequote(&str))")
%if %length(¬str) %then %do;
and not index(_infile_,"%sysfunc(dequote(¬str))")
%end;
then do;
numlines+1;
printmore=&printmore;
%if &print EQ Y %then %do;
%if &silent EQ Y %then %do;
if gotit eq 0 then put / ">>>>>>>>>>>>>>>>>>> scanning file &file";
%end;
put &_n_ _infile_;
%end;
gotit=1;
end;
%if %length(&untilstr) %then %do;
else if printmore>0 and
index(_infile_,"%sysfunc(dequote(&untilstr))") then do;
printmore=0;
put &_n_ _infile_;
end;
%end;
else do;
if printmore>0 then do;
put &_n_ _infile_;
printmore=printmore-1;
end;
end;
%end;
%end;
return;
eof:
if _n_=1 and _infile_=" " then call symput('_lines_',"EMPTY");
else call symput('_lines_',compress(put(numlines,13.)));
stop;
return;
run;
%goto skip;
%exit: %put &err: (scanfile) Leaving macro due to problem(s) listed;
%skip:
options &savopts;
%mend scanfile;