tefil 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGES +13 -2
- data/Gemfile +4 -3
- data/VERSION +1 -1
- data/bin/{formcolumn → columnform} +4 -10
- data/bin/indentconv +41 -0
- data/bin/indentstat +24 -0
- data/bin/linesub +12 -19
- data/bin/percentpack +26 -0
- data/bin/zshescape +24 -0
- data/lib/tefil/columnformer.rb +40 -8
- data/lib/tefil/indentconverter.rb +24 -0
- data/lib/tefil/indentstatistics.rb +47 -0
- data/lib/tefil/linesubstituter.rb +17 -0
- data/lib/tefil/percentpacker.rb +23 -0
- data/lib/tefil/textfilterbase.rb +109 -0
- data/lib/tefil/zshescaper.rb +12 -0
- data/lib/tefil.rb +7 -78
- data/tefil.gemspec +95 -0
- data/test/columnformer/sample.txt +2 -0
- data/test/helper.rb +1 -0
- data/test/indentconv/sample0.txt +11 -0
- data/test/indentconv/sample1.txt +11 -0
- data/test/indentstat/indent4.txt +11 -0
- data/test/indentstat/sample0.txt +11 -0
- data/test/indentstat/sample1.txt +11 -0
- data/test/percentpack/sample.txt +1 -0
- data/test/test_columnformer.rb +26 -3
- data/test/test_indentconverter.rb +44 -0
- data/test/test_indentstatistics.rb +38 -0
- data/test/test_linesubstituter.rb +45 -0
- data/test/test_percentpacker.rb +29 -0
- data/test/test_textfilterbase.rb +182 -0
- data/test/test_zshescaper.rb +29 -0
- data/test/zshescape/sample.txt +2 -0
- metadata +70 -47
- data/bin/lineedit +0 -38
- data/bin/lineescapezsh +0 -30
- data/test/test_tefil.rb +0 -130
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f742434ea5277ddf6d1c079d8d5fcf9f1525cb0c
|
4
|
+
data.tar.gz: 50abba718b395336d1f00d6e3f75149374356f21
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 95669e49a633c958998c4117958e210306ba09831d9c2599f59259f1e436e68b41d6feeb35294842e5c52a46724bc650e7691456005cfbc7d74e64343b22e397
|
7
|
+
data.tar.gz: e4bf80f0b585940963a01cc13c65ae31c30f5b1b92354c6cd734b0f3cb288586753cb16bc5870af877693dfe226b3fc74871fdeda83695565630e202b6e6aae1
|
data/CHANGES
CHANGED
@@ -1,6 +1,17 @@
|
|
1
|
-
=
|
1
|
+
= tefil changelog
|
2
2
|
|
3
|
-
== Master
|
3
|
+
== Master (for 0.1.1)
|
4
|
+
|
5
|
+
== Version 0.1.0 [2015-07-07] released
|
6
|
+
* Add bin/packpercent .
|
7
|
+
* Add usage banner.
|
8
|
+
* Rename bin/formcolumn to columnform
|
9
|
+
* Add bin/indentstat .
|
10
|
+
* Add bin/indentconv .
|
11
|
+
* Module Tefil converted to class Tefil
|
12
|
+
* Delete require "tempfile" for Ruby 2.1
|
13
|
+
* Rewrite test to use capture_stdout
|
14
|
+
* Remove Tefil::TextFilterBase.filter (class method)
|
4
15
|
|
5
16
|
== Version 0.0.3 [2013-05-10] released
|
6
17
|
* Rename TextFilter to Tefil
|
data/Gemfile
CHANGED
@@ -6,9 +6,10 @@ source "http://rubygems.org"
|
|
6
6
|
# Add dependencies to develop your gem here.
|
7
7
|
# Include everything needed to run rake, tests, features, etc.
|
8
8
|
group :development do
|
9
|
-
gem "rdoc", "~>
|
10
|
-
gem "bundler", "~> 1.
|
11
|
-
gem "jeweler", "~>
|
9
|
+
gem "rdoc", "~> 4.0.1"
|
10
|
+
gem "bundler", "~> 1.9.2"
|
11
|
+
gem "jeweler", "~> 2.0.1"
|
12
|
+
gem "capture_stdout", "~> 0.0.1"
|
12
13
|
gem "simplecov", ">= 0"
|
13
14
|
gem "builtinextension", ">= 0.1.0"
|
14
15
|
#gem "psych", ">= 0"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
@@ -3,8 +3,6 @@
|
|
3
3
|
#
|
4
4
|
# USAGE: formcolumn [options] files ...
|
5
5
|
|
6
|
-
INPUT_SEPARATOR = /\s+/
|
7
|
-
|
8
6
|
|
9
7
|
require "pp"
|
10
8
|
require "optparse"
|
@@ -14,18 +12,14 @@ require "tefil"
|
|
14
12
|
# option analysis
|
15
13
|
OPTIONS = {:separator => ' '}
|
16
14
|
op = OptionParser.new
|
15
|
+
op.banner = [
|
16
|
+
"Usage: #{File.basename("#{__FILE__}")} [options] [files]",
|
17
|
+
].join("\n")
|
17
18
|
op.on("-o" , "--overwrite" , "Overwrite."){ OPTIONS[:overwrite] = true}
|
18
19
|
op.on("-s char", "--separator=char", "Indicate separator."){|val| OPTIONS[:separator] = val}
|
19
20
|
op.on("-l" , "--left-alline" , "Left alline."){ OPTIONS[:left] = true}
|
20
21
|
op.parse!(ARGV)
|
21
22
|
|
22
|
-
module Tefil
|
23
|
-
def self.process_stream(in_io, out_io)
|
24
|
-
rows = in_io.readlines.map { |line| line.strip.split(INPUT_SEPARATOR) }
|
25
|
-
Tefil::ColumnFormer.form(rows, out_io, OPTIONS[:separator], OPTIONS[:left])
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
23
|
OPTIONS[:overwrite] ||= false
|
30
24
|
|
31
|
-
Tefil.
|
25
|
+
Tefil::ColumnFormer.new(OPTIONS).filter(ARGV)
|
data/bin/indentconv
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require "pp"
|
5
|
+
require "optparse"
|
6
|
+
require "rubygems"
|
7
|
+
require "tefil"
|
8
|
+
|
9
|
+
# option analysis
|
10
|
+
OPTIONS = {}
|
11
|
+
op = OptionParser.new
|
12
|
+
op.banner = [
|
13
|
+
"Usage: #{File.basename("#{__FILE__}")} old_width new_width [options] [files]",
|
14
|
+
"Example: #{File.basename("#{__FILE__}")} 4 2 [options] [files]",
|
15
|
+
].join("\n")
|
16
|
+
op.on("-o" , "--overwrite" , "Overwrite."){ OPTIONS[:overwrite] = true}
|
17
|
+
#op.on("-m", "--minimum", "Show only minimum indent (not zero)."){ OPTIONS[:minimum] = true}
|
18
|
+
#op.on("-l" , "--left-alline" , "Left alline."){ OPTIONS[:left] = true}
|
19
|
+
op.parse!(ARGV)
|
20
|
+
|
21
|
+
#数値として識別できない文字列(to_i で 0 になる)はタブにする。
|
22
|
+
num = ARGV.shift.to_i
|
23
|
+
if num == 0
|
24
|
+
old_char = "\t"
|
25
|
+
old_width = 1
|
26
|
+
else
|
27
|
+
old_char = ' '
|
28
|
+
old_width = num
|
29
|
+
end
|
30
|
+
|
31
|
+
num = ARGV.shift.to_i
|
32
|
+
if num == 0
|
33
|
+
new_char = "\t"
|
34
|
+
new_width = 1
|
35
|
+
else
|
36
|
+
new_char = ' '
|
37
|
+
new_width = num
|
38
|
+
end
|
39
|
+
|
40
|
+
f = Tefil::IndentConverter.new(old_char, old_width, new_char, new_width, OPTIONS)
|
41
|
+
f.filter(ARGV)
|
data/bin/indentstat
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
require "pp"
|
5
|
+
require "optparse"
|
6
|
+
require "rubygems"
|
7
|
+
require "tefil"
|
8
|
+
|
9
|
+
|
10
|
+
# option analysis
|
11
|
+
options = {}
|
12
|
+
op = OptionParser.new
|
13
|
+
#op.banner = [
|
14
|
+
# "Usage: #{File.basename("#{__FILE__}")} [options] [files]",
|
15
|
+
#].join("\n")
|
16
|
+
op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
|
17
|
+
op.on("-m", "--minimum", "Show minimum width (not zero). If no indent, show '0'."){ options[:minimum] = true}
|
18
|
+
op.parse!(ARGV)
|
19
|
+
|
20
|
+
|
21
|
+
options[:overwrite] ||= false
|
22
|
+
|
23
|
+
is = Tefil::IndentStatistics.new options
|
24
|
+
is.filter(ARGV)
|
data/bin/linesub
CHANGED
@@ -6,31 +6,24 @@
|
|
6
6
|
require "pp"
|
7
7
|
require "optparse"
|
8
8
|
require "rubygems"
|
9
|
+
require "tempfile"
|
9
10
|
#gem "tefil"
|
10
11
|
require "tefil.rb"
|
11
12
|
|
12
13
|
## option analysis
|
13
|
-
|
14
|
+
options = {}
|
14
15
|
op = OptionParser.new
|
15
|
-
op.
|
16
|
-
|
16
|
+
op.banner = [
|
17
|
+
"Usage: #{File.basename("#{__FILE__}")} old_str new_str [options] [files]",
|
18
|
+
].join("\n")
|
19
|
+
op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
|
20
|
+
op.on("-g" , "--global" , "Globally substitute."){ options[:global] = true}
|
17
21
|
op.parse!(ARGV)
|
18
22
|
|
19
|
-
|
20
|
-
|
23
|
+
old_str = ARGV.shift
|
24
|
+
new_str = ARGV.shift
|
21
25
|
|
22
|
-
|
23
|
-
def self.process_stream(in_io, out_io)
|
24
|
-
in_io.each do |line|
|
25
|
-
if OPTIONS[:global]
|
26
|
-
out_io.puts line.gsub(OLD_PATTERN, NEW_STR)
|
27
|
-
else
|
28
|
-
out_io.puts line.sub(OLD_PATTERN, NEW_STR)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
26
|
+
options[:overwrite] ||= false
|
33
27
|
|
34
|
-
|
35
|
-
|
36
|
-
Tefil.run(ARGV, OPTIONS[:overwrite])
|
28
|
+
tf = Tefil::LineSubstituter.new(old_str, new_str, options)
|
29
|
+
tf.filter(ARGV)
|
data/bin/percentpack
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
#
|
4
|
+
# USAGE: linesub [options] str0 str1 files ...
|
5
|
+
|
6
|
+
require "pp"
|
7
|
+
require "optparse"
|
8
|
+
require "rubygems"
|
9
|
+
#gem "tefil"
|
10
|
+
require "tefil.rb"
|
11
|
+
|
12
|
+
## option analysis
|
13
|
+
options = {}
|
14
|
+
op = OptionParser.new
|
15
|
+
op.banner = [
|
16
|
+
"Usage: #{File.basename("#{__FILE__}")} [options] [files]",
|
17
|
+
" E.g., echo '%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB' | packpercent" #サンプル
|
18
|
+
].join("\n")
|
19
|
+
op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
|
20
|
+
op.parse!(ARGV)
|
21
|
+
|
22
|
+
options[:overwrite] ||= false
|
23
|
+
options[:overwrite]
|
24
|
+
|
25
|
+
tf = Tefil::PercentPacker.new(options)
|
26
|
+
tf.filter(ARGV)
|
data/bin/zshescape
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
#
|
4
|
+
# USAGE: lineescapezsh [options] files ...
|
5
|
+
|
6
|
+
require "pp"
|
7
|
+
require "optparse"
|
8
|
+
require "rubygems"
|
9
|
+
#gem "tefil"
|
10
|
+
require "tefil.rb"
|
11
|
+
|
12
|
+
## option analysis
|
13
|
+
options = {}
|
14
|
+
op = OptionParser.new
|
15
|
+
op.banner = [
|
16
|
+
"Usage: #{File.basename("#{__FILE__}")} [options] [files]",
|
17
|
+
].join("\n")
|
18
|
+
op.on("-o" , "--overwrite" , "Overwrite."){ options[:overwrite] = true}
|
19
|
+
op.parse!(ARGV)
|
20
|
+
|
21
|
+
options[:overwrite] ||= false
|
22
|
+
|
23
|
+
tf = Tefil::ZshEscaper.new(options)
|
24
|
+
tf.filter(ARGV)
|
data/lib/tefil/columnformer.rb
CHANGED
@@ -1,18 +1,37 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
+
INPUT_SEPARATOR = /\s+/
|
5
|
+
|
6
|
+
class String
|
7
|
+
#http://www.techscore.com/blog/2012/12/25/
|
8
|
+
def mb_ljust(width, padding=' ')
|
9
|
+
output_width = each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
|
10
|
+
padding_size = [0, width - output_width].max
|
11
|
+
self + padding * padding_size
|
12
|
+
end
|
13
|
+
|
14
|
+
def mb_rjust(width, padding=' ')
|
15
|
+
output_width = each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
|
16
|
+
padding_size = [0, width - output_width].max
|
17
|
+
padding * padding_size + self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
4
22
|
#
|
5
23
|
#
|
6
24
|
#
|
7
|
-
|
8
|
-
|
25
|
+
class Tefil::ColumnFormer < Tefil::TextFilterBase
|
26
|
+
|
27
|
+
def form(matrix, io = $stdout, separator = " ", left = false)
|
9
28
|
#Obtain max length for each column.
|
10
29
|
max_lengths = []
|
11
30
|
matrix.each do |row|
|
12
31
|
row.each_with_index do |item, index|
|
13
32
|
item = item.to_s
|
14
33
|
max_lengths[index] ||= 0
|
15
|
-
size = item
|
34
|
+
size = print_size(item)
|
16
35
|
max_lengths[index] = size if max_lengths[index] < size
|
17
36
|
end
|
18
37
|
end
|
@@ -20,15 +39,28 @@ module Tefil::ColumnFormer
|
|
20
39
|
#Output
|
21
40
|
matrix.each do |row|
|
22
41
|
new_items = []
|
23
|
-
|
24
|
-
form_left = ""
|
25
|
-
form_left = "-" if left
|
26
|
-
|
27
42
|
row.each_with_index do |item, index|
|
28
|
-
|
43
|
+
method = :mb_rjust
|
44
|
+
method = :mb_ljust if left
|
45
|
+
new_items[index] = item.send(method, max_lengths[index])
|
29
46
|
end
|
30
47
|
io.puts new_items.join(separator).sub(/ +$/, "")
|
31
48
|
end
|
32
49
|
end
|
50
|
+
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def process_stream(in_io, out_io)
|
55
|
+
rows = in_io.readlines.map do |line|
|
56
|
+
line.strip.split(INPUT_SEPARATOR)
|
57
|
+
end
|
58
|
+
form(rows, out_io, OPTIONS[:separator], OPTIONS[:left])
|
59
|
+
end
|
60
|
+
|
61
|
+
def print_size(string)
|
62
|
+
string.each_char.map{|c| c.bytesize == 1 ? 1 : 2}.reduce(0, &:+)
|
63
|
+
end
|
64
|
+
|
33
65
|
end
|
34
66
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
|
4
|
+
class Tefil::IndentConverter < Tefil::TextFilterBase
|
5
|
+
def initialize(old_char, old_width, new_char, new_width, options)
|
6
|
+
@old_width = old_width
|
7
|
+
@new_width = new_width
|
8
|
+
@old_char = old_char
|
9
|
+
@new_char = new_char
|
10
|
+
super(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_stream(in_io, out_io)
|
14
|
+
in_io.readlines.each do |line|
|
15
|
+
/^(#{@old_char}*)(.*)$/ =~ line
|
16
|
+
indent = $1
|
17
|
+
body = $2
|
18
|
+
new_indent = indent.size * @new_width / @old_width
|
19
|
+
out_io.puts "#{@new_char * new_indent}#{body}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class Tefil::IndentStatistics < Tefil::TextFilterBase
|
2
|
+
|
3
|
+
HISTGRAM_LIMIT = 50
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
options[:smart_filename] = true
|
7
|
+
@minimum = options[:minimum]
|
8
|
+
super(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def process_stream(in_io, out_io)
|
12
|
+
frequencies = {}
|
13
|
+
in_io.readlines.each do |line|
|
14
|
+
#/^(\s*)/ =~ line #改行文字が含まれる。
|
15
|
+
/^( *)/ =~ line
|
16
|
+
width = $1.size
|
17
|
+
frequencies[width] ||= 0
|
18
|
+
frequencies[width] += 1
|
19
|
+
end
|
20
|
+
|
21
|
+
if @minimum
|
22
|
+
frequencies.delete(0)
|
23
|
+
output = frequencies.keys.min
|
24
|
+
output = 0 if frequencies.empty?
|
25
|
+
else
|
26
|
+
output = ''
|
27
|
+
output = "\n" if ARGV.size >= 2
|
28
|
+
output += self.histgram(frequencies)
|
29
|
+
end
|
30
|
+
|
31
|
+
out_io.puts output
|
32
|
+
end
|
33
|
+
|
34
|
+
def histgram(frequencies)
|
35
|
+
result = ''
|
36
|
+
max = frequencies.values.max
|
37
|
+
frequencies.keys.sort.each do |key|
|
38
|
+
num = frequencies[key]
|
39
|
+
num = num * HISTGRAM_LIMIT / max if max > HISTGRAM_LIMIT
|
40
|
+
result += sprintf("%2d|", key)
|
41
|
+
result += "*" * num
|
42
|
+
result += "\n"
|
43
|
+
end
|
44
|
+
result
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Tefil::LineSubstituter < Tefil::TextFilterBase
|
2
|
+
def initialize(old_str, new_str, options = {})
|
3
|
+
@old_str = old_str
|
4
|
+
@new_str = new_str
|
5
|
+
@global = options[:global]
|
6
|
+
super(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def process_stream(in_io, out_io)
|
10
|
+
in_io.each do |line|
|
11
|
+
method = :sub
|
12
|
+
method = :gsub if @global
|
13
|
+
out_io.puts line.send(method, @old_str, @new_str)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Tefil::PercentPacker < Tefil::TextFilterBase
|
2
|
+
def process_stream(in_io, out_io)
|
3
|
+
in_io.each do |line|
|
4
|
+
old_chars = line.split("")
|
5
|
+
|
6
|
+
new_str = ""
|
7
|
+
new_index = 0
|
8
|
+
old_index = 0
|
9
|
+
while old_index < old_chars.size
|
10
|
+
if old_chars[old_index] == "%"
|
11
|
+
new_str += [old_chars[(old_index +1) .. (old_index + 2)].join].pack("H*")
|
12
|
+
old_index += 2
|
13
|
+
else
|
14
|
+
new_str += old_chars[old_index]
|
15
|
+
end
|
16
|
+
old_index += 1
|
17
|
+
new_index += 1
|
18
|
+
end
|
19
|
+
out_io.print new_str
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,109 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
#
|
4
|
+
#require "tefil"
|
5
|
+
#require "tempfile"
|
6
|
+
#require "tmpdir"
|
7
|
+
|
8
|
+
# Module that provide a framework for text filters.
|
9
|
+
#
|
10
|
+
# Provide basic functions for a command like below:
|
11
|
+
# Input:
|
12
|
+
# - Without indicating filenames like "command.rb",
|
13
|
+
# input data is eaten from STDIN.
|
14
|
+
# - With indicating filenames like "command.rb *.txt",
|
15
|
+
# input data is eaten from indicated files in order.
|
16
|
+
# When the file which is in the filenames does not exist,
|
17
|
+
# output report to STDERR.
|
18
|
+
#
|
19
|
+
# Output:
|
20
|
+
# - Without indicating "--overwrite" option,
|
21
|
+
# output to STDOUT even from many files.
|
22
|
+
# - With indicating "--overwrite" option,
|
23
|
+
# - With indicating input files,
|
24
|
+
# overwrite each file.
|
25
|
+
# - Without indicating input files,
|
26
|
+
# output to STDOUT in one stream.
|
27
|
+
#
|
28
|
+
# If indicated file(s) not found,
|
29
|
+
# this program notify on stderr and does not throw an exception.
|
30
|
+
#
|
31
|
+
class Tefil::TextFilterBase
|
32
|
+
|
33
|
+
class NotRedefinedMethodError < Exception; end
|
34
|
+
class TypeError < Exception; end
|
35
|
+
|
36
|
+
def initialize(options = {})
|
37
|
+
@overwrite = options[:overwrite]
|
38
|
+
@smart_filename = options[:smart_filename]
|
39
|
+
end
|
40
|
+
|
41
|
+
# 保持している入力ファイル対して、順に処理を実行する。
|
42
|
+
# filenames.size が 0 ならば STDIN からの入力を待つことになる。
|
43
|
+
#
|
44
|
+
# STDIN からの入力だった場合、出力先は必ず STDOUT になる。
|
45
|
+
# STDIN からの入力ではない、すなわちファイル入力であった場合、
|
46
|
+
# - overwrite_flag が false ならば STDOUT に出力する。
|
47
|
+
# - overwrite_flag が true ならば 個々のファイルに上書きする。
|
48
|
+
#
|
49
|
+
# Process of each file is defined in 'process_stream' method.
|
50
|
+
#
|
51
|
+
#def self.filter(filenames, options)
|
52
|
+
# self.class.new(options).filter(filenames)
|
53
|
+
#end
|
54
|
+
|
55
|
+
#line ごとのファイル名表示はここでは提供せず、したければ process_stream で作る。
|
56
|
+
#grep のように行数を表示するなど、複雑な表示をすることもありうる。
|
57
|
+
def filter(filenames)
|
58
|
+
@num_files = filenames.size
|
59
|
+
input_io = $stdin
|
60
|
+
output_io = $stdout
|
61
|
+
if filenames.size == 0
|
62
|
+
process_stream( input_io, output_io)
|
63
|
+
else
|
64
|
+
filenames.each do |filename|
|
65
|
+
@filename = filename
|
66
|
+
input_io = File.open(filename, "r")
|
67
|
+
output_io = Tempfile.new("tefil", "/tmp") if @overwrite
|
68
|
+
smart_filename(output_io)
|
69
|
+
begin
|
70
|
+
process_stream(input_io, output_io)
|
71
|
+
rescue ArgumentError, Errno::EISDIR
|
72
|
+
$stderr.puts $!
|
73
|
+
next
|
74
|
+
end
|
75
|
+
|
76
|
+
if @overwrite
|
77
|
+
output_io.open
|
78
|
+
File.open(filename, "w") do |overwrite_io|
|
79
|
+
output_io.each { |line| overwrite_io.puts(line) }
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# @smart_filename が真, かつ
|
89
|
+
# @overwrite が 偽, かつ
|
90
|
+
# filter メソッドに渡されたファイル数が複数のときには
|
91
|
+
# 処理中のファイル名を output_io に出力する。
|
92
|
+
def smart_filename(output_io)
|
93
|
+
if @smart_filename && (! @overwrite) && (@num_files >= 2)
|
94
|
+
output_io.puts("#{@filename}:")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Process a file.
|
100
|
+
# An argument 'in_io' indicates an io (file handle) for input.
|
101
|
+
# Another argument 'out_io' indicates an io (file handle) for output.
|
102
|
+
# This method must be redefined in a subclass or be overridden.
|
103
|
+
# If not redefined, raise an exception Tefil::NotRedefinedMethodError.
|
104
|
+
def process_stream(in_io, out_io)
|
105
|
+
raise NotRedefinedMethodError
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
data/lib/tefil.rb
CHANGED
@@ -1,83 +1,12 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
2
|
# coding: utf-8
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
# Module that provide a framework for text filters.
|
7
|
-
#
|
8
|
-
# Provide basic functions for a command like below:
|
9
|
-
# Input:
|
10
|
-
# - Without indicating filenames like "command.rb",
|
11
|
-
# input data is eaten from STDIN.
|
12
|
-
# - With indicating filenames like "command.rb *.txt",
|
13
|
-
# input data is eaten from indicated files in order.
|
14
|
-
# When the file which is in the filenames does not exist,
|
15
|
-
# output report to STDERR.
|
16
|
-
#
|
17
|
-
# Output:
|
18
|
-
# - Without indicating "--overwrite" option,
|
19
|
-
# output to STDOUT even from many files.
|
20
|
-
# - With indicating "--overwrite" option,
|
21
|
-
# - With indicating input files,
|
22
|
-
# overwrite each file.
|
23
|
-
# - Without indicating input files,
|
24
|
-
# output to STDOUT in one stream.
|
25
|
-
#
|
26
|
-
# If indicated file(s) not found,
|
27
|
-
# this program notify on stderr and does not throw an exception.
|
28
|
-
#
|
29
|
-
module Tefil
|
30
|
-
|
31
|
-
class NotRedefinedMethodError < Exception; end
|
32
|
-
class TypeError < Exception; end
|
33
|
-
|
34
|
-
# 保持している入力ファイル対して、順に処理を実行する。
|
35
|
-
# filenames.size が 0 ならば STDIN からの入力を待つことになる。
|
36
|
-
#
|
37
|
-
# STDIN からの入力だった場合、出力先は必ず STDOUT になる。
|
38
|
-
# STDIN からの入力ではない、すなわちファイル入力であった場合、
|
39
|
-
# - overwrite_flag が false ならば STDOUT に出力する。
|
40
|
-
# - overwrite_flag が true ならば 個々のファイルに上書きする。
|
41
|
-
#
|
42
|
-
# Process of each file is defined in 'process_stream' method.
|
43
|
-
#
|
44
|
-
def self.run(filenames, overwrite_flag = false)
|
45
|
-
#p self; exit
|
46
|
-
if filenames.size == 0
|
47
|
-
self.process_stream( $stdin, $stdout )
|
48
|
-
else
|
49
|
-
#p filenames
|
50
|
-
filenames.each do |filename|
|
51
|
-
if overwrite_flag
|
52
|
-
tempfile = Tempfile.new("tefil", "/tmp")
|
53
|
-
File.open(filename, "r") do |input_file|
|
54
|
-
self.process_stream(input_file, tempfile)
|
55
|
-
end
|
56
|
-
tempfile.close
|
57
|
-
tempfile.open
|
58
|
-
File.open(filename, "w") do |output_file|
|
59
|
-
tempfile.each { |line| output_file.puts(line) }
|
60
|
-
end
|
61
|
-
else
|
62
|
-
File.open(filename, "r") do |input_file|
|
63
|
-
self.process_stream(input_file, $stdout)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
private
|
71
|
-
|
72
|
-
# Process a file.
|
73
|
-
# An argument 'in_io' indicates an io (file handle) for input.
|
74
|
-
# Another argument 'out_io' indicates an io (file handle) for output.
|
75
|
-
# This method must be redefined in a subclass or be overridden.
|
76
|
-
# If not redefined, raise an exception Tefil::NotRedefinedMethodError.
|
77
|
-
def self.process_stream(in_io, out_io)
|
78
|
-
raise NotRedefinedMethodError
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
4
|
+
module Tefil; end
|
5
|
+
require "tefil/textfilterbase"
|
82
6
|
|
83
7
|
require "tefil/columnformer"
|
8
|
+
require "tefil/indentconverter"
|
9
|
+
require "tefil/indentstatistics"
|
10
|
+
require "tefil/linesubstituter"
|
11
|
+
require "tefil/percentpacker"
|
12
|
+
require "tefil/zshescaper"
|