Top Level Namespace

Defined Under Namespace

Modules: Enumerable, Tb

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.decode_a1_addressing_col(str) ⇒ Object



58
59
60
# File 'lib/tb/cmd_crop.rb', line 58

def (Tb::Cmd).decode_a1_addressing_col(str)
  (26**str.length-1)/25+str.tr("A-Z", "0-9A-P").to_i(26)
end

.exit_if_help(subcommand) ⇒ Object



86
87
88
89
90
91
# File 'lib/tb/cmd_help.rb', line 86

def (Tb::Cmd).exit_if_help(subcommand)
  if 0 < Tb::Cmd.opt_help
    show_help(subcommand)
    exit
  end
end

.git_each_commit(f) ⇒ Object



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/tb/cmd_git.rb', line 196

def (Tb::Cmd).git_each_commit(f)
  while chunk = f.gets("\x01commit-separator\x01\n")
    chunk.chomp!("\x01commit-separator\x01\n")
    next if chunk.empty? # beginning of the output
    if /\nend-commit\n/ !~ chunk
      warn "unexpected git output (end-commit): #{chunk.inspect}"
      next
    end
    commit_info, files = $`, $'
    files.sub!(/\A\n/, '')
    h = git_parse_commit(commit_info, files)
    yield h
  end

end

.git_parse_commit(commit_info, files) ⇒ Object



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/tb/cmd_git.rb', line 148

def (Tb::Cmd).git_parse_commit(commit_info, files)
  commit_info = commit_info.split(/\n(?=[a-z])/)
  files_raw = {}
  files_numstat = {}
  files.split(/\n/).each {|file_line|
    if /\A:(\d+) (\d+) ([0-9a-f]+) ([0-9a-f]+) (\S+)\t(.+)\z/ =~ file_line
      mode1, mode2, hash1, hash2, status, filename = $1, $2, $3, $4, $5, $6
      filename = git_unescape_filename(filename)
      files_raw[filename] = [mode1, mode2, hash1, hash2, status]
    elsif /\A(\d+|-)\t(\d+|-)\t(.+)\z/ =~ file_line
      add, del, filename = $1, $2, $3
      add = add == '-' ? nil : add.to_i
      del = del == '-' ? nil : del.to_i
      filename = git_unescape_filename(filename)
      files_numstat[filename] = [add, del]
    else
      warn "unexpected git output (raw/numstat): #{file_line.inspect}"
    end
  }
  files_csv = ""
  files_csv << %w[mode1 mode2 hash1 hash2 add del status filename].to_csv
  files_raw.each {|filename, (mode1, mode2, hash1, hash2, status)|
    add, del = files_numstat[filename]
    files_csv << [mode1, mode2, hash1, hash2, add, del, status, filename].to_csv
  }
  h = {}
  commit_info.each {|s|
    if /:/ !~ s
      warn "unexpected git output (header:value): #{s.inspect}"
      next
    end
    k = $`
    v = $'.gsub(/\n /, "\n") # remove indent generated by %w(0,0,1)
    case k
    when /\A(?:author-date|committer-date)/
      v = v.sub(/\A(\d+-\d\d-\d\d) (\d\d:\d\d:\d\d) ([-+]\d\d\d\d)\z/, '\1T\2\3')
    when /\Aparents\z/
      v = ['parent', *v.split(/ /)].map {|c| c + "\n" }.join("")
    when /\Aref-names\z/
      v = v.strip.gsub(/\A\(|\)\z/, '')
      v = ['ref-name', *v.split(/, /)].map {|c| c + "\n" }.join("")
    end
    h[k] = v
  }
  h['files'] = files_csv
  h
end

.git_unescape_filename(filename) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/tb/cmd_git.rb', line 121

def (Tb::Cmd).git_unescape_filename(filename)
  if /\A"/ =~ filename
    $'.chomp('"').gsub(/\\((\d\d\d)|[abtnvfr"\\])/) {
      str = $1
      if $2
        [str.to_i(8)].pack("C")
      else
        case str
        when 'a' then "\a"
        when 'b' then "\b"
        when 't' then "\t"
        when 'n' then "\n"
        when 'v' then "\v"
        when 'f' then "\f"
        when 'r' then "\r"
        when '"' then '"'
        when '\\' then "\\"
        else
          warn "unexpected escape: #{str.inspect}"
        end
      end
    }
  else
    filename
  end
end

.git_with_git_log(dir) ⇒ Object



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/tb/cmd_git.rb', line 86

def (Tb::Cmd).git_with_git_log(dir)
  if Tb::Cmd.opt_git_debug_input
    File.open(Tb::Cmd.opt_git_debug_input) {|f|
      yield f
    }
  else
    git = Tb::Cmd.opt_git_command || 'git'
    # depends Ruby 1.9.
    command = [
      git,
      'log',
      "--pretty=#{Tb::Cmd::GIT_LOG_PRETTY_FORMAT}",
      '--decorate=full',
      '--raw',
      '--numstat',
      '--abbrev=40',
      '.',
      {:chdir=>dir}
    ]
    $stderr.puts "git command line: #{command.inspect}" if 1 <= Tb::Cmd.opt_debug
    if Tb::Cmd.opt_git_debug_output
      # File.realdirpath is required before Ruby 2.0.
      command.last[:out] = File.realdirpath(Tb::Cmd.opt_git_debug_output)
      system(*command)
      File.open(Tb::Cmd.opt_git_debug_output) {|f|
        yield f
      }
    else
      IO.popen(command) {|f|
        yield f
      }
    end
  end
end

.list_summary_of_subcommandsObject



46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/tb/cmd_help.rb', line 46

def (Tb::Cmd).list_summary_of_subcommands
  with_output {|f|
    f.print <<'End'
End
    subcommand_maxlen = Tb::Cmd.subcommands.map {|subcommand| subcommand.length }.max
    fmt = "%-#{subcommand_maxlen}s : %s"
    Tb::Cmd.subcommands.each {|subcommand|
      banner = self.subcommand_send("op", subcommand).banner
      summary = banner[/^Usage: (.*)\n(.*)/, 2]
      f.puts(fmt % [subcommand, summary])
    }
  }
end

.main(argv) ⇒ Object



43
44
45
46
47
48
49
50
# File 'lib/tb/cmdmain.rb', line 43

def (Tb::Cmd).main(argv)
  main_body(argv)
rescue SystemExit
  $stderr.puts $!.message if $!.message != 'exit'
  raise
rescue Errno::EPIPE
  exit false
end

.main_body(argv) ⇒ Object

Copyright © 2011-2012 Tanaka Akira <[email protected]>

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
   copyright notice, this list of conditions and the following
   disclaimer in the documentation and/or other materials provided
   with the distribution.
3. The name of the author may not be used to endorse or promote
   products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.



29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/tb/cmdmain.rb', line 29

def (Tb::Cmd).main_body(argv)
  subcommand = argv.shift
  if subcommand == '-h' || subcommand == '--help'
    main_help(argv)
  elsif Tb::Cmd.subcommands.include?(subcommand)
    self.subcommand_send("main", subcommand, argv)
  elsif subcommand == nil
    usage_list_subcommands
    true
  else
    err "unexpected subcommand: #{subcommand.inspect}"
  end
end

.main_cat(argv) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/tb/cmd_cat.rb', line 61

def (Tb::Cmd).main_cat(argv)
  op_cat.parse!(argv)
  exit_if_help('cat')
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N, Tb::Cmd.opt_cat_with_filename)
  output_tbenum(creader)
end

.main_consecutive(argv) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/tb/cmd_consecutive.rb', line 56

def (Tb::Cmd).main_consecutive(argv)
  op_consecutive.parse!(argv)
  exit_if_help('consecutive')
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    buf = []
    empty = true
    creader.with_cumulative_header {|header0|
      if header0
        y.set_header header0.map {|f| (1..Tb::Cmd.opt_consecutive_n).map {|i| "#{f}_#{i}" } }.flatten(1)
      end
    }.each {|pairs, header|
      buf << pairs
      if buf.length == Tb::Cmd.opt_consecutive_n
        pairs2 = {}
        header.each {|f|
          Tb::Cmd.opt_consecutive_n.times {|i|
            ps = buf[i]
            next if !ps.has_key?(f)
            v = ps[f]
            if Tb::Cmd.opt_N
              pairs2[((f.to_i-1) * Tb::Cmd.opt_consecutive_n + i + 1).to_s] = v
            else
              pairs2["#{f}_#{i+1}"] = v
            end
          }
        }
        empty = false
        y.yield pairs2
        buf.shift
      end
    }
  }
  output_tbenum(er)
end

.main_crop(argv) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/tb/cmd_crop.rb', line 62

