tb 0.1 → 0.2

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.
Files changed (81) hide show
  1. data/README +156 -5
  2. data/bin/tb +2 -1110
  3. data/lib/tb.rb +4 -2
  4. data/lib/tb/catreader.rb +131 -0
  5. data/lib/tb/cmd_cat.rb +65 -0
  6. data/lib/tb/cmd_consecutive.rb +79 -0
  7. data/lib/tb/cmd_crop.rb +105 -0
  8. data/lib/tb/cmd_cross.rb +119 -0
  9. data/lib/tb/cmd_csv.rb +42 -0
  10. data/lib/tb/cmd_cut.rb +77 -0
  11. data/lib/tb/cmd_grep.rb +76 -0
  12. data/lib/tb/cmd_group.rb +82 -0
  13. data/lib/tb/cmd_gsub.rb +77 -0
  14. data/lib/tb/cmd_help.rb +98 -0
  15. data/lib/tb/cmd_join.rb +81 -0
  16. data/lib/tb/cmd_json.rb +60 -0
  17. data/lib/tb/cmd_ls.rb +273 -0
  18. data/lib/tb/cmd_mheader.rb +77 -0
  19. data/lib/tb/cmd_newfield.rb +59 -0
  20. data/lib/tb/cmd_pnm.rb +43 -0
  21. data/lib/tb/cmd_pp.rb +70 -0
  22. data/lib/tb/cmd_rename.rb +58 -0
  23. data/lib/tb/cmd_shape.rb +67 -0
  24. data/lib/tb/cmd_sort.rb +58 -0
  25. data/lib/tb/cmd_svn_log.rb +158 -0
  26. data/lib/tb/cmd_tsv.rb +43 -0
  27. data/lib/tb/cmd_yaml.rb +47 -0
  28. data/lib/tb/cmdmain.rb +45 -0
  29. data/lib/tb/cmdtop.rb +58 -0
  30. data/lib/tb/cmdutil.rb +327 -0
  31. data/lib/tb/csv.rb +30 -6
  32. data/lib/tb/fieldset.rb +39 -41
  33. data/lib/tb/pager.rb +132 -0
  34. data/lib/tb/pnm.rb +357 -0
  35. data/lib/tb/reader.rb +18 -128
  36. data/lib/tb/record.rb +3 -3
  37. data/lib/tb/ropen.rb +70 -0
  38. data/lib/tb/{pathfinder.rb → search.rb} +69 -34
  39. data/lib/tb/tsv.rb +29 -1
  40. data/sample/colors.ppm +0 -0
  41. data/sample/gradation.pgm +0 -0
  42. data/sample/langs.csv +46 -0
  43. data/sample/tbplot +293 -0
  44. data/test-all-cov.rb +65 -0
  45. data/test-all.rb +5 -0
  46. data/test/test_basic.rb +99 -2
  47. data/test/test_catreader.rb +27 -0
  48. data/test/test_cmd_cat.rb +118 -0
  49. data/test/test_cmd_consecutive.rb +90 -0
  50. data/test/test_cmd_crop.rb +101 -0
  51. data/test/test_cmd_cross.rb +113 -0
  52. data/test/test_cmd_csv.rb +129 -0
  53. data/test/test_cmd_cut.rb +100 -0
  54. data/test/test_cmd_grep.rb +89 -0
  55. data/test/test_cmd_group.rb +181 -0
  56. data/test/test_cmd_gsub.rb +103 -0
  57. data/test/test_cmd_help.rb +190 -0
  58. data/test/test_cmd_join.rb +197 -0
  59. data/test/test_cmd_json.rb +75 -0
  60. data/test/test_cmd_ls.rb +203 -0
  61. data/test/test_cmd_mheader.rb +86 -0
  62. data/test/test_cmd_newfield.rb +63 -0
  63. data/test/test_cmd_pnm.rb +35 -0
  64. data/test/test_cmd_pp.rb +62 -0
  65. data/test/test_cmd_rename.rb +91 -0
  66. data/test/test_cmd_shape.rb +50 -0
  67. data/test/test_cmd_sort.rb +105 -0
  68. data/test/test_cmd_tsv.rb +67 -0
  69. data/test/test_cmd_yaml.rb +55 -0
  70. data/test/test_cmdtty.rb +154 -0
  71. data/test/test_cmdutil.rb +43 -0
  72. data/test/test_csv.rb +10 -0
  73. data/test/test_fieldset.rb +42 -0
  74. data/test/test_pager.rb +142 -0
  75. data/test/test_pnm.rb +374 -0
  76. data/test/test_reader.rb +147 -0
  77. data/test/test_record.rb +49 -0
  78. data/test/test_search.rb +575 -0
  79. data/test/test_tsv.rb +7 -0
  80. metadata +108 -5
  81. data/lib/tb/qtsv.rb +0 -93
