/[eiffelstudio]/FreeELKS/trunk/library/kernel/directory.e
ViewVC logotype

Contents of /FreeELKS/trunk/library/kernel/directory.e

Parent Directory Parent Directory | Revision Log Revision Log


Revision 92166 - (show annotations)
Sun Feb 3 22:18:37 2013 UTC (6 years, 11 months ago) by manus_eiffel
File size: 17940 byte(s)
Made it compile by adding inheritance to NATIVE_STRING_HANDLER.

1 note
2 description: "Directories, in the Unix sense, with creation and exploration features"
3 library: "Free implementation of ELKS library"
4 copyright: "Copyright (c) 1986-2008, Eiffel Software and others"
5 license: "Eiffel Forum License v2 (see forum.txt)"
6 date: "$Date$"
7 revision: "$Revision$"
8
9 class DIRECTORY
10
11 inherit
12 DISPOSABLE
13
14 NATIVE_STRING_HANDLER
15
16 create
17 make, make_open_read
18
19 feature -- Initialization
20
21 make (dn: STRING)
22 -- Create directory object for directory
23 -- of name `dn'.
24 require
25 string_exists: dn /= Void
26 do
27 set_name (dn)
28 mode := Close_directory
29 end
30
31 make_open_read (dn: STRING)
32 -- Create directory object for directory
33 -- of name `dn' and open it for reading.
34 require
35 string_exists: dn /= Void
36 do
37 make (dn)
38 open_read
39 end
40
41 create_dir
42 -- Create a physical directory.
43 require
44 physical_not_exists: not exists
45 do
46 file_mkdir (internal_name_pointer.item)
47 end
48
49 recursive_create_dir
50 -- Create the directory `a_directory_name' recursively.
51 --
52 -- Ex: if /temp/ exists but not /temp/test, calling
53 -- `recursive_create_directory ("/temp/test/toto")'
54 -- will create /temp/test and then /temp/test/toto.
55 require
56 physical_not_exists: not exists
57 local
58 l_directory: DIRECTORY
59 l_new_directory_name: DIRECTORY_NAME
60 l_directories_to_build: ARRAYED_LIST [STRING]
61 l_built_directory: STRING
62 l_loc_directory_name: STRING
63 l_separator_index: INTEGER
64 l_io_exception: IO_FAILURE
65 do
66 create l_directories_to_build.make (10)
67
68 -- Find the first existing directory in the path name
69 from
70 l_built_directory := name.twin
71 l_separator_index := l_built_directory.count
72 create l_directory.make (l_built_directory)
73 until
74 l_directory.exists
75 loop
76 l_separator_index := l_built_directory.last_index_of (Operating_environment.Directory_separator, l_built_directory.count)
77 if l_separator_index = 0 then
78 create l_io_exception
79 l_io_exception.set_message ("Invalid directory: " + l_built_directory)
80 l_io_exception.raise
81 end
82 l_directories_to_build.extend (l_built_directory.substring (l_separator_index + 1, l_built_directory.count))
83 if l_built_directory @ (l_separator_index - 1) = ':' then
84 l_loc_directory_name := l_built_directory.substring (1, l_separator_index)
85 else
86 l_loc_directory_name := l_built_directory.substring (1, l_separator_index - 1)
87 end
88 l_built_directory := l_built_directory.substring (1, l_separator_index - 1)
89 create l_directory.make (l_loc_directory_name)
90 end
91
92 -- Recursively create the directory.
93 from
94 l_directories_to_build.finish
95 create l_new_directory_name.make_from_string (l_built_directory)
96 until
97 l_directories_to_build.before
98 loop
99 l_new_directory_name.extend (l_directories_to_build.item)
100 l_directories_to_build.back
101
102 create l_directory.make (l_new_directory_name)
103 l_directory.create_dir
104 if not l_directory.exists then
105 create l_io_exception
106 l_io_exception.set_message ("Cannot create: " + l_new_directory_name)
107 l_io_exception.raise
108 end
109 end
110 ensure
111 physical_exists: exists
112 end
113
114 feature -- Access
115
116 readentry
117 -- Read next directory entry
118 -- make result available in `lastentry'.
119 -- Make result Void if all entries have been read.
120 require
121 is_opened: not is_closed
122 do
123 last_entry_pointer := eif_dir_next (directory_pointer)
124 if last_entry_pointer /= default_pointer then
125 -- For backward compatibility, we had to leave `lastentry' as an attribute
126 -- which is being updated at each `readentry' call.
127 lastentry := file_info.pointer_to_file_name_8 (last_entry_pointer)
128 else
129 -- We reached the end of our iteration.
130 lastentry := Void
131 end
132 end
133
134 name: STRING_8
135 -- File name as a STRING_8 instance. The value might be truncated
136 -- from the original name used to create the current FILE instance.
137 do
138 Result := internal_name.as_string_8
139 ensure then
140 name_not_empty: not Result.is_empty
141 end
142
143 has_entry (entry_name: STRING): BOOLEAN
144 -- Has directory the entry `entry_name'?
145 -- The use of `dir_temp' is required not
146 -- to change the position in the current
147 -- directory entries list.
148 require
149 string_exists: entry_name /= Void
150 local
151 dir_temp: DIRECTORY
152 e: like last_entry_pointer
153 do
154 create dir_temp.make_open_read (internal_name)
155 from
156 dir_temp.start
157 dir_temp.readentry
158 e := dir_temp.last_entry_pointer
159 until
160 Result or e = default_pointer
161 loop
162 if attached {READABLE_STRING_8} entry_name then
163 -- We were given a STRING_8 instance, we lookup the name using the old way
164 Result := entry_name.same_string (file_info.pointer_to_file_name_8 (e))
165 elseif attached file_info.pointer_to_file_name_32 (e) as l_str then
166 Result := entry_name.same_string (l_str)
167 end
168 dir_temp.readentry
169 e := dir_temp.last_entry_pointer
170 end
171 dir_temp.close
172 end
173
174 open_read
175 -- Open directory for reading.
176 do
177 directory_pointer := dir_open (internal_name_pointer.item)
178 mode := Read_directory
179 end
180
181 close
182 -- Close directory.
183 require
184 is_open: not is_closed
185 do
186 dir_close (directory_pointer)
187 directory_pointer := default_pointer
188 mode := Close_directory
189 end
190
191 start
192 -- Go to first entry of directory.
193 require
194 is_opened: not is_closed
195 do
196 directory_pointer := dir_rewind (directory_pointer, internal_name_pointer.item)
197 end
198
199 change_name (new_name: STRING)
200 -- Change directory `name' to `new_name'.
201 require
202 new_name_not_void: new_name /= Void
203 directory_exists: exists
204 local
205 l_ptr: MANAGED_POINTER
206 do
207 l_ptr := file_info.file_name_to_pointer (new_name, Void)
208 eif_dir_rename (internal_name_pointer.item, l_ptr.item)
209 set_name (new_name)
210 ensure
211 name_changed: internal_name = new_name
212 end
213
214 feature -- Measurement
215
216 count: INTEGER
217 -- Number of entries in directory.
218 require
219 directory_exists: exists
220 local
221 dir_temp: DIRECTORY
222 counter: INTEGER
223 do
224 create dir_temp.make_open_read (internal_name)
225 from
226 dir_temp.start
227 dir_temp.readentry
228 until
229 dir_temp.last_entry_pointer = default_pointer
230 loop
231 counter := counter + 1
232 dir_temp.readentry
233 end
234 Result := counter
235 dir_temp.close
236 end
237
238 feature -- Conversion
239
240 linear_representation: ARRAYED_LIST [STRING]
241 -- The entries, in sequential format. Entries that can be
242 -- expressed in Unicode are excluded and one has to use
243 -- `linear_representation' to get them.
244 obsolete
245 "Use `entries' instead if your application is using Unicode file names."
246 local
247 dir_temp: DIRECTORY
248 e: like last_entry_pointer
249 do
250 create dir_temp.make_open_read (internal_name)
251 -- Arbitrary size for arrayed_list creation to avoid
252 -- querying `count' which traverses list of entries
253 -- in current directory as we do here, making current
254 -- less efficient if Current has a lot of entries.
255 create Result.make (16)
256 from
257 dir_temp.start
258 dir_temp.readentry
259 e := dir_temp.last_entry_pointer
260 until
261 e = default_pointer
262 loop
263 Result.extend (file_info.pointer_to_file_name_8 (e))
264 dir_temp.readentry
265 e := dir_temp.last_entry_pointer
266 end
267 dir_temp.close
268 end
269
270 feature -- Status report
271
272 last_entry_8: detachable STRING_8
273 -- Raw byte sequence of the last found entry if this entry cannot be
274 -- expressed with Unicode characters. This is useful
275 -- when handling a file that is not a valid UTF-8 sequence on Unix.
276 do
277 if last_entry_pointer /= default_pointer then
278 Result := file_info.pointer_to_file_name_8 (last_entry_pointer)
279 end
280 end
281
282 lastentry: detachable STRING_8
283 -- Last entry which is
284 obsolete
285 "Use `last_entry_32' for Unicode file names, or `last_entry_8' otherwise."
286 attribute
287 end
288
289 is_closed: BOOLEAN
290 -- Is current directory closed?
291 do
292 Result := mode = Close_directory
293 end
294
295 is_empty: BOOLEAN
296 -- Is directory empty?
297 require
298 directory_exists: exists
299 do
300 -- count = 2, since there are "." and ".." which
301 -- are symbolic representations but not effective directories.
302 Result := (count = 2)
303 end
304
305 empty: BOOLEAN
306 -- Is directory empty?
307 obsolete
308 "Use `is_empty' instead"
309 do
310 Result := is_empty
311 end
312
313 exists: BOOLEAN
314 -- Does the directory exist?
315 do
316 Result := eif_dir_exists (internal_name_pointer.item)
317 end
318
319 is_readable: BOOLEAN
320 -- Is the directory readable?
321 require
322 directory_exists: exists
323 do
324 Result := eif_dir_is_readable (internal_name_pointer.item)
325 end
326
327 is_executable: BOOLEAN
328 -- Is the directory executable?
329 require
330 directory_exists: exists
331 do
332 Result := eif_dir_is_executable (internal_name_pointer.item)
333 end
334
335 is_writable: BOOLEAN
336 -- Is the directory writable?
337 require
338 directory_exists: exists
339 do
340 Result := eif_dir_is_writable (internal_name_pointer.item)
341 end
342
343 feature -- Removal
344
345 delete
346 -- Delete directory if empty.
347 require
348 directory_exists: exists
349 empty_directory: is_empty
350 do
351 eif_dir_delete (internal_name_pointer.item)
352 end
353
354 delete_content
355 -- <Precursor>
356 do
357 delete_content_with_action (Void, Void, 0)
358 end
359
360 recursive_delete
361 -- Delete directory and all content contained within.
362 require
363 directory_exists: exists
364 do
365 delete_content
366 if is_empty then
367 delete
368 end
369 end
370
371 delete_content_with_action (
372 action: PROCEDURE [ANY, TUPLE [LIST [STRING]]]
373 is_cancel_requested: FUNCTION [ANY, TUPLE, BOOLEAN]
374 file_number: INTEGER)
375 -- Delete all files located in directory and subdirectories.
376 --
377 -- `action' is called each time `file_number' files has
378 -- been deleted and before the function exits.
379 -- `action' may be set to Void if you don't need it.
380 --
381 -- Same for `is_cancel_requested'.
382 -- Make it return `True' to cancel the operation.
383 -- `is_cancel_requested' may be set to Void if you don't need it.
384 local
385 file_name: detachable FILE_NAME
386 file: detachable RAW_FILE
387 l_info: like file_info
388 dir: detachable DIRECTORY
389 dir_temp: DIRECTORY
390 l_last_entry_pointer: like last_entry_pointer
391 l_name: detachable STRING_8
392 file_count: INTEGER
393 deleted_files: ARRAYED_LIST [STRING]
394 requested_cancel: BOOLEAN
395 do
396 file_count := 1
397 create deleted_files.make (file_number)
398
399 from
400 -- To delete files we do not need to follow symbolic links.
401 l_info := file_info
402 l_info.set_is_following_symlinks (False)
403 -- Create a new directory that we will use to list all of its content.
404 create dir_temp.make_open_read (internal_name)
405 dir_temp.start
406 dir_temp.readentry
407 l_last_entry_pointer := dir_temp.last_entry_pointer
408 until
409 l_last_entry_pointer = default_pointer or requested_cancel
410 loop
411 -- Ignore current and parent directories.
412 l_name := l_info.pointer_to_file_name_8 (l_last_entry_pointer)
413 if (not l_name.same_string (current_directory_string) and not l_name.same_string (parent_directory_string)) then
414 -- Avoid creating too many objects.
415 if file_name /= Void then
416 file_name.make_from_string (internal_name)
417 else
418 create file_name.make_from_string (internal_name)
419 end
420 file_name.set_file_name (l_name)
421 l_info.update (file_name)
422 if l_info.exists then
423 if not l_info.is_symlink and then l_info.is_directory then
424 -- Start the recursion for true directory, we do not follow links to delete their content.
425 if dir /= Void then
426 dir.make (file_name)
427 else
428 create dir.make (file_name)
429 end
430 dir.recursive_delete_with_action (action, is_cancel_requested, file_number)
431 elseif l_info.is_writable then
432 if file /= Void then
433 file.reset (file_name)
434 else
435 create file.make_with_name (file_name)
436 end
437 file.delete
438 end
439
440 -- Add the name of the deleted file to our array
441 -- of deleted files.
442 deleted_files.extend (file_name)
443 file_count := file_count + 1
444
445 -- If `file_number' has been reached, call `action'.
446 if file_count > file_number then
447 if action /= Void then
448 action.call ([deleted_files])
449 end
450 if is_cancel_requested /= Void then
451 requested_cancel := is_cancel_requested.item (Void)
452 end
453 deleted_files.wipe_out
454 file_count := 1
455 end
456 end
457 end
458 dir_temp.readentry
459 l_last_entry_pointer := dir_temp.last_entry_pointer
460 end
461 dir_temp.close
462
463 -- If there is more than one deleted file and no
464 -- agent has been called, call one now.
465 if file_count > 1 and action /= Void then
466 action.call ([deleted_files])
467 end
468 end
469
470 recursive_delete_with_action (
471 action: PROCEDURE [ANY, TUPLE [LIST [STRING]]]
472 is_cancel_requested: FUNCTION [ANY, TUPLE, BOOLEAN]
473 file_number: INTEGER)
474 -- Delete directory and all content contained within.
475 --
476 -- `action' is called each time `file_number' files has
477 -- been deleted and before the function exits.
478 require
479 directory_exists: exists
480 local
481 deleted_files: ARRAYED_LIST [STRING]
482 do
483 delete_content_with_action (action, is_cancel_requested, file_number)
484 if (is_cancel_requested = Void) or else (not is_cancel_requested.item (Void)) then
485 delete
486 -- Call the agent with the name of the directory
487 if action /= Void then
488 create deleted_files.make (1)
489 deleted_files.extend (internal_name)
490 action.call ([deleted_files])
491 end
492 end
493 end
494
495 dispose
496 -- Ensure this medium is closed when garbage collected.
497 do
498 if not is_closed then
499 close
500 end
501 end
502
503 feature {DIRECTORY} -- Implementation
504
505 directory_pointer: POINTER
506 -- Directory pointer as required in C
507
508 last_entry_pointer: POINTER
509 -- Pointer to the underlying C memory representing the last directory entry.
510
511 feature {NONE} -- Implementation
512
513 set_name (a_name: STRING)
514 -- Set `name' with `a_name'.
515 do
516 internal_name := a_name
517 internal_detachable_name_pointer := file_info.file_name_to_pointer (a_name, internal_detachable_name_pointer)
518 ensure
519 name_set: internal_name = a_name
520 end
521
522 internal_name: STRING
523 -- Store the name of the file as it was given to us by the user
524 -- to avoid conversion on storing as it is not necessary.
525
526 internal_name_pointer: MANAGED_POINTER
527 -- File system specific encoding of `internal_name'.
528 -- Typically a UTF-16 sequence on Windows, a UTF-8 sequence on Unix.
529 do
530 if attached internal_detachable_name_pointer as l_ptr then
531 Result := l_ptr
532 else
533 -- This is never True because `internal_detachable_name_pointer' is set during the creation
534 -- of the FILE object.
535 check internal_name_pointer_set: False then end
536 end
537 end
538
539 internal_detachable_name_pointer: detachable MANAGED_POINTER note option: stable attribute end
540 -- File system specific encoding of `internal_name'.
541 -- Typically a UTF-16 sequence on Windows, a UTF-8 sequence on Unix.
542
543 mode: INTEGER
544 -- Status mode of the directory.
545 -- Possible values are the following:
546
547 Close_directory: INTEGER = 1
548
549 Read_directory: INTEGER = 2
550
551 current_directory_string: STRING = "."
552 parent_directory_string: STRING = ".."
553 -- Constants to represent current (".") and parent ("..") directory.
554
555 directory_separator_string: STRING
556 -- Constant representing the directory separator
557 once
558 create Result.make (1)
559 Result.append_character (operating_environment.directory_separator)
560 end
561
562 file_info: FILE_INFO
563 -- To avoid creating objects when querying for files.
564 once
565 create Result.make
566 end
567
568 file_mkdir (dir_name: POINTER)
569 -- Make directory `dir_name'.
570 external
571 "C signature (EIF_FILENAME) use %"eif_file.h%""
572 alias
573 "eif_file_mkdir"
574 end
575
576 dir_open (dir_name: POINTER): POINTER
577 -- Open the directory `dir_name'.
578 external
579 "C signature (EIF_FILENAME): EIF_POINTER use %"eif_dir.h%""
580 alias
581 "eif_dir_open"
582 end
583
584 dir_rewind (dir_ptr: POINTER; dir_name: POINTER): POINTER
585 -- Rewind the directory `dir_ptr' with name `a_name' and return a new directory traversal pointer.
586 external
587 "C signature (EIF_POINTER, EIF_FILENAME): EIF_POINTER use %"eif_dir.h%""
588 alias
589 "eif_dir_rewind"
590 end
591
592 dir_close (dir_ptr: POINTER)
593 -- Close the directory `dir_ptr'.
594 external
595 "C use %"eif_dir.h%""
596 alias
597 "eif_dir_close"
598 end
599
600 eif_dir_next (dir_ptr: POINTER): POINTER
601 -- Return pointer to the next entry in the current iteration.
602 external
603 "C use %"eif_dir.h%""
604 end
605
606 eif_dir_delete (dir_name: POINTER)
607 -- Delete the directory `dir_name'.
608 external
609 "C signature (EIF_FILENAME) use %"eif_file.h%""
610 alias
611 "eif_file_unlink"
612 end
613
614 eif_dir_exists (dir_name: POINTER): BOOLEAN
615 -- Does the directory `dir_name' exist?
616 external
617 "C signature (EIF_FILENAME): EIF_BOOLEAN use %"eif_dir.h%""
618 end
619
620 eif_dir_is_readable (dir_name: POINTER): BOOLEAN
621 -- Is `dir_name' readable?
622 external
623 "C signature (EIF_FILENAME): EIF_BOOLEAN use %"eif_dir.h%""
624 end
625
626 eif_dir_is_executable (dir_name: POINTER): BOOLEAN
627 -- Is `dir_name' executable?
628 external
629 "C signature (EIF_FILENAME): EIF_BOOLEAN use %"eif_dir.h%""
630 end
631
632 eif_dir_is_writable (dir_name: POINTER): BOOLEAN
633 -- Is `dir_name' writable?
634 external
635 "C signature (EIF_FILENAME): EIF_BOOLEAN use %"eif_dir.h%""
636 end
637
638 eif_dir_rename (old_name, new_name: POINTER)
639 -- Change directory name from `old_name' to `new_name'.
640 external
641 "C signature (EIF_FILENAME, EIF_FILENAME) use %"eif_file.h%""
642 alias
643 "eif_file_rename"
644 end
645
646 invariant
647 name_attached: attached internal_name
648
649 note
650 copyright: "Copyright (c) 1984-2012, Eiffel Software and others"
651 license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
652 source: "[
653 Eiffel Software
654 5949 Hollister Ave., Goleta, CA 93117 USA
655 Telephone 805-685-1006, Fax 805-685-6869
656 Website http://www.eiffel.com
657 Customer support http://support.eiffel.com
658 ]"
659 end

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

  ViewVC Help
Powered by ViewVC 1.1.23