Class: Rfd::Controller

Inherits:
Object
  • Object
show all
Includes:
Commands
Defined in:
lib/rfd.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Commands

#-, #/, #C, #D, #F, #G, #H, #K, #L, #M, #O, #S, #T, #a, #c, #click, #ctrl_a, #ctrl_b, #ctrl_f, #ctrl_l, #ctrl_n, #ctrl_p, #ctrl_w, #d, #del, #double_click, #e, #enter, #f, #g, #h, #j, #k, #l, #m, #n, #o, #p, #q, #q!, #r, #s, #space, #t, #u, #v, #w, #y, #z

Constructor Details

#initializeController

:nodoc:



55
56
57
58
59
60
61
62
# File 'lib/rfd.rb', line 55

def initialize
  @main = MainWindow.new
  @header_l = HeaderLeftWindow.new
  @header_r = HeaderRightWindow.new
  @command_line = CommandLineWindow.new
  @debug = DebugWindow.new if ENV['DEBUG']
  @direction, @dir_history, @last_command, @times, @yanked_items = nil, [], nil, nil, nil
end

Instance Attribute Details

#command_lineObject (readonly)

Returns the value of attribute command_line.



52
53
54
# File 'lib/rfd.rb', line 52

def command_line
  @command_line
end

#current_dirObject (readonly)

Returns the value of attribute current_dir.



52
53
54
# File 'lib/rfd.rb', line 52

def current_dir
  @current_dir
end

#current_pageObject (readonly)

Returns the value of attribute current_page.



52
53
54
# File 'lib/rfd.rb', line 52

def current_page
  @current_page
end

#current_rowObject (readonly)

Returns the value of attribute current_row.



52
53
54
# File 'lib/rfd.rb', line 52

def current_row
  @current_row
end

#current_zipObject (readonly)

Returns the value of attribute current_zip.



52
53
54
# File 'lib/rfd.rb', line 52

def current_zip
  @current_zip
end

#displayed_itemsObject (readonly)

Returns the value of attribute displayed_items.



52
53
54
# File 'lib/rfd.rb', line 52

def displayed_items
  @displayed_items
end

#header_lObject (readonly)

Returns the value of attribute header_l.



52
53
54
# File 'lib/rfd.rb', line 52

def header_l
  @header_l
end

#header_rObject (readonly)

Returns the value of attribute header_r.



52
53
54
# File 'lib/rfd.rb', line 52

def header_r
  @header_r
end

#itemsObject (readonly)

Returns the value of attribute items.



52
53
54
# File 'lib/rfd.rb', line 52

def items
  @items
end

#mainObject (readonly)

Returns the value of attribute main.



52
53
54
# File 'lib/rfd.rb', line 52

def main
  @main
end

Instance Method Details

#ask(prompt = '(y/n)') ⇒ Object

Let the user answer y or n.

Parameters

  • prompt - Prompt message



688
689
690
691
692
693
694
695
696
# File 'lib/rfd.rb', line 688

def ask(prompt = '(y/n)')
  command_line.set_prompt prompt
  command_line.refresh
  while (c = Curses.getch)
    next unless [?N, ?Y, ?n, ?y, 3, 27] .include? c  # N, Y, n, y, ^c, esc
    clear_command_line
    break (c == 'y') || (c == 'Y')
  end
end

#cd(dir = '~', pushd: true) ⇒ Object

Change the current directory.



178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/rfd.rb', line 178

def cd(dir = '~', pushd: true)
  dir = load_item path: expand_path(dir) unless dir.is_a? Item
  unless dir.zip?
    Dir.chdir dir
    @current_zip = nil
  else
    @current_zip = dir
  end
  @dir_history << current_dir if current_dir && pushd
  @current_dir, @current_page, @current_row = dir, 0, nil
  main.activate_pane 0
  ls
  @current_dir
end

#chmod(mode = nil) ⇒ Object

