#!/usr/bin/env ruby # # Copyright (c) 2010-2019 Kenshi Muto, Minero Aoki # 1999-2007 Minero Aoki # # This program is free software. # You can distribute or modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # For details of the GNU LGPL, see the file "COPYING". # $LOAD_PATH.unshift(File.realpath('../lib', __dir__)) require 'review' require 'optparse' include ReVIEW::TextUtils def sigmain Signal.trap(:INT) { exit 1 } unless RUBY_PLATFORM.match?(/mswin(?!ce)|mingw|cygwin|bccwin/) Signal.trap(:PIPE, 'IGNORE') end main rescue Errno::EPIPE exit 0 end def main @config = ReVIEW::Configure.values @book = ReVIEW::Book::Base.new(config: @config) @logger = ReVIEW.logger modes = nil files = ARGV unless ARGV.empty? opts = OptionParser.new opts.version = ReVIEW::VERSION opts.on('-a', '--all-chapters', 'Check all chapters.') do files = @book.chapters.map(&:path) end opts.on('-s', '--section N', 'Check section N. (deprecated)') do |n| ents = @book.parts[Integer(n) - 1] or raise ReVIEW::ApplicationError, "section #{n} not exist" files = ents.map(&:path) end opts.on('--text', 'Check text.') do modes ||= [] modes.push(:text) end opts.on('--help', 'print this message and quit.') do puts opts.help exit 0 end begin opts.parse! rescue OptionParser::ParseError => e @logger.error(e.message) puts opts.help exit 1 end unless files @logger.error('no input') exit 1 end modes ||= [:text] modes.each do |mode| case mode when :text check_text(files) else raise 'must not happen' end end end def check_text(files) re, neg = words_re("#{@book.basedir}/#{@book.reject_file}") files.each do |path| File.open(path) do |f| each_paragraph(f) do |para, lineno| s = para.join m = re.match(s) next if m.nil? || m[0] == @review_utils_word_ok next if neg && neg =~ s str, offset = find_line(para, re) out = sprintf("%s:%d: %s\n", path, lineno + offset, str) print out end end end end def find_line(lines, re) # single line? lines.each_with_index do |line, idx| if re&.match?(line) return line.gsub(re, '<<<\&>>>'), idx end end # multiple lines? i = 0 while i < lines.size - 1 str = lines[i] + lines[i + 1] return str.gsub(re, '<<<\&>>>'), i if re&.match?(str) i += 1 end raise 'must not happen' end def words_re(rc) words = [] nega = [] File.foreach(rc) do |line| next if line[0, 1] == '#' if / !/.match?(line) line, n = *line.split('!', 2) nega.push(n.strip) end words.push(line.strip) end return Regexp.compile(words.join('|')), nega.empty? ? nil : Regexp.compile(nega.join('|')) end def each_paragraph(f) @review_utils_word_ok = nil while line = f.gets case line when /\A\#@ok\((.*)\)/ @review_utils_word_ok = $1 when /\A\#@/, /\A\s*\z/ # do nothing next when %r{\A//caption\{(.*?)//\}} yield [$1], f.filename, f.lineno when %r<\A//\w.*\{\s*\z> while line = f.gets break if %r{//\}}.match?(line) end when /\A=/ yield [line.slice(/\A=+(?:\[.*?\])?\s+(.*)/, 1).strip], f.lineno else buf = [line.strip] lineno = f.lineno while line = f.gets break if line.strip.empty? break if %r{\A(?:=|//[\w\}])}.match?(line) next if /\A\#@/.match?(line) buf.push(line.strip) end yield buf, lineno @review_utils_word_ok = nil end end end def each_paragraph_line(f, &block) each_paragraph(f) do |para, *| para.each(&block) end end sigmain