def (Tb::Cmd).main_crop(argv)
  op_crop.parse!(argv)
  exit_if_help('crop')
  argv = ['-'] if argv.empty?
  stream = false
  if Tb::Cmd.opt_crop_range
    case Tb::Cmd.opt_crop_range
    when /\AR(\d+)C(\d+):R(\d+)C(\d+)\z/ # 1-based (R1C1 reference style)
      stream = true
      range_row1 = $1.to_i
      range_col1 = $2.to_i
      range_row2 = $3.to_i
      range_col2 = $4.to_i
    when /\A([A-Z]+)(\d+):([A-Z]+)(\d+)\z/ # 1-based (A1 reference style)
      stream = true
      range_col1 = decode_a1_addressing_col($1)
      range_row1 = $2.to_i
      range_col2 = decode_a1_addressing_col($3)
      range_row2 = $4.to_i
    else
      raise ArgumentError, "unexpected range argument: #{Tb::Cmd.opt_crop_range.inspect}"
    end
  end
  if stream
    creader = Tb::CatReader.open(argv, true)
    er = Tb::Enumerator.new {|y|
      rownum = 1
      creader.each {|pairs|
        if range_row2 < rownum
          break
        end
        if range_row1 <= rownum
          pairs2 = pairs.reject {|f, v|
            f = f.to_i
            f < range_col1 || range_col2 < f
          }
          pairs2 = pairs2.map {|f, v|
            [(f.to_i - range_col1 + 1).to_s, v]
          }
          y.yield Hash[pairs2]
        end
        rownum += 1
      }
    }
    Tb::Cmd.opt_N = true
    output_tbenum(er)
  else
    creader = Tb::CatReader.open(argv, true)
    last_nonempty_row = nil
    lmargin_min = nil
    ter = Enumerator.new {|y|
      numrows = 0
      creader.each {|pairs|
        ary = []
        pairs.each {|f, v|
          ary[f.to_i-1] = v
        }
        while !ary.empty? && (ary.last.nil? || ary.last == '')
          ary.pop
        end
        if numrows == 0 && ary.empty?
          next
        end
        if !ary.empty?
          lmargin = 0
          while lmargin < ary.length
            if !ary[lmargin].nil? && ary[lmargin] != ''
              break
            end
            lmargin += 1
          end
          if lmargin_min.nil? || lmargin < lmargin_min
            lmargin_min = lmargin
          end
        end
        last_nonempty_row = numrows if !ary.empty?
        y.yield ary
        numrows += 1
      }
    }.to_fileenumerator
    er = Tb::Enumerator.new {|y|
      ter.each_with_index {|ary, rownum|
        if last_nonempty_row < rownum
          break
        end
        ary.slice!(0, lmargin_min)
        pairs = Hash[ary.map.with_index {|v, i| ["#{i+1}", v]}]
        y.yield pairs
      }
    }
    Tb::Cmd.opt_N = true
    output_tbenum(er)
  end
end

.main_cross(argv) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/tb/cmd_cross.rb', line 69

def (Tb::Cmd).main_cross(argv)
  op_cross.parse!(argv)
  exit_if_help('cross')
  err('no vkey-fields given.') if argv.empty?
  vkfs = split_field_list_argument(argv.shift)
  err('no hkey-fields given.') if argv.empty?
  hkfs = split_field_list_argument(argv.shift)
  vhkfs = vkfs + hkfs
  if Tb::Cmd.opt_cross_fields.empty?
    num_aggregate_fields = 1
    opt_cross_fields = (vkfs + hkfs).map {|f| [f, Tb::Func::First, f] } +
      [['count', Tb::Func::Count, nil]]
  else
    num_aggregate_fields = Tb::Cmd.opt_cross_fields.length
    opt_cross_fields = (vkfs + hkfs).map {|f| [f, Tb::Func::First, f] } +
      Tb::Cmd.opt_cross_fields.map {|arg|
      agg_spec, new_field = split_field_list_argument(arg)
      new_field ||= agg_spec
      begin
        func_srcf = parse_aggregator_spec2(agg_spec)
      rescue ArgumentError
        err($!.message)
      end
      [new_field, *func_srcf]
    }
  end
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    hvs_hash = {}
    hvs_list = nil
    aggs_hash = nil
    op = Tb::Zipper.new(opt_cross_fields.map {|dstf, func, srcf| func })
    er = creader.with_header {|header0|
      vhkfs.each {|f|
        if !header0.include?(f)
          err("field not found: #{f}")
        end
      }
    }.extsort_reduce(op) {|pairs|
      vvs = vkfs.map {|f| pairs[f] }
      hvs = hkfs.map {|f| pairs[f] }
      vvsc = vvs.map {|v| Tb::Func.smart_cmp_value(v) }
      hvsc = hvs.map {|v| Tb::Func.smart_cmp_value(v) }
      hvs_hash[hvs] = hvsc
      aggs = opt_cross_fields.map {|dstf, func, srcf| func.start(srcf ? pairs[srcf] : true) }
      [[vvsc, hvsc], aggs]
    }
    all_representative = lambda {|_| 1 }
    all_before = lambda {|_|
      hvs_list = hvs_hash.keys.sort_by {|hvs| hvs_hash[hvs] }
      n = vkfs.length + hvs_list.length * num_aggregate_fields
      header1 = (1..n).map {|i| i.to_s }
      y.set_header header1
      hkfs.each_with_index {|hkf, i|
        next if Tb::Cmd.opt_cross_compact && i == hkfs.length - 1
        h1 = {}
        j = vkfs.length
        h1[j.to_s] = hkf
        hvs_list.each {|hkvs|
          num_aggregate_fields.times {
            j += 1
            h1[j.to_s] = hkvs[i]
          }
        }
        y.yield h1
      }
      h2 = {}
      j = 0
      vkfs.each {|vkf|
        j += 1
        h2[j.to_s] = vkf
      }
      hvs_list.each {|hkvs|
        opt_cross_fields.last(num_aggregate_fields).each {|dstf, func, srcf|
          j += 1
          if Tb::Cmd.opt_cross_compact
            h2[j.to_s] = hkvs[-1]
          else
            h2[j.to_s] = dstf
          end
        }
      }
      y.yield h2
    }
    v_representative = lambda {|((vvsc, _), _)|
      vvsc
    }
    v_before = lambda {|_|
      aggs_hash = {}
    }
    body = lambda {|(_, aggs)|
      hvs = aggs[vkfs.length, hkfs.length]
      aggs_hash[hvs] = op.aggregate(aggs)
    }
    v_after = lambda {|(_, aggs)|
      vvs = aggs[0, vkfs.length]
      ary = vvs
      hvs_list.each {|hvs|
        if aggs_hash.has_key? hvs
          ary.concat(aggs_hash[hvs].last(num_aggregate_fields))
        else
          ary.concat([nil] * num_aggregate_fields)
        end
      }
      pairs = {}
      ary.each_with_index {|v, i|
        pairs[(i+1).to_s] = v
      }
      y.yield pairs
    }
    er.detect_nested_group_by(
      [[all_representative, all_before],
       [v_representative, v_before, v_after]]).each(&body)
  }
  Tb::Cmd.opt_N = true
  output_tbenum(er)
end

.main_cut(argv) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/tb/cmd_cut.rb', line 59

def (Tb::Cmd).main_cut(argv)
  op_cut.parse!(argv)
  exit_if_help('cut')
  err('no fields given.') if argv.empty?
  fs = split_field_list_argument(argv.shift)
  argv = ['-'] if argv.empty?
  Tb::CatReader.open(argv, Tb::Cmd.opt_N) {|tblreader|
    if Tb::Cmd.opt_cut_v
      er = Tb::Enumerator.new {|y|
        tblreader.with_header {|header0|
          if header0
            y.set_header header0 - fs
          end
        }.each {|pairs|
          y.yield pairs.reject {|k, v| fs.include? k }
        }
      }
      output_tbenum(er)
    else
      er = Tb::Enumerator.new {|y|
        y.set_header fs
        tblreader.each {|pairs|
          y.yield pairs.reject {|k, v| !fs.include?(k) }
        }
      }
      output_tbenum(er)
    end
  }
end

.main_git(argv) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/tb/cmd_git.rb', line 212

def (Tb::Cmd).main_git(argv)
  op_git.parse!(argv)
  exit_if_help('git')
  argv = ['.'] if argv.empty?
  er = Tb::Enumerator.new {|y|
    y.set_header Tb::Cmd::GIT_LOG_HEADER
    argv.each {|dir|
      git_with_git_log(dir) {|f|
        f.set_encoding("ASCII-8BIT") if f.respond_to? :set_encoding
        git_each_commit(f) {|h|
          y.yield h
        }
      }
    }
  }
  output_tbenum(er)
end

.main_group(argv) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/tb/cmd_group.rb', line 76

def (Tb::Cmd).main_group(argv)
  op_group.parse!(argv)
  exit_if_help('group')
  err("no key fields given.") if argv.empty?
  kfs = split_field_list_argument(argv.shift)
  opt_group_fields = kfs.map {|f| [f, Tb::Func::First, f] } +
    Tb::Cmd.opt_group_fields.map {|arg|
    aggregation_spec, new_field = split_field_list_argument(arg)
    new_field ||= aggregation_spec
    [new_field,
      *begin
        parse_aggregator_spec2(aggregation_spec)
      rescue ArgumentError
        err($!.message)
      end
    ]
  }
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  result = Tb::Enumerator.new {|y|
    op = Tb::Zipper.new(opt_group_fields.map {|dstf, func, srcf| func })
    er = creader.extsort_reduce(op) {|pairs|
      [kfs.map {|f| Tb::Func.smart_cmp_value(pairs[f]) },
       opt_group_fields.map {|dstf, func, srcf| func.start(srcf ? pairs[srcf] : true) } ]
    }
    fields = opt_group_fields.map {|dstf, func, srcf| dstf }
    y.set_header(fields)
    er.each {|_, vals|
      pairs = opt_group_fields.zip(vals).map {|(dstf, func, _), val|
        [dstf, func.aggregate(val)]
      }
      y.yield Hash[pairs]
    }
  }
  output_tbenum(result)
