# から入手できます.
#
# トップディレクトリへの相対パス
PATH_TO_TOP = './'
# リンクの際に省略するファイルのリスト.
# たとえば foo/index.html へのリンクを HREF="foo/" としたいなら
# INDEX_FILE = ['index.html'] などとする.
INDEX_FILE = ['index.html', 'index.htm']
# 過去何日分の変更内容を HTML 化するかの指定.
SINCE = 10
# 変更を無視するファイル名のパターン.
EXCLUDE_PATTERN = /^(tools|bin)\//
# ChangeLog の前に置く文字列
PREFIX = <<'EOP'
やまだあきらのぺーぢの ChangeLog
やまだあきらのぺーぢの ChangeLog
ここ #{SINCE} 日の間に行った更新の内容です.
まあ, 参考までに.
EOP
# ChangeLog の後に置く文字列
SUFFIX = <<'EOS'
Copyright © 1998
akira yamada.
All rights reserved.
EOS
# あるひとかたまりの変更内容の format.
# year, month, day, hour, min, sec, wday, wday_name には
# 変更日時に関する情報が格納され,
# changes には変更内容が CHANGES_FORMAT に従った形で入る.
# type には変更日時のタイプが入り,
# type == ChangeLog::DATE_TYPE_CURRENT の場合には
# time に HH:MM:SS 形式の文字列が入る(それ以外の場合には空文字列).
# name には変更した人の名前が入る.
FORMAT = <<'EOF'
#{year} 年 #{month} 月 #{day} 日 (#{wday_name}) #{time}
#{changes}
EOF
WDAY_NAME = ['日', '月', '火', '水', '木', '金', '土']
# 変更内容の format.
# items には個々の変更点が ITEMS_FORMAT に従った形で入る.
CHANGES_FORMAT = <<'EOF'
#{items}
EOF
# 個々の変更点の format.
# targets には変更対象となったファイル(とそのインデックス)に
# タグを付けたものが, change にはその具体的な内容が入る.
ITEMS_FORMAT = <<'EOF'
#{if targets.size > 0; targets + ':'; else '*'; end}
#{change}
EOF
# ここまで
VERSION_INFO = <<'EOV'
EOV
# WDAY_NAME のデフォルト
unless defined?(WDAY_NAME)
WDAY_NAME = ['日', '月', '火', '水', '木', '金', '土']
end
class ChangeLog
WDAY =
{'Mon' => 1, 'Tue' => 2, 'Wed' => 3, 'Thu' => 4,
'Fri' => 5, 'Sat' => 6, 'Sun' => 0}
MONTH =
{'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8,
'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12}
# 変更内容が記録された日時をみつけるためのパターン.
DATE_PATTERN_ISO8601 =
/^(\d{4})-(\d{2})-(\d{2})\s+/
DATE_PATTERN_CURRENT =
/^(#{WDAY.keys.join('|')}) (#{MONTH.keys.join('|')}) ( \d{1}|\d{2}) (\d{2}):(\d{2}):(\d{2}) (\d{2}|\d{4})\s+/i
# 個々の変更項目をみつけるためのパターン.
ITEM_PATTERN = /^[ \t]+\*\s*/
FOLDED_ITEM_PATTERN = /^\s*([^*]*)$/
# 個々の変更項目のパターン.
CHANGE_PATTERN = /^([^:]+):\s*(.*)$/
# ある日付に記録された変更内容
CHANGES = Struct.new('Changes',
:date, # 変更が記録された日時
:date_type, # 変更が記録された日時のタイプ
:name, # 変更を記録した人の名前
:item) # CHANGE の配列
DATE_TYPE_CURRENT = 'current'
DATE_TYPE_ISO8601 = 'iso8601'
# 個々の変更項目の内容.
CHANGE = Struct.new('Change',
:targets, # TARGETS の配列
:change) # 変更内容
# 個々の変更項目の対象.
TARGETS = Struct.new('Targets',
:filename, # 変更されたファイルの名前
:index) # 詳しい変更箇所の配列
def initialize(io)
@io = io
@next_time = true # 次の変更内容が記録された日時
@next_time_type = nil # 次の変更内容が記録された日時のタイプ
@next_name = nil # 次の変更内容を記録された人の名前
unless get_next
raise('ChageLog is Empty!')
end
end
def get_next
next_time = nil
next_time_type = nil
change_log = nil
if @next_time
# 続く(未処理の)変更項目がある.
change_log = CHANGES.new(@next_time, @next_time_type, @next_name, [])
while line = @io.gets()
# 空行は無視する.
next if /^\s*$/o =~ line
line.chomp!
if DATE_PATTERN_CURRENT =~ line
# 次の Log の開始をみつけた.
next_time = Time.local($7.to_i, MONTH[$2], $3.to_i,
$4.to_i, $5.to_i, $6.to_i)
next_time_type = DATE_TYPE_CURRENT
next_name = $'
break
elsif DATE_PATTERN_ISO8601 =~ line
# 次の Log の開始をみつけた.
next_time = Time.local($1.to_i, $2.to_i, $3.to_i)
next_time_type = DATE_TYPE_ISO8601
next_name = $'
break
elsif ITEM_PATTERN =~ line
# 変更項目の開始をみつけた.
change_log.item << CHANGE.new(nil, $')
elsif change_log.item.size > 0 && FOLDED_ITEM_PATTERN =~ line
# 変更項目の継続をみつけた.
c = $1
if /[\x21-\x7f]$/o =~ change_log.item[-1].change ||
/^[\x21-\x7f]$/o =~ c
# この行の行頭か前の行の行末が ASCII 文字なら
# ' ' を加える(TeX のごとく).
change_log.item[-1].change << ' '
end
change_log.item[-1].change << c
end
end
change_log.item.each_index do |i|
change_log.item[i].targets, change_log.item[i].change =
parse_change(change_log.item[i].change)
end
@next_time = next_time
@next_time_type = next_time_type
@next_name = next_name
return change_log
else
# これ以上変更項目はない.
#raise('No more changes')
return nil
end
end
def parse_change(line)
if CHANGE_PATTERN =~ line
return parse_targets($1), $2
else
return nil, line
end
end
private :parse_change
def parse_targets(target)
targets = []
target.scan(/([^,\(\)]+)(\(([^\(\)]+)\))?/) do |x|
filename = $1
index = $3
filename.gsub!(/^\s*|\s*$/, '')
if index && index.size > 0
index = index.split(/,\s*/)
if index.kind_of?(String)
index = [index]
end
else
index = nil
end
targets << TARGETS.new(filename, index)
end
return targets
end
end
require 'getoptlong'
# とても簡易な使いかたの説明.
def usage(message = nil)
$stderr.print "#{$0}: #{message}\n" if message
$stderr.print "usage: #{$0} [--rootdir dir]\n"
# $stderr.print "usage: #{$0} [--outfile file] [--rootdir dir]\n"
end
# dir/file または dir/file/{indexes} の有無をチェックする.
def file_exist?(dir, file, indexes)
target = [dir, file].join('/')
r = false
if FileTest.exist?(target)
if FileTest.directory?(target)
if indexes && indexes.size > 0
for f in indexes
if FileTest.exist?([target, f].join('/'))
r = true
break
end
end
end
else
r = true
end
end
return r
end
# '<' や '&' などを '<' や '&' に.
def html_escape(str)
tmp = str.dup
tmp.gsub!(/&/o, '&')
tmp.gsub!(/\"/o, '"')
tmp.gsub!(//o, '>')
return tmp
end
getoptlong =
GetoptLong.new(['--outfile', '-o', GetoptLong::OPTIONAL_ARGUMENT],
['--rootdir', '-r', GetoptLong::OPTIONAL_ARGUMENT])
outfile = nil
rootdir = '.'
getoptlong.each_option {|name, arg|
case name
when '--outfile'
outfile = arg
when '--rootdir'
rootdir = arg
end
}
SINCE_TIME = Time.now - 60*60*24*SINCE
t = ChangeLog.new(ARGF)
print eval('"' + PREFIX.gsub(/\"/o, '\"') + '"')
begin
while tt = t.get_next
break if tt.date < SINCE_TIME
name = html_escape(tt.name)
year = tt.date.year
month = tt.date.mon
day = tt.date.day
hour = tt.date.hour
min = tt.date.min
sec = tt.date.sec
wday = tt.date.wday
wday_name = html_escape(WDAY_NAME[tt.date.wday])
type = tt.date_type
time = if type == ChangeLog::DATE_TYPE_CURRENT
sprintf('%02d:%02d:%02d', hour, min, sec)
else
''
end
items = ''
targets = ''
changes = ''
tt.item.each {|x|
targets = []
x.targets.each {|y|
filename =
y.filename.gsub(/(^|\/)(#{INDEX_FILE.join('|')})/o,
'\1') if INDEX_FILE
filename = PATH_TO_TOP if filename.size == 0
if EXCLUDE_PATTERN =~ filename
targets = nil
break
end
if file_exist?(rootdir, filename, INDEX_FILE)
link = sprintf('%%s', filename)
else
link = sprintf('%%s', filename)
end
targets << sprintf(link, '', y.filename)
tmp = []
y.index.each {|z|
tmp << sprintf(link, '#' + z, z)
} if y.index
targets[-1] << ' (' + tmp.join(', ') + ')' if tmp.size > 0
} if x.targets
next unless targets
targets = targets.join(', ')
change = html_escape(x.change)
items << eval('"' + ITEMS_FORMAT + '"')
}
next if items.size == 0
changes << eval('"' + CHANGES_FORMAT + '"')
print eval('"' + FORMAT + '"')
end
rescue
usage($!)
exit 1
end
print eval('"' + SUFFIX.gsub(/\"/o, '\"') + '"')
print VERSION_INFO