Change the file permission of the selected files and directories.

Parameters

  • mode - Unix chmod string (e.g. +w, g-r, 755, 0644)



240
241
242
243
244
245
246
247
248
249
# File 'lib/rfd.rb', line 240

def chmod(mode = nil)
  return unless mode
  begin
    Integer mode
    mode = Integer mode.size == 3 ? "0#{mode}" : mode
  rescue ArgumentError
  end
  FileUtils.chmod mode, selected_items.map(&:path)
  ls
end

#chown(user_and_group) ⇒ Object

Change the file owner of the selected files and directories.

Parameters

  • user_and_group - user name and group name separated by : (e.g. alice, nobody:nobody, :admin)



255
256
257
258
259
260
# File 'lib/rfd.rb', line 255

def chown(user_and_group)
  return unless user_and_group
  user, group = user_and_group.split(':').map {|s| s == '' ? nil : s}
  FileUtils.chown user, group, selected_items.map(&:path)
  ls
end

#clear_command_lineObject



647
648
649
650
651
# File 'lib/rfd.rb', line 647

def clear_command_line
  command_line.writeln 0, ""
  command_line.clear
  command_line.noutrefresh
end

#clipboardObject

Copy selected files and directories’ path into clipboard on OSX.



524
525
526
# File 'lib/rfd.rb', line 524

def clipboard
  IO.popen('pbcopy', 'w') {|f| f << selected_items.map(&:path).join(' ')} if osx?
end

#cp(dest) ⇒ Object

Copy selected files and directories to the destination.



361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/rfd.rb', line 361

def cp(dest)
  unless in_zip?
    src = (m = marked_items).any? ? m.map(&:path) : current_item
    FileUtils.cp_r src, expand_path(dest)
  else
    raise 'cping multiple items in .zip is not supported.' if selected_items.size > 1
    Zip::File.open(current_zip) do |zip|
      entry = zip.find_entry(selected_items.first.name).dup
      entry.name, entry.name_length = dest, dest.size
      zip.instance_variable_get(:@entry_set) << entry
    end
  end
  ls
end

#current_itemObject

The file or directory on which the cursor is on.



136
137
138
# File 'lib/rfd.rb', line 136

def current_item
  items[current_row]
end

#deleteObject

Delete selected files and directories.



435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
# File 'lib/rfd.rb', line 435

def delete
  unless in_zip?
    FileUtils.rm_rf selected_items.map(&:path)
  else
    Zip::File.open(current_zip) do |zip|
      zip.select {|e| selected_items.map(&:name).include? e.to_s}.each do |entry|
        if entry.name_is_directory?
          zip.dir.delete entry.to_s
        else
          zip.file.delete entry.to_s
        end
      end
    end
  end
  @current_row -= selected_items.count {|i| i.index <= current_row}
  ls
end

#draw_itemsObject

Update the main window with the loaded files and directories. Also update the header.



303
304
305
306
307
308
# File 'lib/rfd.rb', line 303

def draw_items
  main.newpad items
  @displayed_items = items[current_page * max_items, max_items]
  main.display current_page
  header_l.draw_path_and_page_number path: current_dir.path, current: current_page + 1, total: total_pages
end

#draw_marked_itemsObject

Update the header information concerning currently marked files or directories.



626
627
628
629
# File 'lib/rfd.rb', line 626

def draw_marked_items
  items = marked_items
  header_r.draw_marked_items count: items.size, size: items.inject(0) {|sum, i| sum += i.size}
end

#draw_total_itemsObject

Update the header information concerning total files and directories in the current directory.



632
633
634
# File 'lib/rfd.rb', line 632

def draw_total_items
  header_r.draw_total_items count: items.size, size: items.inject(0) {|sum, i| sum += i.size}
end

#editObject

Open current file or directory with the editor.



699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
# File 'lib/rfd.rb', line 699