end

.main_gsub(argv) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/tb/cmd_gsub.rb', line 65

def (Tb::Cmd).main_gsub(argv)
  op_gsub.parse!(argv)
  exit_if_help('gsub')
  if Tb::Cmd.opt_gsub_e
    re = Regexp.new(Tb::Cmd.opt_gsub_e)
  else
    err('no regexp given.') if argv.empty?
    re = Regexp.new(argv.shift)
  end
  err('no substitution given.') if argv.empty?
  repl = argv.shift
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    creader.with_cumulative_header {|header0|
      if header0
        y.set_header header0
      end
    }.each {|pairs, header|
      fs = header.dup
      fs.pop while !fs.empty? && !pairs.has_key?(fs.last)
      if Tb::Cmd.opt_gsub_f
        pairs2 = pairs.map {|f, v|
          if f == Tb::Cmd.opt_gsub_f
            v ||= ''
            [f, v.gsub(re, repl)]
          else
            [f, v]
          end
        }
      else
        pairs2 = pairs.map {|f, v|
          v ||= ''
          [f, v.gsub(re, repl)]
        }
      end
      y.yield Hash[pairs2]
    }
  }
  output_tbenum(er)
end

.main_help(argv) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/tb/cmd_help.rb', line 108

def (Tb::Cmd).main_help(argv)
  op_help.parse!(argv)
  if Tb::Cmd.opt_help_s
    list_summary_of_subcommands
    exit
  elsif argv.empty?
    if Tb::Cmd.opt_help == 0
      usage_list_subcommands
      return true
    else
      argv.unshift 'help'
      Tb::Cmd.opt_help -= 1
    end
  end
  Tb::Cmd.opt_help += 1
  subcommand = argv.shift
  exit_if_help(subcommand)
end

.main_join(argv) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/tb/cmd_join.rb', line 90

def (Tb::Cmd).main_join(argv)
  op_join.parse!(argv)
  exit_if_help('join')
  retain_left = Tb::Cmd.opt_join_retain_left
  retain_right = Tb::Cmd.opt_join_retain_right
  err('two tables required at least.') if argv.length < 2
  result = tablereader_open(argv.shift)
  argv.each {|filename|
    tbl = tablereader_open(filename)
    $stderr.puts "shared keys: #{(result.list_fields & tbl.list_fields).inspect}" if 1 <= Tb::Cmd.opt_debug
    result = result.natjoin2_outer(tbl, Tb::Cmd.opt_join_outer_missing, retain_left, retain_right)
  }
  output_tbenum(result)
end

.main_ls(argv) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/tb/cmd_ls.rb', line 57

def (Tb::Cmd).main_ls(argv)
  op_ls.parse!(argv)
  exit_if_help('ls')
  argv = ['.'] if argv.empty?
  opts = {
    :a => Tb::Cmd.opt_ls_a,
    :A => Tb::Cmd.opt_ls_A,
    :l => Tb::Cmd.opt_ls_l,
    :R => Tb::Cmd.opt_ls_R,
  }
  ls = nil
  er = Tb::Enumerator.new {|y|
    ls = Tb::Cmd::Ls.new(y, opts)
    ls.set_header
    argv.each {|arg|
      ls.ls_run(Pathname(ls.real_pathname_string(arg)))
    }
  }
  output_tbenum(er)
  if ls.fail
    exit false
  end
end

.main_melt(argv) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/tb/cmd_melt.rb', line 79

def (Tb::Cmd).main_melt(argv)
  op_melt.parse!(argv)
  exit_if_help('melt')
  err('no key-fields given.') if argv.empty?
  key_fields = split_field_list_argument(argv.shift)
  key_fields_hash = Hash[key_fields.map {|f| [f, true] }]
  if Tb::Cmd.opt_melt_regexps.empty? && Tb::Cmd.opt_melt_list.empty?
    melt_fields_pattern = //
  else
    list = Tb::Cmd.opt_melt_list + Tb::Cmd.opt_melt_regexps
    melt_fields_pattern = /\A#{Regexp.union(list)}\z/
  end
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    header = []
    header << Tb::Cmd.opt_melt_recnum if Tb::Cmd.opt_melt_recnum
    header.concat key_fields
    header << Tb::Cmd.opt_melt_variable_field
    header << Tb::Cmd.opt_melt_value_field
    y.set_header header
    creader.each_with_index {|pairs, i|
      recnum = i + 1
      h0 = {}
      h0[Tb::Cmd.opt_melt_recnum] = nil if Tb::Cmd.opt_melt_recnum
      key_fields.each {|kf|
        h0[kf] = pairs[kf]
      }
      pairs.each {|f, v|
        next if key_fields_hash[f]
        next if melt_fields_pattern !~ f
        h = h0.dup
        h[Tb::Cmd.opt_melt_recnum] = recnum if Tb::Cmd.opt_melt_recnum
        h[Tb::Cmd.opt_melt_variable_field] = f
        h[Tb::Cmd.opt_melt_value_field] = v
        y.yield h
      }
    }
  }
  output_tbenum(er)
end

.main_mheader(argv) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/tb/cmd_mheader.rb', line 57

def (Tb::Cmd).main_mheader(argv)
  op_mheader.parse!(argv)
  exit_if_help('mheader')
  argv = ['-'] if argv.empty?
  header = []
  if Tb::Cmd.opt_mheader_count
    c = Tb::Cmd.opt_mheader_count
    header_end_p = lambda {
      c -= 1
      c == 0 ? header.map {|a| a.compact.join(' ').strip } : nil
    }
  else
    header_end_p = lambda {
      h2 = header.map {|a| a.compact.join(' ').strip }.uniq
      header.length == h2.length ? h2 : nil
    }
  end
  creader = Tb::CatReader.open(argv, true)
  er = Tb::Enumerator.new {|y|
    creader.each {|pairs|
      if header
        ary = []
        pairs.each {|f, v| ary[f.to_i-1] = v }
        ary.each_with_index {|v,i|
          header[i] ||= []
          header[i] << v if header[i].empty? || header[i].last != v
        }
        h2 = header_end_p.call
        if h2
          pairs2 = Hash[h2.map.with_index {|v, i| ["#{i+1}", v] }]
          y.yield pairs2
          header = nil
        end
      else
        y.yield pairs
      end
    }
  }
  Tb::Cmd.opt_N = true
  output_tbenum(er)
  if header
    warn "unique header fields not recognized."
  end
end

.main_nest(argv) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/tb/cmd_nest.rb', line 61

def (Tb::Cmd).main_nest(argv)
  op_nest.parse!(argv)
  exit_if_help('nest')
  err('no fields given.') if argv.empty?
  fields = split_field_list_argument(argv.shift)
  newfield, *oldfields = fields
  oldfields_hash = {}
  oldfields.each {|f| oldfields_hash[f] = true }
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    sorted = creader.with_header {|header0|
      oldfields.each {|f|
        if !header0.include?(f)
          err("field not found: #{f.inspect}")
        end
      }
      y.set_header(header0.reject {|f| oldfields_hash[f] } + [newfield])
    }.map {|pairs|
      cv = pairs.reject {|f, v|
        oldfields_hash[f]
      }.map {|f, v|
        [Tb::Func.smart_cmp_value(f), Tb::Func.smart_cmp_value(v)]
      }.sort
      [cv, pairs]
    }

    nested = nil
    before_group = lambda {|(_, _)|
      nested = []
    }
    body = lambda {|(_, pairs)|
      nested << pairs.reject {|f, v| !oldfields_hash[f] }
    }
    after_group = lambda {|(_, last_pairs)|
      nested_csv = ""
      nested_csv << oldfields.to_csv
      nested.each {|npairs|
        nested_csv << oldfields.map {|of| npairs[of] }.to_csv
      }
      assoc = last_pairs.reject {|f, v| oldfields_hash[f] }.to_a
      assoc << [newfield, nested_csv]
      pairs = Hash[assoc]
      y.yield pairs
    }
    sorted.detect_group_by(before_group, after_group) {|cv,| cv }.each(&body)
  }
  output_tbenum(er)
end

.main_newfield(argv) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/tb/cmd_newfield.rb', line 61

def (Tb::Cmd).main_newfield(argv)
  op_newfield.parse!(argv)
  exit_if_help('newfield')
  err('no new field name given.') if argv.empty?
  field = argv.shift
  if Tb::Cmd.opt_newfield_ruby
    rubyexp = Tb::Cmd.opt_newfield_ruby
    pr = eval("lambda {|_| #{rubyexp} }")
  else
    err('no value given.') if argv.empty?
    value = argv.shift
    pr = lambda {|_| value }
  end
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = creader.newfield(field) {|pairs| pr.call(pairs) }
  output_tbenum(er)
end

.main_rename(argv) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/tb/cmd_rename.rb', line 59

