Macros for SAS Application Developers
https://github.com/sasjs/core
Loading...
Searching...
No Matches
mp_dirlist.sas
Go to the documentation of this file.
1/**
2 @file
3 @brief Returns all files and subdirectories within a specified parent
4 @details When used with getattrs=NO, is not OS specific (uses dopen / dread).
5
6 Credit for the rename approach:
7 https://communities.sas.com/t5/SAS-Programming/SAS-Function-to-convert-string-to-Legal-SAS-Name/m-p/27375/highlight/true#M5003
8
9 Usage:
10
11 %mp_dirlist(path=/some/location, outds=myTable, maxdepth=MAX)
12
13 %mp_dirlist(outds=cwdfileprops, getattrs=YES)
14
15 %mp_dirlist(fref=MYFREF)
16
17 @warning In a Unix environment, the existence of a named pipe will cause this
18 macro to hang. Therefore this tool should be used with caution in a SAS 9 web
19 application, as it can use up all available multibridge sessions if requests
20 are resubmitted.
21 If anyone finds a way to positively identify a named pipe using SAS (without
22 X CMD) do please raise an issue!
23
24
25 @param [in] path= (%sysfunc(pathname(work))) Path for which to return contents
26 @param [in] fref= (0) Provide a DISK engine fileref as an alternative to PATH
27 @param [in] maxdepth= (0) Set to a positive integer to indicate the level of
28 subdirectory scan recursion - eg 3, to go `./3/levels/deep`. For unlimited
29 recursion, set to MAX.
30 @param [in] showparent= (NO) By default, the initial parent directory is not
31 part of the results. Set to YES to include it. For this record only,
32 directory=filepath.
33 @param [out] outds= (work.mp_dirlist) The output dataset to create
34 @param [out] getattrs= (NO) If getattrs=YES then the doptname / foptname
35 functions are used to scan all properties - any characters that are not
36 valid in a SAS name (v7) are simply stripped, and the table is transposed
37 so theat each property is a column and there is one file per row. An
38 attempt is made to get all properties whether a file or folder, but some
39 files/folders cannot be accessed, and so not all properties can / will be
40 populated.
41
42
43 @returns outds contains the following variables:
44 - directory (containing folder)
45 - file_or_folder (file / folder)
46 - filepath (path/to/file.name)
47 - filename (just the file name)
48 - ext (.extension)
49 - msg (system message if any issues)
50 - level (depth of folder)
51 - OS SPECIFIC variables, if <code>getattrs=</code> is used.
52
53 <h4> SAS Macros </h4>
54 @li mf_existds.sas
55 @li mf_getvarlist.sas
56 @li mf_wordsinstr1butnotstr2.sas
57 @li mp_dropmembers.sas
58
59 <h4> Related Macros </h4>
60 @li mp_dirlist.test.sas
61
62 @version 9.2
63**/
64
65%macro mp_dirlist(path=%sysfunc(pathname(work))
66 , fref=0
67 , outds=work.mp_dirlist
68 , getattrs=NO
69 , showparent=NO
70 , maxdepth=0
71 , level=0 /* The level of recursion to perform. For internal use only. */
72)/*/STORE SOURCE*/;
73%let getattrs=%upcase(&getattrs)XX;
74
75/* temp table */
76%local out_ds;
77data;run;
78%let out_ds=%str(&syslast);
79
80/* drop main (top) table if it exists */
81%if &level=0 %then %do;
82 %mp_dropmembers(%scan(&outds,-1,.), libref=WORK)
83%end;
84
85data &out_ds(compress=no
86 keep=file_or_folder filepath filename ext msg directory level
87 );
88 length directory filepath $500 fref fref2 $8 file_or_folder $6 filename $80
89 ext $20 msg $200 foption $16;
90 if _n_=1 then call missing(of _all_);
91 retain level &level;
92 %if &fref=0 %then %do;
93 rc = filename(fref, "&path");
94 %end;
95 %else %do;
96 fref="&fref";
97 rc=0;
98 %end;
99 if rc = 0 then do;
100 did = dopen(fref);
101 if did=0 then do;
102 putlog "NOTE: This directory is empty, or does not exist - &path";
103 msg=sysmsg();
104 put (_all_)(=);
105 stop;
106 end;
107 /* attribute is OS-dependent - could be "Directory" or "Directory Name" */
108 numopts=doptnum(did);
109 do i=1 to numopts;
110 foption=doptname(did,i);
111 if foption=:'Directory' then i=numopts;
112 end;
113 directory=dinfo(did,foption);
114 rc = filename(fref);
115 end;
116 else do;
117 msg=sysmsg();
118 put _all_;
119 stop;
120 end;
121 dnum = dnum(did);
122 do i = 1 to dnum;
123 filename = dread(did, i);
124 filepath=cats(directory,'/',filename);
125 rc = filename(fref2,filepath);
126 midd=dopen(fref2);
127 dmsg=sysmsg();
128 if did > 0 then file_or_folder='folder';
129 rc=dclose(midd);
130 midf=fopen(fref2);
131 fmsg=sysmsg();
132 if midf > 0 then file_or_folder='file';
133 rc=fclose(midf);
134
135 if index(fmsg,'File is in use') or index(dmsg,'is not a directory')
136 then file_or_folder='file';
137 else if index(fmsg,'Insufficient authorization') then file_or_folder='file';
138 else if file_or_folder='' then file_or_folder='locked';
139
140 if file_or_folder='file' then do;
141 ext = prxchange('s/.*\.{1,1}(.*)/$1/', 1, filename);
142 if filename = ext then ext = ' ';
143 end;
144 else do;
145 ext='';
146 file_or_folder='folder';
147 end;
148 output;
149 end;
150 rc = dclose(did);
151 %if &showparent=YES and &level=0 %then %do;
152 filepath=directory;
153 file_or_folder='folder';
154 ext='';
155 filename=scan(directory,-1,'/\');
156 msg='';
157 level=&level;
158 output;
159 %end;
160 stop;
161run;
162
163%if %substr(&getattrs,1,1)=Y %then %do;
164 data &out_ds;
165 set &out_ds;
166 length infoname infoval $60 fref $8;
167 if _n_=1 then call missing(fref);
168 rc=filename(fref,filepath);
169 drop rc infoname fid i close fref;
170 if file_or_folder='file' then do;
171 fid=fopen(fref);
172 if fid le 0 then do;
173 msg=sysmsg();
174 putlog "Could not open file:" filepath fid= ;
175 sasname='_MCNOTVALID_';
176 output;
177 end;
178 else do i=1 to foptnum(fid);
179 infoname=foptname(fid,i);
180 infoval=finfo(fid,infoname);
181 sasname=compress(infoname, '_', 'adik');
182 if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
183 if upcase(sasname) ne 'FILENAME' then output;
184 end;
185 close=fclose(fid);
186 end;
187 else do;
188 fid=dopen(fref);
189 if fid le 0 then do;
190 msg=sysmsg();
191 putlog "Could not open folder:" filepath fid= ;
192 sasname='_MCNOTVALID_';
193 output;
194 end;
195 else do i=1 to doptnum(fid);
196 infoname=doptname(fid,i);
197 infoval=dinfo(fid,infoname);
198 sasname=compress(infoname, '_', 'adik');
199 if anydigit(sasname)=1 then sasname=substr(sasname,anyalpha(sasname));
200 if upcase(sasname) ne 'FILENAME' then output;
201 end;
202 close=dclose(fid);
203 end;
204 run;
205 proc sort;
206 by filepath sasname;
207 proc transpose data=&out_ds out=&out_ds(drop=_:);
208 id sasname;
209 var infoval;
210 by filepath file_or_folder filename ext ;
211 run;
212%end;
213
214data &out_ds;
215 set &out_ds(where=(filepath ne ''));
216run;
217
218/**
219 * The above transpose can mean that some updates create additional columns.
220 * This necessitates the occasional use of datastep over proc append.
221 */
222%if %mf_existds(&outds) %then %do;
223 %local basevars appvars newvars;
224 %let basevars=%mf_getvarlist(&outds);
225 %let appvars=%mf_getvarlist(&out_ds);
226 %let newvars=%length(%mf_wordsinstr1butnotstr2(Str1=&appvars,Str2=&basevars));
227 %if &newvars>0 %then %do;
228 data &outds;
229 set &outds &out_ds;
230 run;
231 %end;
232 %else %do;
233 proc append base=&outds data=&out_ds force nowarn;
234 run;
235 %end;
236%end;
237%else %do;
238 proc append base=&outds data=&out_ds;
239 run;
240%end;
241
242/* recursive call */
243%if &maxdepth>&level or &maxdepth=MAX %then %do;
244 data _null_;
245 set &out_ds;
246 where file_or_folder='folder';
247 %if &showparent=YES and &level=0 %then %do;
248 if filepath ne directory;
249 %end;
250 length code $10000;
251 code=cats('%nrstr(%mp_dirlist(path=',filepath,",outds=&outds"
252 ,",getattrs=&getattrs,level=%eval(&level+1),maxdepth=&maxdepth))");
253 put code=;
254 call execute(code);
255 run;
256%end;
257
258/* tidy up */
259proc sql;
260drop table &out_ds;
261
262%mend mp_dirlist;