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