def edit
  execute_external_command do
    editor = ENV['EDITOR'] || 'vim'
    unless in_zip?
      system %Q[#{editor} "#{current_item.path}"]
    else
      begin
        tmpdir, tmpfile_name = nil
        Zip::File.open(current_zip) do |zip|
          tmpdir = Dir.mktmpdir
          FileUtils.mkdir_p File.join(tmpdir, File.dirname(current_item.name))
          tmpfile_name = File.join(tmpdir, current_item.name)
          File.open(tmpfile_name, 'w') {|f| f.puts zip.file.read(current_item.name)}
          system %Q[#{editor} "#{tmpfile_name}"]
          zip.add(current_item.name, tmpfile_name) { true }
        end
        ls
      ensure
        FileUtils.remove_entry_secure tmpdir if tmpdir
      end
    end
  end
end

#fetch_items_from_filesystem_or_zipObject

Fetch files from current directory or current .zip file.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/rfd.rb', line 263

def fetch_items_from_filesystem_or_zip
  unless in_zip?
    @items = Dir.foreach(current_dir).map {|fn|
      load_item dir: current_dir, name: fn
    }.to_a.partition {|i| %w(. ..).include? i.name}.flatten
  else
    @items = [load_item(dir: current_dir, name: '.', stat: File.stat(current_dir)),
      load_item(dir: current_dir, name: '..', stat: File.stat(File.dirname(current_dir)))]
    zf = Zip::File.new current_dir
    zf.each {|entry|
      next if entry.name_is_directory?
      stat = zf.file.stat entry.name
      @items << load_item(dir: current_dir, name: entry.name, stat: stat)
    }
  end
end

#find(str) ⇒ Object

Focus at the first file or directory of which name starts with the given String.



281
282
283
284
# File 'lib/rfd.rb', line 281

def find(str)
  index = items.index {|i| i.index > current_row && i.name.start_with?(str)} || items.index {|i| i.name.start_with? str}
  move_cursor index if index
end

#find_reverse(str) ⇒ Object

Focus at the last file or directory of which name starts with the given String.



287
288
289
290
# File 'lib/rfd.rb', line 287

def find_reverse(str)
  index = items.reverse.index {|i| i.index < current_row && i.name.start_with?(str)} || items.reverse.index {|i| i.name.start_with? str}
  move_cursor items.size - index - 1 if index
end

#first_page?Boolean

Current page is the first page?

Returns:

  • (Boolean)


601
602
603
# File 'lib/rfd.rb', line 601

def first_page?
  current_page == 0
end

#get_charObject

Get a char as a String from user input.



642
643
644
645
# File 'lib/rfd.rb', line 642

def get_char
  c = Curses.getch
  c if (0..255) === c.ord
end

#grep(pattern = '.*') ⇒ Object

Search files and directories from the current directory, and update the screen.

  • pattern - Search pattern against file names in Ruby Regexp string.

Example

a : Search files that contains the letter “a” in their file name .*.pdf$ : Search PDF files



349
350
351
352
353
354
355
356
357
358
# File 'lib/rfd.rb', line 349

def grep(pattern = '.*')
  regexp = Regexp.new(pattern)
  fetch_items_from_filesystem_or_zip
  @items = items.shift(2) + items.select {|i| i.name =~ regexp}
  sort_items_according_to_current_direction
  draw_items
  draw_total_items
  switch_page 0
  move_cursor 0
end

#last_page?Boolean

Do we have more pages?

Returns:

  • (Boolean)


606
607
608
# File 'lib/rfd.rb', line 606

def last_page?
  current_page == total_pages - 1
end

#lsObject

Fetch files from current directory. Then update each windows reflecting the newest information.



200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/rfd.rb', line 200

def ls
  fetch_items_from_filesystem_or_zip
  sort_items_according_to_current_direction

  @current_page ||= 0
  draw_items
  move_cursor (current_row ? [current_row, items.size - 1].min : nil)

  draw_marked_items
  draw_total_items
  true
end

#marked_itemsObject

  • marked files and directories.



141
142
143
# File 'lib/rfd.rb', line 141

def marked_items
  items.select(&:marked?)
end

#max_itemsObject

Number of files or directories that the current main window can show in a page.



298
299
300
# File 'lib/rfd.rb', line 298

def max_items
  main.max_items
end

#maxyObject

Height of the currently active pane.



293
294
295
# File 'lib/rfd.rb', line 293

def maxy
  main.maxy
end

#mkdir(dir) ⇒ Object

Create a new directory.



454
455
456
457
458
459
460
461
462
463
# File 'lib/rfd.rb', line 454

def mkdir(dir)
  unless in_zip?
    FileUtils.mkdir_p current_dir.join(dir)
  else
    Zip::File.open(current_zip) do |zip|
      zip.dir.mkdir dir
    end
  end
  ls
end

#move_cursor(row = nil) ⇒ Object

Move the cursor to specified row.

The main window and the headers will be updated reflecting the displayed files and directories. The row number can be out of range of the current page.



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/rfd.rb', line 156

def move_cursor(row = nil)
  if row
    if (prev_item = items[current_row])
      main.draw_item prev_item
    end
    page = row / max_items
    switch_page page if page != current_page
    main.activate_pane row / maxy
    @current_row = row
  else
    @current_row = 0
  end

  item = items[current_row]
  main.draw_item item, current: true
  main.display current_page

  header_l.draw_current_file_info item
  @current_row
end

#move_cursor_by_click(y: nil, x: nil) ⇒ Object



746
747
748
749
750
751
# File 'lib/rfd.rb', line 746

def move_cursor_by_click(y: nil, x: nil)
  if (idx = main.pane_index_at(y: y, x: x))
    row = current_page * max_items + main.maxy * idx + y - main.begy
    move_cursor row if (row >= 0) && (row < items.size)
  end
end

#mv(dest) ⇒ Object

Move selected files and directories to the destination.



377
378
379
380
381
382
383
384
385
386
# File 'lib/rfd.rb', line 377

def mv(dest)
  unless in_zip?
    src = (m = marked_items).any? ? m.map(&:path) : current_item
    FileUtils.mv src, expand_path(dest)
  else
    raise 'mving multiple items in .zip is not supported.' if selected_items.size > 1
    rename "#{selected_items.first.name}/#{dest}"
  end
  ls
end

#pasteObject

Paste yanked files / directories here.



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
# File 'lib/rfd.rb', line 501

def paste
  if @yanked_items
    if current_item.directory?
      FileUtils.cp_r @yanked_items.map(&:path), current_item
    else
      @yanked_items.each do |item|
        if items.include? item
          i = 1
          while i += 1
            new_item = load_item dir: current_dir, name: "#{item.basename}_#{i}#{item.extname}", stat: item.stat
            break unless File.exist? new_item.path
          end
          FileUtils.cp_r item, new_item
        else
          FileUtils.cp_r item, current_dir
        end
      end
    end
    ls
  end
end

#popdObject

cd to the previous directory.



194
195
196
# File 'lib/rfd.rb', line 194

def popd
  cd @dir_history.pop, pushd: false if @dir_history.any?
end

#process_command_line(preset_command: nil, default_argument: nil) ⇒ Object

Accept user input, and directly execute it as a Ruby method call to the controller.

Parameters

  • preset_command - A command that would be displayed at the command line before user input.

  • default_argument - A default argument for the command.



658
659
660
661
662
663
664
665
666
667
668
669
# File 'lib/rfd.rb', line 658

def process_command_line(preset_command: nil, default_argument: nil)
  prompt = preset_command ? ":#{preset_command} " : ':'
  command_line.set_prompt prompt
  cmd, *args = command_line.get_command(prompt: prompt, default: default_argument).split(' ')
  if cmd && !cmd.empty? && respond_to?(cmd)
    ret = self.public_send cmd, *args
    clear_command_line
    ret
  end
rescue Interrupt
  clear_command_line
end

#process_shell_commandObject

Accept user input, and directly execute it in an external shell.



672
673
674
675
676
677
678
679
680
681
682
# File 'lib/rfd.rb', line 672

def process_shell_command
  command_line.set_prompt ':!'
  cmd = command_line.get_command(prompt: ':!')[1..-1]
  execute_external_command pause: true do
    system cmd
  end
rescue Interrupt
ensure
  command_line.clear
  command_line.noutrefresh
end

#rename(pattern) ⇒ Object

Rename selected files and directories.

Parameters

  • pattern - new filename, or a shash separated Regexp like string



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/rfd.rb', line 392

def rename(pattern)
  from, to = pattern.sub(/^\//, '').sub(/\/$/, '').split '/'
  if to.nil?
    from, to = current_item.name, from
  else
    from = Regexp.new from
  end
  unless in_zip?
    selected_items.each do |item|
      name = item.name.gsub from, to
      FileUtils.mv item, current_dir.join(name) if item.name != name
    end
  else
    Zip::File.open(current_zip) do |zip|
      selected_items.each do |item|
        name = item.name.gsub from, to
        zip.rename item.name, name
      end
    end
  end
  ls
end

#runObject

The main loop.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rfd.rb', line 65

def run
  loop do
    begin
      number_pressed = false
      ret = case (c = Curses.getch)
      when 10, 13  # enter, return
        enter
      when 27  # ESC
        q
      when ' '  # space
        space
      when 127  # DEL
        del
      when Curses::KEY_DOWN
        j
      when Curses::KEY_UP
        k
      when Curses::KEY_LEFT
        h
      when Curses::KEY_RIGHT
        l
      when Curses::KEY_CTRL_A..Curses::KEY_CTRL_Z
        chr = ((c - 1 + 65) ^ 0b0100000).chr
        public_send "ctrl_#{chr}" if respond_to?("ctrl_#{chr}")
      when ?0..?9
        public_send c
        number_pressed = true
      when ?!..?~
        if respond_to? c
          public_send c
        else
          debug "key: #{c}" if ENV['DEBUG']
        end
      when Curses::KEY_MOUSE
        if (mouse_event = Curses.getmouse)
          case mouse_event.bstate
          when Curses::BUTTON1_CLICKED
            click y: mouse_event.y, x: mouse_event.x
          when Curses::BUTTON1_DOUBLE_CLICKED
            double_click y: mouse_event.y, x: mouse_event.x
          end
        end
      else
        debug "key: #{c}" if ENV['DEBUG']
      end
      Curses.doupdate if ret
      @times = nil unless number_pressed
    rescue StopIteration
      raise
    rescue => e
      Rfd.logger.error e if Rfd.logger
      command_line.show_error e.to_s
      raise if ENV['DEBUG']
    end
  end
ensure
  Curses.close_screen
end

#selected_itemsObject

Marked files and directories or Array(the current file or directory).

. and .. will not be included.



148
149
150
# File 'lib/rfd.rb', line 148

def selected_items
  ((m = marked_items).any? ? m : Array(current_item)).reject {|i| %w(. ..).include? i.name}
end

#sort(direction = nil) ⇒ Object

Sort the whole files and directories in the current directory, then refresh the screen.

Parameters

  • direction - Sort order in a String.

    nil   : order by name
    r     : reverse order by name
    s, S  : order by file size
    sr, Sr: reverse order by file size
    t     : order by mtime
    tr    : reverse order by mtime
    c     : order by ctime
    cr    : reverse order by ctime
    u     : order by atime
    ur    : reverse order by atime
    e     : order by extname
    er    : reverse order by extname
    


229
230
231
232
233
234
# File 'lib/rfd.rb', line 229

def sort(direction = nil)
  @direction, @current_page = direction, 0
  sort_items_according_to_current_direction
  switch_page 0
  move_cursor 0
end

#sort_items_according_to_current_directionObject

Sort the loaded files and directories in already given sort order.



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# File 'lib/rfd.rb', line 311

def sort_items_according_to_current_direction
  case @direction
  when nil
    @items = items.shift(2) + items.partition(&:directory?).flat_map(&:sort)
  when 'r'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort.reverse}
  when 'S', 's'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by {|i| -i.size}}
  when 'Sr', 'sr'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:size)}
  when 't'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.mtime <=> x.mtime}}
  when 'tr'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:mtime)}
  when 'c'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.ctime <=> x.ctime}}
  when 'cr'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:ctime)}
  when 'u'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.atime <=> x.atime}}
  when 'ur'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:atime)}
  when 'e'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort {|x, y| y.extname <=> x.extname}}
  when 'er'
    @items = items.shift(2) + items.partition(&:directory?).flat_map {|arr| arr.sort_by(&:extname)}
  end
  items.each.with_index {|item, index| item.index = index}
