From owner-doc-jp@jp.freebsd.org  Wed May  5 07:17:54 1999
Received: (from daemon@localhost)
	by castle.jp.freebsd.org (8.9.3+3.2W/8.7.3) id HAA44719;
	Wed, 5 May 1999 07:17:54 +0900 (JST)
	(envelope-from owner-doc-jp@jp.FreeBSD.org)
Received: from smtp.teleway.ne.jp (smtp.teleway.ne.jp [203.140.129.13])
	by castle.jp.freebsd.org (8.9.3+3.2W/8.7.3) with ESMTP id HAA44714
	for <doc-jp@jp.freebsd.org>; Wed, 5 May 1999 07:17:53 +0900 (JST)
	(envelope-from yasuf@big.or.jp)
Received: from post1a.po.teleway.ne.jp (post1a.teleway.ne.jp [203.140.129.22])
	by smtp.teleway.ne.jp (8.9.3+3.2W/3.7W) with ESMTP id HAA28156
	for <doc-jp@jp.freebsd.org>; Wed, 5 May 1999 07:17:23 +0900 (JST)
	(envelope-from yasuf@big.or.jp)
Received: from localhost ([210.234.51.4]) by post1a.po.teleway.ne.jp
          (Post.Office MTA v3.0 release "turbo_2"
          ID# 110-55583U50000L50000S0V35) with ESMTP id AAA14942
          for <doc-jp@jp.freebsd.org>; Wed, 5 May 1999 07:17:20 +0900
To: doc-jp@jp.freebsd.org
In-Reply-To: Your message of "Mon, 03 May 1999 00:27:58 +0900"
	<199905021528.AAA07571@tasogare.imasy.or.jp>
References: <199905021528.AAA07571@tasogare.imasy.or.jp>
X-Mailer: Mew version 1.93 on XEmacs 20.4 (Emerald)
Mime-Version: 1.0
Content-Type: Multipart/Mixed;
	boundary="--Next_Part(Wed_May__5_07:09:03_1999_809)--"
Content-Transfer-Encoding: 7bit
Message-Id: <19990505071719V.yasuf@big.or.jp>
Date: Wed, 05 May 1999 07:17:19 +0900
From: Yasuhiro Fukuma <yasuf@big.or.jp>
X-Dispatcher: imput version 980905(IM100)
Lines: 381
Reply-To: doc-jp@jp.freebsd.org
Precedence: list
X-Distribute: distribute version 2.1 (Alpha) patchlevel 24e+990430
X-Sequence: doc-jp 6224
Subject: [doc-jp 6224] Re: <www> Updates for links to handbook
Errors-To: owner-doc-jp@jp.freebsd.org
Sender: owner-doc-jp@jp.freebsd.org
X-Originator: yasuf@big.or.jp

----Next_Part(Wed_May__5_07:09:03_1999_809)--
Content-Type: Text/Plain; charset=iso-2022-jp
Content-Transfer-Encoding: 7bit

$BJ!4V(B@$BJ!2,(B $B$G$9!#(B

$B5-;v(B <199905021528.AAA07571@tasogare.imasy.or.jp> $B$K$F!"(B
Mitsuru IWASAKI <iwasaki@jp.freebsd.org> $B$5$s$O=q$-$^$7$?(B:

> >  $B!H(Bsyncstat.cgi$B$r<+:n$7$F$7$^$C$?!#$3$l$OJXMx(B(^^)$B!I(B
> 
> cvsup.jp.freebsd.org $B$+$i;}$C$F$3$l$k(B syncstat.pl $B$O(B
> $B$b$H$b$H(B CGI $B%P!<%8%g%s$+$i$O$8$a$?$s$G$9$,!"(B
> $B$$$m$$$m2~B$$5$l$F$b$O$d(B CGI $B$GF0$+$9$K$O=E$/$J$C$A$c$$$^$7$?!#(B
> $B7ZNL2=$N;29M$K$7$?$$$N$G!"$A$g$3$C$H8+$;$F$/$l$^$;$s(B?

$B$($($H!"<B$O$<$s$<$s7Z$/$J$+$C$?$j$7$^$9(B(^^;;

$B$*$^$1$K!"%3!<%I$K$b>lEv$?$jE*$JItJ,$,$?$/$5$s$"$C$?$j$7$F1x$$$N$G!"(B
$B$=$N$&$A$-$l$$$K=q$-D>$=$&$H$O;W$C$F$$$k$N$G$9$,!D!#(B

$B$=$&$$$&$o$1$G$9$,!"0l1~$/$CIU$1$F$*$-$^$9!#$A$J$_$K(BRuby$B$G=q$+$l$F$$$^$9!#(B
/usr/ports/lang/ruby $B$+$i$I$&$>!#(B

---
 Yasuhiro Fukuma ($BJ!4V(B $B9/90(B) as Yasu.F @ Kitakyushu.Fukuoka.Japan
 Web site: http://www8.big.or.jp/~yasuf/
 $B!H(BInterNIC is NOT NSI!$B!I(B

----Next_Part(Wed_May__5_07:09:03_1999_809)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename=syncstat

#!/usr/local/bin/ruby

# Copyright (c) 1999 Yasuhiro Fukuma  All rights reserved.
#
# 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.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.

RCSID = %q$Id: syncstat,v 1.4 1999/05/04 21:15:32 yasuf Exp $

CVSROOT = '/home/ncvs'

CVSWEB = 'cvsweb.cgi'

DOCUMENT = {
  'handbook' => { :title => 'FreeBSD Handbook',
                  :en => 'doc/en/handbook', :ja => 'doc/ja/handbook',
		  :skip =>
		   [ 'jcontrib', 'jauthors.ent', 'README' ] },
  'FAQ'      => { :title => 'FreeBSD FAQ',
                  :en => 'doc/FAQ',         :ja => 'doc/ja/FAQ',
		  :skip =>
		   [ 'jcontrib.sgml', 'jmembers.sgml' ] },
  'www'      => { :title => 'FreeBSD WWW',
                  :en => 'www/en',          :ja => 'www/ja',
		  :skip =>
		   [ 'cgi', 'gifs', 'java', 'newsletter',
		     'jabout.sgml', 'jmember.sgml', 'web.mk', 'robots.txt',
		     'commercial/consulting.sgml',
		     'commercial/consulting_bycat.sgml',
		     'commercial/hardware.sgml', 'commercial/misc.sgml',
		     'commercial/software.sgml',
		     'commercial/software_bycat.sgml',
		     'copyright/COPYING', 'copyright/COPYING.LIB',
		     'gallery/gallery.db', 'gallery/cgallery.sgml',
		     'gallery/npgallery.sgml', 'gallery/pgallery.sgml',
		     'internal/README.mirror',
		     'internal/homepage.sgml', 'internal/photos.sgml',
		     'ports/comments.ja', 'ports/packages', 'search/site.map',
		     /\.pl$/, %r{^commercial/.*\.(desc|raw)$},
		     %r{^releases/(1|2\.(0|1(R|\..*/announce\.sgml$)))} ] },
}

###

class SyncStat
  COMMAND = {
    :co => 'co',
    :rlog => 'rlog',
    :rcsdiff => 'rcsdiff',
  }

  attr_reader :path, :master_rev, :slave_rev, :original_rev,
              :added, :deleted, :total

  def SyncStat.get_this_revision(file)
    rcsfile = File.open("#{file},v", 'r')
    rcsfile.each do |l|
      if /^\s*head\s+([\d.]+)\s*;/
        rcsfile.close
        return $1
      end
    end
    rcsfile.close
    nil

=begin the "traditional" way
    rlog = IO.popen("#{COMMAND[:rlog]} -h '#{file}'", 'r') 
    rlog.each do |l|
      if /^head:\s*([\d.]+)$/ === l
        rlog.close
        return $1
      end
    end
    rlog.close
    nil
=end of the "traditional" way

  end

  def SyncStat.get_original_revision(file)
    co = IO.popen("#{COMMAND[:co]} -q -p. '#{file}'", 'r')
    co.each do |l|
      if /Original [Rr]evision:\s*([\d.]+)/ === l
        co.read  # against Broken pipe
        co.close
	return $1
      end
    end
    co.close
    nil
  end

  def initialize(path, master, slave)
    @path = path
    @added, @deleted, @total = 0,0,0

    @master_rev = if master then
      SyncStat.get_this_revision(mpath = master + '/' + path) else false end
    @slave_rev = if slave then
      SyncStat.get_this_revision(spath = slave + '/' + path) else false end

    return unless @master_rev and @slave_rev

    @original_rev = SyncStat.get_original_revision(spath)

    if @master_rev != @original_rev
      rcsdiff = IO.popen("#{COMMAND[:rcsdiff]} -q -u -r#@original_rev -r. '#{mpath}'", 'r')
      rcsdiff.each do |l|
        break if l[0,4] == '+++ '
      end
      rcsdiff.each do |l|
        @total += 1
        case l[0]
	when ?+
	  @added += 1
	when ?-
	  @deleted += 1
	end
      end
      rcsdiff.close
    end
  end

  def master_only?() @slave_rev == false end
  def slave_only?() @master_rev == false end

  def difference
    if master_only?
      'English only'
    elsif slave_only?
      'Japanese only'
    elsif !@master_rev or !@original_rev
      'unknown'
    elsif @master_rev == @original_rev
      'sync'
    else
      "+#@added, -#@deleted, total #@total"
    end
  end

  def SyncStat.do_tree(master, slave, skiplist = [], subdir = '/')
    skiplist ||= []
    syncstat = []
  
    masterdir = begin Dir.open(master + subdir).sort rescue Errno::ENOENT; [] end
    slavedir = begin Dir.open(slave + subdir).sort rescue Errno::ENOENT; [] end

    master_only = masterdir - slavedir
    slave_only = slavedir - masterdir

    (masterdir|slavedir).sort.each do |f|
      case f
      when 'Attic', /^\.\.?$/ then next
      else next if skiplist.find {|pat| pat === (subdir+f).sub(/,v$/,'')[1..-1] }
      end

      path = subdir + f
      $stderr.puts path if $DEBUG
      mpath, spath = master + path, slave + path
  
      if FileTest.directory? mpath or FileTest.directory? spath
        syncstat.concat do_tree(master, slave, skiplist, path + '/')
        next
      else
        path.sub!(/,v$/, '') or next
        path[0,1] = ''
      end

      syncstat << SyncStat.new(path,
                    if slave_only.include? f then nil else master end,
		    if master_only.include? f then nil else slave end)
    end

    syncstat
  end
end

def usage
  if $cgi_mode
    myname = ENV['SCRIPT_NAME']
    print <<EOM
Content-Type: text/html

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<HTML>
<HEAD>
<TITLE>Sync Status Report</TITLE>
</HEAD>
<BODY>
<H1>Sync Status Report</H1>

<UL>
#{DOCUMENT.keys.sort.collect {|k|
  %Q{<LI><A href="#{myname}?#{k}">#{DOCUMENT[k][:title]}</A>} }.join("\n") }
</UL>
</BODY>
</HTML>
EOM
    exit
  else
    STDERR.puts "usage: #$0 document-type"
    exit 2
  end
end

###

$cgi_mode = true if ENV['REQUEST_METHOD']

if $cgi_mode
  $html_mode = true
  doctype = ENV['QUERY_STRING']
else
  ARGV.each do |arg|
    case arg
    when '--html'
      $html_mode = true
    else
      doctype = arg
    end
  end
end

usage unless doctype and DOCUMENT[doctype]

en_path = CVSROOT + '/' + DOCUMENT[doctype][:en]
ja_path = CVSROOT + '/' + DOCUMENT[doctype][:ja]
skiplist = DOCUMENT[doctype][:skip]

begin
  syncstat = SyncStat.do_tree(en_path, ja_path, skiplist)
rescue
  raise unless $cgi_mode

  print <<EOH
Content-Type: text/plain
Status: 500 CGI Error

EOH
  STDERR.reopen($stdout)
  raise
end

print "Content-Type: text/html\n\n" if $cgi_mode

if $html_mode
  print <<EOH
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
<HTML>
<HEAD>
<TITLE>Sync Status Report: #{DOCUMENT[doctype][:title]}</TITLE>
</HEAD>
<BODY>
<H1>Sync Status Report: #{DOCUMENT[doctype][:title]}</H1>

<TABLE border=1>
<THEAD>
 <TR><TH>File</TH><TH>Japanese</TH><TH>English</TH><TH>Status (diff)</TH>
</THEAD>
<TBODY>
EOH
  sync, unsync, missing, original, unknown = 0,0,0,0,0
  cvsweb_en = CVSWEB + '/' + DOCUMENT[doctype][:en]
  cvsweb_ja = CVSWEB + '/' + DOCUMENT[doctype][:ja]
  syncstat.each do |stat|
    print ' <TR><TH>'
    if stat.master_only?
      print stat.path
    else
      print %Q{<A href="#{cvsweb_ja}/#{stat.path}">#{stat.path}</A>}
    end
    print '</TH><TD>'
    print %Q{<A href="#{cvsweb_ja}/#{stat.path}?rev=#{stat.slave_rev}">#{stat.original_rev||'???'}</A>} unless stat.master_only?
    print '</TD><TD>'
    unless stat.slave_only?
      if stat.master_rev
        print %Q{<A href="#{cvsweb_en}/#{stat.path}?rev=#{stat.master_rev}">#{stat.master_rev}</A>}
      else
        print '???'
      end
    end
    print '</TD><TD>'
    if stat.master_only?
      missing += 1
      print %Q{<A href="#{cvsweb_en}/#{stat.path}"><EM>English only</EM></A>}
    elsif stat.slave_only?
      original += 1
      print '<EM>Japanese only</EM>'
    elsif !stat.master_rev or !stat.original_rev
      unknown += 1
      print 'unknown'
    elsif stat.master_rev == stat.original_rev
      sync += 1
      print 'sync'
    else
      unsync += 1
      print %Q{<A href="#{cvsweb_en}/#{stat.path}.diff?r1=#{stat.original_rev}&amp;r2=#{stat.master_rev}">#{stat.original_rev} -&gt; #{stat.master_rev} (#{stat.difference})</A>}
    end
    puts '</TD></TR>'
  end
  print <<EOF
</TBODY>
<TFOOT>
 <TR><TD colspan=4>
   Status: #{
    [ if sync.nonzero? then "#{sync} sync" end,
      if unsync.nonzero? then "#{unsync} unsync" end,
      if missing.nonzero? then "#{missing} missing" end,
      if original.nonzero? then "#{original} original" end,
      if unknown.nonzero? then "#{unknown} unknown" end ].compact.join(', ') }
 </TD></TR>
</TFOOT>
</TABLE>

</BODY>
</HTML>
EOF
else
  syncstat.each do |stat|
    puts [stat.path,
          if stat.slave_only? then 'none'
          else stat.master_rev || '???' end,
          if stat.master_only? then 'none'
          else stat.original_rev || '???' end,
          stat.difference].join("\t")
  end
end

----Next_Part(Wed_May__5_07:09:03_1999_809)----
