tb 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +156 -5
- data/bin/tb +2 -1110
- data/lib/tb.rb +4 -2
- data/lib/tb/catreader.rb +131 -0
- data/lib/tb/cmd_cat.rb +65 -0
- data/lib/tb/cmd_consecutive.rb +79 -0
- data/lib/tb/cmd_crop.rb +105 -0
- data/lib/tb/cmd_cross.rb +119 -0
- data/lib/tb/cmd_csv.rb +42 -0
- data/lib/tb/cmd_cut.rb +77 -0
- data/lib/tb/cmd_grep.rb +76 -0
- data/lib/tb/cmd_group.rb +82 -0
- data/lib/tb/cmd_gsub.rb +77 -0
- data/lib/tb/cmd_help.rb +98 -0
- data/lib/tb/cmd_join.rb +81 -0
- data/lib/tb/cmd_json.rb +60 -0
- data/lib/tb/cmd_ls.rb +273 -0
- data/lib/tb/cmd_mheader.rb +77 -0
- data/lib/tb/cmd_newfield.rb +59 -0
- data/lib/tb/cmd_pnm.rb +43 -0
- data/lib/tb/cmd_pp.rb +70 -0
- data/lib/tb/cmd_rename.rb +58 -0
- data/lib/tb/cmd_shape.rb +67 -0
- data/lib/tb/cmd_sort.rb +58 -0
- data/lib/tb/cmd_svn_log.rb +158 -0
- data/lib/tb/cmd_tsv.rb +43 -0
- data/lib/tb/cmd_yaml.rb +47 -0
- data/lib/tb/cmdmain.rb +45 -0
- data/lib/tb/cmdtop.rb +58 -0
- data/lib/tb/cmdutil.rb +327 -0
- data/lib/tb/csv.rb +30 -6
- data/lib/tb/fieldset.rb +39 -41
- data/lib/tb/pager.rb +132 -0
- data/lib/tb/pnm.rb +357 -0
- data/lib/tb/reader.rb +18 -128
- data/lib/tb/record.rb +3 -3
- data/lib/tb/ropen.rb +70 -0
- data/lib/tb/{pathfinder.rb → search.rb} +69 -34
- data/lib/tb/tsv.rb +29 -1
- data/sample/colors.ppm +0 -0
- data/sample/gradation.pgm +0 -0
- data/sample/langs.csv +46 -0
- data/sample/tbplot +293 -0
- data/test-all-cov.rb +65 -0
- data/test-all.rb +5 -0
- data/test/test_basic.rb +99 -2
- data/test/test_catreader.rb +27 -0
- data/test/test_cmd_cat.rb +118 -0
- data/test/test_cmd_consecutive.rb +90 -0
- data/test/test_cmd_crop.rb +101 -0
- data/test/test_cmd_cross.rb +113 -0
- data/test/test_cmd_csv.rb +129 -0
- data/test/test_cmd_cut.rb +100 -0
- data/test/test_cmd_grep.rb +89 -0
- data/test/test_cmd_group.rb +181 -0
- data/test/test_cmd_gsub.rb +103 -0
- data/test/test_cmd_help.rb +190 -0
- data/test/test_cmd_join.rb +197 -0
- data/test/test_cmd_json.rb +75 -0
- data/test/test_cmd_ls.rb +203 -0
- data/test/test_cmd_mheader.rb +86 -0
- data/test/test_cmd_newfield.rb +63 -0
- data/test/test_cmd_pnm.rb +35 -0
- data/test/test_cmd_pp.rb +62 -0
- data/test/test_cmd_rename.rb +91 -0
- data/test/test_cmd_shape.rb +50 -0
- data/test/test_cmd_sort.rb +105 -0
- data/test/test_cmd_tsv.rb +67 -0
- data/test/test_cmd_yaml.rb +55 -0
- data/test/test_cmdtty.rb +154 -0
- data/test/test_cmdutil.rb +43 -0
- data/test/test_csv.rb +10 -0
- data/test/test_fieldset.rb +42 -0
- data/test/test_pager.rb +142 -0
- data/test/test_pnm.rb +374 -0
- data/test/test_reader.rb +147 -0
- data/test/test_record.rb +49 -0
- data/test/test_search.rb +575 -0
- data/test/test_tsv.rb +7 -0
- metadata +108 -5
- data/lib/tb/qtsv.rb +0 -93
data/lib/tb/cmd_join.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# Copyright (C) 2011 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
Tb::Cmd.subcommands << 'join'
|
26
|
+
|
27
|
+
Tb::Cmd.default_option[:opt_join_outer_missing] = nil
|
28
|
+
Tb::Cmd.default_option[:opt_join_retain_left] = nil
|
29
|
+
Tb::Cmd.default_option[:opt_join_retain_right] = nil
|
30
|
+
|
31
|
+
def (Tb::Cmd).op_join
|
32
|
+
op = OptionParser.new
|
33
|
+
op.banner = 'Usage: tb join [OPTS] [TABLE1 TABLE2 ...]'
|
34
|
+
define_common_option(op, 'hNod', '--no-pager', '--debug')
|
35
|
+
op.def_option('--outer', 'outer join') {
|
36
|
+
Tb::Cmd.opt_join_retain_left = true
|
37
|
+
Tb::Cmd.opt_join_retain_right = true
|
38
|
+
}
|
39
|
+
op.def_option('--left', 'left outer join') {
|
40
|
+
Tb::Cmd.opt_join_retain_left = true
|
41
|
+
Tb::Cmd.opt_join_retain_right = false
|
42
|
+
}
|
43
|
+
op.def_option('--right', 'right outer join') {
|
44
|
+
Tb::Cmd.opt_join_retain_left = false
|
45
|
+
Tb::Cmd.opt_join_retain_right = true
|
46
|
+
}
|
47
|
+
op.def_option('--outer-missing=DEFAULT', 'missing value for outer join') {|missing|
|
48
|
+
if Tb::Cmd.opt_join_retain_left == nil
|
49
|
+
Tb::Cmd.opt_join_retain_left = true
|
50
|
+
Tb::Cmd.opt_join_retain_right = true
|
51
|
+
end
|
52
|
+
Tb::Cmd.opt_join_outer_missing = missing
|
53
|
+
}
|
54
|
+
op
|
55
|
+
end
|
56
|
+
|
57
|
+
def (Tb::Cmd).main_join(argv)
|
58
|
+
op_join.parse!(argv)
|
59
|
+
exit_if_help('join')
|
60
|
+
retain_left = Tb::Cmd.opt_join_retain_left
|
61
|
+
retain_right = Tb::Cmd.opt_join_retain_right
|
62
|
+
err('two tables required at least.') if argv.length < 2
|
63
|
+
result = load_table(argv.shift)
|
64
|
+
if retain_left || retain_right
|
65
|
+
argv.each {|filename|
|
66
|
+
tbl = load_table(filename)
|
67
|
+
STDERR.puts "shared keys: #{(result.list_fields & tbl.list_fields).inspect}" if 1 <= Tb::Cmd.opt_debug
|
68
|
+
result = result.natjoin2_outer(tbl, Tb::Cmd.opt_join_outer_missing, retain_left, retain_right)
|
69
|
+
}
|
70
|
+
else
|
71
|
+
argv.each {|filename|
|
72
|
+
tbl = load_table(filename)
|
73
|
+
STDERR.puts "shared keys: #{(result.list_fields & tbl.list_fields).inspect}" if 1 <= Tb::Cmd.opt_debug
|
74
|
+
result = result.natjoin2(tbl)
|
75
|
+
}
|
76
|
+
end
|
77
|
+
with_output {|out|
|
78
|
+
tbl_generate_csv(result, out)
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
data/lib/tb/cmd_json.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
# Copyright (C) 2011 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
Tb::Cmd.subcommands << 'json'
|
26
|
+
|
27
|
+
def (Tb::Cmd).op_json
|
28
|
+
op = OptionParser.new
|
29
|
+
op.banner = 'Usage: tb json [OPTS] [TABLE]'
|
30
|
+
define_common_option(op, "hNo", "--no-pager")
|
31
|
+
op
|
32
|
+
end
|
33
|
+
|
34
|
+
def (Tb::Cmd).main_json(argv)
|
35
|
+
require 'json'
|
36
|
+
op_json.parse!(argv)
|
37
|
+
exit_if_help('json')
|
38
|
+
argv = ['-'] if argv.empty?
|
39
|
+
with_output {|out|
|
40
|
+
out.print "["
|
41
|
+
sep = nil
|
42
|
+
argv.each {|filename|
|
43
|
+
sep = ",\n\n" if sep
|
44
|
+
tablereader_open(filename) {|tblreader|
|
45
|
+
tblreader.each {|ary|
|
46
|
+
out.print sep if sep
|
47
|
+
header = tblreader.header
|
48
|
+
h = {}
|
49
|
+
ary.each_with_index {|e, i|
|
50
|
+
h[header[i]] = e if !e.nil?
|
51
|
+
}
|
52
|
+
out.print JSON.pretty_generate(h)
|
53
|
+
sep = ",\n"
|
54
|
+
}
|
55
|
+
}
|
56
|
+
}
|
57
|
+
out.puts "]"
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
data/lib/tb/cmd_ls.rb
ADDED
@@ -0,0 +1,273 @@
|
|
1
|
+
# Copyright (C) 2011 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
Tb::Cmd.subcommands << 'ls'
|
26
|
+
|
27
|
+
Tb::Cmd.default_option[:opt_ls_a] = nil
|
28
|
+
Tb::Cmd.default_option[:opt_ls_A] = nil
|
29
|
+
Tb::Cmd.default_option[:opt_ls_l] = 0
|
30
|
+
Tb::Cmd.default_option[:opt_ls_R] = nil
|
31
|
+
|
32
|
+
def (Tb::Cmd).op_ls
|
33
|
+
op = OptionParser.new
|
34
|
+
op.banner = 'Usage: tb ls [OPTS] [FILE ...]'
|
35
|
+
define_common_option(op, "hNo", "--no-pager")
|
36
|
+
op.def_option('-a', 'don\'t ignore filenames beginning with a period.') {|fs| Tb::Cmd.opt_ls_a = true }
|
37
|
+
op.def_option('-A', 'don\'t ignore filenames beginning with a period, except "." and "..".') {|fs| Tb::Cmd.opt_ls_A = true }
|
38
|
+
op.def_option('-l', 'show attributes. -ll for more attributes.') {|fs| Tb::Cmd.opt_ls_l += 1 }
|
39
|
+
op.def_option('-R', 'recursive.') {|fs| Tb::Cmd.opt_ls_R = true }
|
40
|
+
op
|
41
|
+
end
|
42
|
+
|
43
|
+
def (Tb::Cmd).main_ls(argv)
|
44
|
+
op_ls.parse!(argv)
|
45
|
+
exit_if_help('ls')
|
46
|
+
argv = ['.'] if argv.empty?
|
47
|
+
@fail = false
|
48
|
+
with_table_stream_output {|gen|
|
49
|
+
if Tb::Cmd.opt_ls_l == 0
|
50
|
+
gen.output_header ['filename'] # don't generate the header when -N.
|
51
|
+
else
|
52
|
+
gen.output_header(ls_long_header()) # don't generate the header when -N.
|
53
|
+
end
|
54
|
+
argv.each {|arg|
|
55
|
+
ls_run(gen, Pathname(real_pathname_string(arg)))
|
56
|
+
}
|
57
|
+
}
|
58
|
+
if @fail
|
59
|
+
exit false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def (Tb::Cmd).ls_run(gen, path)
|
64
|
+
st = ls_get_stat(path)
|
65
|
+
return if !st
|
66
|
+
if st.directory?
|
67
|
+
ls_dir(gen, path, st)
|
68
|
+
else
|
69
|
+
ls_file(gen, path, st)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def (Tb::Cmd).ls_dir(gen, dir, st)
|
74
|
+
begin
|
75
|
+
entries = Dir.entries(dir)
|
76
|
+
rescue SystemCallError
|
77
|
+
@fail = true
|
78
|
+
warn "tb: #{$!}: #{dir}"
|
79
|
+
return
|
80
|
+
end
|
81
|
+
entries.map! {|filename| real_pathname_string(filename) }
|
82
|
+
entries = entries.sort_by {|filename| smart_cmp_value(filename) }
|
83
|
+
if Tb::Cmd.opt_ls_a || Tb::Cmd.opt_ls_A
|
84
|
+
entries1, entries2 = entries.partition {|filename| /\A\./ =~ filename }
|
85
|
+
entries0, entries1 = entries1.partition {|filename| filename == '.' || filename == '..' }
|
86
|
+
entries0.sort!
|
87
|
+
if Tb::Cmd.opt_ls_A
|
88
|
+
entries = entries1 + entries2
|
89
|
+
else
|
90
|
+
entries = entries0 + entries1 + entries2
|
91
|
+
end
|
92
|
+
else
|
93
|
+
entries.reject! {|filename| /\A\./ =~ filename }
|
94
|
+
end
|
95
|
+
if !Tb::Cmd.opt_ls_R
|
96
|
+
entries.each {|filename|
|
97
|
+
ls_file(gen, dir + filename, nil)
|
98
|
+
}
|
99
|
+
else
|
100
|
+
dirs = []
|
101
|
+
entries.each {|filename|
|
102
|
+
path = dir + filename
|
103
|
+
st2 = ls_get_stat(path)
|
104
|
+
next if !st2
|
105
|
+
if filename == '.' || filename == '..'
|
106
|
+
if dir.to_s != '.'
|
107
|
+
path = Pathname(dir.to_s + "/" + filename)
|
108
|
+
end
|
109
|
+
ls_file(gen, path, st2)
|
110
|
+
elsif st2.directory?
|
111
|
+
dirs << [path, st2]
|
112
|
+
else
|
113
|
+
ls_file(gen, path, st2)
|
114
|
+
end
|
115
|
+
}
|
116
|
+
dirs.each {|path, st2|
|
117
|
+
ls_file(gen, path, st2)
|
118
|
+
ls_dir(gen, path, st2)
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def (Tb::Cmd).ls_file(gen, path, st)
|
124
|
+
if 0 < Tb::Cmd.opt_ls_l
|
125
|
+
if !st
|
126
|
+
st = ls_get_stat(path)
|
127
|
+
return if !st
|
128
|
+
end
|
129
|
+
gen << ls_long_info(path, st)
|
130
|
+
else
|
131
|
+
gen << [path.to_s]
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def (Tb::Cmd).ls_get_stat(path)
|
136
|
+
begin
|
137
|
+
st = path.lstat
|
138
|
+
rescue SystemCallError
|
139
|
+
@fail = true
|
140
|
+
warn "tb: #{$!}: #{path}"
|
141
|
+
return nil
|
142
|
+
end
|
143
|
+
st
|
144
|
+
end
|
145
|
+
|
146
|
+
def (Tb::Cmd).ls_long_header
|
147
|
+
if 1 < Tb::Cmd.opt_ls_l
|
148
|
+
%w[dev ino mode filemode nlink uid user gid group rdev size blksize blocks atime mtime ctime filename symlink]
|
149
|
+
else
|
150
|
+
%w[filemode nlink user group size mtime filename symlink]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def (Tb::Cmd).ls_long_info(path, st)
|
155
|
+
ls_long_header.map {|info_type|
|
156
|
+
self.send("ls_info_#{info_type}", path, st)
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
def (Tb::Cmd).ls_info_dev(path, st) sprintf("0x%x", st.dev) end
|
161
|
+
def (Tb::Cmd).ls_info_ino(path, st) st.ino end
|
162
|
+
def (Tb::Cmd).ls_info_mode(path, st) sprintf("0%o", st.mode) end
|
163
|
+
def (Tb::Cmd).ls_info_nlink(path, st) st.nlink end
|
164
|
+
def (Tb::Cmd).ls_info_uid(path, st) st.uid end
|
165
|
+
def (Tb::Cmd).ls_info_gid(path, st) st.gid end
|
166
|
+
def (Tb::Cmd).ls_info_rdev(path, st) sprintf("0x%x", st.rdev) end
|
167
|
+
def (Tb::Cmd).ls_info_size(path, st) st.size end
|
168
|
+
def (Tb::Cmd).ls_info_blksize(path, st) st.blksize end
|
169
|
+
def (Tb::Cmd).ls_info_blocks(path, st) st.blocks end
|
170
|
+
|
171
|
+
def (Tb::Cmd).ls_info_filemode(path, st)
|
172
|
+
entry_type =
|
173
|
+
case st.ftype
|
174
|
+
when "file" then '-'
|
175
|
+
when "directory" then 'd'
|
176
|
+
when "characterSpecial" then 'c'
|
177
|
+
when "blockSpecial" then 'b'
|
178
|
+
when "fifo" then 'p'
|
179
|
+
when "link" then 'l'
|
180
|
+
when "socket" then 's'
|
181
|
+
when "unknown" then '?'
|
182
|
+
else '?'
|
183
|
+
end
|
184
|
+
m = st.mode
|
185
|
+
sprintf("%s%c%c%c%c%c%c%c%c%c",
|
186
|
+
entry_type,
|
187
|
+
(m & 0400 == 0 ? ?- : ?r),
|
188
|
+
(m & 0200 == 0 ? ?- : ?w),
|
189
|
+
(m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
|
190
|
+
(m & 04000 == 0 ? ?x : ?s)),
|
191
|
+
(m & 0040 == 0 ? ?- : ?r),
|
192
|
+
(m & 0020 == 0 ? ?- : ?w),
|
193
|
+
(m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
|
194
|
+
(m & 02000 == 0 ? ?x : ?s)),
|
195
|
+
(m & 0004 == 0 ? ?- : ?r),
|
196
|
+
(m & 0002 == 0 ? ?- : ?w),
|
197
|
+
(m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
|
198
|
+
(m & 01000 == 0 ? ?x : ?t)))
|
199
|
+
end
|
200
|
+
|
201
|
+
def (Tb::Cmd).ls_info_user(path, st)
|
202
|
+
uid = st.uid
|
203
|
+
begin
|
204
|
+
pw = Etc.getpwuid(uid)
|
205
|
+
rescue ArgumentError
|
206
|
+
end
|
207
|
+
if pw
|
208
|
+
pw.name
|
209
|
+
else
|
210
|
+
uid
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def (Tb::Cmd).ls_info_group(path, st)
|
215
|
+
gid = st.gid
|
216
|
+
begin
|
217
|
+
gr = Etc.getgrgid(gid)
|
218
|
+
rescue ArgumentError
|
219
|
+
end
|
220
|
+
if gr
|
221
|
+
gr.name
|
222
|
+
else
|
223
|
+
gid
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def (Tb::Cmd).ls_info_atime(path, st)
|
228
|
+
if 1 < Tb::Cmd.opt_ls_l
|
229
|
+
st.atime.iso8601(9)
|
230
|
+
else
|
231
|
+
st.atime.iso8601
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def (Tb::Cmd).ls_info_mtime(path, st)
|
236
|
+
if 1 < Tb::Cmd.opt_ls_l
|
237
|
+
st.mtime.iso8601(9)
|
238
|
+
else
|
239
|
+
st.mtime.iso8601
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
def (Tb::Cmd).ls_info_ctime(path, st)
|
244
|
+
if 1 < Tb::Cmd.opt_ls_l
|
245
|
+
st.ctime.iso8601(9)
|
246
|
+
else
|
247
|
+
st.ctime.iso8601
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def (Tb::Cmd).ls_info_filename(path, st)
|
252
|
+
path
|
253
|
+
end
|
254
|
+
|
255
|
+
def (Tb::Cmd).ls_info_symlink(path, st)
|
256
|
+
return nil if !st.symlink?
|
257
|
+
begin
|
258
|
+
File.readlink(path)
|
259
|
+
rescue SystemCallError
|
260
|
+
@fail = true
|
261
|
+
warn "tb: #{$!}: #{path}"
|
262
|
+
return nil
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def (Tb::Cmd).real_pathname_string(str)
|
267
|
+
if str.respond_to? :force_encoding
|
268
|
+
# pathname is a sequence of bytes on Unix.
|
269
|
+
str.dup.force_encoding("ASCII-8BIT")
|
270
|
+
else
|
271
|
+
str
|
272
|
+
end
|
273
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# Copyright (C) 2011 Tanaka Akira <akr@fsij.org>
|
2
|
+
#
|
3
|
+
# Redistribution and use in source and binary forms, with or without
|
4
|
+
# modification, are permitted provided that the following conditions are met:
|
5
|
+
#
|
6
|
+
# 1. Redistributions of source code must retain the above copyright notice, this
|
7
|
+
# list of conditions and the following disclaimer.
|
8
|
+
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
9
|
+
# this list of conditions and the following disclaimer in the documentation
|
10
|
+
# and/or other materials provided with the distribution.
|
11
|
+
# 3. The name of the author may not be used to endorse or promote products
|
12
|
+
# derived from this software without specific prior written permission.
|
13
|
+
#
|
14
|
+
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
15
|
+
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
16
|
+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
17
|
+
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
18
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
|
19
|
+
# OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
20
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
21
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
22
|
+
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
23
|
+
# OF SUCH DAMAGE.
|
24
|
+
|
25
|
+
Tb::Cmd.subcommands << 'mheader'
|
26
|
+
|
27
|
+
Tb::Cmd.default_option[:opt_mheader_count] = nil
|
28
|
+
|
29
|
+
def (Tb::Cmd).op_mheader
|
30
|
+
op = OptionParser.new
|
31
|
+
op.banner = 'Usage: tb mheader [OPTS] [TABLE]'
|
32
|
+
define_common_option(op, "ho", "--no-pager")
|
33
|
+
op.def_option('-c N', 'number of header records') {|arg| Tb::Cmd.opt_mheader_count = arg.to_i }
|
34
|
+
op
|
35
|
+
end
|
36
|
+
|
37
|
+
def (Tb::Cmd).main_mheader(argv)
|
38
|
+
op_mheader.parse!(argv)
|
39
|
+
exit_if_help('mheader')
|
40
|
+
argv = ['-'] if argv.empty?
|
41
|
+
header = []
|
42
|
+
if Tb::Cmd.opt_mheader_count
|
43
|
+
c = Tb::Cmd.opt_mheader_count
|
44
|
+
header_end_p = lambda {
|
45
|
+
c -= 1
|
46
|
+
c == 0 ? header.map {|a| a.compact.join(' ').strip } : nil
|
47
|
+
}
|
48
|
+
else
|
49
|
+
header_end_p = lambda {
|
50
|
+
h2 = header.map {|a| a.compact.join(' ').strip }.uniq
|
51
|
+
header.length == h2.length ? h2 : nil
|
52
|
+
}
|
53
|
+
end
|
54
|
+
with_table_stream_output {|gen|
|
55
|
+
Tb::CatReader.open(argv, true) {|tblreader|
|
56
|
+
tblreader.each {|ary|
|
57
|
+
if header
|
58
|
+
ary.each_with_index {|v,i|
|
59
|
+
header[i] ||= []
|
60
|
+
header[i] << v if header[i].empty? || header[i].last != v
|
61
|
+
}
|
62
|
+
h2 = header_end_p.call
|
63
|
+
if h2
|
64
|
+
gen << h2
|
65
|
+
header = nil
|
66
|
+
end
|
67
|
+
else
|
68
|
+
gen << ary
|
69
|
+
end
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
if header
|
74
|
+
warn "no header found."
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|