end

#spawn_panes(num) ⇒ Object

Change the number of columns in the main window.



125
126
127
128
# File 'lib/rfd.rb', line 125

def spawn_panes(num)
  main.number_of_panes = num
  @current_row = @current_page = 0
end

#switch_page(page) ⇒ Object

Move to the given page number.

Parameters

  • page - Target page number



619
620
621
622
623
# File 'lib/rfd.rb', line 619

def switch_page(page)
  main.display (@current_page = page)
  @displayed_items = items[current_page * max_items, max_items]
  header_l.draw_path_and_page_number path: current_dir.path, current: current_page + 1, total: total_pages
end

Create a symlink to the current file or directory.



481
482
483
484
# File 'lib/rfd.rb', line 481

def symlink(name)
  FileUtils.ln_s current_item, name
  ls
end

#timesObject

Number of times to repeat the next command.



131
132
133
# File 'lib/rfd.rb', line 131

def times
  (@times || 1).to_i
end

#toggle_markObject

Swktch on / off marking on the current file or directory.



637
638
639
# File 'lib/rfd.rb', line 637

def toggle_mark
  main.toggle_mark current_item
end

#total_pagesObject

Number of pages in the current directory.



611
612
613
# File 'lib/rfd.rb', line 611

def total_pages
  (items.size - 1) / max_items + 1
