ソース

wikipage.rb

 #!/usr/bin/ruby -Ke
 require 'nkf'
 require 'cgi'
 require '../wiki2xhtml.rb'
 class WikiPage
   BaseDir='./data/'		# クラス変数で保存ディレクトリを設定
   @@basedir=BaseDir		# クラス変数で保存ディレクトリを設定
   @@kcode='-e'			# 保存漢字コード
   @@curname="現在のページ"	# 更新履歴でのカレントwiki名
   @@curfname='current.txt'	# 現在の最新ページファイル名
   @@how2read='how2read.txt'	# 読み方ファイル名
   @@timefmt='%Y%m%d%H%M%S'	# バックアップファイルの時間フォーマット
   @@historyfmt="%Y年%m月%d日%H時%M分%S秒" # 表示用の時間フォーマット
   @@pfix='.txt'			# バックアップファイルの拡張子
   def initialize(name="Top")	# 特殊ページ名(検索,最近の更新,一覧)の時はpublicメソッドをオーバライドする 
     @name=name			# wiki名
     @dir=@@basedir+CGI::escape(NKF.nkf(@@kcode,@name))+'/' #eucの名前をurlエンコードする
   end
   def exist?			# ページの本文が存在するならば真を出力。 
     test(?e,@dir+@@curfname)
   end
   def name			# ページ名@nameを出力する。 
     @name
   end
   def mtime			# 最終更新時刻を出力する。Timeオブジェクトを返す。 
     File.mtime(@dir+@@curfname) if test(?e,@dir+@@curfname)
   end
   def view 			# 内容を出力する、もしページが存在しないならばeditメソッドを呼ぶ 
     fname=@dir+@@curfname
     if test(?e,fname)
       Wiki2xhtml.new("#{File.open(fname){|f| f.read}}").conv(@name)
     else 
       edit
     end
   end
   def source 			# 高速化の為にそのまま表示する
     fname=@dir+@@curfname
     if test(?e,fname)
       "#{File.open(fname){|f| f.read}}"
     else 
       ""
     end
   end
   def edit 			# 内容を編集用に出力する(<→&gt;等) 全体を#edit()で囲む 
     fname=@dir+@@curfname
     Wiki2xhtml.new("#{File.open(fname){|f| f.read} if test(?e,fname)}").conv_edit(@name)
   end
   def history(timestamp="") 	# timestampである以前のページを出力する,timestampが""の時には履歴を出力する。 
     fname=@dir+timestamp+@@pfix
     if test(?e,fname)
       Wiki2xhtml.new("#{File.open(fname){|f| f.read}}").conv(@name)
     else			# ここはwikiフォーマットが現れるので改善の余地あり
       r=[]
       Dir.glob(@dir+"[0-9]*"){|fn|
 	t=File.mtime(fn)
 	r.push(Wiki2xhtml.history(t.strftime(@@historyfmt),@name,t.strftime(@@timefmt)))
       }
       r.sort.reverse.join
     end
   end
   def reference 		# 参照ページを検索しリストを出力する。[[ページ名]]で検索と同じ 
     %Q(Test reference)
   end
   def how2read			#ページの読み方
     File.open(@dir+@@how2read){|f| f.read}
   end
   def store(content="Damy")	#ページ内容を保存する。
     fname=@dir+@@curfname
     if !test(?e,@dir)
       Dir.mkdir(@dir)
       File.open(@dir+@@how2read,&#39;w&#39;){|f| f.write(`echo "#{@name}"|kakasi -JH -KH`.upcase.chop)}
     end
     File.rename(fname,@dir+(File.mtime(fname).strftime(@@timefmt))+@@pfix) if test(?e,fname)
     File.open(fname,&#39;w&#39;){|f|f.write(NKF.nkf(@@kcode,content))} 
     #File.utime(Time.now,Time.now,@dir) # フォルダの更新時刻を最新にする
     `touch #{@dir}`		# だめだcgiでフォルダのtouchができない
     self			# とりあえず自身を返しておく
   end
   def replace(src="Damy src",dist="Damy dist",num=1) # コメント等の為に利用する。n回目マッチ規則が必要 
     content=&#39;&#39;
     fname=@dir+@@curfname
     File.open(fname){|f|content=f.read}
     Dir.mkdir(@dir) unless test(?e,@dir)
     File.rename(fname,@dir+(File.mtime(fname).strftime(@@timefmt))+@@pfix) if test(?e,fname)
     a=content.split(src)	#n回目にマッチした文字を置換する
     content=a[0..num-1].join(src)+dist+a[num..(a.size-1)].join(src) # あっているか不明テストを柔軟にかくこと
     File.open(fname,&#39;w&#39;){|f|f.write(NKF.nkf(@@kcode,content))} 
     `touch #{@dir}`		# storeと同じにしておく
     self
   end
   def delete 			# ページを削除する。(管理者モード) 
     `rm -rf #{@dir}`		# ううっ外部コマンドで軟着陸
   end
   def match(regexp=/./)
     fname=@dir+@@curfname
     if test(?e,fname) && regexp.match(File.open(fname){|f| f.read})
       true
     else
       false
     end
   end
   def upload(content="Damy Stream data") # 画像用のアップローダ 
     %Q(Damy Upload Filename)
   end
   def delcontent(num=1)
     %Q(Damy Delete Content Filename)
   end
   def contents 			# 画像等のコンテンツのリストを出す コンテンツの削除も必要かもしれない。 
     %Q(Damy Contents List)
   end
 end
 
 class WikiPages
   @@basedir=WikiPage::BaseDir
   include Enumerable
   def initialize(nregexp=/./,cregexp=nil)
     @nregexp,@cregexp=nregexp,cregexp
   end
   def each			# Enumerable用 実装がWikiPageの構造に依存しまくっていて気持ち悪い。
     Dir.glob(@@basedir+"*"){|filename|
       page=WikiPage.new(CGI::unescape(File.basename(filename)))
       if @nregexp.match(page.name)
 	if !@cregexp || @cregexp.match(page.source) # 本当はviewがいいんだろうけど…速度の為に
 	  yield page
 	end
       end
     }
   end
 end

wiki2xhtml.rb

 #!/usr/bin/ruby
 
 class Wiki2xhtml
   NS="http://www.n9d.sytes.net/mxwiki"
   def initialize(data="")
     @data=data
   end
 
   class Line			# wikiにおける行を出力するクラス
     Multi={&#39; &#39;=>&#39;pre&#39;,&#39;|&#39;=>&#39;table&#39;,&#39;,&#39;=>&#39;csv&#39;,&#39;>&#39;=>&#39;indent&#39;,&#39;=&#39;=>&#39;nlist&#39;,&#39;-&#39;=>&#39;list&#39;}
     include Enumerable
     def initialize(s="")
       @s=s
     end
     def each			# @mを含む行は複数行で返す
       s=@s.split("\n")
       while a=s.shift
 	while !s.empty? && Multi[a[0..0]] && Multi[a[0..0]] == Multi[s[0][0..0]]
 	  a+="\n"+s.shift
 	end
 	yield a			# イテレータ使用
       end
     end
   end
 
   def convline(a="")		# リンク及び文字飾りを処理する
     a=a.gsub(/(http:\/\/[a-zA-Z0-9\.\?\&=\/%;\-~#_\+:]+)/){%Q(<wiki:url uri="#{$1}" />)}
     a=a.gsub(/\[\[(.+?)::(.+?)::(.+?)\]\]/){%Q(<wiki:anchor title="#{$1}" name="#{$2}" timestamp="#{$3}" />)} #パックアップwikiリンク
     a=a.gsub(/\[\[(.+?):(<url[^>]+) \/>\]\]/i){%Q(#{$2} title="#{$1}" />)} # URLを含むalias
     a=a.gsub(/\[\[(.+?):(.+?):#(.+?)\]\]/){%Q(<wiki:anchor title="#{$1}" name="#{$2} id="#{$3}" />)}#見出しwikiリンク別名
     a=a.gsub(/\[\[(.+?):#(.+?)\]\]/){%Q(<wiki:anchor name="#{$1}" id="#{$2} />")} #見出しwikiリンク
     a=a.gsub(/\[\[(.+?):(.+?)\]\]/){%Q(<wiki:anchor title="#{$1}" name="$2" />)}#aliasのwikiリンク
     a=a.gsub(/\[\[(.+?)\]\]/){%Q(<wiki:anchor name="#{$1}" />)} #通常wikiリンク(ヒット順序=バグの元)
     a=a.gsub(/&#39;&#39;&#39;(.*?)&#39;&#39;&#39;/){"<em>#{$1}</em>"} # イタリック
     a=a.gsub(/&#39;&#39;(.*?)&#39;&#39;/){"<strong>#{$1}</strong>"} # ボールド
     a=a.gsub(/__(.*?)__/){%Q(<span style="text-decoration:underline">#{$1}</span>)} # 下線
     a=a.gsub(/%%(.*?)%%/){%Q(<span style="text-decoration:line-through">#{$1}</span>)} # 打ち消し線
     a=a.gsub(/\(\((.*?)\)\)/){%Q(<wiki:sup content="#{$1}" />)} # 脚注
     a
   end
 
   def table(str="")
     %Q(<table class="normal">\n)+str.split("\n").map{|b|
       b.gsub(/^\|/,&#39;<tr><td>&#39;).gsub(/\|[^\|]*$/,&#39;</td></tr>&#39;).gsub(/\|/,&#39;</td><td>&#39;)
     }.join("\n")+"\n</table>"
   end
 
   def list(str="",mode=&#39;-&#39;,stab=&#39;<ul>&#39;,etab=&#39;</ul>&#39;,snode=&#39;<li>&#39;,enode=&#39;</li>&#39;)	# リスト
     r,level="",0
     str.split("\n").each{|ln|
       /^(#{mode}+)/.match(ln)
       l=$1.size-level
       r+=stab*l if l>0
       r+=etab*(-l) if l<0
       level=$1.size
       r+=snode+ln.sub($1,&#39;&#39;)+enode+"\n"
     }
     r+etab*level
   end
 
   def conv(name)
     Line.new(@data).map{|line|
       if line[0..0]==&#39; &#39; # 整形なし(行頭の&#39; &#39;は削除
 	line=line.gsub(&#39;&&#39;,&#39;&&#39;).gsub(&#39;<&#39;,&#39;<&#39;).gsub(&#39;>&#39;,&#39;>&#39;).gsub(&#39;"&#39;,&#39;quot;&#39;)
 	"<pre>\n"+line.split("\n").map{|b|b.gsub(/^ /,&#39;&#39;)}.join("\n")+"\n</pre>"
       elsif line[0..0]==&#39;>&#39;	# >モード未完成(よくわからん)
 	#	line=convline(list(line,&#39;>&#39;,&#39;<blockquote>&#39;,&#39;</blockquote>&#39;,&#39;&#39;,&#39;&#39;))
 	line=line.gsub(&#39;&&#39;,&#39;&&#39;).gsub(&#39;<&#39;,&#39;<&#39;).gsub(&#39;>&#39;,&#39;>&#39;).gsub(&#39;"&#39;,&#39;quot;&#39;)
       else	
 	line=line.gsub(&#39;&&#39;,&#39;&&#39;).gsub(&#39;<&#39;,&#39;<&#39;).gsub(&#39;>&#39;,&#39;>&#39;).gsub(&#39;"&#39;,&#39;quot;&#39;)
 	line.gsub!(/\$([^\$]+)\$/){%Q(<wiki:macro cmd="#{$1}" />)}
 	case line[0..0]
 	when &#39;*&#39;			# 番号付見出し
 	  line.gsub!(/^(\*+)/,&#39;&#39;)
 	  %Q(<wiki:caption level="#{$1.size}" name="#{line}" />)
 	when &#39;+&#39;			# 番号無見出し
 	  line.gsub!(/^(\++)/,&#39;&#39;)
 	  %Q(<wiki:heading level="#{$1.size}" name="#{line}" />)
 	when &#39;-&#39;
 	  convline(list(line))
 	when &#39;=&#39;
 	  convline(list(line,&#39;=&#39;,&#39;<ol>&#39;,&#39;</ol>&#39;,&#39;<li>&#39;,&#39;</li>&#39;))
 	when &#39;:&#39;			# 説明文
 	  convline("<dl><dt>#{$1}</dt><dd>#{$2}</dd></dl>") if /^:([^:]*):(.+)$/.match(line)
 	when &#39;|&#39;			# 表
 	  convline(table(line))
 	when &#39;#&#39;			# command
 	  %Q(<wiki:cmd cmd="#{$1}" name="#{name}" #{%Q(arg="#{$2}" ) if $2}/>) if /^#([^\)]*)(?:\((.*)\)|$)/.match(line)
 	else
 	  line=convline(line)
 	  if line==""
 	    "<BR />"
 	  else
 	    %Q(<div class="normal">#{line}</div>)
 	  end
 	end
       end
     }.join("\n")
     end
 
   def conv_edit(name)
     %Q(<wiki:edit name="#{name}" content="#{@data.gsub(&#39;&&#39;,&#39;&&#39;).gsub(&#39;<&#39;,&#39;<&#39;).gsub(&#39;>&#39;,&#39;>&#39;).gsub(&#39;"&#39;,&#39;"&#39;)}" />)
   end
   
   def self.history(title,name,timestamp)
     %Q(<wiki:anchor title="#{title}" name="#{name}" timestamp="#{timestamp}" /><BR />\n)#パックアップwikiリンク
   end
 
 end
 
 if __FILE__==$0			# ライブラリテスト用コード
   class CGI
     CGI_PARAMS={&#39;form&#39;=>[&#39;value&#39;]}
     CGI_COOKIES= nil
   end
 
   File.open("helpdata.txt"){|f|
     print <<TESTEND
 <?xml version="1.0" encoding="EUC-JP"?>
 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:wiki="http://www.n9d.sytes.net/wiki" xml:lang="ja" lang="ja">
 <head><title>test</title></head>