recls-ruby 2.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,197 @@
1
+ # ######################################################################### #
2
+ # File: recls/ximpl/windows.rb
3
+ #
4
+ # Purpose: Windows-specific constructs for the recls library.
5
+ #
6
+ # Created: 19th February 2014
7
+ # Updated: 29th December 2015
8
+ #
9
+ # Author: Matthew Wilson
10
+ #
11
+ # Copyright (c) 2012-2015, Matthew Wilson and Synesis Software
12
+ # All rights reserved.
13
+ #
14
+ # Redistribution and use in source and binary forms, with or without
15
+ # modification, are permitted provided that the following conditions are met:
16
+ #
17
+ # * Redistributions of source code must retain the above copyright notice,
18
+ # this list of conditions and the following disclaimer.
19
+ #
20
+ # * Redistributions in binary form must reproduce the above copyright notice,
21
+ # this list of conditions and the following disclaimer in the documentation
22
+ # and/or other materials provided with the distribution.
23
+ #
24
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
28
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34
+ # POSSIBILITY OF SUCH DAMAGE.
35
+ #
36
+ # ######################################################################### #
37
+
38
+
39
+ require 'Win32API'
40
+
41
+ module Recls
42
+
43
+ module Ximpl
44
+
45
+ class FileStat < File::Stat
46
+
47
+ private
48
+ GetFileAttributes = Win32API.new('kernel32', 'GetFileAttributes', [ 'P' ], 'I')
49
+ GetFileInformationByHandle = Win32API.new('kernel32', 'GetFileInformationByHandle', [ 'L', 'P' ], 'I')
50
+ GetShortPathName = Win32API.new('kernel32', 'GetShortPathName', [ 'P', 'P', 'L' ], 'L')
51
+ CreateFile = Win32API.new('kernel32', 'CreateFile', [ 'P', 'L', 'L', 'L', 'L', 'L', 'L' ], 'L')
52
+ CloseHandle = Win32API.new('kernel32', 'CloseHandle', [ 'L' ], 'L')
53
+ FILE_ATTRIBUTE_READONLY = 0x00000001
54
+ FILE_ATTRIBUTE_HIDDEN = 0x00000002
55
+ FILE_ATTRIBUTE_SYSTEM = 0x00000004
56
+ FILE_ATTRIBUTE_DIRECTORY = 0x00000010
57
+ FILE_ATTRIBUTE_ARCHIVE = 0x00000020
58
+ FILE_ATTRIBUTE_DEVICE = 0x00000040
59
+ FILE_ATTRIBUTE_NORMAL = 0x00000080
60
+ FILE_ATTRIBUTE_TEMPORARY = 0x00000100
61
+ FILE_ATTRIBUTE_COMPRESSED = 0x00000800
62
+ FILE_ATTRIBUTE_ENCRYPTED = 0x00004000
63
+
64
+ OPEN_EXISTING = 0x00000003
65
+ FILE_FLAG_OVERLAPPED = 0x40000000
66
+ NULL = 0x00000000
67
+ INVALID_HANDLE_VALUE = 0xFFFFFFFF
68
+
69
+ MAX_PATH = 260
70
+
71
+ BHFI_pack_string = 'LQQQLLLLLL'
72
+
73
+ class ByHandleInformation
74
+
75
+ def initialize(path)
76
+
77
+ @volume_id = 0
78
+ @file_index = 0
79
+ @num_links = 0
80
+
81
+ # for some reason not forcing this new string causes 'can't modify frozen string (TypeError)' (in Ruby 1.8.x)
82
+ hFile = CreateFile.call("#{path}", 0, 0, NULL, OPEN_EXISTING, 0, NULL);
83
+ if INVALID_HANDLE_VALUE != hFile
84
+
85
+ begin
86
+ bhfi = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]
87
+ bhfi = bhfi.pack(BHFI_pack_string)
88
+
89
+ if GetFileInformationByHandle.call(hFile, bhfi)
90
+
91
+ bhfi = bhfi.unpack(BHFI_pack_string)
92
+
93
+ @volume_id = bhfi[4]
94
+ @file_index = (bhfi[8] << 32) | bhfi[9]
95
+ @num_links = bhfi[7]
96
+ else
97
+ end
98
+ ensure
99
+ CloseHandle.call(hFile)
100
+ end
101
+ end
102
+ end
103
+
104
+ attr_reader :volume_id
105
+ attr_reader :file_index
106
+ attr_reader :num_links
107
+ end
108
+
109
+ private
110
+ def has_attribute_? (attr)
111
+
112
+ 0 != (attr & @attributes)
113
+ end
114
+
115
+ private
116
+ def initialize(path)
117
+
118
+ @path = path
119
+
120
+ # for some reason not forcing this new string causes 'can't modify frozen string (TypeError)'
121
+ attributes = GetFileAttributes.call("#{path}")
122
+
123
+ if 0xffffffff == attributes
124
+ @attributes = 0
125
+ else
126
+ @attributes = attributes
127
+ end
128
+
129
+ super(path)
130
+
131
+ @by_handle_information = ByHandleInformation.new(path)
132
+
133
+ buff = ' ' * MAX_PATH
134
+ n = GetShortPathName.call(path, buff, buff.length)
135
+ @short_path = (0 == n) ? nil : buff[0...n]
136
+ end
137
+
138
+ public
139
+ attr_reader :attributes
140
+ attr_reader :path
141
+ attr_reader :by_handle_information
142
+ attr_reader :short_path
143
+
144
+ def hidden?
145
+
146
+ 0 != (FILE_ATTRIBUTE_HIDDEN & @attributes)
147
+ end
148
+
149
+ # Windows-specific attributes
150
+
151
+ def system?
152
+
153
+ has_attribute_? FILE_ATTRIBUTE_SYSTEM
154
+ end
155
+
156
+ def archive?
157
+
158
+ has_attribute_? FILE_ATTRIBUTE_ARCHIVE
159
+ end
160
+
161
+ def device?
162
+
163
+ has_attribute_? FILE_ATTRIBUTE_DEVICE
164
+ end
165
+
166
+ def normal?
167
+
168
+ has_attribute_? FILE_ATTRIBUTE_NORMAL
169
+ end
170
+
171
+ def temporary?
172
+
173
+ has_attribute_? FILE_ATTRIBUTE_TEMPORARY
174
+ end
175
+
176
+ def compressed?
177
+
178
+ has_attribute_? FILE_ATTRIBUTE_COMPRESSED
179
+ end
180
+
181
+ def encrypted?
182
+
183
+ has_attribute_? FILE_ATTRIBUTE_ENCRYPTED
184
+ end
185
+
186
+
187
+ public
188
+ def FileStat.stat(path)
189
+
190
+ Recls::Ximpl::FileStat.new(path)
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ # ############################## end of file ############################# #
197
+
@@ -0,0 +1,40 @@
1
+ #! /usr/bin/ruby
2
+ #
3
+ # test Recls entry methods
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+ require 'recls'
8
+
9
+ Recls::FileSearch.new(nil, nil, Recls::FILES | Recls::RECURSIVE).each do |fe|
10
+
11
+ puts
12
+ puts fe.path
13
+ puts fe.short_path
14
+ puts fe.directory_path
15
+ puts fe.drive
16
+ puts "".ljust(fe.drive.size) + fe.directory
17
+ puts "".ljust(fe.directory_path.size) + fe.file
18
+ puts "".ljust(fe.directory_path.size) + fe.file_short_name
19
+ puts "".ljust(fe.directory_path.size) + fe.stem
20
+ puts "".ljust(fe.directory_path.size + fe.stem.size) + fe.extension
21
+ n = fe.drive.size
22
+ fe.directory_parts.each do |part|
23
+
24
+ puts "".ljust(n) + part
25
+ n += part.size
26
+ end
27
+
28
+ puts fe.search_directory
29
+ puts "".ljust(fe.search_directory.size) + fe.search_relative_path
30
+ puts "".ljust(fe.search_directory.size) + fe.search_relative_directory_path
31
+ puts "".ljust(fe.search_directory.size) + fe.search_relative_directory
32
+
33
+ n = fe.search_directory.size
34
+ fe.search_relative_directory_parts.each do |part|
35
+
36
+ puts "".ljust(n) + part
37
+ n += part.size
38
+ end
39
+ end
40
+
@@ -0,0 +1,81 @@
1
+ #! /usr/bin/ruby
2
+ #
3
+ # test Recls entry methods
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+ require 'recls'
8
+
9
+ puts "PATH_NAME_SEPARATOR:\t[#{Recls::PATH_NAME_SEPARATOR}]"
10
+ puts "PATH_SEPARATOR:\t[#{Recls::PATH_SEPARATOR}]"
11
+ puts "WILDCARDS_ALL:\t[#{Recls::WILDCARDS_ALL}]"
12
+
13
+ def show_entry f
14
+
15
+ puts "entry:"
16
+ puts "\t#{'(entry)'.ljust(20)}\t#{f}"
17
+ puts "\t#{'f.path'.ljust(20)}\t#{f.path}"
18
+ puts "\t#{'f.drive'.ljust(20)}\t#{f.drive}"
19
+ puts "\t#{'f.directory_path'.ljust(20)}\t#{f.directory_path}"
20
+ puts "\t#{'f.directory'.ljust(20)}\t#{f.directory}"
21
+ directory_parts = f.directory_parts
22
+ puts "\t#{'f.directory_parts'.ljust(20)}\t[#{directory_parts.size}]"
23
+ directory_parts.each do |part|
24
+ puts "\t#{''.ljust(20)}\t\t#{part}"
25
+ end
26
+ search_relative_directory_parts = f.search_relative_directory_parts
27
+ puts "\t#{'f.search_relative_directory_parts'.ljust(20)}\t[#{search_relative_directory_parts.size}]"
28
+ search_relative_directory_parts.each do |part|
29
+ puts "\t#{''.ljust(20)}\t\t#{part}"
30
+ end
31
+ puts "\t#{'f.file_full_name'.ljust(20)}\t#{f.file_full_name}"
32
+ puts "\t#{'f.file_short_name'.ljust(20)}\t#{f.file_short_name}"
33
+ puts "\t#{'f.file_name_only'.ljust(20)}\t#{f.file_name_only}"
34
+ puts "\t#{'f.stem'.ljust(20)}\t#{f.stem}"
35
+ puts "\t#{'f.file_extension'.ljust(20)}\t#{f.file_extension}"
36
+ puts "\t#{'f.search_directory'.ljust(20)}\t#{f.search_directory}"
37
+ puts "\t#{'f.search_relative_path'.ljust(20)}\t#{f.search_relative_path}"
38
+ puts "\t#{'f.search_relative_directory'.ljust(20)}\t#{f.search_relative_directory}"
39
+ puts "\t#{'f.search_relative_directory_path'.ljust(20)}\t#{f.search_relative_directory_path}"
40
+
41
+ puts "\t#{'f.size'.ljust(20)}\t#{f.size}"
42
+
43
+ puts "\t#{'f.exist?'.ljust(20)}\t#{f.exist?}"
44
+ puts "\t#{'f.hidden?'.ljust(20)}\t#{f.hidden?}"
45
+ puts "\t#{'f.readonly?'.ljust(20)}\t#{f.readonly?}"
46
+ puts "\t#{'f.directory?'.ljust(20)}\t#{f.directory?}"
47
+ puts "\t#{'f.file?'.ljust(20)}\t#{f.file?}"
48
+ puts "\t#{'f.socket?'.ljust(20)}\t#{f.socket?}"
49
+
50
+ puts "\t#{'f.modification_time'.ljust(20)}\t#{f.modification_time}"
51
+ puts "\t#{'f.last_access_time'.ljust(20)}\t#{f.last_access_time}"
52
+ end
53
+
54
+ puts
55
+ puts "stat of '.':"
56
+ show_entry Recls::stat '.'
57
+
58
+ puts
59
+ puts "stat of '~':"
60
+ show_entry Recls::stat '~'
61
+
62
+ puts
63
+ puts "directories:"
64
+ num_directories = 0
65
+ Recls::FileSearch.new('.', '*', Recls::DIRECTORIES).each do |fe|
66
+
67
+ num_directories += 1
68
+ puts "\t[#{fe.search_relative_path}]"
69
+ end
70
+ puts " #{num_directories} directories"
71
+
72
+ puts
73
+ puts "files:"
74
+ num_files = 0
75
+ Recls::FileSearch.new('.', '*.rb', Recls::RECURSIVE | Recls::FILES).each do |fe|
76
+
77
+ num_files += 1
78
+ puts "\t<#{fe.search_relative_path}>"
79
+ end
80
+ puts " #{num_files} file(s)"
81
+
@@ -0,0 +1,41 @@
1
+ #! /usr/bin/ruby
2
+ #
3
+ # test Recls search methods
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+
8
+ #require 'recls/util'
9
+ require 'recls/foreach'
10
+ #require 'recls'
11
+
12
+ puts
13
+ puts "with given block:"
14
+ count_1 = 0
15
+ Recls.foreach(Recls::FileSearch.new(nil, '*.rb', Recls::RECURSIVE)).each do |line, line_number, fe|
16
+
17
+ line = line.chomp
18
+ line_number = 1 + line_number
19
+
20
+ puts "#{fe.search_relative_path}(#{line_number + 1}): #{line}"
21
+
22
+ count_1 += 1
23
+ break if 20 == count_1
24
+ end
25
+
26
+ puts
27
+ puts "as returned enumerator:"
28
+ e = Recls.foreach(Recls::FileSearch.new(nil, '*.rb', Recls::RECURSIVE))
29
+ count_2 = 0
30
+ e.each do |line, line_number, fe|
31
+
32
+ line = line.chomp
33
+ line_number = 1 + line_number
34
+
35
+ puts "#{fe.search_relative_path}(#{line_number + 1}): #{line}"
36
+
37
+ count_2 += 1
38
+ break if 20 == count_2
39
+ end
40
+
41
+
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Demonstrates use of dev and ino attributes
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+ require 'recls'
8
+
9
+ Recls::FileSearch.new('.', Recls::WILDCARDS_ALL, Recls::FILES | Recls::RECURSIVE).each do |fe|
10
+
11
+ puts "#{fe.search_relative_path}\t#{fe.dev}\t#{fe.ino}\t#{fe.nlink}"
12
+ end
13
+
@@ -0,0 +1,30 @@
1
+ #! /usr/bin/ruby
2
+ #
3
+ # Demonstrates search of hidden files
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+ require 'recls'
8
+
9
+ puts
10
+ puts "Hidden directories:"
11
+ Recls::FileSearch.new(nil, nil, Recls::RECURSIVE | Recls::DIRECTORIES | Recls::SHOW_HIDDEN).each do |fe|
12
+
13
+ puts "\t#{fe.search_relative_path}" if fe.hidden?
14
+ end
15
+
16
+ puts
17
+ puts "Hidden files:"
18
+ Recls::FileSearch.new(nil, nil, Recls::RECURSIVE | Recls::FILES | Recls::SHOW_HIDDEN).each do |fe|
19
+
20
+ puts "\t#{fe.search_relative_path}" if fe.hidden?
21
+ end
22
+
23
+
24
+ puts
25
+ puts "Hidden directories & files:"
26
+ Recls::FileSearch.new(nil, nil, Recls::RECURSIVE | Recls::DIRECTORIES | Recls::FILES | Recls::MARK_DIRECTORIES | Recls::SHOW_HIDDEN).each do |fe|
27
+
28
+ puts "\t#{fe.search_relative_path}" if fe.hidden?
29
+ end
30
+
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # test Recls entries
4
+
5
+ $:.unshift File.join(File.dirname(__FILE__), '../..', 'lib')
6
+
7
+
8
+ require 'recls'
9
+
10
+ require 'test/unit'
11
+
12
+ class Test_Recls_entries < Test::Unit::TestCase
13
+
14
+ unless defined? assert_false
15
+ def assert_false arg0, *args
16
+ assert !arg0, *args
17
+ end
18
+ end
19
+
20
+ def test_entries_in_directory_have_unique_names
21
+
22
+ entries = Recls::FileSearch.new('~', Recls::WILDCARDS_ALL, Recls::FILES).to_a
23
+
24
+ hashed_entries = Hash[entries.each_with_index.map { |fe, index| [ fe, index] }]
25
+
26
+ # ensure that no duplicates in hash (and hence Entry.hash and Entry.<=> work)
27
+ assert_equal entries.size, hashed_entries.size
28
+
29
+ entries.each_with_index do |entry, index|
30
+
31
+ entry_a = entries[index]
32
+ entry_b = Recls.stat(entries[index].path)
33
+
34
+ # ensure that different entry instances representing the same path evaluate
35
+ # equal (and hence Entry.eq? and Entry.== work)
36
+
37
+ assert entry_a.eql? entry_a
38
+ assert entry_a.eql? entry_b
39
+ assert_false entry_a.eql? entry_a.path
40
+ assert_false entry_a.eql? entry_b.path
41
+
42
+ assert_equal entry_a, entry_a
43
+ assert_equal entry_a, entry_b
44
+ assert_equal entry_a, entry_a.path
45
+ assert_equal entry_a, entry_b.path
46
+
47
+ assert_same entry_a, entries[index]
48
+ assert_not_same entry_a, entry_b
49
+
50
+ assert_equal index, hashed_entries[entry_a]
51
+ assert_equal index, hashed_entries[entry_b]
52
+ end
53
+ end
54
+ end
55
+