end

#touch(filename) ⇒ Object

Create a new empty file.



466
467
468
469
470
471
472
473
474
475
476
477
478
# File 'lib/rfd.rb', line 466

def touch(filename)
  unless in_zip?
    FileUtils.touch current_dir.join(filename)
  else
    Zip::File.open(current_zip) do |zip|
      # zip.file.open(filename, 'w') {|_f| }  #HAXX this code creates an unneeded temporary file
      zip.instance_variable_get(:@entry_set) << Zip::Entry.new(current_zip, filename)
    end
  end

  ls
  move_cursor items.index {|i| i.name == filename}
end

#touch_t(timestamp) ⇒ Object

Change the timestamp of the selected files and directories.

Parameters

  • timestamp - A string that can be parsed with ‘Time.parse`. Note that this parameter is not compatible with UNIX `touch -t`.



490
491
492
493
# File 'lib/rfd.rb', line 490

def touch_t(timestamp)
  FileUtils.touch selected_items, mtime: Time.parse(timestamp)
  ls
end

#trashObject

Soft delete selected files and directories.

If the OS is not OSX, performs the same as ‘delete` command.



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
# File 'lib/rfd.rb', line 418

def trash
  unless in_zip?
    if osx?
      FileUtils.mv selected_items.map(&:path), File.expand_path('~/.Trash/')
    else
      #TODO support other OS
      FileUtils.rm_rf selected_items.map(&:path)
    end
  else
    return unless ask %Q[Trashing zip entries is not supported. Actually the files will be deleted. Are you sure want to proceed? (y/n)]
    delete
  end
  @current_row -= selected_items.count {|i| i.index <= current_row}
  ls
