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, #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:



51
52
53
54
55
56
57
58
# File 'lib/rfd.rb', line 51

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.



48
49
50
# File 'lib/rfd.rb', line 48

def command_line
  @command_line
end

#current_dirObject (readonly)

Returns the value of attribute current_dir.



48
49
50
# File 'lib/rfd.rb', line 48

def current_dir
  @current_dir
end

#current_pageObject (readonly)

Returns the value of attribute current_page.



48
49
50
# File 'lib/rfd.rb', line 48

def current_page
  @current_page
end

#current_rowObject (readonly)

Returns the value of attribute current_row.



48
49
50
# File 'lib/rfd.rb', line 48

def current_row
  @current_row
end

#current_zipObject (readonly)

Returns the value of attribute current_zip.



48
49
50
# File 'lib/rfd.rb', line 48

def current_zip
  @current_zip
end

#displayed_itemsObject (readonly)

Returns the value of attribute displayed_items.



48
49
50
# File 'lib/rfd.rb', line 48

def displayed_items
  @displayed_items
end

#header_lObject (readonly)

Returns the value of attribute header_l.



48
49
50
# File 'lib/rfd.rb', line 48

def header_l
  @header_l
end

#header_rObject (readonly)

Returns the value of attribute header_r.



48
49
50
# File 'lib/rfd.rb', line 48

def header_r
  @header_r
end

#itemsObject (readonly)

Returns the value of attribute items.



48
49
50
# File 'lib/rfd.rb', line 48

def items
  @items
end

#mainObject (readonly)

Returns the value of attribute main.



48
49
50
# File 'lib/rfd.rb', line 48

def main
  @main
end

Instance Method Details

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

Let the user answer y or n.

Parameters

  • prompt - Prompt message



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

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
    command_line.clear
    command_line.noutrefresh
    break (c == 'y') || (c == 'Y')
  end
end

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

Change the current directory.



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/rfd.rb', line 173

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)



235
236
237
238
239
240
241
242
243
244
# File 'lib/rfd.rb', line 235

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)



250
251
252
253
254
255
# File 'lib/rfd.rb', line 250

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



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

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.



508
509
510
# File 'lib/rfd.rb', line 508

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.



356
357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/rfd.rb', line 356

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.



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

def current_item
  items[current_row]
end

#deleteObject

Delete selected files and directories.



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/rfd.rb', line 430

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.



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

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.



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

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.



616
617
618
# File 'lib/rfd.rb', line 616

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.



683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
# File 'lib/rfd.rb', line 683

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.



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# File 'lib/rfd.rb', line 258

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.



276
277
278
279
# File 'lib/rfd.rb', line 276

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.



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

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)


585
586
587
# File 'lib/rfd.rb', line 585

def first_page?
  current_page == 0
end

#get_charObject

Get a char as a String from user input.



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

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



344
345
346
347
348
349
350
351
352
353
# File 'lib/rfd.rb', line 344

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)


590
591
592
# File 'lib/rfd.rb', line 590

def last_page?
  current_page == total_pages - 1
end

#lsObject

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



195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/rfd.rb', line 195

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.



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

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

#max_itemsObject

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



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

def max_items
  main.max_items
end

#maxyObject

Height of the currently active pane.



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

def maxy
  main.maxy
end

#mkdir(dir) ⇒ Object

Create a new directory.



449
450
451
452
453
454
455
456
457
458
# File 'lib/rfd.rb', line 449

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.



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/rfd.rb', line 151

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



730
731
732
733
734
735
# File 'lib/rfd.rb', line 730

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.



372
373
374
375
376
377
378
379
380
381
# File 'lib/rfd.rb', line 372

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.



485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/rfd.rb', line 485

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.



189
190
191
# File 'lib/rfd.rb', line 189

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

#process_command_line(preset_command: 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.



641
642
643
644
645
646
647
648
649
650
651
652
# File 'lib/rfd.rb', line 641

def process_command_line(preset_command: nil)
  prompt = preset_command ? ":#{preset_command} " : ':'
  command_line.set_prompt prompt
  cmd, *args = command_line.get_command(prompt: prompt).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.



655
656
657
658
659
660
661
662
663
664
665
# File 'lib/rfd.rb', line 655

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



387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/rfd.rb', line 387

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.



61
62
63
64
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
# File 'lib/rfd.rb', line 61

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
      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.



143
144
145
# File 'lib/rfd.rb', line 143

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
    


224
225
226
227
228
229
# File 'lib/rfd.rb', line 224

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.



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/rfd.rb', line 306

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.



120
121
122
123
# File 'lib/rfd.rb', line 120

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



603
604
605
606
607
# File 'lib/rfd.rb', line 603

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.



474
475
476
477
# File 'lib/rfd.rb', line 474

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

#timesObject

Number of times to repeat the next command.



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

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

#toggle_markObject

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



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

def toggle_mark
  main.toggle_mark current_item
end

#total_pagesObject

Number of pages in the current directory.



595
596
597
# File 'lib/rfd.rb', line 595

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

#touch(filename) ⇒ Object

Create a new empty file.



461
462
463
464
465
466
467
468
469
470
471
# File 'lib/rfd.rb', line 461

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
end

#trashObject

Soft delete selected files and directories.

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



413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/rfd.rb', line 413

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.



533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
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
# File 'lib/rfd.rb', line 533

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.



708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# File 'lib/rfd.rb', line 708

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.



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

def yank
  @yanked_items = selected_items
end

#zip(zipfile_name) ⇒ Object

Archive selected files and directories into a .zip file.



513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
# File 'lib/rfd.rb', line 513

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