def (Tb::Cmd).main_rename(argv)
  op_rename.parse!(argv)
  exit_if_help('rename')
  err('rename fields not given.') if argv.empty?
  fs = split_field_list_argument(argv.shift)
  argv = ['-'] if argv.empty?
  h = {}
  fs.each_slice(2) {|sf, df| h[sf] = df }
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    header = nil
    creader.with_header {|header0|
      header = header0
      h.each {|sf, df|
        unless header.include? sf
          err "field not found: #{sf.inspect}"
        end
      }
      y.set_header header.map {|f| h.fetch(f, f) }
    }.each {|pairs|
      y.yield Hash[pairs.map {|f, v| [h.fetch(f, f), v] }]
    }
  }
  output_tbenum(er)
end

.main_search(argv) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/tb/cmd_search.rb', line 60

def (Tb::Cmd).main_search(argv)
  op_search.parse!(argv)
  exit_if_help('search')
  if Tb::Cmd.opt_search_ruby
    pred = eval("lambda {|_| #{Tb::Cmd.opt_search_ruby} }")
  elsif Tb::Cmd.opt_search_e
    re = Regexp.new(Tb::Cmd.opt_search_e)
    pred = Tb::Cmd.opt_search_f ?
      lambda {|_| re =~ _[Tb::Cmd.opt_search_f] } :
      lambda {|_| _.any? {|k, v| re =~ v.to_s } }
  else
    err("no regexp given.") if argv.empty?
    re = Regexp.new(argv.shift)
    pred = Tb::Cmd.opt_search_f ?
      lambda {|_| re =~ _[Tb::Cmd.opt_search_f] } :
      lambda {|_| _.any? {|k, v| re =~ v.to_s } }
  end
  opt_v = Tb::Cmd.opt_search_v ? true : false
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    header = nil
    creader.with_header {|header0|
      header = header0
      y.set_header header
    }.each {|pairs|
      h = {}
      pairs.each {|f, v|
        h[f] = v
      }
      found = pred.call(h)
      found = opt_v ^ !!(found)
      if found
        y.yield pairs
      end
    }
  }
  output_tbenum(er)
end

.main_shape(argv) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/tb/cmd_shape.rb', line 61

def (Tb::Cmd).main_shape(argv)
  op_shape.parse!(argv)
  exit_if_help('shape')
  filenames = argv.empty? ? ['-'] : argv
  ter = Tb::Enumerator.new {|y|
    filenames.each {|filename|
      tablereader_open(filename) {|tblreader|
        tblreader.enable_warning = false if tblreader.respond_to? :enable_warning
        num_records = 0
        num_header_fields = nil
        min_num_fields = nil
        max_num_fields = nil
        if tblreader.respond_to? :header_array_hook
          tblreader.header_array_hook = lambda {|header|
            num_header_fields = header.length
          }
        end
        if tblreader.respond_to? :row_array_hook
          tblreader.row_array_hook = lambda {|ary|
            n = ary.length
            if min_num_fields.nil?
              min_num_fields = max_num_fields = n
            else
              min_num_fields = n if n < min_num_fields
              max_num_fields = n if max_num_fields < n
            end
          }
        end
        min_num_pairs = nil
        max_num_pairs = nil
        tblreader.each {|pairs|
          num_records += 1
          n = pairs.length
          if min_num_pairs.nil?
            min_num_pairs = max_num_pairs = n
          else
            min_num_pairs = n if n < min_num_pairs
            max_num_pairs = n if max_num_pairs < n
          end
        }
        h = {
          'filename'=>filename,
          'records'=>num_records,
          'min_pairs'=>min_num_pairs,
          'max_pairs'=>max_num_pairs,
        }
        h['header_fields'] = num_header_fields if num_header_fields
        h['min_fields'] = min_num_fields if min_num_fields
        h['max_fields'] = max_num_fields if max_num_fields
        y.yield(h)
      }
    }
  }
  Tb::Cmd.opt_N = false
  output_tbenum(ter)
end

.main_sort(argv) ⇒ Object



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/tb/cmd_sort.rb', line 64

def (Tb::Cmd).main_sort(argv)
  op_sort.parse!(argv)
  exit_if_help('sort')
  argv = ['-'] if argv.empty?
  if Tb::Cmd.opt_sort_f
    fs = split_field_list_argument(Tb::Cmd.opt_sort_f)
  else
    fs = nil
  end
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  header = []
  if fs
    blk = lambda {|pairs| fs.map {|f| Tb::Func.smart_cmp_value(pairs[f]) } }
  else
    blk = lambda {|pairs| header.map {|f| Tb::Func.smart_cmp_value(pairs[f]) } }
  end
  if Tb::Cmd.opt_sort_r
    blk1 = blk
    blk = lambda {|pairs| Tb::RevCmp.new(blk1.call(pairs)) }
  end
  er = Tb::Enumerator.new {|y|
    creader.with_cumulative_header {|header0|
      if header0
        y.set_header(header0)
      end
    }.each {|pairs, header1|
      header = header1
      y.yield pairs
    }
  }.extsort_by(&blk)
  output_tbenum(er)
end

.main_svn(argv) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
# File 'lib/tb/cmd_svn.rb', line 173

def (Tb::Cmd).main_svn(argv)
  op_svn.parse!(argv)
  exit_if_help('svn')
  er = Tb::Enumerator.new {|y|
    svn_with_svn_log(argv) {|f|
      listener = Tb::Cmd::SVNLOGListener.new(y)
      REXML::Parsers::StreamParser.new(f, listener).parse
    }
  }
  output_tbenum(er)
end

.main_tar(argv) ⇒ Object



494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
# File 'lib/tb/cmd_tar.rb', line 494

def (Tb::Cmd).main_tar(argv)
  op_tar.parse!(argv)
  exit_if_help('tar')
  if Tb::Cmd.opt_tar_hash.any? {|alg| !Tb::Cmd::TAR_HASH_ALGORITHMS[alg] }
    STDERR.puts "Unexpected hash algorithm: #{Tb::Cmd.opt_tar_hash.reject {|alg| Tb::Cmd::TAR_HASH_ALGORITHMS[alg] }.join(",")}"
    exit false
  end
  argv = ['-'] if argv.empty?
  er = Tb::Enumerator.new {|y|
    if Tb::Cmd.opt_tar_l == 0
      header = Tb::Cmd::TAR_CSV_HEADER
    else
      header = Tb::Cmd::TAR_CSV_LONG_HEADER
    end
    header += Tb::Cmd.opt_tar_hash
    y.set_header header
    argv.each {|filename|
      tar_open_with(filename) {|f|
        tar_each(f) {|h|
          formatted = {}
          formatted["mode"] = sprintf("0%o", h[:mode])
          formatted["filemode"] = tar_format_filemode(h[:typeflag], h[:mode])
          formatted["uid"] = h[:uid].to_s
          formatted["gid"] = h[:gid].to_s
          formatted["size"] = h[:size].to_s
          formatted["mtime"] = h[:mtime].iso8601(0 < Tb::Cmd.opt_tar_l ? 9 : 0)
          formatted["atime"] = h[:atime].iso8601(0 < Tb::Cmd.opt_tar_l ? 9 : 0) if h[:atime]
          formatted["ctime"] = h[:ctime].iso8601(0 < Tb::Cmd.opt_tar_l ? 9 : 0) if h[:ctime]
          formatted["user"] = h[:uname]
          formatted["group"] = h[:gname]
          formatted["devmajor"] = h[:devmajor].to_s
          formatted["devminor"] = h[:devminor].to_s
          formatted["path"] = h[:path]
          formatted["linkname"] = h[:linkname]
          formatted["size_in_tar"] = h[:size_in_tar]
          formatted["tar_chksum"] = h[:chksum]
          formatted["tar_typeflag"] = h[:typeflag]
          formatted["tar_magic"] = h[:magic]
          formatted["tar_version"] = h[:version]
          Tb::Cmd.opt_tar_hash.each {|alg| formatted[alg] = h[alg] }
          y.yield Hash[header.map {|f2| [f2, formatted[f2]] }]
        }
      }
    }
  }
  output_tbenum(er)
end

