tefil 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|