end

#unarchiveObject

Unarchive .zip and .tar.gz files within selected files and directories into current_directory.



549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
# File 'lib/rfd.rb', line 549

def unarchive
  unless in_zip?
    zips, gzs = selected_items.partition(&:zip?).tap {|z, others| break [z, *others.partition(&:gz?)]}
    zips.each do |item|
      FileUtils.mkdir_p current_dir.join(item.basename)
      Zip::File.open(item) do |zip|
        zip.each do |entry|
          FileUtils.mkdir_p File.join(item.basename, File.dirname(entry.to_s))
          zip.extract(entry, File.join(item.basename, entry.to_s)) { true }
        end
      end
    end
    gzs.each do |item|
      Zlib::GzipReader.open(item) do |gz|
        Gem::Package::TarReader.new(gz) do |tar|
          dest_dir = current_dir.join (gz.orig_name || item.basename).sub(/\.tar$/, '')
          tar.each do |entry|
            dest = nil
            if entry.full_name == '././@LongLink'
              dest = File.join dest_dir, entry.read.strip
              next
            end
            dest ||= File.join dest_dir, entry.full_name
            if entry.directory?
              FileUtils.mkdir_p dest, mode: entry.header.mode
            elsif entry.file?
              FileUtils.mkdir_p dest_dir
              File.open(dest, 'wb') {|f| f.print entry.read}
              FileUtils.chmod entry.header.mode, dest
            elsif entry.header.typeflag == '2'  # symlink
              File.symlink entry.header.linkname, dest
            end
            unless Dir.exist? dest_dir
              FileUtils.mkdir_p dest_dir
              File.open(File.join(dest_dir, gz.orig_name || item.basename), 'wb') {|f| f.print gz.read}
            end
          end
        end
      end
    end
  else
    Zip::File.open(current_zip) do |zip|
      zip.select {|e| selected_items.map(&:name).include? e.to_s}.each do |entry|
        FileUtils.mkdir_p File.join(current_zip.dir, current_zip.basename, File.dirname(entry.to_s))
        zip.extract(entry, File.join(current_zip.dir, current_zip.basename, entry.to_s)) { true }
      end
    end
  end
  ls
