1 /******************************************************************************
2 *
3 * Copyright (C) 2004-2007, The Gentee Group. All rights reserved.
4 * This file is part of the Gentee open source project - http://www.gentee.com.
5 *
6 * THIS FILE IS PROVIDED UNDER THE TERMS OF THE GENTEE LICENSE ("AGREEMENT").
7 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS FILE CONSTITUTES RECIPIENTS
8 * ACCEPTANCE OF THE AGREEMENT.
9 *
10 * Author: Alexey Krivonogov ( gentee )
11 *
12 ******************************************************************************/
13
14 define <export> {
15 /*-----------------------------------------------------------------------------
16 * Id: findflags D
17 *
18 * Summary: Flags for searching files.
19 *
20 -----------------------------------------------------------------------------*/
21 FIND_DIR = 0x0001 // Search only for directories.
22 FIND_FILE = 0x0002 // Search only for files.
23 FIND_RECURSE = 0x0004 // Search in all subdirectories.
24
25 /*-----------------------------------------------------------------------------
26 * Id: fdelflags D
27 *
28 * Summary: Flags for delfiles.
29 *
30 -----------------------------------------------------------------------------*/
31 DELF_RO = 0x0100 // Delete files with the attribute read-only.
32
33 //-----------------------------------------------------------------------------
34 }
35
36 /*-----------------------------------------------------------------------------
37 * Id: tfinfo T finfo
38 *
39 * Summary: File information structure. This structure is used by
40 #a(getfileinfo) function and #a(ffind_opfor, foreach ) operator.
41 *
42 -----------------------------------------------------------------------------*/
43
44 type finfo {
45 str fullname // The full name of the file or directory.
46 str name // The name of the file or directory.
47 uint attrib // File attributes.
48 filetime lastwrite // Last modification time.
49 uint sizehi // High size uint.
50 uint sizelo // Low size uint.
51 }
52
53 //-----------------------------------------------------------------------------
54
55 type fstack {
56 finfo info
57 str path
58 uint find
59 uint ok
60 }
61
62 /*-----------------------------------------------------------------------------
63 * Id: ffind T
64 *
65 * Summary: File search structure. This structure is used in
66 #a(ffind_opfor,foreach ) operator. You must not modify fields of
67 #i(ffind) variable. You must initialize it with #a(ffind_init)
68 method.
69 *
70 -----------------------------------------------------------------------------*/
71
72 type ffind <index = finfo> {
73 stack deep of fstack // Hidden data.
74 str initname // Hidden data.
75 str wildcard // Hidden data.
76 uint flag // Hidden data.
77 }
78
79 //-----------------------------------------------------------------------------
80
81 method fstack.delete()
82 {
83 if this.find
84 {
85 FindClose( this.find )
86 this.find = 0
87 }
88 }
89
90 /*-----------------------------------------------------------------------------
91 * Id: ffind_init F2
92 *
93 * Summary: Initializing file search. An object of the #a(ffind) type is used to
94 search for files and directories by mask. Before starting the
95 search, you should call the init method. After this it is possible
96 to use the initiated object in the #b(foreach) loop. The #a(tfinfo)
97 structure will be returned for each found file.
98 *
99 * Params: name - The mask for searching files and directories.
100 flag - The combination of the following flags:$$[findflags]
101 *
102 -----------------------------------------------------------------------------*/
103
104 method ffind.init( str name, uint flag )
105 {
106 arr ss of fstack
107 this.deep.clear()
108 this.flag = flag
109 this.initname = name
110 this.initname.fdelslash()
111 this.wildcard.fnameext( this.initname )
112 ss.insert( 0, 1 )
113 this.deep.push()
114 }
115
116 func wfd2finfo( WIN32_FIND_DATA wfd, finfo fi, str path )
117 {
118 fi.name.copy( &wfd.cFileName )
119 ( fi.fullname = path ).faddname( fi.name )
120 fi.attrib = wfd.dwFileAttributes
121 fi.lastwrite = wfd.ftLastWriteTime
122 fi.sizehi = wfd.nFileSizeHigh
123 fi.sizelo = wfd.nFileSizeLow
124 }
125
126 operator finfo =( finfo left, finfo right )
127 {
128 left.fullname = right.fullname
129 left.name = right.name
130 left.attrib = right.attrib
131 left.lastwrite = right.lastwrite
132 left.sizehi = right.sizehi
133 left.sizelo = right.sizelo
134
135 return left
136 }
137
138 method finfo ffind.getinfo
139 {
140 return this.deep.top()->finfo
141 }
142
143 method finfo ffind.found( WIN32_FIND_DATA wfd )
144 {
145 uint flag = this.flag
146 uint current = this.deep.top()
147
148 if !wfd.cFileName[0] : goto next
149
150 label again
151 current as fstack
152
153 current.ok = 0
154 if wfd.cFileName[0] == '.' && ( !wfd.cFileName[1] ||
155 ( wfd.cFileName[1] == '.' && !wfd.cFileName[2] ))
156 {
157 goto next
158 }
159 if wfd.dwFileAttributes & $FILE_ATTRIBUTE_DIRECTORY
160 {
161 if flag & $FIND_DIR : current.ok = 1
162 }
163 else : if flag & $FIND_FILE : current.ok = 1
164
165 if current.ok
166 {
167 //current.ok = sfwildcard( &wfd.cFileName, this.wildcard.ptr())
168 str fn
169 fn.copy( &wfd.cFileName )
170 current.ok = fn.fwildcard( this.wildcard )
171 }
172
173 if wfd.dwFileAttributes & $FILE_ATTRIBUTE_DIRECTORY &&
174 flag & $FIND_RECURSE
175 {
176 str newfld
177 uint find
178
179 wfd2finfo( wfd, current.info, current.path )
180
181 newfld = current.info.fullname
182 current as this.deep.push()
183 current as fstack
184 current.path = newfld
185 newfld.faddname( "*" )
186
187 current.find = FindFirstFile( newfld.ptr(), wfd )
188 if current.find != $INVALID_HANDLE_VALUE
189 {
190 goto again
191 }
192 current as this.deep.pop()
193 current as fstack
194 }
195 if current.ok
196 {
197 wfd2finfo( wfd, current.info, current.path )
198 return this.getinfo()
199 }
200
201 label next
202 if FindNextFile( current.find, wfd ) : goto again
203
204 FindClose( current.find )
205 current.find = 0
206
207 if *this.deep > 1
208 {
209 current as this.deep.pop()
210 current as fstack
211 if ( current.ok ) : this.getinfo()
212 else : goto next
213 }
214
215 return this.getinfo()
216 }
217
218 /*-----------------------------------------------------------------------------
219 * Id: ffind_opfor F5
220 *
221 * Summary: Foreach operator. You can use #b(foreach) operator to look over
222 files in some directory with the specified wildcard. The #a(tfinfo)
223 structure will be returned for each found file. You must call
224 #a(ffind_init) before using #b(foreach). #srcg[
225 |ffind fd
226 |fd.init( "c:\\*.exe", $FIND_FILE | $FIND_RECURSE )
227 |foreach finfo cur,fd
228 |{
229 | print( "\( cur.fullname )\n" )
230 |}]
231 *
232 * Title: foreach var,ffind
233 *
234 * Define: foreach variable,ffind {...}
235 *
236 -----------------------------------------------------------------------------*/
237
238 method uint ffind.next( fordata fd)
239 {
240 WIN32_FIND_DATA wfd
241
242 return &this.found( wfd )
243 }
244
245 method uint ffind.first( fordata fd )
246 {
247 WIN32_FIND_DATA wfd
248 str temp
249 uint start
250
251 start = this.deep.top()
252 start as fstack
253 if !*this.initname
254 {
255 start.find = 0
256 return &start.info
257 }
258 ( temp = this.initname ).fdelslash()
259
260 if this.flag & $FIND_RECURSE
261 {
262 temp.fgetdir( temp )
263 temp.faddname( "*" )
264 }
265 start.find = FindFirstFile( temp.ptr(), wfd )
266 if start.find == $INVALID_HANDLE_VALUE
267 {
268 start.find = 0
269 return &start.info
270 }
271 start.path.fgetdir( temp )
272 return &this.found( wfd )
273 }
274
275 method uint ffind.eof( fordata fd )
276 {
277 return !this.deep.top()->fstack.find
278 }
279
280 /*-----------------------------------------------------------------------------
281 * Id: getfileinfo F
282 *
283 * Summary: Get information about a file or directory.
284 *
285 * Params: name - The name of a file or directory.
286 fi - The structure #a(tfinfo) all the information will be written to.
287 *
288 * Return: It returns 1 if the file is found, it returns 0 otherwise.
289 *
290 -----------------------------------------------------------------------------*/
291
292 func uint getfileinfo( str name, finfo fi )
293 {
294 ffind fd
295
296 fd.init( name, $FIND_DIR | $FIND_FILE )
297 foreach finfo cur, fd
298 {
299 fi = cur
300 return 1
301 }
302
303 return 0
304 }
305
306 /*-----------------------------------------------------------------------------
307 ** Id: delfiles F
308 *
309 * Summary: Deleting files and directories by mask. Directories are deleted
310 together with all files and subdirectories. Be really careful while
311 using this function. For example, calling
312
313 |#srcg[delfiles( "c:\\temp", $FIND_DIR | $FIND_FILE | $FIND_RECURSE )]
314
315 will delete all files and directories named temp on the disk N:
316 including a search in all directories. In this case temp is
317 considered a mask and since the flag $FIND_RECURSE is specified, the
318 entire disk C: will be searched. If you just need to delete the
319 directory temp with all its subdirectories and files, you should
320 call
321
322 |#srcg[delfiles("c:\\temp", $FIND_DIR )]
323 Calling
324
325 |#srcg[delfiles( "c:\\temp\\*.tmp", $FIND_FILE )]
326 will delete all files in the directory tmp leaving subdirectories.
327 *
328 * Params: name - The name of mask for searching.
329 flag - Search and delete flags.$$[findflags]$$[fdelflags]
330 *
331 -----------------------------------------------------------------------------*/
332
333 func delfiles( str name, uint flag )
334 {
335 ffind fd
336
337 fd.init( name, flag )
338
339 foreach finfo cur, fd
340 {
341 if cur.attrib & $FILE_ATTRIBUTE_DIRECTORY
342 {
343 delfiles( cur.fullname + "\\*.*" , flag | $FIND_FILE )
344 deletedir( cur.fullname )
345 }
346 else
347 {
348 if flag & $DELF_RO && cur.attrib & $FILE_ATTRIBUTE_READONLY
349 {
350 setattribnormal( cur.fullname )
351 }
352 deletefile( cur.fullname )
353 }
354 }
355 }
356
Edit