tb 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+