directory_paradise 1.4.5

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,682 @@
1
+ #!/usr/bin/ruby -w
2
+ # Encoding: UTF-8
3
+ # frozen_string_literal: true
4
+ # =========================================================================== #
5
+ # === DirectoryParadise::Content
6
+ #
7
+ # This class can be used to keep track of the content of a directory.
8
+ #
9
+ # It can not directly be used to report its findings; if you need that
10
+ # functionality then please use the class defined in the file
11
+ # directory_paradise/report/report.rb instead.
12
+ #
13
+ # One goal of this class is to be as fast as possible. Because of this
14
+ # goal, and because this class will not directly report anything to
15
+ # the user, ever, it may not use colours.
16
+ #
17
+ # Usage example:
18
+ #
19
+ # require 'directory_paradise'
20
+ #
21
+ # DirectoryParadise::Content.new(ARGV)
22
+ #
23
+ # =========================================================================== #
24
+ # require 'directory_paradise/content/content.rb'
25
+ # =========================================================================== #
26
+ require 'directory_paradise/base/base.rb'
27
+
28
+ module DirectoryParadise
29
+
30
+ class Content < Base # === DirectoryParadise::Content
31
+
32
+ require 'etc'
33
+ require 'directory_paradise/content/constants.rb'
34
+
35
+ begin
36
+ require 'roebe/classes/permission_ascii_format.rb'
37
+ rescue LoadError; end
38
+
39
+ # ========================================================================= #
40
+ # === initialize
41
+ # ========================================================================= #
42
+ def initialize(
43
+ work_on_this_directory = ARGV,
44
+ run_already = true
45
+ )
46
+ reset
47
+ set_work_on_this_directory(
48
+ work_on_this_directory
49
+ )
50
+ run if run_already
51
+ end
52
+
53
+ # ========================================================================= #
54
+ # === reset (reset tag)
55
+ # ========================================================================= #
56
+ def reset
57
+ # ======================================================================= #
58
+ # === @internal_hash
59
+ # ======================================================================= #
60
+ @internal_hash = {}
61
+ # ======================================================================= #
62
+ # === :show_the_full_path
63
+ #
64
+ # If the next variable is true then the full filepath will be shown.
65
+ # ======================================================================= #
66
+ @internal_hash[:show_the_full_path] = true
67
+ # ======================================================================= #
68
+ # === :ignore_dot_only_entries
69
+ #
70
+ # If this variable is true then we will not keep '.' and '..'
71
+ # entries.
72
+ # ======================================================================= #
73
+ @internal_hash[:ignore_dot_only_entries] = true
74
+ # ======================================================================= #
75
+ # === :work_on_this_directory
76
+ #
77
+ # The variable :work_on_this_directory must always "point" to the
78
+ # directory we are interested in; this will be the directory that
79
+ # potentially contains other files, directories or symlinks. It
80
+ # will default to the current working directory on startup, so
81
+ # it must never be undefined really.
82
+ # ======================================================================= #
83
+ set_work_on_this_directory(return_pwd)
84
+ end
85
+
86
+ # ========================================================================= #
87
+ # === obtain_all_entries_as_specified_by_work_on_this_directory (obtain tag)
88
+ #
89
+ # This method will obtain through all entries found in the given
90
+ # directory, specified by :work_on_this_directory.
91
+ #
92
+ # Note that this method will NOT do any sorting at all whatsoever by
93
+ # default. If you need to sort then you should use the method
94
+ # called .do_sort().
95
+ # ========================================================================= #
96
+ def obtain_all_entries_as_specified_by_work_on_this_directory(
97
+ _ = work_on_this_directory?
98
+ )
99
+ _ = _.first if _.is_a? Array
100
+ case _ # case tag
101
+ # ======================================================================= #
102
+ # === :pwd
103
+ # ======================================================================= #
104
+ when :pwd,
105
+ :return_pwd,
106
+ :default,
107
+ nil
108
+ _ = return_pwd
109
+ # ======================================================================= #
110
+ # === :pwd_or_first_argument
111
+ # ======================================================================= #
112
+ when :pwd_or_first_argument
113
+ if @commandline_arguments.empty?
114
+ _ = return_pwd
115
+ else
116
+ _ = @commandline_arguments.first
117
+ end
118
+ end
119
+ # ======================================================================= #
120
+ # Make sure that a trailing '/' is used.
121
+ # ======================================================================= #
122
+ unless _.end_with? '/'
123
+ _ = _.dup if _.frozen?
124
+ _ << '/'
125
+ end
126
+ entries = Dir.glob(
127
+ "#{_}*",
128
+ File::FNM_DOTMATCH
129
+ )
130
+ set_content(entries)
131
+ sanitize_the_content
132
+ end; alias obtain obtain_all_entries_as_specified_by_work_on_this_directory # === obtain
133
+ alias determine_entries obtain_all_entries_as_specified_by_work_on_this_directory # === determine_entries
134
+ alias obtain_directory_listing obtain_all_entries_as_specified_by_work_on_this_directory # === obtain_directory_listing
135
+ alias gather obtain_all_entries_as_specified_by_work_on_this_directory # === gather
136
+ alias gather_content obtain_all_entries_as_specified_by_work_on_this_directory # === gather_content
137
+ alias run_main_loop obtain_all_entries_as_specified_by_work_on_this_directory # === run_main_loop
138
+
139
+ # ========================================================================= #
140
+ # === internal_hash?
141
+ # ========================================================================= #
142
+ def internal_hash?
143
+ @internal_hash
144
+ end; alias hash internal_hash? # === hash
145
+ alias hash? internal_hash? # === hash?
146
+
147
+ # ========================================================================= #
148
+ # === reset_the_main_hash
149
+ # ========================================================================= #
150
+ def reset_the_main_hash
151
+ @internal_hash = {} # This Hash will store everything.
152
+ end
153
+
154
+ # ========================================================================= #
155
+ # === set_work_on_this_directory
156
+ # ========================================================================= #
157
+ def set_work_on_this_directory(
158
+ i = :default
159
+ )
160
+ i = i.first if i.is_a? Array
161
+ case i
162
+ # ======================================================================= #
163
+ # === :default
164
+ # ======================================================================= #
165
+ when :default,
166
+ nil
167
+ i = return_pwd
168
+ end
169
+ i = i.dup if i.frozen?
170
+ if File.directory?(i) and !i.end_with?('/')
171
+ i << '/'
172
+ end
173
+ i.squeeze!('/') if i.include? '//'
174
+ @internal_hash[:work_on_this_directory] = i
175
+ end; alias set_input set_work_on_this_directory # === set_input
176
+ alias set_target_directory set_work_on_this_directory # === set_target_directory
177
+
178
+ # ========================================================================= #
179
+ # === work_on_this_directory?
180
+ # ========================================================================= #
181
+ def work_on_this_directory?
182
+ @internal_hash[:work_on_this_directory]
183
+ end; alias input_directory? work_on_this_directory? # === input_directory?
184
+ alias input? work_on_this_directory? # === input?
185
+ alias target? work_on_this_directory? # === target?
186
+ alias input_dir? work_on_this_directory? # === input_dir?
187
+ alias from_which_directory? work_on_this_directory? # === from_which_directory?
188
+ alias work_on_which_directory? work_on_this_directory? # === work_on_which_directory?
189
+
190
+ # ========================================================================= #
191
+ # === use_current_directory
192
+ #
193
+ # This method will guarantee that the class will work with the current
194
+ # working directory, as-is.
195
+ # ========================================================================= #
196
+ def use_current_directory
197
+ set_work_on_this_directory(return_pwd)
198
+ end
199
+
200
+ # ========================================================================= #
201
+ # === sanitize_the_content
202
+ #
203
+ # This method will analyse information about the various entries in
204
+ # a given directory. It will turn the Array that should be stored at
205
+ # @internal_hash[:content] into a Hash.
206
+ #
207
+ # We need to keep in mind that a user may wish to show hidden files.
208
+ #
209
+ # The available entries for our main Hash are:
210
+ #
211
+ # :size
212
+ # :type
213
+ # :is_executable
214
+ # :gid
215
+ # :inode_number
216
+ # :uid
217
+ # :access_mode
218
+ # :last_modified
219
+ # :name_of_owner
220
+ # :name_of_group
221
+ # :ascii_representation
222
+ #
223
+ # ========================================================================= #
224
+ def sanitize_the_content(
225
+ i = content?
226
+ )
227
+ _ = {} # Our new Hash.
228
+ # ======================================================================= #
229
+ # Specify some variables that will be checked against in the next
230
+ # .each clause.
231
+ # ======================================================================= #
232
+ show_the_full_path = show_the_full_path?
233
+ i.each {|name_of_the_handle|
234
+ _[name_of_the_handle] = {}
235
+ # ===================================================================== #
236
+ # Next, we will populate our main hash here with the required
237
+ # datastructures.
238
+ # ===================================================================== #
239
+ # ===================================================================== #
240
+ # === :size
241
+ #
242
+ # This is also called the file_size.
243
+ # ===================================================================== #
244
+ _[name_of_the_handle][:size] = File.size?(name_of_the_handle)
245
+ # ===================================================================== #
246
+ # === :type
247
+ #
248
+ # This is also called "file_type" or "filetype".
249
+ # ===================================================================== #
250
+ _[name_of_the_handle][:type] = File.ftype(name_of_the_handle)
251
+ # ===================================================================== #
252
+ # === :is_executable
253
+ # ===================================================================== #
254
+ _[name_of_the_handle][:is_executable] = File.executable?(name_of_the_handle)
255
+ # ===================================================================== #
256
+ # Next, retain the full path, if it was so determined to.
257
+ # ===================================================================== #
258
+ if show_the_full_path
259
+ path = File.absolute_path(name_of_the_handle)
260
+ else
261
+ path = File.basename(name_of_the_handle)
262
+ end
263
+ path.squeeze!('/')
264
+ unless path.end_with? '/'
265
+ if File.directory?(path)
266
+ path = path.dup if path.frozen?
267
+ path << '/'
268
+ end
269
+ end
270
+ if path
271
+ unless File.exist? path
272
+ e "#{rev}No file exists at the target location `#{path}`."
273
+ end
274
+ if File.directory?(path) and !path.end_with?('/')
275
+ path << '/'
276
+ end
277
+ end
278
+ # ===================================================================== #
279
+ # === :path
280
+ #
281
+ # Keep track of the path-entry here, by assigning to :path. This is
282
+ # also known as the "filename".
283
+ # ===================================================================== #
284
+ _[name_of_the_handle][:path] = path
285
+ # ===================================================================== #
286
+ # === :ascii_representation
287
+ # ===================================================================== #
288
+ _[name_of_the_handle][:ascii_representation] = return_ascii_from(name_of_the_handle)
289
+ # ===================================================================== #
290
+ # Next, work with a stat object:
291
+ # ===================================================================== #
292
+ begin
293
+ stat_object = File.stat(path)
294
+ # =================================================================== #
295
+ # === :last_modified
296
+ #
297
+ # This is mtime, also known as the "modification_time."
298
+ # =================================================================== #
299
+ _[name_of_the_handle][:last_modified] = File.mtime(name_of_the_handle)
300
+ # =================================================================== #
301
+ # === :gid
302
+ # =================================================================== #
303
+ _[name_of_the_handle][:gid] = stat_object.gid
304
+ # =================================================================== #
305
+ # === :inode_number
306
+ # =================================================================== #
307
+ _[name_of_the_handle][:inode_number] = stat_object.ino
308
+ # =================================================================== #
309
+ # === :uid
310
+ # =================================================================== #
311
+ _[name_of_the_handle][:uid] = stat_object.uid
312
+ # =================================================================== #
313
+ # === :access_mode
314
+ #
315
+ # This is, for example, "644". It is also called "permissions".
316
+ # =================================================================== #
317
+ _[name_of_the_handle][:access_mode] = stat_object.mode.to_s(8)[3..5]
318
+ # =================================================================== #
319
+ # === :name_of_owner
320
+ #
321
+ # This is also called "inode_owner".
322
+ # =================================================================== #
323
+ _[name_of_the_handle][:name_of_owner] = return_owner(stat_object.uid)
324
+ # =================================================================== #
325
+ # On windows, the following may fail. Thus we guard against it
326
+ # next.
327
+ # =================================================================== #
328
+ if is_on_windows?
329
+ _[name_of_the_handle][:name_of_group] = UNKNOWN
330
+ else
331
+ begin
332
+ group_id = Etc.getgrgid(stat_object.gid)
333
+ if _
334
+ _[name_of_the_handle][:name_of_group] = group_id.name
335
+ end
336
+ rescue ArgumentError # This is an error such as "can't find group for 1107"
337
+ _[name_of_the_handle][:name_of_group] = UNKNOWN
338
+ end
339
+ end
340
+ rescue Exception => error
341
+ unless is_on_windows?
342
+ e 'An error occurred:'
343
+ pp error
344
+ end
345
+ end
346
+ }
347
+ @internal_hash[:content] = _
348
+ end; alias do_analyse_the_discovered_entries sanitize_the_content # === do_analyse_the_discovered_entries
349
+ alias populate_hash sanitize_the_content # === populate_hash
350
+
351
+ # ========================================================================= #
352
+ # === return_ascii_from
353
+ #
354
+ # r if reading is permitted, - if it is not.
355
+ # w if writing is permitted, - if it is not.
356
+ # x if execution is permitted, - if it is not.
357
+ #
358
+ # ========================================================================= #
359
+ def return_ascii_from(i)
360
+ if Object.const_defined?(:Roebe) and
361
+ Roebe.const_defined?(:PermissionAsciiFormat)
362
+ Roebe::PermissionAsciiFormat[i]
363
+ else
364
+ ''
365
+ end
366
+ end
367
+
368
+ # ========================================================================= #
369
+ # === show_the_full_path?
370
+ # ========================================================================= #
371
+ def show_the_full_path?
372
+ @internal_hash[:show_the_full_path]
373
+ end
374
+
375
+ # ========================================================================= #
376
+ # === directories?
377
+ #
378
+ # This method will return all directories found in the main directory-
379
+ # ========================================================================= #
380
+ def directories?
381
+ entries?.select {|key, value| value[:type] == 'directory' }.keys
382
+ end
383
+
384
+ # ========================================================================= #
385
+ # === retain_only_the_directories
386
+ #
387
+ # This method will filter away everything but the directories.
388
+ # ========================================================================= #
389
+ def retain_only_the_directories
390
+ entries?.select! {|key, value| value[:type] == 'directory' }
391
+ end
392
+
393
+ # ========================================================================= #
394
+ # === menu (menu tag)
395
+ # ========================================================================= #
396
+ def menu(
397
+ i = work_on_which_directory?
398
+ )
399
+ if i.is_a? Array
400
+ i.each {|entry| menu(entry) }
401
+ else
402
+ case i
403
+ # ======================================================================= #
404
+ # === --detail
405
+ # ======================================================================= #
406
+ when /^-?-?detail\??$/
407
+ set_work_on_this_directory(return_pwd)
408
+ run
409
+ end
410
+ end
411
+ end
412
+
413
+ # ========================================================================= #
414
+ # === reject_hidden_files
415
+ # ========================================================================= #
416
+ def reject_hidden_files
417
+ entries?.reject! {|key, value| value[:path].include?('/.') }
418
+ end
419
+
420
+ # ========================================================================= #
421
+ # === ignore_backups
422
+ # ========================================================================= #
423
+ def ignore_backups
424
+ entries?.reject! {|key, value| value[:path].end_with? '~' }
425
+ end
426
+
427
+ # ========================================================================= #
428
+ # === retain_only_the_symlinks
429
+ #
430
+ # This method will filter away everything but the symlinks.
431
+ # ========================================================================= #
432
+ def retain_only_the_symlinks
433
+ entries?.select! {|key, value| value[:type] == 'link' }
434
+ end
435
+
436
+ # ========================================================================= #
437
+ # === files?
438
+ # ========================================================================= #
439
+ def files?
440
+ entries?.select {|key, value| value[:type] == 'file' }.keys
441
+ end
442
+
443
+ # ========================================================================= #
444
+ # === symlinks?
445
+ #
446
+ # This method will return all symlink entries found in the main
447
+ # directory.
448
+ # ========================================================================= #
449
+ def symlinks?
450
+ entries?.select {|key, value| value[:type] == 'link' }.keys
451
+ end
452
+
453
+ # ========================================================================= #
454
+ # === do_sort (sort tag)
455
+ #
456
+ # This is the specific sorting-action of this class. Whenever you want
457
+ # to sort the kept dataset, make use of this method.
458
+ # ========================================================================= #
459
+ def do_sort(
460
+ sort_how = :default
461
+ )
462
+ case sort_how.to_sym # We only respond to Symbols.
463
+ # ======================================================================= #
464
+ # === :size
465
+ # ======================================================================= #
466
+ when :size, # Sort by size here.
467
+ :by_size
468
+ _ = entries?.sort_by {|filename, value| value[:size] }.reverse
469
+ _ = Hash[_]
470
+ set_content(_)
471
+ # ======================================================================= #
472
+ # === :default
473
+ #
474
+ # Default sorting will refer to date-based sorting, aka the modification
475
+ # date.
476
+ # ======================================================================= #
477
+ when :default,
478
+ :date, # Sort by date here. The modification date.
479
+ :sort_by_date
480
+ _ = entries?.sort_by {|filename, value| value[:last_modified] }.reverse
481
+ # ===================================================================== #
482
+ # This will be e. g.:
483
+ #
484
+ # ["/Depot/j/BLACKCHA.TXT", {:size=>2652,
485
+ #
486
+ # but we need a Hash.
487
+ # ===================================================================== #
488
+ _ = Hash[_]
489
+ set_content(_)
490
+ # ======================================================================= #
491
+ # === modification_time
492
+ # ======================================================================= #
493
+ when :modification_time
494
+ _ = entries?.sort_by {|entry| entry[4] }.reverse
495
+ _ = Hash[_]
496
+ set_content(_)
497
+ # ======================================================================= #
498
+ # === :reversed
499
+ # ======================================================================= #
500
+ when :reversed
501
+ _ = entries?.sort_by {|filename, value| filename }.reverse
502
+ _ = Hash[_]
503
+ set_content(_)
504
+ else
505
+ e 'Unknown sorting mode: '+sort_how.to_s
506
+ end
507
+ end; alias consider_sorting_by do_sort # === consider_sorting_by
508
+
509
+ # ========================================================================= #
510
+ # === set_content
511
+ # ========================================================================= #
512
+ def set_content(i)
513
+ @internal_hash[:content] = i
514
+ end; alias set_entries set_content # === set_entries
515
+
516
+ # ========================================================================= #
517
+ # === do_ignore_dot_only_entries
518
+ #
519
+ # This is the method that will remove '.' and '..' from the entries.
520
+ # This will only be done if specified to do so, via the reset()
521
+ # method.
522
+ # ========================================================================= #
523
+ def do_ignore_dot_only_entries
524
+ # ======================================================================= #
525
+ # Next eliminate rid of '.' and '..'
526
+ # ======================================================================= #
527
+ content?.reject! { |entry|
528
+ entry =~ /\.{1,2}$/ # Remove "." and ".." entries here.
529
+ }
530
+ end
531
+
532
+ # ========================================================================= #
533
+ # === n_entries?
534
+ # ========================================================================= #
535
+ def n_entries?
536
+ directory_content?.size
537
+ end
538
+
539
+ # ========================================================================= #
540
+ # === clear_the_old_content
541
+ # ========================================================================= #
542
+ def clear_the_old_content
543
+ @internal_hash[:content] = {}
544
+ end
545
+
546
+ # ========================================================================= #
547
+ # === first_entry?
548
+ # ========================================================================= #
549
+ def first_entry?
550
+ return content?.keys.first
551
+ end
552
+
553
+ # ========================================================================= #
554
+ # === ignore_dot_only_entries?
555
+ # ========================================================================= #
556
+ def ignore_dot_only_entries?
557
+ @internal_hash[:ignore_dot_only_entries]
558
+ end
559
+
560
+ # ========================================================================= #
561
+ # === consider_discarding_dot_only_entries
562
+ #
563
+ # This method should be called by external classes, rather than be the
564
+ # default.
565
+ # ========================================================================= #
566
+ def consider_discarding_dot_only_entries
567
+ if ignore_dot_only_entries?
568
+ do_ignore_dot_only_entries
569
+ end
570
+ end
571
+
572
+ # ========================================================================= #
573
+ # === obtain_entries_from
574
+ #
575
+ # This is similar to obtain_all_entries_as_specified_by_work_on_this_directory,
576
+ # but it will also set @internal_hash[:work_on_this_directory].
577
+ # ========================================================================= #
578
+ def obtain_entries_from(i)
579
+ # ======================================================================= #
580
+ # Re-set the internal "pointer" next, by updating the relevant
581
+ # variable that keeps track of the directory we are working on.
582
+ # ======================================================================= #
583
+ @internal_hash[:work_on_this_directory] = i
584
+ determine_entries
585
+ end; alias determine_the_entries_from_this_directory obtain_entries_from # === determine_the_entries_from_this_directory
586
+ alias obtain_all_entries_in_that_directory obtain_entries_from # === obtain_all_entries_in_that_directory
587
+ alias obtain_from obtain_entries_from # === obtain_from
588
+
589
+ # ========================================================================= #
590
+ # === build_up_a_hash_from_these_files
591
+ #
592
+ # The input argument should be an Array.
593
+ # ========================================================================= #
594
+ def build_up_a_hash_from_these_files(these_files)
595
+ clear_the_old_content
596
+ sanitize_the_content(these_files)
597
+ end
598
+
599
+ # ========================================================================= #
600
+ # === content?
601
+ # ========================================================================= #
602
+ def content?
603
+ @internal_hash[:content]
604
+ end; alias content content? # === content
605
+ alias entries? content? # === entries?
606
+ alias entries content? # === entries
607
+ alias directory_content? content? # === directory_content?
608
+ alias return_entries content? # === return_entries
609
+ alias data? content? # === data?
610
+ alias data content? # === data
611
+ alias results? content? # === results?
612
+
613
+ # ========================================================================= #
614
+ # === keys?
615
+ # ========================================================================= #
616
+ def keys?
617
+ content?.keys
618
+ end; alias keys keys? # === keys
619
+
620
+ # ========================================================================= #
621
+ # == filesize?
622
+ #
623
+ # This method will return the total filesize of all entries.
624
+ # ========================================================================= #
625
+ def filesize?
626
+ result = 0
627
+ content?.each_pair {|key, hash| result += hash[:size] }
628
+ return result
629
+ end
630
+
631
+ # ========================================================================= #
632
+ # === run (run tag)
633
+ # ========================================================================= #
634
+ def run
635
+ menu
636
+ obtain_all_entries_as_specified_by_work_on_this_directory
637
+ end
638
+
639
+ # ========================================================================= #
640
+ # === DirectoryContent.return_entries
641
+ # ========================================================================= #
642
+ def self.return_entries(from)
643
+ new(from).return_entries
644
+ end
645
+
646
+ # ========================================================================= #
647
+ # === DirectoryParadise::Content[]
648
+ # ========================================================================= #
649
+ def self.[](i = ARGV)
650
+ new(i)
651
+ end
652
+
653
+ end; end
654
+
655
+ if __FILE__ == $PROGRAM_NAME
656
+ require 'colours/autoinclude'
657
+ _ = DirectoryParadise::Content.new(ARGV) # or DirectoryParadise::Content[]
658
+ e rev+
659
+ steelblue(_.n_entries?.to_s)+' entries were found in this directory.'
660
+ e 'The first entry is: '+royalblue(_.first_entry?)
661
+ # ========================================================================= #
662
+ # Next follows some debug-code:
663
+ # ========================================================================= #
664
+ # pp _.entries?
665
+ # _.do_sort :by_size
666
+ # pp _.entries?
667
+ # pp _.directories?
668
+ # _.retain_only_the_directories
669
+ # pp _.directories?
670
+ # pp _.entries?.size
671
+ # pp _.entries?
672
+ # pp _.hash?
673
+ # pp _.directory_content?
674
+ #pp _.entries?
675
+ # _.consider_sorting_by :sort_by_date
676
+ #pp _.entries?
677
+ # e 'Symlinks:'
678
+ # pp _.symlinks?
679
+ #pp _
680
+ end # dcontent
681
+ # dcontent /home/Temp
682
+ # dcontent /Depot/jjj