@@ -0,0 +1,59 @@
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 << 'newfield'
26
+
27
+ def (Tb::Cmd).op_newfield
28
+ op = OptionParser.new
29
+ op.banner = 'Usage: tb newfield [OPTS] FIELD RUBY-EXP [TABLE]'
30
+ define_common_option(op, "ho", "--no-pager")
31
+ op
32
+ end
33
+
34
+ def (Tb::Cmd).main_newfield(argv)
35
+ op_newfield.parse!(argv)
36
+ exit_if_help('newfield')
37
+ err('no new field name given.') if argv.empty?
38
+ field = argv.shift
39
+ err('no ruby expression given.') if argv.empty?
40
+ rubyexp = argv.shift
41
+ pr = eval("lambda {|_| #{rubyexp} }")
42
+ argv = ['-'] if argv.empty?
43
+ Tb::CatReader.open(argv, Tb::Cmd.opt_N) {|tblreader|
44
+ renamed_header = [field] + tblreader.header
45
+ with_table_stream_output {|gen|
46
+ gen.output_header(renamed_header)
47
+ tblreader.each {|ary|
48
+ h = {}
49
+ ary.each_with_index {|str, i|
50
+ f = tblreader.field_from_index_ex(i)
51
+ h[f] = str
52
+ }
53
+ gen << [pr.call(h), *ary]
54
+ }
55
+ }
56
+ }
57
+ end
58
+
59
+
@@ -0,0 +1,43 @@
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 << 'pnm'
26
+
27
+ def (Tb::Cmd).op_pnm
28
+ op = OptionParser.new
29
+ op.banner = 'Usage: tb pnm [OPTS] [TABLE]'
30
+ define_common_option(op, "hNo", "--no-pager")
31
+ op
32
+ end
33
+
34
+ def (Tb::Cmd).main_pnm(argv)
35
+ op_pnm.parse!(argv)
36
+ exit_if_help('pnm')
37
+ argv = ['-'] if argv.empty?
38
+ tbl = Tb::CatReader.open(argv, Tb::Cmd.opt_N) {|creader| build_table(creader) }
39
+ with_output {|out|
40
+ tbl.generate_pnm(out)
41
+ }
42
+ end
43
+
@@ -0,0 +1,70 @@
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 << 'pp'
26
+
27
+ def (Tb::Cmd).op_pp
28
+ op = OptionParser.new
29
+ op.banner = 'Usage: tb pp [OPTS] [TABLE]'
30
+ define_common_option(op, "hNo", "--no-pager")
31
+ op
32
+ end
33
+
34
+ def (Tb::Cmd).main_pp(argv)
35
+ op_pp.parse!(argv)
36
+ exit_if_help('pp')
37
+ argv.unshift '-' if argv.empty?
38
+ with_output {|out|
39
+ argv.each {|filename|
40
+ tablereader_open(filename) {|tblreader|
41
+ tblreader.each {|ary|
42
+ a = []
43
+ ary.each_with_index {|v, i|
44
+ next if v.nil?
45
+ a << [tblreader.field_from_index_ex(i), v]
46
+ }
47
+ q = PP.new(out, 79)
48
+ q.guard_inspect_key {
49
+ q.group(1, '{', '}') {
50
+ q.seplist(a, nil, :each) {|kv|
51
+ k, v = kv
52
+ q.group {
53
+ q.pp k
54
+ q.text '=>'
55
+ q.group(1) {
56
+ q.breakable ''
57
+ q.pp v
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ q.flush
64
+ out << "\n"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ end
70
+
@@ -0,0 +1,58 @@
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 << 'rename'
26
+
27
+ def (Tb::Cmd).op_rename
28
+ op = OptionParser.new
29
+ op.banner = 'Usage: tb rename [OPTS] SRC,DST,... [TABLE]'
30
+ define_common_option(op, "ho", "--no-pager")
31
+ op
32
+ end
33
+
34
+ def (Tb::Cmd).main_rename(argv)
35
+ op_rename.parse!(argv)
36
+ exit_if_help('rename')
37
+ err('rename fields not given.') if argv.empty?
38
+ fs = split_field_list_argument(argv.shift)
39
+ argv = ['-'] if argv.empty?
40
+ h = {}
41
+ fs.each_slice(2) {|sf, df| h[sf] = df }
42
+ Tb::CatReader.open(argv, Tb::Cmd.opt_N) {|tblreader|
43
+ header = tblreader.header
44
+ h.each {|sf, df|
45
+ unless header.include? sf
46
+ err "field not found: #{sf.inspect}"
47
+ end
48
+ }
49
+ renamed_header = tblreader.header.map {|f| h.fetch(f, f) }
50
+ with_table_stream_output {|gen|
51
+ gen.output_header(renamed_header)
52
+ tblreader.each {|ary|
53
+ gen << ary
54
+ }
55
+ }
56
+ }
57
+ end
58
+
@@ -0,0 +1,67 @@
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 << 'shape'
26
+
27
+ def (Tb::Cmd).op_shape
28
+ op = OptionParser.new
29
+ op.banner = 'Usage: tb shape [OPTS] [TABLE ...]'
30
+ define_common_option(op, "hNo", "--no-pager")
31
+ op
32
+ end
33
+
34
+ def (Tb::Cmd).main_shape(argv)
35
+ op_shape.parse!(argv)
36
+ exit_if_help('shape')
37
+ filenames = argv.empty? ? ['-'] : argv
38
+ result = Tb.new(%w[header_fields min_fields max_fields records filename])
39
+ filenames.each {|filename|
40
+ tablereader_open(filename) {|tblreader|
41
+ num_header_fields = tblreader.header.length
42
+ min_num_fields = nil
43
+ max_num_fields = nil
44
+ num_records = 0
45
+ tblreader.each {|ary|
46
+ num_records += 1
47
+ n = ary.length
48
+ if min_num_fields.nil?
49
+ min_num_fields = max_num_fields = n
50
+ else
51
+ min_num_fields = n if n < min_num_fields
52
+ max_num_fields = n if max_num_fields < n
53
+ end
54
+ }
55
+ result.insert({'header_fields'=>num_header_fields,
56
+ 'min_fields'=>min_num_fields,
57
+ 'max_fields'=>max_num_fields,
58
+ 'records'=>num_records,
59
+ 'filename'=>filename})
60
+ }
61
+ }
62
+ with_output {|out|
63
+ # don't use tbl_generate_csv() because the header should always outputted.
64
+ result.generate_csv(out)
65
+ }
66
+ end
67
+
@@ -0,0 +1,58 @@
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 << 'sort'
26
+
27
+ Tb::Cmd.default_option[:opt_sort_f] = nil
28
+
29
+ def (Tb::Cmd).op_sort
30
+ op = OptionParser.new
31
+ op.banner = 'Usage: tb sort [OPTS] [TABLE]'
32
+ define_common_option(op, "hNo", "--no-pager")
33
+ op.def_option('-f FIELD,...', 'specify sort keys') {|fs| Tb::Cmd.opt_sort_f = fs }
34
+ op
35
+ end
36
+
37
+ def (Tb::Cmd).main_sort(argv)
38
+ op_sort.parse!(argv)
39
+ exit_if_help('sort')
40
+ argv = ['-'] if argv.empty?
41
+ if Tb::Cmd.opt_sort_f
42
+ fs = split_field_list_argument(Tb::Cmd.opt_sort_f)
43
+ else
44
+ fs = nil
45
+ end
46
+ tbl = Tb::CatReader.open(argv, Tb::Cmd.opt_N) {|reader| build_table(reader) }
47
+ if fs
48
+ blk = lambda {|rec| fs.map {|f| smart_cmp_value(rec[f]) } }
49
+ else
50
+ blk = lambda {|rec| rec.map {|k, v| smart_cmp_value(v) } }
51
+ end
52
+ tbl2 = tbl.reorder_records_by(&blk)
53
+ with_output {|out|
54
+ tbl_generate_csv(tbl2, out)
55
+ }
56
+ end
57
+
58
+
@@ -0,0 +1,158 @@
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
+ require 'rexml/document'
26
+
27
+ Tb::Cmd.subcommands << 'svn-log'
28
+
29
+ Tb::Cmd.default_option[:opt_svn_log_svn_command] = nil
30
+ Tb::Cmd.default_option[:opt_svn_log_xml] = nil
31
+
32
+ def (Tb::Cmd).op_svn_log
33
+ op = OptionParser.new
34
+ op.banner = 'Usage: tb svn-log [OPTS] -- [SVN-LOG-ARGS]'
35
+ define_common_option(op, "hNo", "--no-pager")
36
+ op.def_option('--svn-command COMMAND', 'specify the svn command (default: svn)') {|command| Tb::Cmd.opt_svn_log_svn_command = command }
37
+ op.def_option('--svn-log-xml FILE', 'specify the result svn log --xml') {|filename| Tb::Cmd.opt_svn_log_xml = filename }
38
+ op
39
+ end
40
+
41
+ class Tb::Cmd::SVNLOGListener
42
+ def initialize(gen)
43
+ @gen = gen
44
+ @header = nil
45
+ @elt_stack = []
46
+ @att_stack = []
47
+ @log = nil
48
+ end
49
+
50
+ def tag_start(name, attrs)
51
+ @elt_stack.push name
52
+ @att_stack.push attrs
53
+ case @elt_stack
54
+ when %w[log logentry]
55
+ @log = { 'rev' => attrs['revision'] }
56
+ when %w[log logentry paths]
57
+ @log['paths'] = []
58
+ when %w[log logentry paths path]
59
+ @log['paths'] << { 'kind' => attrs['kind'], 'action' => attrs['action'] }
60
+ end
61
+ end
62
+
63
+ def text(text)
64
+ case @elt_stack
65
+ when %w[log logentry author]
66
+ @log['author'] = text
67
+ when %w[log logentry date]
68
+ @log['date'] = text
69
+ when %w[log logentry paths path]
70
+ @log['paths'].last['path'] = text
71
+ when %w[log logentry msg]
72
+ @log['msg'] = text
73
+ end
74
+ end
75
+
76
+ def tag_end(name)
77
+ case @elt_stack
78
+ when %w[log logentry]
79
+ if !@header
80
+ if @log['paths']
81
+ @header = %w[rev author date msg kind action path]
82
+ else
83
+ @header = %w[rev author date msg]
84
+ end
85
+ @gen.output_header @header
86
+ end
87
+ if @log['paths']
88
+ @log['paths'].each {|h|
89
+ @gen << (@log.values_at(*%w[rev author date msg]) + h.values_at(*%w[kind action path]))
90
+ }
91
+ else
92
+ @gen << @log.values_at(*%w[rev author date msg])
93
+ end
94
+ @log = nil
95
+ end
96
+ @elt_stack.pop
97
+ @att_stack.pop
98
+ end
99
+
100
+ def instruction(name, instruction)
101
+ end
102
+
103
+ def comment(comment)
104
+ end
105
+
106
+ def doctype(name, pub_sys, long_name, uri)
107
+ end
108
+
109
+ def doctype_end
110
+ end
111
+
112
+ def attlistdecl(element_name, attributes, raw_content)
113
+ end
114
+
115
+ def elementdecl(content)
116
+ end
117
+
118
+ def entitydecl(content)
119
+ end
120
+
121
+ def notationdecl(content)
122
+ end
123
+
124
+ def entity(content)
125
+ end
126
+
127
+ def cdata(content)
128
+ # I guess svn doesn't use CDATA...
129
+ end
130
+
131
+ def xmldecl(version, encoding, standalone)
132
+ end
133
+ end
134
+
135
+ def (Tb::Cmd).svn_log_with_svn_log(argv)
136
+ if Tb::Cmd.opt_svn_log_xml
137
+ File.open(Tb::Cmd.opt_svn_log_xml) {|f|
138
+ yield f
139
+ }
140
+ else
141
+ svn = Tb::Cmd.opt_svn_log_svn_command || 'svn'
142
+ IO.popen(['svn', 'log', '--xml', *argv]) {|f|
143
+ yield f
144
+ }
145
+ end
146
+ end
147
+
148
+ def (Tb::Cmd).main_svn_log(argv)
149
+ op_svn_log.parse!(argv)
150
+ exit_if_help('svn-log')
151
+ with_table_stream_output {|gen|
152
+ svn_log_with_svn_log(argv) {|f|
153
+ listener = Tb::Cmd::SVNLOGListener.new(gen)
154
+ REXML::Parsers::StreamParser.new(f, listener).parse
155
+ }
156
+ }
157
+ end
158
+