end

#viewObject

Open current file or directory with the viewer.



724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
# File 'lib/rfd.rb', line 724

def view
  pager = ENV['PAGER'] || 'less'
  execute_external_command do
    unless in_zip?
      system %Q[#{pager} "#{current_item.path}"]
    else
      begin
        tmpdir, tmpfile_name = nil
        Zip::File.open(current_zip) do |zip|
          tmpdir = Dir.mktmpdir
          FileUtils.mkdir_p File.join(tmpdir, File.dirname(current_item.name))
          tmpfile_name = File.join(tmpdir, current_item.name)
          File.open(tmpfile_name, 'w') {|f| f.puts zip.file.read(current_item.name)}
        end
        system %Q[#{pager} "#{tmpfile_name}"]
      ensure
        FileUtils.remove_entry_secure tmpdir if tmpdir
      end
    end
  end
end

#yankObject

Yank selected file / directory names.



496
497
498
# File 'lib/rfd.rb', line 496

def yank
  @yanked_items = selected_items
end

#zip(zipfile_name) ⇒ Object

Archive selected files and directories into a .zip file.



529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/rfd.rb', line 529

def zip(zipfile_name)
  return unless zipfile_name
  zipfile_name += '.zip' unless zipfile_name.end_with? '.zip'

  Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
    selected_items.each do |item|
      next if item.symlink?
      if item.directory?
        Dir[item.join('**/**')].each do |file|
          zipfile.add file.sub("#{current_dir}/", ''), file
        end
      else
        zipfile.add item.name, item
      end
    end
  end
  ls
end