libpath-ruby 0.2.2 → 0.2.2.1
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.
- checksums.yaml +4 -4
- data/LICENSE +33 -0
- data/README.md +25 -8
- data/examples/path_from_arg0.md +43 -43
- data/examples/path_from_arg0.rb +25 -23
- data/lib/libpath/constants/unix.rb +71 -71
- data/lib/libpath/constants/windows.rb +72 -72
- data/lib/libpath/constants.rb +18 -19
- data/lib/libpath/diagnostics/parameter_checking.rb +25 -25
- data/lib/libpath/diagnostics.rb +1 -1
- data/lib/libpath/exceptions/libpath_base_exception.rb +13 -13
- data/lib/libpath/exceptions/malformed_name_exception.rb +23 -22
- data/lib/libpath/exceptions.rb +1 -1
- data/lib/libpath/form/unix.rb +104 -103
- data/lib/libpath/form/windows.rb +200 -199
- data/lib/libpath/form.rb +18 -18
- data/lib/libpath/internal_/array.rb +53 -53
- data/lib/libpath/internal_/platform.rb +24 -24
- data/lib/libpath/internal_/string.rb +18 -18
- data/lib/libpath/internal_/unix/form.rb +116 -116
- data/lib/libpath/internal_/windows/drive.rb +59 -59
- data/lib/libpath/internal_/windows/form.rb +189 -189
- data/lib/libpath/libpath.rb +3 -0
- data/lib/libpath/path/unix.rb +114 -112
- data/lib/libpath/path/windows.rb +120 -118
- data/lib/libpath/path.rb +18 -20
- data/lib/libpath/util/unix.rb +258 -258
- data/lib/libpath/util/windows.rb +400 -400
- data/lib/libpath/util.rb +18 -19
- data/lib/libpath/version.rb +23 -24
- data/lib/libpath.rb +1 -1
- data/test/performance/benchmark_drive_letter.rb +50 -10
- data/test/performance/benchmark_gsub_string_or_regex.rb +27 -23
- data/test/performance/benchmark_rindex2.rb +56 -48
- data/test/performance/benchmark_split.rb +16 -10
- data/test/unit/compare/ts_all.rb +4 -4
- data/test/unit/equate/ts_all.rb +4 -4
- data/test/unit/equate/unix/ts_all.rb +4 -4
- data/test/unit/equate/windows/ts_all.rb +4 -4
- data/test/unit/exceptions/tc_libpath_base_exception.rb +10 -8
- data/test/unit/exceptions/tc_malformed_name_exception.rb +14 -12
- data/test/unit/exceptions/ts_all.rb +4 -4
- data/test/unit/form/tc_absolute_functions.rb +242 -240
- data/test/unit/form/ts_all.rb +4 -4
- data/test/unit/form/unix/tc_absolute_functions.rb +158 -155
- data/test/unit/form/unix/ts_all.rb +4 -4
- data/test/unit/form/windows/tc_absolute_functions.rb +628 -625
- data/test/unit/form/windows/ts_all.rb +4 -4
- data/test/unit/internal_/tc_array.rb +36 -32
- data/test/unit/internal_/ts_all.rb +4 -4
- data/test/unit/internal_/unix/form/tc_slash_functions.rb +38 -35
- data/test/unit/internal_/unix/form/ts_all.rb +4 -4
- data/test/unit/internal_/unix/tc_split_path.rb +387 -384
- data/test/unit/internal_/unix/ts_all.rb +4 -4
- data/test/unit/internal_/windows/form/tc_get_windows_volume.rb +157 -153
- data/test/unit/internal_/windows/form/tc_slash_functions.rb +39 -35
- data/test/unit/internal_/windows/form/ts_all.rb +4 -4
- data/test/unit/internal_/windows/tc_split_path.rb +874 -869
- data/test/unit/internal_/windows/ts_all.rb +4 -4
- data/test/unit/parse/ts_all.rb +4 -4
- data/test/unit/path/tc_path.rb +732 -732
- data/test/unit/path/ts_all.rb +4 -4
- data/test/unit/path/unix/tc_path.rb +544 -540
- data/test/unit/path/unix/ts_all.rb +4 -4
- data/test/unit/path/windows/tc_path.rb +608 -603
- data/test/unit/path/windows/ts_all.rb +4 -4
- data/test/unit/tc_version.rb +25 -22
- data/test/unit/ts_all.rb +4 -4
- data/test/unit/util/tc_combine_paths.rb +110 -105
- data/test/unit/util/tc_derive_relative_path.rb +8 -3
- data/test/unit/util/tc_make_path_canonical.rb +142 -138
- data/test/unit/util/ts_all.rb +4 -4
- data/test/unit/util/unix/tc_combine_paths.rb +38 -34
- data/test/unit/util/unix/tc_derive_relative_path.rb +80 -75
- data/test/unit/util/unix/tc_make_path_absolute.rb +74 -70
- data/test/unit/util/unix/tc_make_path_canonical.rb +95 -90
- data/test/unit/util/unix/ts_all.rb +4 -4
- data/test/unit/util/windows/tc_combine_paths.rb +82 -77
- data/test/unit/util/windows/tc_derive_relative_path.rb +98 -93
- data/test/unit/util/windows/tc_make_path_absolute.rb +105 -101
- data/test/unit/util/windows/tc_make_path_canonical.rb +158 -153
- data/test/unit/util/windows/ts_all.rb +4 -4
- metadata +9 -8
data/lib/libpath/form/windows.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
|
2
2
|
# ######################################################################## #
|
3
|
-
# File:
|
3
|
+
# File: libpath/form/windows.rb
|
4
4
|
#
|
5
|
-
# Purpose:
|
5
|
+
# Purpose: LibPath::Form::Windows module
|
6
6
|
#
|
7
|
-
# Created:
|
8
|
-
# Updated:
|
7
|
+
# Created: 8th January 2019
|
8
|
+
# Updated: 6th April 2024
|
9
9
|
#
|
10
|
-
# Home:
|
10
|
+
# Home: http://github.com/synesissoftware/libpath.Ruby
|
11
11
|
#
|
12
|
-
# Author:
|
12
|
+
# Author: Matthew Wilson
|
13
13
|
#
|
14
|
+
# Copyright (c) 2019-2024, Matthew Wilson and Synesis Information Systems
|
14
15
|
# Copyright (c) 2019, Matthew Wilson and Synesis Software
|
15
16
|
# All rights reserved.
|
16
17
|
#
|
@@ -44,7 +45,6 @@
|
|
44
45
|
# ######################################################################## #
|
45
46
|
|
46
47
|
|
47
|
-
|
48
48
|
=begin
|
49
49
|
=end
|
50
50
|
|
@@ -53,6 +53,7 @@ require 'libpath/diagnostics'
|
|
53
53
|
require 'libpath/internal_/windows/drive'
|
54
54
|
require 'libpath/internal_/windows/form'
|
55
55
|
|
56
|
+
|
56
57
|
module LibPath # :nodoc:
|
57
58
|
module Form # :nodoc:
|
58
59
|
module Windows # :nodoc:
|
@@ -61,289 +62,289 @@ module Windows # :nodoc:
|
|
61
62
|
# any class or module including/extending module LibPath::Form::Windows
|
62
63
|
module LibPath_Form_Windows_Methods
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
65
|
+
# Classifies a path
|
66
|
+
#
|
67
|
+
# === Return
|
68
|
+
#
|
69
|
+
# One of +:absolute+, +:drived+, +:homed+, +:relative+, +:rooted+, for
|
70
|
+
# any paths that match precisely those classifications, or +nil+ if the
|
71
|
+
# path is empty
|
72
|
+
def classify_path path
|
72
73
|
|
73
|
-
|
74
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
74
75
|
|
75
|
-
|
76
|
+
return nil if path.nil? || path.empty?
|
76
77
|
|
77
|
-
|
78
|
+
return :homed if path_is_homed? path
|
78
79
|
|
79
|
-
|
80
|
+
vol, rem, _ = Internal_::Windows::Form.get_windows_volume(path)
|
80
81
|
|
81
|
-
|
82
|
+
rooted = Internal_::Windows::Form.char_is_path_name_separator? rem[0]
|
82
83
|
|
83
|
-
|
84
|
+
if rooted
|
84
85
|
|
85
|
-
|
86
|
+
if vol
|
86
87
|
|
87
|
-
|
88
|
-
|
88
|
+
return :absolute
|
89
|
+
else
|
89
90
|
|
90
|
-
|
91
|
-
|
92
|
-
|
91
|
+
return :rooted
|
92
|
+
end
|
93
|
+
else
|
93
94
|
|
94
|
-
|
95
|
+
if vol
|
95
96
|
|
96
|
-
|
97
|
-
|
97
|
+
return :drived
|
98
|
+
else
|
98
99
|
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
return :relative
|
101
|
+
end
|
102
|
+
end
|
102
103
|
|
103
|
-
|
104
|
+
end
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
106
|
+
# Evaluates whether the given name is malformed, according to the given
|
107
|
+
# options.
|
108
|
+
#
|
109
|
+
# If no options are specified, the only invalid character(s) are: +'\0'+
|
110
|
+
#
|
111
|
+
# === Signature
|
112
|
+
#
|
113
|
+
# * *Options:*
|
114
|
+
# - +:reject_path_name_separators+:: (boolean) Reject the path
|
115
|
+
# separator character(s): +'\\'+ and +'/'+
|
116
|
+
# - +:reject_path_separators+:: (boolean) Reject the path separator
|
117
|
+
# character(s): +';'+
|
118
|
+
# - +:reject_shell_characters+:: (boolean) Reject the shell
|
119
|
+
# character(s): +'*'+, +'?'+, +'|'+
|
120
|
+
def name_is_malformed? name, **options
|
120
121
|
|
121
|
-
|
122
|
+
_Constants = ::LibPath::Constants::Windows
|
122
123
|
|
123
|
-
|
124
|
+
if name
|
124
125
|
|
125
|
-
|
126
|
+
if options[:reject_shell_characters]
|
126
127
|
|
127
|
-
|
128
|
-
|
128
|
+
return true if name =~ _Constants::InvalidCharacters::Shell::RE
|
129
|
+
end
|
129
130
|
|
130
|
-
|
131
|
+
if options[:reject_path_separators]
|
131
132
|
|
132
|
-
|
133
|
-
|
133
|
+
return true if name =~ _Constants::InvalidCharacters::PathSeparators::RE
|
134
|
+
end
|
134
135
|
|
135
|
-
|
136
|
+
if options[:reject_path_name_separators]
|
136
137
|
|
137
|
-
|
138
|
-
|
138
|
+
return true if name =~ _Constants::InvalidCharacters::PathNameSeparators::RE
|
139
|
+
end
|
139
140
|
|
140
|
-
|
141
|
+
return true if name =~ _Constants::InvalidCharacters::Innate::RE
|
141
142
|
|
142
|
-
|
143
|
+
if '\\' == name[0] && '\\' == name[1]
|
143
144
|
|
144
|
-
|
145
|
-
|
145
|
+
return true if name !~ /^\\\\[^\\]+\\[^\\]+/
|
146
|
+
end
|
146
147
|
|
147
|
-
|
148
|
-
|
148
|
+
false
|
149
|
+
else
|
149
150
|
|
150
|
-
|
151
|
-
|
152
|
-
|
151
|
+
true
|
152
|
+
end
|
153
|
+
end
|
153
154
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
155
|
+
# Evaluates whether the given path is absolute, which means it is either
|
156
|
+
# rooted (begins with '/') or is homed (is '~' or begins with '~/')
|
157
|
+
#
|
158
|
+
# === Signature
|
159
|
+
#
|
160
|
+
# * *Parameters:*
|
161
|
+
# - +path+:: (String) The path to be evaluated. May not be +nil+
|
162
|
+
def path_is_absolute? path
|
162
163
|
|
163
|
-
|
164
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
164
165
|
|
165
|
-
|
166
|
+
return true if path_is_homed? path
|
166
167
|
|
167
|
-
|
168
|
+
vol, rem, _ = Internal_::Windows::Form.get_windows_volume(path)
|
168
169
|
|
169
|
-
|
170
|
-
|
170
|
+
return false unless vol
|
171
|
+
return false unless rem
|
171
172
|
|
172
|
-
|
173
|
-
|
173
|
+
Internal_::Windows::Form.char_is_path_name_separator? rem[0]
|
174
|
+
end
|
174
175
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
176
|
+
# Evaluates whether the given path is "letter drived", which means that
|
177
|
+
# it contains a drive specification. Given the two letter sequence 'X:'
|
178
|
+
# representing any ASCII letter (a-zA-Z) and a colon, this function
|
179
|
+
# recognises six sequences: +'X:'+, +'X:\'+, +'X:/'+, +'\\?\X:'+,
|
180
|
+
# +'\\?\X:\'+, +'\\?\X:/'+
|
181
|
+
#
|
182
|
+
# === Return
|
183
|
+
# - +nil+:: if it is not "drived";
|
184
|
+
# - 2:: it begins with the form +'X:'+
|
185
|
+
# - 3:: it begins with the form +'X:\'+ or +'X:/'+
|
186
|
+
# - 6:: it begins with the form +'\\?\X:'+
|
187
|
+
# - 7:: it begins with the form +'\\?\X:\'+ or +'\\?\X:/'+
|
188
|
+
def path_is_letter_drived? path
|
188
189
|
|
189
|
-
|
190
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
190
191
|
|
191
|
-
|
192
|
+
if path.size >= 2
|
192
193
|
|
193
|
-
|
194
|
+
base_index = 0
|
194
195
|
|
195
|
-
|
196
|
+
if '\\' == path[0]
|
196
197
|
|
197
|
-
|
198
|
+
if '\\' == path[1]
|
198
199
|
|
199
|
-
|
200
|
+
if '?' == path[2]
|
200
201
|
|
201
|
-
|
202
|
+
if '\\' == path[3]
|
202
203
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
204
|
+
base_index = 4
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
208
209
|
|
209
|
-
|
210
|
+
if ':' == path[base_index + 1]
|
210
211
|
|
211
|
-
|
212
|
+
if Internal_::Windows::Drive.character_is_drive_letter? path[base_index + 0]
|
212
213
|
|
213
|
-
|
214
|
+
if Internal_::Windows::Form.char_is_path_name_separator? path[base_index + 2]
|
214
215
|
|
215
|
-
|
216
|
-
|
216
|
+
return 4 == base_index ? 7 : 3
|
217
|
+
else
|
217
218
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
219
|
+
return 4 == base_index ? 6 : 2
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
223
224
|
|
224
|
-
|
225
|
-
|
225
|
+
nil
|
226
|
+
end
|
226
227
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
228
|
+
# Evaluates whether the given path is homed, which means it is '~' or
|
229
|
+
# begins with '~/' or '~\'
|
230
|
+
#
|
231
|
+
# === Signature
|
232
|
+
#
|
233
|
+
# * *Parameters:*
|
234
|
+
# - +path+:: (String) The path to be evaluated. May not be +nil+
|
235
|
+
def path_is_homed? path
|
235
236
|
|
236
|
-
|
237
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
237
238
|
|
238
|
-
|
239
|
+
return false unless '~' == path[0]
|
239
240
|
|
240
|
-
|
241
|
+
if path.size > 1
|
241
242
|
|
242
|
-
|
243
|
-
|
243
|
+
return Internal_::Windows::Form.char_is_path_name_separator? path[1]
|
244
|
+
end
|
244
245
|
|
245
|
-
|
246
|
-
|
246
|
+
true
|
247
|
+
end
|
247
248
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
249
|
+
# Evalutes whether the given path is rooted, which means it begins with
|
250
|
+
# '/'
|
251
|
+
#
|
252
|
+
# === Signature
|
253
|
+
#
|
254
|
+
# * *Parameters:*
|
255
|
+
# - +path+:: (String) The path to be evaluated. May not be +nil+
|
256
|
+
def path_is_rooted? path
|
256
257
|
|
257
|
-
|
258
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
258
259
|
|
259
|
-
|
260
|
-
|
260
|
+
case path[0]
|
261
|
+
when '/'
|
261
262
|
|
262
|
-
|
263
|
-
|
263
|
+
true
|
264
|
+
when '\\'
|
264
265
|
|
265
|
-
|
266
|
-
|
266
|
+
case path[1]
|
267
|
+
when '\\'
|
267
268
|
|
268
|
-
|
269
|
+
vol, rem, _ = Internal_::Windows::Form.get_windows_volume(path)
|
269
270
|
|
270
|
-
|
271
|
+
return false unless vol
|
271
272
|
|
272
|
-
|
273
|
+
if rem && Internal_::Windows::Form.char_is_path_name_separator?(rem[0])
|
273
274
|
|
274
|
-
|
275
|
-
|
275
|
+
true
|
276
|
+
else
|
276
277
|
|
277
|
-
|
278
|
-
|
279
|
-
|
278
|
+
false
|
279
|
+
end
|
280
|
+
else
|
280
281
|
|
281
|
-
|
282
|
-
|
283
|
-
|
282
|
+
true
|
283
|
+
end
|
284
|
+
else
|
284
285
|
|
285
|
-
|
286
|
+
if path.size > 2
|
286
287
|
|
287
|
-
|
288
|
+
if ':' == path[1]
|
288
289
|
|
289
|
-
|
290
|
+
if Internal_::Windows::Drive.character_is_drive_letter? path[0]
|
290
291
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
292
|
+
return Internal_::Windows::Form.char_is_path_name_separator? path[2]
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
295
296
|
|
296
|
-
|
297
|
-
|
298
|
-
|
297
|
+
false
|
298
|
+
end
|
299
|
+
end
|
299
300
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
301
|
+
# Evalutes whether the given path is UNC
|
302
|
+
#
|
303
|
+
# === Signature
|
304
|
+
#
|
305
|
+
# * *Parameters:*
|
306
|
+
# - +path+:: (String) The path to be evaluated. May not be +nil+
|
307
|
+
def path_is_UNC? path
|
307
308
|
|
308
|
-
|
309
|
+
Diagnostics.check_string_parameter(path, "path") if $DEBUG
|
309
310
|
|
310
|
-
|
311
|
-
|
311
|
+
return false unless '\\' == path[0]
|
312
|
+
return false unless '\\' == path[1]
|
312
313
|
|
313
|
-
|
314
|
+
_, _, frm = Internal_::Windows::Form.get_windows_volume(path)
|
314
315
|
|
315
|
-
|
316
|
-
|
316
|
+
case frm
|
317
|
+
when :form_2, :form_3, :form_4, :form_5, :form_6
|
317
318
|
|
318
|
-
|
319
|
-
|
319
|
+
true
|
320
|
+
else
|
320
321
|
|
321
|
-
|
322
|
-
|
323
|
-
|
322
|
+
false
|
323
|
+
end
|
324
|
+
end
|
324
325
|
|
325
326
|
end # module LibPath_Form_Windows_Methods
|
326
327
|
|
327
328
|
# @!visibility private
|
328
329
|
def self.extended receiver # :nodoc:
|
329
330
|
|
330
|
-
|
331
|
+
receiver.class_eval do
|
331
332
|
|
332
|
-
|
333
|
-
|
333
|
+
extend LibPath_Form_Windows_Methods
|
334
|
+
end
|
334
335
|
|
335
|
-
|
336
|
+
$stderr.puts "#{receiver} extended by #{LibPath_Form_Windows_Methods}" if $DEBUG
|
336
337
|
end
|
337
338
|
|
338
339
|
# @!visibility private
|
339
340
|
def self.included receiver # :nodoc:
|
340
341
|
|
341
|
-
|
342
|
+
receiver.class_eval do
|
342
343
|
|
343
|
-
|
344
|
-
|
344
|
+
include LibPath_Form_Windows_Methods
|
345
|
+
end
|
345
346
|
|
346
|
-
|
347
|
+
$stderr.puts "#{receiver} included #{LibPath_Form_Windows_Methods}" if $DEBUG
|
347
348
|
end
|
348
349
|
|
349
350
|
extend LibPath_Form_Windows_Methods
|
@@ -353,6 +354,6 @@ end # module Windows
|
|
353
354
|
end # module Form
|
354
355
|
end # module LibPath
|
355
356
|
|
356
|
-
# ############################## end of file ############################# #
|
357
357
|
|
358
|
+
# ############################## end of file ############################# #
|
358
359
|
|
data/lib/libpath/form.rb
CHANGED
@@ -3,40 +3,40 @@ require 'libpath/internal_/platform'
|
|
3
3
|
|
4
4
|
if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
|
5
5
|
|
6
|
-
|
6
|
+
require 'libpath/form/windows'
|
7
7
|
else
|
8
8
|
|
9
|
-
|
9
|
+
require 'libpath/form/unix'
|
10
10
|
end
|
11
11
|
|
12
12
|
module LibPath # :nodoc:
|
13
13
|
module Form # :nodoc:
|
14
14
|
|
15
|
-
|
15
|
+
if ::LibPath::Internal_::Platform::Constants::PLATFORM_IS_WINDOWS then
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
extend ::LibPath::Form::Windows
|
18
|
+
include ::LibPath::Form::Windows
|
19
|
+
else
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
extend ::LibPath::Form::Unix
|
22
|
+
include ::LibPath::Form::Unix
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
# @!visibility private
|
26
|
+
def self.extended receiver # :nodoc:
|
27
27
|
|
28
|
-
|
29
|
-
|
28
|
+
$stderr.puts "#{receiver} extended by #{self}" if $DEBUG
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
# @!visibility private
|
32
|
+
def self.included receiver # :nodoc:
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
$stderr.puts "#{receiver} included #{self}" if $DEBUG
|
35
|
+
end
|
36
36
|
|
37
37
|
end # module Form
|
38
38
|
end # module LibPath
|
39
39
|
|
40
|
-
# ############################## end of file ############################# #
|
41
40
|
|
41
|
+
# ############################## end of file ############################# #
|
42
42
|
|