rfs 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +7 -0
- data/Rakefile.rb +193 -0
- data/bin/RenameFileSet.rbw +14 -0
- data/bin/rfs.rb +214 -0
- data/bin/rfsd.rb +8 -0
- data/description.txt +24 -0
- data/lib/RenameFileSet.bak.rb +372 -0
- data/lib/errors.rb +15 -0
- data/lib/filters.rb +68 -0
- data/lib/gui.rb +216 -0
- data/lib/innate/array.rb +104 -0
- data/lib/innate/coverage.rb +315 -0
- data/lib/innate/debug.rb +12 -0
- data/lib/innate/debugger.rb +944 -0
- data/lib/innate/file.rb +5 -0
- data/lib/innate/filelines.rb +148 -0
- data/lib/innate/kernel.rb +41 -0
- data/lib/innate/metaid.rb +30 -0
- data/lib/innate/mkdirs.rb +12 -0
- data/lib/innate/regexp.rb +80 -0
- data/lib/innate/reload.rb +11 -0
- data/lib/innate/roman.rb +72 -0
- data/lib/innate/scriptlines.rb +60 -0
- data/lib/innate/string.rb +56 -0
- data/lib/innate/test/all_tests.rb +11 -0
- data/lib/innate/test/files/mkdirs_dummy.file +1 -0
- data/lib/innate/test/files/reloadtarget.rb +5 -0
- data/lib/innate/test/files/reloadtarget1.rb +5 -0
- data/lib/innate/test/files/reloadtarget2.rb +5 -0
- data/lib/innate/test/test_coverage.rb +13 -0
- data/lib/innate/test/testarray.rb +98 -0
- data/lib/innate/test/testfile.rb +15 -0
- data/lib/innate/test/testfilelines.rb +106 -0
- data/lib/innate/test/testkernel.rb +30 -0
- data/lib/innate/test/testmkdirs.rb +19 -0
- data/lib/innate/test/testregexp.rb +86 -0
- data/lib/innate/test/testreload.rb +61 -0
- data/lib/innate/test/testroman.rb +54 -0
- data/lib/innate/test/testscriptlines.rb +39 -0
- data/lib/innate/test/teststring.rb +52 -0
- data/lib/innate/test/testtitlecase.rb +20 -0
- data/lib/innate/titlecase.rb +64 -0
- data/lib/innate/tracerequire.rb +22 -0
- data/lib/namesource.rb +53 -0
- data/lib/options.rb +64 -0
- data/lib/providers.rb +93 -0
- data/lib/regexp.rb +56 -0
- data/lib/rename_functions.rb +142 -0
- data/lib/renamer.rb +210 -0
- data/lib/results.rb +83 -0
- data/lib/test/test_helper.rb +147 -0
- data/tests/dir/file1 +0 -0
- data/tests/dir/file2 +0 -0
- data/tests/dir/file3 +0 -0
- data/tests/test_helper.rb +147 -0
- data/tests/test_rename_functions.rb +605 -0
- metadata +105 -0
data/lib/regexp.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
class PrepareRegexp
|
2
|
+
def initialize(pattern)
|
3
|
+
@s = pattern
|
4
|
+
end
|
5
|
+
|
6
|
+
#def new_regexp(pattern)
|
7
|
+
#begin
|
8
|
+
#Regexp.new(pattern)
|
9
|
+
#end
|
10
|
+
|
11
|
+
def create(ignore_case, verbose, roman, &b)
|
12
|
+
begin
|
13
|
+
c = verbose ? VerboseRegexp : Regexp
|
14
|
+
o = ignore_case ? Regexp::IGNORECASE : nil
|
15
|
+
s = roman ? @s.gsub('(roman)', "(#{Regexp.ROMAN_PATTERN})") : @s
|
16
|
+
r = c.new s, o
|
17
|
+
r.result_proc = b if verbose
|
18
|
+
r
|
19
|
+
rescue
|
20
|
+
b.call :error, "Error parsing regular expression:\n#{$!}"
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.each(options)
|
26
|
+
options[:filter].each do |f|
|
27
|
+
if f.regexp.is_a? PrepareRegexp
|
28
|
+
f.regexp = yield :filter, f.regexp
|
29
|
+
end
|
30
|
+
end
|
31
|
+
options.each do |k, v|
|
32
|
+
if v.is_a? PrepareRegexp
|
33
|
+
options[k] = yield k, v
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class VerboseRegexp < Regexp
|
40
|
+
attr_accessor :k
|
41
|
+
attr_accessor :result_proc
|
42
|
+
|
43
|
+
def match(*a)
|
44
|
+
m = super
|
45
|
+
s = "MatchData breakdown for #{@k}: "
|
46
|
+
if m
|
47
|
+
s += "\n" + m.display_breakdown
|
48
|
+
else
|
49
|
+
s += 'no match'
|
50
|
+
end
|
51
|
+
@result_proc.call :match, s
|
52
|
+
m
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
module RenameFunctions
|
3
|
+
protected
|
4
|
+
def rename_replace
|
5
|
+
rename_each_match do |file, md|
|
6
|
+
subst(file, md, opt(:replace, String))
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def rename_name_source
|
11
|
+
rename_each_match do |file, md, path|
|
12
|
+
subst(file, md, opt(:source, NameSource).name(file, md, path))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def rename_add
|
17
|
+
if opt(:add, Numeric) > 0
|
18
|
+
opt(:file_provider, Provider::File::Base).reverse
|
19
|
+
end
|
20
|
+
rename_each_match do |file, md|
|
21
|
+
int = active_capture(md).to_i rescue int = active_capture(md) rescue int = 0
|
22
|
+
subst(file, md, (int + opt(:add, Numeric)).to_s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def rename_count
|
27
|
+
count = 0
|
28
|
+
rename_each_match do |file, md|
|
29
|
+
count += 1
|
30
|
+
subst(file, md, count.to_s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def rename_from_full_name
|
35
|
+
r = opt(:pattern, Regexp)
|
36
|
+
rename_each_match do |file, md, path|
|
37
|
+
fn = File.expand_path(File.join(path, file))
|
38
|
+
m = r.match(fn)
|
39
|
+
verbose {"match on fullname: #{m.display}"}
|
40
|
+
if m
|
41
|
+
subst(file, md, active_capture(m))
|
42
|
+
else
|
43
|
+
[:message, "no match in full name: #{fn}"]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def rename_move_up
|
49
|
+
rename_each_match do |file, md, path|
|
50
|
+
p = File.split(path).first
|
51
|
+
unless p == '\\'
|
52
|
+
[:full_path, File.join(p, file)]
|
53
|
+
else
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def rename_roman
|
60
|
+
s = @options[:search]
|
61
|
+
if s.is_a? Regexp
|
62
|
+
@options[:search] = s.interpolate_roman
|
63
|
+
verbose {"search: #{@options[:search].to_s}"}
|
64
|
+
end
|
65
|
+
rename_each_match do |file, md|
|
66
|
+
subst(file, md, active_capture(md).toggle_roman) if active_capture(md).length > 0
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def rename_mark_change
|
71
|
+
prev = ''
|
72
|
+
rename_each_match do |file, md|
|
73
|
+
m = active_capture(md)
|
74
|
+
unless prev == m
|
75
|
+
prev = m
|
76
|
+
subst(file, opt(:replace_pattern, Regexp).match(file), opt(:replace_text, String))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def rename_tape_numbers
|
82
|
+
rename_each_match do |file, md|
|
83
|
+
t = active_capture(md)
|
84
|
+
n = t.to_i * 2 + (/a/i =~ t[-1,1] ? -1 : 0)
|
85
|
+
n += 2 if n < 1
|
86
|
+
subst(file, md, n.to_s)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def rename_capitalize
|
91
|
+
al = opt(:always_lower, Array) rescue %w{ vol
|
92
|
+
mp3 wav txt avi nfo pdf rar
|
93
|
+
zip rb ape sfk mpg rm qt doc }
|
94
|
+
l = opt(:lower, Array) rescue String.LOWER
|
95
|
+
dc = opt(:double_cap) rescue String.DOUBLE_CAP
|
96
|
+
ca = opt(:cap_after) rescue String.CAP_AFTER
|
97
|
+
sp = opt(:split_pattern) rescue String.SPLIT_PATTERN
|
98
|
+
u = opt(:upper) rescue String.UPPER
|
99
|
+
|
100
|
+
rename_each_match do |file, md|
|
101
|
+
o = active_capture(md)
|
102
|
+
r = o.title_case(:always_lower => al,
|
103
|
+
:lower => l,
|
104
|
+
:double_cap => dc,
|
105
|
+
:cap_after => ca,
|
106
|
+
:split_pattern => sp,
|
107
|
+
:upper => u)
|
108
|
+
subst(file, md, r)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def rename_fill
|
113
|
+
fillRE = opt :fill, Regexp
|
114
|
+
fillText = ''
|
115
|
+
verbose {"fill pattern: #{fillRE.to_s}"}
|
116
|
+
rename_each do |file, md|
|
117
|
+
if md or fillText
|
118
|
+
m = active_capture(md) if md
|
119
|
+
if md and m.length > 0 and not (fillRE and fillRE.match(file))
|
120
|
+
fillText = m
|
121
|
+
[:message, "source: #{file}"]
|
122
|
+
else
|
123
|
+
md = fillRE.match(file)
|
124
|
+
if md
|
125
|
+
[:rename, subst(file, md, fillText)]
|
126
|
+
else
|
127
|
+
[:message, "* No capture to fill * #{file}"]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def rename_remove
|
135
|
+
rename_each_match do |file, md, path|
|
136
|
+
:delete
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
|
142
|
+
|
data/lib/renamer.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'innate/mkdirs'
|
2
|
+
require 'innate/roman'
|
3
|
+
require 'innate/titlecase'
|
4
|
+
require 'providers'
|
5
|
+
require 'rename_functions'
|
6
|
+
require 'stringio'
|
7
|
+
require 'errors'
|
8
|
+
require 'options'
|
9
|
+
require 'filters'
|
10
|
+
require 'regexp'
|
11
|
+
require 'namesource'
|
12
|
+
|
13
|
+
class Renamer
|
14
|
+
def run(name, options, &block)
|
15
|
+
@result_proc = block
|
16
|
+
@options = options
|
17
|
+
method(name).call
|
18
|
+
end
|
19
|
+
|
20
|
+
def rename_functions
|
21
|
+
methods.grep(/^rename_/)
|
22
|
+
end
|
23
|
+
|
24
|
+
include RenameFunctions
|
25
|
+
include Options
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
def subst(src, matchdata, repl)
|
30
|
+
return src unless matchdata
|
31
|
+
matchdata.sub repl, options[:capture_num]
|
32
|
+
end
|
33
|
+
|
34
|
+
def result(type, t, path = nil, file = nil)
|
35
|
+
@result_proc.call type, t.to_s, path, file
|
36
|
+
end
|
37
|
+
|
38
|
+
def verbose
|
39
|
+
@result_proc.call(:verbose, yield) if @options[:verbose]
|
40
|
+
end
|
41
|
+
|
42
|
+
def actual_rename(o, n)
|
43
|
+
File.mkdirs File.split(n).first
|
44
|
+
if File.directory? n
|
45
|
+
raise DirectoryExistsError.new("Can't overwrite an existing directory.")
|
46
|
+
else
|
47
|
+
File.rename(o, n)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def actual_delete(fn)
|
52
|
+
if File.directory? fn
|
53
|
+
Dir.delete fn
|
54
|
+
else
|
55
|
+
File.delete fn
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def confirm(path, oldfn, newfn, full_path)
|
60
|
+
return true if @confirm == :all || !@options[:confirm]
|
61
|
+
exists = @options[:force] && already_exists?(path, oldfn, newfn, full_path)
|
62
|
+
@confirm = opt(:confirm, Proc, Method).call path, oldfn, newfn, full_path, exists, (@confirm || :yes)
|
63
|
+
verbose {"confirmation: #{@confirm}"}
|
64
|
+
case @confirm
|
65
|
+
when :all, :yes
|
66
|
+
return true
|
67
|
+
when :no
|
68
|
+
return false
|
69
|
+
when :cancel
|
70
|
+
throw :cancel
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def dest_file(path, newfn, full_path)
|
75
|
+
if full_path
|
76
|
+
fn = newfn
|
77
|
+
else
|
78
|
+
fn = File.join(path, newfn)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def active_capture(matchdata)
|
83
|
+
n = @options[:capture_num] || 1
|
84
|
+
matchdata[n] || matchdata[0]
|
85
|
+
end
|
86
|
+
|
87
|
+
def already_exists?(path, oldfn, newfn, full_path)
|
88
|
+
fn = dest_file(path, newfn, full_path)
|
89
|
+
if File.exists? fn
|
90
|
+
fn.downcase != File.join(path, oldfn).downcase
|
91
|
+
else
|
92
|
+
false
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def rename(path, oldfn, newfn, full_path = false)
|
97
|
+
old_full = File.join path, oldfn
|
98
|
+
verbose {"rename: `#{old_full}` -> `#{newfn}`"}
|
99
|
+
unless oldfn == newfn or (full_path and old_full == newfn)
|
100
|
+
t = newfn
|
101
|
+
fn = dest_file path, newfn, full_path
|
102
|
+
if !@options[:force] and already_exists?(path, oldfn, newfn, full_path)
|
103
|
+
result :fileerror, 'File already exists', path, oldfn
|
104
|
+
elsif @options[:action] == :commit
|
105
|
+
if confirm path, oldfn, newfn, full_path
|
106
|
+
begin
|
107
|
+
actual_rename old_full, fn
|
108
|
+
rescue => e
|
109
|
+
result :fileerror, "#{e.class}: #{e.message}", path, oldfn
|
110
|
+
else
|
111
|
+
result :ok, t, path, oldfn
|
112
|
+
end
|
113
|
+
else
|
114
|
+
result :skipped, oldfn, path, oldfn
|
115
|
+
end
|
116
|
+
else
|
117
|
+
result :ok, t, path, oldfn
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def delete(path, file)
|
123
|
+
fn = File.join path, file
|
124
|
+
verbose {"delete: `#{fn}` "}
|
125
|
+
if @options[:action] == :commit
|
126
|
+
if confirm path, file, '<<deleted>>', false
|
127
|
+
begin
|
128
|
+
actual_delete fn
|
129
|
+
rescue => e
|
130
|
+
result :fileerror, "#{e.class}: #{e.message}", path, file
|
131
|
+
else
|
132
|
+
result :ok, "<<#{file}>>", path, file
|
133
|
+
end
|
134
|
+
else
|
135
|
+
result :skipped, file, path, file
|
136
|
+
end
|
137
|
+
else
|
138
|
+
result :ok, "<<#{file}>>", path, file
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def rename_each
|
143
|
+
if @options[:action] == :list
|
144
|
+
@options[:file_provider].each do |path, file|
|
145
|
+
opt_if(:filter, Filter) do |filter|
|
146
|
+
unless filter.filter path, file
|
147
|
+
verbose { "filtered: #{file}" }
|
148
|
+
next
|
149
|
+
end
|
150
|
+
end
|
151
|
+
result :ok, "#{file}#{(File.directory?(File.join(path, file)) ? '/' : '')}", path, file
|
152
|
+
end
|
153
|
+
elsif @options[:action] == :commit || @options[:action] == :preview
|
154
|
+
catch :cancel do
|
155
|
+
search = opt :search, Regexp
|
156
|
+
filter = opt_if(:filter, Filter)
|
157
|
+
@options[:file_provider].each do |path, file|
|
158
|
+
verbose {'-------------------------'}
|
159
|
+
if filter; unless filter.filter path, file
|
160
|
+
verbose { "filtered: #{file}" }
|
161
|
+
next
|
162
|
+
end end
|
163
|
+
verbose {"file: #{File.join path, file}"}
|
164
|
+
md = search.match(file)
|
165
|
+
verbose {"match: #{md.display}"}
|
166
|
+
begin
|
167
|
+
action, text = r = yield(file, md, path)
|
168
|
+
verbose {"action: #{action.inspect}"}
|
169
|
+
if action == :rename
|
170
|
+
rename path, file, text if text
|
171
|
+
elsif action == :full_path
|
172
|
+
rename path, file, text, true if text
|
173
|
+
elsif action == :message
|
174
|
+
result :message, text, path, file
|
175
|
+
elsif r == :delete
|
176
|
+
delete path, file
|
177
|
+
elsif r
|
178
|
+
result :fileerror, "Unrecognised response: #{r.inspect}", path, file
|
179
|
+
end
|
180
|
+
rescue => e
|
181
|
+
if e.is_a? RenamerError or (!$DEBUG and !$TESTING)
|
182
|
+
t = file
|
183
|
+
if md
|
184
|
+
t = md.display
|
185
|
+
end
|
186
|
+
result :fileerror, "Error: #{e.class} on #{t}\n#{e.message}", path, file
|
187
|
+
else
|
188
|
+
raise e
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
else
|
194
|
+
raise ActionError.new
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def rename_each_match
|
199
|
+
rename_each do |file, md, path|
|
200
|
+
r = yield(file, md, path) if md
|
201
|
+
if r.is_a? String
|
202
|
+
[:rename, r]
|
203
|
+
else
|
204
|
+
r
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
|
data/lib/results.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
class CollectResults
|
2
|
+
attr_accessor :verbose, :quiet, :silent, :sideways, :lineways, :sorted, :list
|
3
|
+
attr_accessor :out
|
4
|
+
attr_reader :results
|
5
|
+
|
6
|
+
def initialize(out_io)
|
7
|
+
@verbose = false
|
8
|
+
@quiet = false
|
9
|
+
@silent = false
|
10
|
+
@sideways = false
|
11
|
+
@lineways = false
|
12
|
+
@sorted = false
|
13
|
+
@list = false
|
14
|
+
|
15
|
+
@out = out_io
|
16
|
+
|
17
|
+
@results = Hash.new do |h, k|
|
18
|
+
case k when :fileerror, :ok, :skipped
|
19
|
+
h[k] = Hash.new {|hh, kk| hh[kk] = []}
|
20
|
+
else
|
21
|
+
h[k] = []
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def output(mode, text, path = nil, file = nil)
|
27
|
+
if @verbose
|
28
|
+
@out.puts "#{mode}: #{text}"
|
29
|
+
else
|
30
|
+
case mode when :ok, :skipped
|
31
|
+
@results[mode][path] << text
|
32
|
+
when :fileerror
|
33
|
+
@results[mode][path] << "#{file}: #{text}"
|
34
|
+
else
|
35
|
+
@results[mode] << text
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def display
|
41
|
+
result = ""
|
42
|
+
unless @silent
|
43
|
+
m = @sideways ? :rows : :columns
|
44
|
+
ok = 0
|
45
|
+
skipped = 0
|
46
|
+
errors = 0
|
47
|
+
@results.each do |mode, value|
|
48
|
+
case mode when :fileerror, :ok, :skipped
|
49
|
+
value.collect.sort.each do |k, v|
|
50
|
+
case mode when :ok: ok += v.length
|
51
|
+
when :skipped: skipped += v.length
|
52
|
+
when :fileerror: errors += v.length
|
53
|
+
end
|
54
|
+
s = "\n#{File.expand_path(k)}"
|
55
|
+
s += ": #{mode.to_s.upcase} - #{v.length} files" unless @quiet or @list
|
56
|
+
@out.puts s
|
57
|
+
v = v.sort if @sorted
|
58
|
+
if @lineways
|
59
|
+
@out.puts v.join("\n")
|
60
|
+
else
|
61
|
+
@out.puts v.send(m, 80)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
else
|
65
|
+
@out.puts "\n#{mode.to_s.upcase}"
|
66
|
+
if @lineways
|
67
|
+
@out.puts value.join("\n")
|
68
|
+
else
|
69
|
+
@out.puts value.send(m, 80)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
unless @quiet
|
74
|
+
@out.print "\nOK: #{ok}" if ok.nonzero?
|
75
|
+
@out.print "\nSKIPPED: #{skipped}" if skipped.nonzero?
|
76
|
+
@out.print "\nERRORS: #{errors}" if errors.nonzero?
|
77
|
+
@out.puts "\nTOTAL: #{ok + skipped + errors}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|