.main_to_csv(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tb/cmd_to_csv.rb', line 39

def (Tb::Cmd).main_to_csv(argv)
  op_to_csv.parse!(argv)
  exit_if_help('to-csv')
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  header = []
  ter = Tb::Enumerator.new {|y|
    creader.with_cumulative_header.each {|pairs, header1|
      header = header1
      y.yield pairs
    }
  }.to_fileenumerator
  er = Tb::Enumerator.new {|y|
    y.set_header header
    ter.each {|pairs|
      y.yield Hash[header.map {|f| [f, pairs[f]] }]
    }
  }
  with_output {|out|
    er.write_to_csv(out, !Tb::Cmd.opt_N)
  }
end

.main_to_json(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/tb/cmd_to_json.rb', line 39

def (Tb::Cmd).main_to_json(argv)
  require 'json'
  op_to_json.parse!(argv)
  exit_if_help('to-json')
  argv = ['-'] if argv.empty?
  with_output {|out|
    out.print "["
    sep = nil
    argv.each {|filename|
      sep = ",\n\n" if sep
      tablereader_open(filename) {|tblreader|
        tblreader.each {|pairs|
          out.print sep if sep
          out.print JSON.pretty_generate(pairs)
          sep = ",\n"
        }
      }
    }
    out.puts "]"
  }
end

.main_to_ltsv(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/tb/cmd_to_ltsv.rb', line 39

def (Tb::Cmd).main_to_ltsv(argv)
  op_to_ltsv.parse!(argv)
  exit_if_help('to-ltsv')
  argv = ['-'] if argv.empty?
  reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  with_output {|out|
    reader.write_to_ltsv(out)
  }
end

.main_to_pnm(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/tb/cmd_to_pnm.rb', line 39

def (Tb::Cmd).main_to_pnm(argv)
  op_to_pnm.parse!(argv)
  exit_if_help('to-pnm')
  argv = ['-'] if argv.empty?
  reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  with_output {|out|
    reader.write_to_pnm(out)
  }
end

.main_to_pp(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/tb/cmd_to_pp.rb', line 39

def (Tb::Cmd).main_to_pp(argv)
  op_to_pp.parse!(argv)
  exit_if_help('to-pp')
  argv.unshift '-' if argv.empty?
  with_output {|out|
    argv.each {|filename|
      tablereader_open(filename) {|tblreader|
        tblreader.each {|pairs|
          a = pairs.reject {|f, v| v.nil? }
          q = PP.new(out, 79)
          q.guard_inspect_key {
            q.group(1, '{', '}') {
              q.seplist(a, nil, :each) {|kv|
                k, v = kv
                q.group {
                  q.pp k
                  q.text '=>'
                  q.group(1) {
                    q.breakable ''
                    q.pp v
                  }
                }
              }
            }
          }
          q.flush
          out << "\n"
        }
      }
    }
  }
end

.main_to_tsv(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
# File 'lib/tb/cmd_to_tsv.rb', line 39

def (Tb::Cmd).main_to_tsv(argv)
  op_to_tsv.parse!(argv)
  exit_if_help('to-tsv')
  argv = ['-'] if argv.empty?
  reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  with_output {|out|
    reader.write_to_tsv(out, !Tb::Cmd.opt_N)
  }
end

.main_to_yaml(argv) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/tb/cmd_to_yaml.rb', line 39

def (Tb::Cmd).main_to_yaml(argv)
  require 'yaml'
  op_to_yaml.parse!(argv)
  exit_if_help('to-yaml')
  argv = ['-'] if argv.empty?
  reader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  ary = reader.to_a
  with_output {|out|
    YAML.dump(ary, out)
    out.puts
  }
end

.main_unmelt(argv) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/tb/cmd_unmelt.rb', line 76

def (Tb::Cmd).main_unmelt(argv)
  op_unmelt.parse!(argv)
  exit_if_help('unmelt')
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  if Tb::Cmd.opt_unmelt_keys.empty?
    key_fields = nil
  else
    if Tb::Cmd.opt_unmelt_recnum
      key_fields = [Tb::Cmd.opt_unmelt_recnum]
    else
      key_fields = []
    end
    key_fields += Tb::Cmd.opt_unmelt_keys
  end
  melt_fields_hash = {}
  er = Tb::Enumerator.new {|y|
    creader.chunk {|pairs|
      keys = {}
      if key_fields
        key_fields.each {|k|
          keys[k] = pairs[k]
        }
      else
        pairs.each_key {|k|
          next if k == Tb::Cmd.opt_unmelt_variable_field ||
                  k == Tb::Cmd.opt_unmelt_value_field
          keys[k] = pairs[k]
        }
      end
      keys
    }.each {|keys, pairs_ary|
      if Tb::Cmd.opt_unmelt_recnum
        keys.delete Tb::Cmd.opt_unmelt_recnum
      end
      rec = keys.dup
      pairs_ary.each {|pairs|
        var = pairs[Tb::Cmd.opt_unmelt_variable_field]
        val = pairs[Tb::Cmd.opt_unmelt_value_field]
        melt_fields_hash[var] = true
        if rec.has_key? var
          y.yield rec
          rec = keys.dup
        end
        rec[var] = val
      }
      y.yield rec
    }
  }
  if !Tb::Cmd.opt_unmelt_missing_value
    er2 = er
  else
    er2 = Tb::Enumerator.new {|y|
      er.to_fileenumerator.with_header {|header|
        y.set_header header
      }.each {|pairs|
        melt_fields_hash.each_key {|f|
          pairs[f] ||= Tb::Cmd.opt_unmelt_missing_value
        }
        y.yield pairs
      }
    }
  end
  output_tbenum(er2)
end

.main_unnest(argv) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/tb/cmd_unnest.rb', line 65

def (Tb::Cmd).main_unnest(argv)
  op_unnest.parse!(argv)
  exit_if_help('unnest')
  err('no field given.') if argv.empty?
  target_field = argv.shift
  argv = ['-'] if argv.empty?
  creader = Tb::CatReader.open(argv, Tb::Cmd.opt_N)
  er = Tb::Enumerator.new {|y|
    nested_fields = {}
    ter = Tb::Enumerator.new {|y2|
      creader.with_header {|header0|
        unless header0.include? target_field
          err("field not found: #{target_field.inspect}")
        end
        y2.set_header header0
      }.each {|pairs|
        pairs2 = {}
        pairs.each {|f, v|
          if f != target_field
            pairs2[f] = v
          elsif v.nil?
            pairs2[f] = v
          else
            aa = CSV.parse(v)
            reader = Tb::HeaderReader.new(lambda { aa.shift })
            nested_tbl_rows = reader.to_a
            nested_tbl_header = reader.get_named_header
            nested_tbl_header.each {|f2|
              unless nested_fields.has_key? f2
                nested_fields[f2] = nested_fields.size
              end
            }
            pairs2[f] = [nested_tbl_header, nested_tbl_rows]
          end
        }
        y2.yield pairs2
      }
    }.to_fileenumerator
    nested_fields_ary = nested_fields.keys
    if Tb::Cmd.opt_unnest_prefix
      nested_fields_ary.map! {|f| Tb::Cmd.opt_unnest_prefix + f }
    end
    ter.with_header {|header0|
      header2 = []
      header0.each {|f|
        if f != target_field
          header2 << f
        else
          header2.concat nested_fields_ary
        end
      }
      y.set_header header2
    }.each {|pairs|
      pairs2 = {}
      pairs.each {|f, v| pairs2[f] = v if f != target_field }
      ntbl_header, ntbl_rows = pairs[target_field]
      if ntbl_header.nil? || ntbl_rows.empty?
        if Tb::Cmd.opt_unnest_outer
          y.yield pairs2
        end
      else
        ntbl_rows.each {|npairs|
          pairs3 = pairs2.dup
          npairs.each {|nf, nv|
            if Tb::Cmd.opt_unnest_prefix
              nf = Tb::Cmd.opt_unnest_prefix + nf
            end
            pairs3[nf] = nv
          }
          y.yield pairs3
        }
      end
    }
  }
  output_tbenum(er)
end

.op_catObject



33
34
35
36
37
38
39
40
# File 'lib/tb/cmd_cat.rb', line 33

def (Tb::Cmd).op_cat
  op = OptionParser.new
  op.banner = "Usage: tb cat [OPTS] [TABLE ...]\n" +
    "Concatenate tables vertically."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-H', '--with-filename', 'add filename column') { Tb::Cmd.opt_cat_with_filename = true }
  op
end

.op_consecutiveObject



33
34
35
36
37
38
39
40
# File 'lib/tb/cmd_consecutive.rb', line 33

def (Tb::Cmd).op_consecutive
  op = OptionParser.new
  op.banner = "Usage: tb consecutive [OPTS] [TABLE ...]\n" +
    "Concatenate consecutive rows."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-n NUM', 'gather NUM records.  (default: 2)') {|n| Tb::Cmd.opt_consecutive_n = n.to_i }
  op
end

.op_cropObject



33
34
35
36
37
38
39
40
# File 'lib/tb/cmd_crop.rb', line 33

def (Tb::Cmd).op_crop
  op = OptionParser.new
  op.banner = "Usage: tb crop [OPTS] [TABLE ...]\n" +
    "Extract rectangle in a table."
  define_common_option(op, "ho", "--no-pager")
  op.def_option('-r RANGE', 'range.  i.e. "R2C1:R4C3", "B1:D3"') {|arg| Tb::Cmd.opt_crop_range = arg }
  op
end

.op_crossObject



34
35
36
37
38
39
40
41
42
43
# File 'lib/tb/cmd_cross.rb', line 34

def (Tb::Cmd).op_cross
  op = OptionParser.new
  op.banner = "Usage: tb cross [OPTS] VKEY-FIELD1,... HKEY-FIELD1,... [TABLE ...]\n" +
    "Create a cross table. (a.k.a contingency table, pivot table)"
  define_common_option(op, "ho", "--no-pager")
  op.def_option('-a AGGREGATION-SPEC[,NEW-FIELD]',
                '--aggregate AGGREGATION-SPEC[,NEW-FIELD]') {|arg| Tb::Cmd.opt_cross_fields << arg }
  op.def_option('-c', '--compact', 'compact format') { Tb::Cmd.opt_cross_compact = true }
  op
end

.op_cutObject



33
34
35
36
37
38
39
40
# File 'lib/tb/cmd_cut.rb', line 33

def (Tb::Cmd).op_cut
  op = OptionParser.new
  op.banner = "Usage: tb cut [OPTS] FIELD,... [TABLE]\n" +
    "Select columns."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-v', 'invert match') { Tb::Cmd.opt_cut_v = true }
  op
end

.op_gitObject



35
36
37
38
39
40
41
42
43
44
# File 'lib/tb/cmd_git.rb', line 35

def (Tb::Cmd).op_git
  op = OptionParser.new
  op.banner = "Usage: tb git [OPTS] [GIT-DIR ...]\n" +
    "Show the GIT log as a table."
  define_common_option(op, "hNod", "--no-pager", '--debug')
  op.def_option('--git-command COMMAND', 'specify the git command (default: git)') {|command| Tb::Cmd.opt_git_command = command }
  op.def_option('--debug-git-output FILE', 'store the raw output of git (for debug)') {|filename| Tb::Cmd.opt_git_debug_output = filename }
  op.def_option('--debug-git-input FILE', 'use the file as output of git (for debug)') {|filename| Tb::Cmd.opt_git_debug_input = filename }
  op
end

.op_groupObject



33
34
35
36
37
38
39
40
41
42
# File 'lib/tb/cmd_group.rb', line 33

def (Tb::Cmd).op_group
  op = OptionParser.new
  op.banner = "Usage: tb group [OPTS] KEY-FIELD1,... [TABLE ...]\n" +
    "Group and aggregate rows."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-a AGGREGATION-SPEC[,NEW-FIELD]',
                '--aggregate AGGREGATION-SPEC[,NEW-FIELD]') {|arg| Tb::Cmd.opt_group_fields << arg }
  op.def_option('--no-pager', 'don\'t use pager') { Tb::Cmd.opt_no_pager = true }
  op
end

.op_gsubObject



34
35
36
37
38
39
40
41
42
# File 'lib/tb/cmd_gsub.rb', line 34

def (Tb::Cmd).op_gsub
  op = OptionParser.new
  op.banner = "Usage: tb gsub [OPTS] REGEXP STRING [TABLE ...]\n" +
    "Substitute cells."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-f FIELD', 'target field') {|field| Tb::Cmd.opt_gsub_f = field }
  op.def_option('-e REGEXP', 'specify regexp, possibly begins with a hyphen') {|pattern| Tb::Cmd.opt_gsub_e = pattern }
  op
end

.op_helpObject



60
61
62
63
64
65
66
67
# File 'lib/tb/cmd_help.rb', line 60

def (Tb::Cmd).op_help
  op = OptionParser.new
  op.banner = "Usage: tb help [OPTS] [SUBCOMMAND]\n" +
    "Show help message of tb command."
  define_common_option(op, "hvo", "--no-pager")
  op.def_option('-s', 'show summary of subcommands') { Tb::Cmd.opt_help_s = true }
  op
end

.op_joinObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tb/cmd_join.rb', line 35

def (Tb::Cmd).op_join
  op = OptionParser.new
  op.banner = "Usage: tb join [OPTS] [TABLE1 TABLE2 ...]\n" +
    "Concatenate tables horizontally as left/right/full natural join."
  define_common_option(op, 'hNod', '--no-pager', '--debug')
  op.def_option('--outer', 'outer join') {
    Tb::Cmd.opt_join_retain_left = true
    Tb::Cmd.opt_join_retain_right = true
  }
  op.def_option('--left', 'left outer join') {
    Tb::Cmd.opt_join_retain_left = true
    Tb::Cmd.opt_join_retain_right = false
  }
  op.def_option('--right', 'right outer join') {
    Tb::Cmd.opt_join_retain_left = false
    Tb::Cmd.opt_join_retain_right = true
  }
  op.def_option('--outer-missing=DEFAULT', 'missing value for outer join') {|missing|
    if Tb::Cmd.opt_join_retain_left == nil
      Tb::Cmd.opt_join_retain_left = true
      Tb::Cmd.opt_join_retain_right = true
    end
    Tb::Cmd.opt_join_outer_missing = missing
  }
  op
end

.op_lsObject



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/tb/cmd_ls.rb', line 36

def (Tb::Cmd).op_ls
  op = OptionParser.new
  op.banner = "Usage: tb ls [OPTS] [FILE ...]\n" +
    "List directory entries as a table."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-a', 'don\'t ignore filenames beginning with a period.') {|fs| Tb::Cmd.opt_ls_a = true }
  op.def_option('-A', 'don\'t ignore filenames beginning with a period, except "." and "..".') {|fs| Tb::Cmd.opt_ls_A = true }
  op.def_option('-l', 'show attributes.  -ll for more attributes.') {|fs| Tb::Cmd.opt_ls_l += 1 }
  op.def_option('-R', 'recursive.') {|fs| Tb::Cmd.opt_ls_R = true }
  op
end

.op_meltObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/tb/cmd_melt.rb', line 37

def (Tb::Cmd).op_melt
  op = OptionParser.new
  op.banner = "Usage: tb melt KEY-FIELDS-LIST [OPTS] [TABLE ...]\n" +
    "split value fields into records."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('--recnum[=FIELD]',
                'add recnum field (default don\'t add)') {|field|
    Tb::Cmd.opt_melt_recnum = field || 'recnum'
  }
  op.def_option('-R REGEXP',
                '--melt-regexp REGEXP',
                'regexp for melt fields') {|regexp|
    Tb::Cmd.opt_melt_regexps << Regexp.compile(regexp)
  }
  op.def_option('--melt-fields FIELD,...',
                'list of melt fields') {|fields|
    Tb::Cmd.opt_melt_list.concat split_field_list_argument(fields)
  }
  op.def_option('--variable-field FIELD', 'variable field. (default: variable)') {|field|
    Tb::Cmd.opt_melt_variable_field = field
  }
  op.def_option('--value-field FIELD', 'value field. (default: value)') {|field|
    Tb::Cmd.opt_melt_value_field = field
  }
  op
end

.op_mheaderObject



33
34
35
36
37
38
39
40
# File 'lib/tb/cmd_mheader.rb', line 33

def (Tb::Cmd).op_mheader
  op = OptionParser.new
  op.banner = "Usage: tb mheader [OPTS] [TABLE]\n" +
    "Collapse multi rows header."
  define_common_option(op, "ho", "--no-pager")
  op.def_option('-c N', 'number of header records') {|arg| Tb::Cmd.opt_mheader_count = arg.to_i }
  op
end

.op_nestObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_nest.rb', line 31

def (Tb::Cmd).op_nest
  op = OptionParser.new
  op.banner = "Usage: tb nest [OPTS] NEWFIELD,OLDFIELD1,OLDFIELD2,... [TABLE ...]\n" +
    "Nest fields."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_newfieldObject



33
34
35
36
37
38
39
40
41
42
# File 'lib/tb/cmd_newfield.rb', line 33

def (Tb::Cmd).op_newfield
  op = OptionParser.new
  op.banner = "Usage: tb newfield [OPTS] FIELD VALUE [TABLE]\n" +
    "Add a field."
  define_common_option(op, "ho", "--no-pager")
  op.def_option('--ruby RUBY-EXP', 'ruby expression to generate values.  A hash is given as _.  no VALUE argument.') {|ruby_exp|
    Tb::Cmd.opt_newfield_ruby = ruby_exp
  }
  op
end

.op_renameObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_rename.rb', line 31

def (Tb::Cmd).op_rename
  op = OptionParser.new
  op.banner = "Usage: tb rename [OPTS] SRC,DST,... [TABLE]\n" +
    "Rename field names."
  define_common_option(op, "ho", "--no-pager")
  op
end

.op_searchObject



36
37
38
39
40
41
42
43
44
45
46
# File 'lib/tb/cmd_search.rb', line 36

def (Tb::Cmd).op_search
  op = OptionParser.new
  op.banner = "Usage: tb search [OPTS] REGEXP [TABLE ...]\n" +
    "Search rows using regexp or ruby expression."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-f FIELD', 'search field') {|field| Tb::Cmd.opt_search_f = field }
  op.def_option('-e REGEXP', 'specify a regexp.') {|pattern| Tb::Cmd.opt_search_e = pattern }
  op.def_option('--ruby RUBY-EXP', 'predicate written in ruby.  A hash is given as _.  no usual regexp argument.') {|ruby_exp| Tb::Cmd.opt_search_ruby = ruby_exp }
  op.def_option('-v', 'ouput the records which doesn\'t match') { Tb::Cmd.opt_search_v = true }
  op
end

.op_shapeObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_shape.rb', line 31

def (Tb::Cmd).op_shape
  op = OptionParser.new
  op.banner = "Usage: tb shape [OPTS] [TABLE ...]\n" +
    "Show table size."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_sortObject



34
35
36
37
38
39
40
41
42
# File 'lib/tb/cmd_sort.rb', line 34

def (Tb::Cmd).op_sort
  op = OptionParser.new
  op.banner = "Usage: tb sort [OPTS] [TABLE]\n" +
    "Sort rows."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-f FIELD,...', 'specify sort keys') {|fs| Tb::Cmd.opt_sort_f = fs }
  op.def_option('-r', '--reverse', 'reverse order') { Tb::Cmd.opt_sort_r = true }
  op
end

.op_svnObject



36
37
38
39
40
41
42
43
44
# File 'lib/tb/cmd_svn.rb', line 36

def (Tb::Cmd).op_svn
  op = OptionParser.new
  op.banner = "Usage: tb svn [OPTS] -- [SVN-LOG-ARGS]\n" +
    "Show the SVN log as a table."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('--svn-command COMMAND', 'specify the svn command (default: svn)') {|command| Tb::Cmd.opt_svn_command = command }
  op.def_option('--svn-log-xml FILE', 'specify the result svn log --xml') {|filename| Tb::Cmd.opt_svn_log_xml = filename }
  op
end

.op_tarObject



35
36
37
38
39
40
41
42
43
44
# File 'lib/tb/cmd_tar.rb', line 35

def (Tb::Cmd).op_tar
  op = OptionParser.new
  op.banner = "Usage: tb tar [OPTS] [TAR-FILE ...]\n" +
    "Show the file listing of tar file."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('-l', 'show more attributes.') {|fs| Tb::Cmd.opt_tar_l += 1 }
  op.def_option('--ustar', 'ustar format (POSIX.1-1988).  No GNU and POSIX.1-2001 extension.') {|fs| Tb::Cmd.opt_tar_ustar = true }
  op.def_option('--hash=ALGORITHMS', 'show the hash of contents.  algorithms can be some of md5,sha256,sha384,sha512 (default: none)') {|hs| Tb::Cmd.opt_tar_hash.concat split_field_list_argument(hs) }
  op
end

.op_to_csvObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_csv.rb', line 31

def (Tb::Cmd).op_to_csv
  op = OptionParser.new
  op.banner = "Usage: tb to-csv [OPTS] [TABLE ...]\n" +
    "Convert a table to CSV (Comma Separated Values)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_jsonObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_json.rb', line 31

def (Tb::Cmd).op_to_json
  op = OptionParser.new
  op.banner = "Usage: tb to-json [OPTS] [TABLE]\n" +
    "Convert a table to JSON (JavaScript Object Notation)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_ltsvObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_ltsv.rb', line 31

def (Tb::Cmd).op_to_ltsv
  op = OptionParser.new
  op.banner = "Usage: tb to-ltsv [OPTS] [TABLE]\n" +
    "Convert a table to LTSV (Labeled Tab Separated Values)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_pnmObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_pnm.rb', line 31

def (Tb::Cmd).op_to_pnm
  op = OptionParser.new
  op.banner = "Usage: tb to-pnm [OPTS] [TABLE]\n" +
    "Convert a table to PNM (Portable Anymap: PPM, PGM, PBM)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_ppObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_pp.rb', line 31

def (Tb::Cmd).op_to_pp
  op = OptionParser.new
  op.banner = "Usage: tb to-pp [OPTS] [TABLE]\n" +
    "Convert a table to pretty printed format."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_tsvObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_tsv.rb', line 31

def (Tb::Cmd).op_to_tsv
  op = OptionParser.new
  op.banner = "Usage: tb to-tsv [OPTS] [TABLE]\n" +
    "Convert a table to TSV (Tab Separated Values)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_to_yamlObject



31
32
33
34
35
36
37
# File 'lib/tb/cmd_to_yaml.rb', line 31

def (Tb::Cmd).op_to_yaml
  op = OptionParser.new
  op.banner = "Usage: tb to-yaml [OPTS] [TABLE]\n" +
    "Convert a table to YAML (YAML Ain't a Markup Language)."
  define_common_option(op, "hNo", "--no-pager")
  op
end

.op_unmeltObject



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/tb/cmd_unmelt.rb', line 37

def (Tb::Cmd).op_unmelt
  op = OptionParser.new
  op.banner = "Usage: tb unmelt [OPTS] [TABLE ...]\n" +
    "merge melted records into a record."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('--recnum[=FIELD]',
                'use FIELD as an additional key and remove it from the result. (default: not specified)') {|field|
    Tb::Cmd.opt_unmelt_recnum = field || 'recnum'
  }
  op.def_option('--keys FIELD,...', 'key fields. (default: all fields except variable and value)') {|fields|
    Tb::Cmd.opt_unmelt_keys.concat split_field_list_argument(fields)
  }
  op.def_option('--variable-field FIELD', 'variable field. (default: variable)') {|field|
    Tb::Cmd.opt_unmelt_variable_field = field
  }
  op.def_option('--value-field FIELD', 'value field. (default: value)') {|field|
    Tb::Cmd.opt_unmelt_value_field = field
  }
  op.def_option('--missing-value FIELD', 'used for missing values. (default: not specified)') {|value|
    Tb::Cmd.opt_unmelt_missing_value = value
  }
  op
end

.op_unnestObject



34
35
36
37
38
39
40
41
42
# File 'lib/tb/cmd_unnest.rb', line 34

def (Tb::Cmd).op_unnest
  op = OptionParser.new
  op.banner = "Usage: tb unnest [OPTS] FIELD [TABLE ...]\n" +
    "Unnest a field."
  define_common_option(op, "hNo", "--no-pager")
  op.def_option('--prefix PREFIX', 'field prefix') {|prefix| Tb::Cmd.opt_unnest_prefix = prefix }
  op.def_option('--outer', 'retain rows for empty nested table') { Tb::Cmd.opt_unnest_outer = true }
  op
end

.show_help(subcommand) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/tb/cmd_help.rb', line 93

def (Tb::Cmd).show_help(subcommand)
  if Tb::Cmd.subcommands.include?(subcommand)
    with_output {|f|
      f.puts self.subcommand_send("op", subcommand)
      if 2 <= Tb::Cmd.opt_help && Tb::Cmd.verbose_help[subcommand]
        f.puts
        f.puts Tb::Cmd.verbose_help[subcommand]
      end
    }
    true
  else
    err "unexpected subcommand: #{subcommand.inspect}"
  end
end

.svn_with_svn_log(argv) ⇒ Object



160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/tb/cmd_svn.rb', line 160

def (Tb::Cmd).svn_with_svn_log(argv)
  if Tb::Cmd.opt_svn_log_xml
    File.open(Tb::Cmd.opt_svn_log_xml) {|f|
      yield f
    }
  else
    svn = Tb::Cmd.opt_svn_command || 'svn'
    IO.popen([svn, 'log', '--xml', *argv]) {|f|
      yield f
    }
  end
end

.tar_check_extension_record(reader, h, content_blocklength) ⇒ Object



300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/tb/cmd_tar.rb', line 300

def (Tb::Cmd).tar_check_extension_record(reader, h, content_blocklength)
  prefix_parameters = {}
  case h[:typeflag]
  when 'L' # GNU
    content = reader.read_exactly(content_blocklength, 'GNU long file name')[0, h[:size]][/\A[^\0]*/]
    prefix_parameters[:path] = content
    return prefix_parameters
  when 'K' # GNU
    content = reader.read_exactly(content_blocklength, 'GNU long link name')[0, h[:size]][/\A[^\0]*/]
    prefix_parameters[:linkname] = content
    return prefix_parameters
  when 'x' # pax (POSIX.1-2001)
    content = reader.read_exactly(content_blocklength, 'pax Extended Header content')[0, h[:size]]
    while /\A(\d+) / =~ content
      lenlen = $&.length
      len = $1.to_i
      param = content[lenlen, len-lenlen]
      content = content[len..-1]
      if /\n\z/ =~ param
        param.chomp!("\n")
      else
        warn "pax hearder record doesn't end with a newline: #{param.inspect}"
      end
      if /=/ !~ param
        warn "pax hearder record doesn't contain a equal character: #{param.inspect}"
      else
        key = $`
        val = $'
        if Tb::Cmd::TAR_PAX_KEYWORD_RECOGNIZERS[key]
          if val == ''
            prefix_parameters[symkey] = nil
          else
            symkey, recognizer = Tb::Cmd::TAR_PAX_KEYWORD_RECOGNIZERS[key]
            prefix_parameters[symkey] = recognizer.call(val)
          end
        end
      end
    end
    return prefix_parameters
  end
  nil
end

.tar_each(f) ⇒ Object



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
# File 'lib/tb/cmd_tar.rb', line 343

def (Tb::Cmd).tar_each(f)
  offset = 0
  reader = Tb::Cmd::TarReader.new(f)
  prefix_parameters = {}
  while true
    header_record = reader.get_single_record("file header")
    if !header_record
      break
    end
    if /\A\0*\z/ =~ header_record
      tar_read_end_of_archive_indicator(reader)
      break
    end
    h = tar_parse_header(header_record)
    content_numrecords = (h[:size] + Tb::Cmd::TAR_RECORD_LENGTH - 1) / Tb::Cmd::TAR_RECORD_LENGTH
    content_blocklength = content_numrecords * Tb::Cmd::TAR_RECORD_LENGTH
    if !Tb::Cmd.opt_tar_ustar
      extension_params = tar_check_extension_record(reader, h, content_blocklength)
      if extension_params
        prefix_parameters.update extension_params
        next
      end
    end
    prefix_parameters.each {|k, v|
      if v.nil?
        h.delete k
      else
        h[k] = v
      end
    }
    case Tb::Cmd::TAR_TYPEFLAG[h[:typeflag]]
    when :link, :symlink, :directory, :character_special, :block_special, :fifo
      # xxx: hardlink may have contents for posix archive.
    else
      if Tb::Cmd.opt_tar_hash.empty?
        reader.skip(content_blocklength, 'file content')
      else
        reader.calculate_hash(content_blocklength, h[:size], Tb::Cmd.opt_tar_hash).each {|alg, result|
          h[alg] = result
        }
      end
    end
    h[:size_in_tar] = reader.offset - offset
    yield h
    offset = reader.offset
    prefix_parameters = {}
  end
end

.tar_format_filemode(typeflag, mode) ⇒ Object



464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
# File 'lib/tb/cmd_tar.rb', line 464

def (Tb::Cmd).tar_format_filemode(typeflag, mode)
  entry_type =
    case Tb::Cmd::TAR_TYPEFLAG[typeflag]
    when :regular then '-'
    when :directory then 'd'
    when :character_special then 'c'
    when :block_special then 'b'
    when :fifo then 'p'
    when :symlink then 'l'
    when :link then 'h'
    when :contiguous then 'C'
    else '?'
    end
  m = mode
  sprintf("%s%c%c%c%c%c%c%c%c%c",
    entry_type,
    (m & 0400 == 0 ? ?- : ?r),
    (m & 0200 == 0 ? ?- : ?w),
    (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
                     (m & 04000 == 0 ? ?x : ?s)),
    (m & 0040 == 0 ? ?- : ?r),
    (m & 0020 == 0 ? ?- : ?w),
    (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
                     (m & 02000 == 0 ? ?x : ?s)),
    (m & 0004 == 0 ? ?- : ?r),
    (m & 0002 == 0 ? ?- : ?w),
    (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
                     (m & 01000 == 0 ? ?x : ?t)))
end

.tar_open_with(arg) ⇒ Object



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/tb/cmd_tar.rb', line 402

def (Tb::Cmd).tar_open_with(arg)
  tar_open_with0(arg) {|f|
    magic = f.read(8)
    case magic
    when /\A\x1f\x8b/n, /\A\037\235/n # \x1f\x8b is gzip format.  \037\235 is "compress" format of old Unix.
      decompression = ['gzip', '-dc']
    when /\ABZh/
      decompression = ['bzip2', '-dc']
    when /\A\xFD7zXZ\x00/n
      decompression = ['xz', '-dc']
    end
    begin
      f.rewind
      seek_success = true
    rescue Errno::ESPIPE
      seek_success = false
    end
    # Ruby 1.9 dependent.
    if decompression
      if seek_success
        IO.popen(decompression + [{:in => f}], 'rb') {|pipe|
          yield pipe
        }
      else
        IO.pipe {|r, w|
          w.binmode
          IO.popen(decompression + [{:in => r}], 'rb') {|pipe|
            w << magic
            th = Thread.new {
              IO.copy_stream(f, w)
              w.close
            }
            begin
              yield pipe
            ensure
              th.join
            end
          }
        }
      end
    else
      if seek_success
        yield f
      else
        IO.pipe {|r, w|
          w.binmode
          w << magic
          th = Thread.new {
            IO.copy_stream(f, w)
            w.close
          }
          begin
            yield r
          ensure
            th.join
          end
        }
      end
    end
  }
end

.tar_open_with0(arg) ⇒ Object



392
393
394
395
396
397
398
399
400
# File 'lib/tb/cmd_tar.rb', line 392

def (Tb::Cmd).tar_open_with0(arg)
  if arg == '-'
    yield $stdin
  else
    open(arg, 'rb') {|f|
      yield f
    }
  end
end

.tar_parse_header(header_record) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/tb/cmd_tar.rb', line 157

def (Tb::Cmd).tar_parse_header(header_record)
  ary = header_record.unpack(Tb::Cmd::TAR_HEADER_TEPMLATE)
  h = {}
  Tb::Cmd::TAR_HEADER_STRUCTURE.each_with_index {|(k, _), i|
    h[k] = ary[i]
  }
  [:mode, :uid, :gid, :size, :mtime, :chksum, :devmajor, :devminor].each {|k|
    h[k] = h[k].to_i(8)
  }
  h[:mtime] = Time.at(h[:mtime])
  if h[:prefix].empty?
    h[:path] = h[:name]
  else
    h[:path] = h[:prefix] + '/' + h[:name]
  end
  header_record_for_chksum = header_record.dup
  header_record_for_chksum[148, 8] = ' ' * 8
  if header_record_for_chksum.sum(0) != h[:chksum]
    warn "invalid checksum: #{h[:path].inspect}"
  end
  h
end

.tar_parse_seconds_from_epoch(val) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/tb/cmd_tar.rb', line 125

def (Tb::Cmd).tar_parse_seconds_from_epoch(val)
  if /\./ =~ val
    num = ($` + $').to_i
    den = 10 ** $'.length
    t = Rational(num, den)
    begin
      Time.at(t)
    rescue TypeError
      ti = t.floor
      Time.at(ti, ((t-ti) * 1000000).floor)
    end
  else
    Time.at(val.to_i)
  end
end

.tar_read_end_of_archive_indicator(reader) ⇒ Object



284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# File 'lib/tb/cmd_tar.rb', line 284

def (Tb::Cmd).tar_read_end_of_archive_indicator(reader)
  # The end of archive indicator is two consecutive records of NULs.
  # The first record is already read.
  second_end_of_archive_indicator_record = reader.get_single_record("second record of the end of archive indicator")
  if !second_end_of_archive_indicator_record
    # some tarballs have only one record of NULs.
    return
  end
  if /\A\0*\z/ !~ second_end_of_archive_indicator_record
    warn "The second record of end of tar archive indicator is not zero"
    raise Tb::Cmd::TarFormatError
  end
  # It is acceptable that there may be garbage after the end of tar
  # archive indicator.  ("ustar Interchange Format" in POSIX)
end

.usage_list_subcommandsObject



33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/tb/cmd_help.rb', line 33

def (Tb::Cmd).usage_list_subcommands
  with_output {|f|
    f.print <<'End'
Usage:
End
    Tb::Cmd.subcommands.each {|subcommand|
      banner = self.subcommand_send("op", subcommand).banner
      usage = banner[/^Usage: (.*)/, 1]
      f.puts "  " + usage
    }
  }
end

Instance Method Details

#err(msg) ⇒ Object

Raises:

  • (SystemExit)


95
96
97
# File 'lib/tb/cmdutil.rb', line 95

def err(msg)
  raise SystemExit.new(1, msg)
end

#output_tbenum(te) ⇒ Object



165
166
167
168
169
170
171
172
173
174
# File 'lib/tb/cmdutil.rb', line 165

def output_tbenum(te)
  filename = Tb::Cmd.opt_output || '-'
  numeric = Tb::Cmd.opt_N
  filename, fmt = Tb.undecorate_filename(filename, numeric)
  factory = Tb::FormatHash.fetch(fmt)[:writer]
  with_output(filename) {|out|
    writer = factory.new(out)
    te.write_with(writer)
  }
end

#parse_aggregator_spec(spec) ⇒ Object



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/tb/cmdutil.rb', line 99

def parse_aggregator_spec(spec)
  case spec
  when 'count'
    ['count', nil]
  when /\Asum\((.*)\)\z/
    ['sum', $1]
  when /\Aavg\((.*)\)\z/
    ['avg', $1]
  when /\Amax\((.*)\)\z/
    ['max', $1]
  when /\Amin\((.*)\)\z/
    ['min', $1]
  when /\Avalues\((.*)\)\z/
    ['values', $1]
  when /\Auniquevalues\((.*)\)\z/
    ['uniquevalues', $1]
  else
    raise ArgumentError, "unexpected aggregation spec: #{spec.inspect}"
  end
end

#parse_aggregator_spec2(spec) ⇒ Object



120
121
122
123
124
125
126
127
# File 'lib/tb/cmdutil.rb', line 120

def parse_aggregator_spec2(spec)
  name, field = parse_aggregator_spec(spec)
  func = Tb::Func::AggregationFunctions[name]
  if !func
    raise ArgumentError, "unexpected aggregation spec: #{spec.inspect}"
  end
  [func, field]
end

#split_csv_argument(arg) ⇒ Object



133
134
135
# File 'lib/tb/cmdutil.rb', line 133

def split_csv_argument(arg)
  return CSV.new(arg).shift || []
end

#split_field_list_argument(arg) ⇒ Object



129
130
131
# File 'lib/tb/cmdutil.rb', line 129

def split_field_list_argument(arg)
  split_csv_argument(arg).map {|f| f || '' }
end

#tablereader_open(filename, &b) ⇒ Object



137
138
139
# File 'lib/tb/cmdutil.rb', line 137

def tablereader_open(filename, &b)
  Tb.open_reader(filename, Tb::Cmd.opt_N, &b)
end

#with_output(filename = Tb::Cmd.opt_output) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/tb/cmdutil.rb', line 141

def with_output(filename=Tb::Cmd.opt_output)
  if filename && filename != '-'
    tmp = filename + ".part"
    begin
      File.open(tmp, 'w') {|f|
        yield f
      }
      if File.exist?(filename) && FileUtils.compare_file(filename, tmp)
        File.unlink tmp
      else
        File.rename tmp, filename
      end
    ensure
      File.unlink tmp if File.exist? tmp
    end
  elsif $stdout.tty? && !Tb::Cmd.opt_no_pager
    Tb::Pager.open {|pager|
      yield pager
    }
  else
    yield $stdout
  end
end