/[pub]/suikawiki/script/wiki.cgi
Suika

Diff of /suikawiki/script/wiki.cgi

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.8 by wakaba, Sun Mar 24 00:51:52 2002 UTC revision 1.9 by wakaba, Sun Mar 24 00:55:15 2002 UTC
# Line 1  Line 1 
1  #!/usr/bin/perl  #!/usr/bin/perl
2  use lib "../lib";  use lib "../lib";
3  use CGI::Carp 'fatalsToBrowser';  use CGI::Carp 'fatalsToBrowser';
4  use Algorithm::Diff qw(traverse_sequences);  use Algorithm::Diff qw(traverse_sequences);
5  # use strict;  # use strict;
6  #  #
7  # yukiwiki.cgi - Yet another WikiWikiWeb clone.  # yukiwiki.cgi - Yet another WikiWikiWeb clone.
8  #  #
9  # Copyright (C) 2000,2001 by Hiroshi Yuki.  # Copyright (C) 2000,2001 by Hiroshi Yuki.
10  # <hyuki@hyuki.com>  # <hyuki@hyuki.com>
11  # http://www.hyuki.com/yukiwiki/  # http://www.hyuki.com/yukiwiki/
12  #  #
13  # This program is free software; you can redistribute it and/or modify  # This program is free software; you can redistribute it and/or modify
14  # it under the terms of the GNU General Public License as published by  # it under the terms of the GNU General Public License as published by
15  # the Free Software Foundation; either version 2 of the License, or  # the Free Software Foundation; either version 2 of the License, or
16  # (at your option) any later version.  # (at your option) any later version.
17  #  #
18  # This program is distributed in the hope that it will be useful,  # This program is distributed in the hope that it will be useful,
19  # but WITHOUT ANY WARRANTY; without even the implied warranty of  # but WITHOUT ANY WARRANTY; without even the implied warranty of
20  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  # GNU General Public License for more details.  # GNU General Public License for more details.
22  #  #
23  # $Id$  # $Id$
24  ##############################  ##############################
25  my $version = "1.6.6";  my $version = "1.6.6";
26  ##############################  ##############################
27  # 単独テストのときには 1 にする。  # 単独テストのときには 1 にする。
28  my $testing = 0;  my $testing = 0;
29  ##############################  ##############################
30  # 漢字ライブラリ  # 漢字ライブラリ
31  my $jcodelib = 'jcode.pl';  my $jcodelib = 'jcode.pl';
32  ##############################  ##############################
33  # 保存・表示の漢字コード  # 保存・表示の漢字コード
34  my $kanjicode = 'euc';     # 'sjis' 'euc'  my $kanjicode = 'euc';     # 'sjis' 'euc'
35  my $charset = 'euc-jisx0213';  # 'Shift_JIS' 'EUC-JP'  my $charset = 'euc-jisx0213';  # 'Shift_JIS' 'EUC-JP'
36  ##############################  ##############################
37  # dbmopenが使えるなら1、使えないなら0  # dbmopenが使えるなら1、使えないなら0
38  my $dbmopen = 0;  my $dbmopen = 0;
39  ##############################  ##############################
40  # データベース名(.pag, .dir, .dbなどは不要)  # データベース名(.pag, .dir, .dbなどは不要)
41  # $dbmopen = 1のときはデータベース名、  # $dbmopen = 1のときはデータベース名、
42  # $dbmopen = 0のときはディレクトリ名になる。  # $dbmopen = 0のときはディレクトリ名になる。
43  my $dbname = './wikidata';  my $dbname = './wikidata';
44  my $diffdbname = './wikidiff';  my $diffdbname = './wikidiff';
45  ##############################  ##############################
46  # 修正者の氏名(自由に変更してください)  # 修正者の氏名(自由に変更してください)
47  my $modifier = 'suika';  my $modifier = 'suika';
48  ##############################  ##############################
49  # 修正者のWebページ(自由に変更してください)  # 修正者のWebページ(自由に変更してください)
50  my $modifierlink = 'http://suika.fam.cx/';  my $modifierlink = 'http://suika.fam.cx/';
51  ##############################  ##############################
52  # このページのURL  # このページのURL
53  my $thisurl = 'wiki';  my $thisurl = 'wiki';
54  ##############################  ##############################
55  # 開始ページ名  # 開始ページ名
56  my $toppage = 'HomePage';  my $toppage = 'HomePage';
57  ##############################  ##############################
58  # 最終更新ページ名  # 最終更新ページ名
59  my $whatsnew = 'RecentChanges';  my $whatsnew = 'RecentChanges';
60  ##############################  ##############################
61  # 最終更新に掲載するページ数  # 最終更新に掲載するページ数
62  my $maxnew = 50;  my $maxnew = 50;
63  ##############################  ##############################
64  # アイコンファイル名(カラー版)  # アイコンファイル名(カラー版)
65  my $iconfile = '';  my $iconfile = '';
66  ##############################  ##############################
67  # アイコンファイル名(モノクロ版)  # アイコンファイル名(モノクロ版)
68  # my $iconfile = '';  # my $iconfile = '';
69  ##############################  ##############################
70  # ページを変更したときにtouchするファイル(''なら何もしない)  # ページを変更したときにtouchするファイル(''なら何もしない)
71  my $touchfile = 'touch.txt';  my $touchfile = 'touch.txt';
72  ##############################  ##############################
73  # プレビュー用の背景色  # プレビュー用の背景色
74  my $preview_color = '#FFCCCC';  my $preview_color = '#FFCCCC';
75  ##############################  ##############################
76  # 全ページのスタイル  # 全ページのスタイル
77  my $style = <<'EOD';  my $style = <<'EOD';
78  pre, dl, ul, ol, p, blockquote { line-height:120%; }  pre, dl, ul, ol, p, blockquote { line-height:120%; }
79  a { text-decoration: none; }  a { text-decoration: none; }
80  a:link { color: #0000FF; background-color: #FFFFFF; }  a:link { color: #0000FF; background-color: #FFFFFF; }
81  a:visited { color: #9900CC; background-color: #FFFFFF; }  a:visited { color: #9900CC; background-color: #FFFFFF; }
82  a:hover { text-decoration: underline; }  a:hover { text-decoration: underline; }
83  EOD  EOD
84  ##############################  ##############################
85  # テキスト入力部分の大きさ  # テキスト入力部分の大きさ
86  my $cols = 80;  my $cols = 80;
87  my $rows = 20;  my $rows = 20;
88  ##############################  ##############################
89  my %form = ();  my %form = ();
90  my %database = ();  my %database = ();
91  my %diffbase = ();  my %diffbase = ();
92  my $diff_text = '';  my $diff_text = '';
93  my @diff_added = ();  my @diff_added = ();
94  my @diff_deleted = ();  my @diff_deleted = ();
95  my $msgrefA;  my $msgrefA;
96  my $msgrefB;  my $msgrefB;
97  ##############################  ##############################
98  # 編集不可ページ名一覧  # 編集不可ページ名一覧
99  my @uneditable = ( $whatsnew );  my @uneditable = ( $whatsnew );
100  ##############################  ##############################
101  # リンク用の正規表現  # リンク用の正規表現
102  # YukiWikiのリンクは2種類ある。  # YukiWikiのリンクは2種類ある。
103  #  #
104  # (1) WikiName (RecentChangesとかFrontPageのようなもの)  # (1) WikiName (RecentChangesとかFrontPageのようなもの)
105  # (2) BracketName ([[結城浩]]とか[[トラブルシュート]]のようなもの)  # (2) BracketName ([[結城浩]]とか[[トラブルシュート]]のようなもの)
106  #  #
107  # ※シフトJISの2バイト目には ']' が来うるので、  # ※シフトJISの2バイト目には ']' が来うるので、
108  # 文字']'を1つ多くとるようにしている。  # 文字']'を1つ多くとるようにしている。
109  #  #
110  my $WikiName = '([A-Z][a-z]+([A-Z][a-z]+)+)';  my $WikiName = '([A-Z][a-z]+([A-Z][a-z]+)+)';
111  my $BracketName = '\[\[([^>\x09]+?\]?)\]\]';  my $BracketName = '\[\[([^>\x09]+?\]?)\]\]';
112    
113  # アイコン部分のタグ  # アイコン部分のタグ
114  my $IconTag = ''; #<<"EOD";  my $IconTag = ''; #<<"EOD";
115  #<a href="http://www.hyuki.com/yukiwiki/"><img src="$iconfile"  #<a href="http://www.hyuki.com/yukiwiki/"><img src="$iconfile"
116  # border="0" width="80" height="80" alt="[YukiWiki]" /></a>  # border="0" width="80" height="80" alt="[YukiWiki]" /></a>
117  #EOD  #EOD
118    
119  require "$jcodelib";  require "$jcodelib";
120    
121  &init_form($kanjicode);  &init_form($kanjicode);
122    
123  if ($testing) {  if ($testing) {
124      %form = (      %form = (
125          # 'mycmd' => 'write',          # 'mycmd' => 'write',
126          'mycmd' => 'read',          'mycmd' => 'read',
127          #'mycmd' => 'search',          #'mycmd' => 'search',
128          #'mycmd' => 'edit',          #'mycmd' => 'edit',
129          'mymsg' => <<"EOD",          'mymsg' => <<"EOD",
130  はじめまして。  はじめまして。
131  これからいろいろ書き込みますね。  これからいろいろ書き込みますね。
132  LinkPageも見てください。  LinkPageも見てください。
133  TestPageはどうでしょうか。  TestPageはどうでしょうか。
134  どうぞよろしく。  どうぞよろしく。
135  http://www.hyuki.com/  http://www.hyuki.com/
136  [[結城浩]]  [[結城浩]]
137  EOD  EOD
138          'mypage' => '<結城浩>',          'mypage' => '<結城浩>',
139          'myword' => '結',          'myword' => '結',
140          # '3C8C8B8FE98D5F3E' => '',          # '3C8C8B8FE98D5F3E' => '',
141          # 'TestPage' => '',          # 'TestPage' => '',
142      );      );
143  }  }
144  &main;  &main;
145  exit(0);  exit(0);
146    
147  # メイン  # メイン
148  sub main {  sub main {
149      &normalize_form;      &normalize_form;
150      if ($dbmopen) {      if ($dbmopen) {
151          if (!dbmopen(%database, $dbname, 0666)) {          if (!dbmopen(%database, $dbname, 0666)) {
152              &print_error("(dbmopen) $dbname が作れません。");              &print_error("(dbmopen) $dbname が作れません。");
153          }          }
154      } else {      } else {
155          if (!tie(%database, "YukiWikiDB", $dbname)) {          if (!tie(%database, "YukiWikiDB", $dbname)) {
156              &print_error("(tie error)");              &print_error("(tie error)");
157          }          }
158      }      }
159    
160      # myspecial対応      # myspecial対応
161      foreach (keys %form) {      foreach (keys %form) {
162          if (/^myspecial_(.*)/) {          if (/^myspecial_(.*)/) {
163              $form{mycmd} = $1;              $form{mycmd} = $1;
164              last;              last;
165          }          }
166      }      }
167    
168      if ($form{mycmd} eq 'read') {      if ($form{mycmd} eq 'read') {
169          &do_read;          &do_read;
170      } elsif ($form{mycmd} eq 'preview') {      } elsif ($form{mycmd} eq 'preview') {
171          &do_preview;          &do_preview;
172      } elsif ($form{mycmd} eq 'write') {      } elsif ($form{mycmd} eq 'write') {
173          &do_write;          &do_write;
174      } elsif ($form{mycmd} eq 'edit') {      } elsif ($form{mycmd} eq 'edit') {
175          &do_edit;          &do_edit;
176      } elsif ($form{mycmd} eq 'reedit') {      } elsif ($form{mycmd} eq 'reedit') {
177          &do_reedit;          &do_reedit;
178      } elsif ($form{mycmd} eq 'search') {      } elsif ($form{mycmd} eq 'search') {
179          &do_search;          &do_search;
180      } elsif ($form{mycmd} eq 'list') {      } elsif ($form{mycmd} eq 'list') {
181          &do_list;          &do_list;
182      } elsif ($form{mycmd} eq 'diff') {      } elsif ($form{mycmd} eq 'diff') {
183          &do_diff;          &do_diff;
184      } else {      } else {
185          $form{mypage} = $toppage;          $form{mypage} = $toppage;
186          &do_read;          &do_read;
187      }      }
188      if ($dbmopen) {      if ($dbmopen) {
189          dbmclose(%database);          dbmclose(%database);
190      } else {      } else {
191          untie(%database);          untie(%database);
192      }      }
193  }  }
194    
195  # ページの表示  # ページの表示
196  sub do_read {  sub do_read {
197      my $page_name = $form{mypage};      my $page_name = $form{mypage};
198      my $percent_name = &encode_percent($page_name);      my $percent_name = &encode_percent($page_name);
199      &print_header($page_name);      &print_header($page_name);
200      print qq|<h1>$IconTag<a href="$thisurl?mycmd=search;myword=$percent_name">$page_name</a></h1>\n|;      print qq|<h1>$IconTag<a href="$thisurl?mycmd=search;myword=$percent_name">$page_name</a></h1>\n|;
201      &print_toolbar($page_name);      &print_toolbar($page_name);
202      print &convert_html(&get_page($page_name));      print &convert_html(&get_page($page_name));
203      &print_footer;      &print_footer;
204  }  }
205    
206  # ページの編集  # ページの編集
207  sub do_edit {  sub do_edit {
208      if (not &is_editable($form{mypage})) {      if (not &is_editable($form{mypage})) {
209          # 編集不可ページは表示のみ          # 編集不可ページは表示のみ
210          &do_read;          &do_read;
211          return;          return;
212      }      }
213      &editpage(&get_page($form{mypage}));      &editpage(&get_page($form{mypage}));
214  }  }
215    
216  # ページの再編集  # ページの再編集
217  sub do_reedit {  sub do_reedit {
218      if (not &is_editable($form{mypage})) {      if (not &is_editable($form{mypage})) {
219          # 編集不可ページは表示のみ          # 編集不可ページは表示のみ
220          &do_read;          &do_read;
221      } else {      } else {
222          &editpage($form{mymsg});          &editpage($form{mymsg});
223      }      }
224  }  }
225    
226  sub editpage {  sub editpage {
227      my $page_msg = shift;      my $page_msg = shift;
228      my $page_name = $form{mypage};      my $page_name = $form{mypage};
229      my $digest = &calc_message_digest($page_msg);      my $digest = &calc_message_digest($page_msg);
230      &print_header($page_name);      &print_header($page_name);
231      print qq|<h1>$IconTag${page_name}の編集</h1>\n|;      print qq|<h1>$IconTag${page_name}の編集</h1>\n|;
232      &print_toolbar($page_name);      &print_toolbar($page_name);
233      $page_msg = &escape($page_msg);      $page_msg = &escape($page_msg);
234      print <<"EOD";      print <<"EOD";
235  <form action="$thisurl" method="post">  <form action="$thisurl" method="post">
236  <!--<input type="hidden" name="mycmd" value="preview">-->  <!--<input type="hidden" name="mycmd" value="preview">-->
237  <input type="hidden" name="mypage" value="$page_name">  <input type="hidden" name="mypage" value="$page_name">
238  <input type="hidden" name="mydigest" value="$digest">  <input type="hidden" name="mydigest" value="$digest">
239  <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$page_msg</textarea><br>  <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$page_msg</textarea><br>
240  <input type="submit" name="myspecial_preview" value="確認">  <input type="submit" name="myspecial_preview" value="確認">
241  <input type="submit" name="myspecial_write" value="確認せず変更">  <input type="submit" name="myspecial_write" value="確認せず変更">
242  </form>  </form>
243  <hr>  <hr>
244  <h3>テキスト整形のルール</h3>  <h3>テキスト整形のルール</h3>
245    
246  <p>通常は入力した文字がそのまま出力されますが、  <p>通常は入力した文字がそのまま出力されますが、
247  以下のルールに従ってテキスト整形を行うことができます。</p>  以下のルールに従ってテキスト整形を行うことができます。</p>
248    
249  <ul>  <ul>
250  <li>  <li>
251  空行は段落の区切りとなります。  空行は段落の区切りとなります。
252    
253  <li>  <li>
254  HTMLのタグは書けません。  HTMLのタグは書けません。
255    
256  <li>  <li>
257  ''強調''のようにシングルクォート二つではさむと、強調になります。  ''強調''のようにシングルクォート二つではさむと、強調になります。
258    
259  <li>  <li>
260  '''更に強調'''のようにシングルクォート三つではさむと、更に強調になります。  '''更に強調'''のようにシングルクォート三つではさむと、更に強調になります。
261    
262  <li>  <li>
263  ----のようにマイナス4つがあると、水平線になります。  ----のようにマイナス4つがあると、水平線になります。
264    
265  <li>  <li>
266  *を行頭に書くと大見出しになります。  *を行頭に書くと大見出しになります。
267    
268  <li>  <li>
269  **を行頭に書くと小見出しになります。  **を行頭に書くと小見出しになります。
270    
271  <li>  <li>
272  -を行頭に書くと箇条書きになります。- -- --- の3レベルがあります。  -を行頭に書くと箇条書きになります。- -- --- の3レベルがあります。
273    
274  <li>  <li>
275  :を行頭に書くと用語と解説文が作れます。  :を行頭に書くと用語と解説文が作れます。
276    
277  <pre>  <pre>
278      :用語1:いろいろ書いた解説文1      :用語1:いろいろ書いた解説文1
279      :用語2:いろいろ書いた解説文2      :用語2:いろいろ書いた解説文2
280      :用語3:いろいろ書いた解説文3      :用語3:いろいろ書いた解説文3
281  </pre>  </pre>
282    
283  <li>  <li>
284  http://www.hyuki.com/ のようなURLは自動的にリンクになります。  http://www.hyuki.com/ のようなURLは自動的にリンクになります。
285    
286  <li>  <li>
287  YukiWikiのように大文字小文字を混ぜた英文字列を書くと、  YukiWikiのように大文字小文字を混ぜた英文字列を書くと、
288  YukiWikiのページ名になります。  YukiWikiのページ名になります。
289    
290  <li>  <li>
291  [[結城浩]]のように二重の大かっこ[[ ]]でくくった文字列を書くと、  [[結城浩]]のように二重の大かっこ[[ ]]でくくった文字列を書くと、
292  YukiWikiのページ名になります。  YukiWikiのページ名になります。
293  大かっこの中にはスペースを含めてはいけません。  大かっこの中にはスペースを含めてはいけません。
294  日本語も使えます。  日本語も使えます。
295    
296  <li>  <li>
297  行頭がスペースで始まっていると、  行頭がスペースで始まっていると、
298  その段落は整形済み扱われます。  その段落は整形済み扱われます。
299  プログラムを書き込むときに使うと便利です。  プログラムを書き込むときに使うと便利です。
300    
301  <li>  <li>
302  &gt; を行頭に書くと、  &gt; を行頭に書くと、
303  引用文が書けます。  引用文が書けます。
304  &gt; の数が多いとインデントが深くなります(3レベルまで)。  &gt; の数が多いとインデントが深くなります(3レベルまで)。
305    
306  </ul>  </ul>
307  EOD  EOD
308      &print_footer;      &print_footer;
309  }  }
310    
311  # ページの検索  # ページの検索
312  sub do_search {  sub do_search {
313      if ($form{myword}) {      if ($form{myword}) {
314          &print_header('検索結果');          &print_header('検索結果');
315          print qq|<h1>$IconTag$form{myword}の検索結果</h1>\n|;          print qq|<h1>$IconTag$form{myword}の検索結果</h1>\n|;
316          &print_toolbar();          &print_toolbar();
317          print qq|<ul>\n|;          print qq|<ul>\n|;
318          my $count = 0;          my $count = 0;
319          foreach my $page_name (sort keys %database) {    # sortするのは無謀かな          foreach my $page_name (sort keys %database) {    # sortするのは無謀かな
320              if ($database{$page_name} =~ /\Q$form{'myword'}\E/) {              if ($database{$page_name} =~ /\Q$form{'myword'}\E/) {
321                  my $encoded = &encode_percent($page_name);                  my $encoded = &encode_percent($page_name);
322                  print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|;                  print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|;
323                  $count++;                  $count++;
324              }              }
325          }          }
326          print qq|</ul>\n|;          print qq|</ul>\n|;
327          if ($count > 0) {          if ($count > 0) {
328              print qq|<p><strong>$form{myword}</strong>を含むページは、上に示す<strong>$count</strong> ページです。</p>\n|;              print qq|<p><strong>$form{myword}</strong>を含むページは、上に示す<strong>$count</strong> ページです。</p>\n|;
329          } else {          } else {
330              print qq|<p><strong>$form{myword}</strong>を含むページは見つかりません。</p>\n|;              print qq|<p><strong>$form{myword}</strong>を含むページは見つかりません。</p>\n|;
331          }          }
332      } else {      } else {
333          &print_header('単語検索');          &print_header('単語検索');
334          print qq|<h1>$IconTag単語検索</h1>\n|;          print qq|<h1>$IconTag単語検索</h1>\n|;
335          &print_toolbar();          &print_toolbar();
336      }      }
337      print <<"EOD";      print <<"EOD";
338  <p>  <p>
339  <form action="$thisurl" method="post">  <form action="$thisurl" method="post">
340  <input type="hidden" name="mycmd" value="search">  <input type="hidden" name="mycmd" value="search">
341  <input type="text" name="myword" size="20" value="$form{myword}">  <input type="text" name="myword" size="20" value="$form{myword}">
342  <input type="submit" value="単語検索">  <input type="submit" value="単語検索">
343  </form>  </form>
344  </p>  </p>
345  EOD  EOD
346      &print_footer;      &print_footer;
347  }  }
348    
349  # ページの一覧  # ページの一覧
350  sub do_list {  sub do_list {
351      &print_header('ページ一覧');      &print_header('ページ一覧');
352      print qq|<h1>$IconTag ページ一覧</h1>\n|;      print qq|<h1>$IconTag ページ一覧</h1>\n|;
353      &print_toolbar();      &print_toolbar();
354      print qq|<ul>\n|;      print qq|<ul>\n|;
355      foreach my $page_name (sort keys %database) {    # sortするのは無謀かな      foreach my $page_name (sort keys %database) {    # sortするのは無謀かな
356          my $encoded = &encode_percent($page_name);          my $encoded = &encode_percent($page_name);
357          print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|          print qq|<li><a href="$thisurl?mycmd=read;mypage=$encoded">$page_name</a>\n|
358      }      }
359      print qq|</ul>\n|;      print qq|</ul>\n|;
360      &print_footer;      &print_footer;
361  }  }
362    
363  # プレビュー  # プレビュー
364  sub do_preview {  sub do_preview {
365      my $page_name = $form{mypage};      my $page_name = $form{mypage};
366      my $escapedmsg = &escape($form{mymsg});      my $escapedmsg = &escape($form{mymsg});
367      &print_header($page_name);      &print_header($page_name);
368      print qq|<h1>$IconTag${page_name}のプレビュー</h1>\n|;      print qq|<h1>$IconTag${page_name}のプレビュー</h1>\n|;
369      &print_toolbar($page_name);      &print_toolbar($page_name);
370      # local $percent_name = &encode_percent($page_name);      # local $percent_name = &encode_percent($page_name);
371      print qq|<p>以下のプレビューを確認して、よければページ下部のボタンで更新してください。</p>\n|;      print qq|<p>以下のプレビューを確認して、よければページ下部のボタンで更新してください。</p>\n|;
372      if ($form{mymsg}) {      if ($form{mymsg}) {
373          print qq|<table width="100%" bgcolor="$preview_color" ><tr><td>\n|;          print qq|<table width="100%" bgcolor="$preview_color" ><tr><td>\n|;
374          # print &convert_html($escapedmsg);          # print &convert_html($escapedmsg);
375          print &convert_html($form{mymsg});          print &convert_html($form{mymsg});
376          print qq|</td></tr></table>\n|;          print qq|</td></tr></table>\n|;
377      } else {      } else {
378          print qq|<p>(ページの内容は空です。更新するとこのページは<strong>削除</strong>されます。)</p>\n|;          print qq|<p>(ページの内容は空です。更新するとこのページは<strong>削除</strong>されます。)</p>\n|;
379      }      }
380      &print_preview_buttons($page_name, $escapedmsg, $form{mydigest});      &print_preview_buttons($page_name, $escapedmsg, $form{mydigest});
381      &print_footer;      &print_footer;
382  }  }
383    
384  sub print_preview_buttons {  sub print_preview_buttons {
385      my ($page_name, $escapedmsg, $digest) = @_;      my ($page_name, $escapedmsg, $digest) = @_;
386      print <<"EOD";      print <<"EOD";
387      <form action="$thisurl" method="post">      <form action="$thisurl" method="post">
388      <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$escapedmsg</textarea>      <textarea cols="$cols" rows="$rows" name="mymsg" wrap="virtual">$escapedmsg</textarea>
389      <br />      <br />
390      <input type="hidden" name="mypage" value="$page_name">      <input type="hidden" name="mypage" value="$page_name">
391      <input type="hidden" name="mydigest" value="$digest">      <input type="hidden" name="mydigest" value="$digest">
392      <input type="submit" name="myspecial_preview" value="再度プレビュー">      <input type="submit" name="myspecial_preview" value="再度プレビュー">
393      <input type="submit" name="myspecial_write" value="ページの更新">      <input type="submit" name="myspecial_write" value="ページの更新">
394      </form>      </form>
395  EOD  EOD
396  }  }
397    
398  # 書き込む  # 書き込む
399  sub do_write {  sub do_write {
400      if (not &is_editable($form{mypage})) {      if (not &is_editable($form{mypage})) {
401          # 編集不可ページは表示のみ          # 編集不可ページは表示のみ
402          &do_read;          &do_read;
403          return;          return;
404      }      }
405    
406      my $page_name = $form{mypage};      my $page_name = $form{mypage};
407    
408      # digestを使って、更新の衝突チェック      # digestを使って、更新の衝突チェック
409      my $original_digest = &calc_message_digest(&get_page($page_name));      my $original_digest = &calc_message_digest(&get_page($page_name));
410      if ($form{mydigest} ne $original_digest) {      if ($form{mydigest} ne $original_digest) {
411          &print_header($page_name);          &print_header($page_name);
412          print qq|<h1>$IconTag${page_name}で【更新の衝突】が起きました</h1>\n|;          print qq|<h1>$IconTag${page_name}で【更新の衝突】が起きました</h1>\n|;
413          print <<"EOD";          print <<"EOD";
414  <p>あなたがこのページを編集している間に、  <p>あなたがこのページを編集している間に、
415  他の人が同じページを更新してしまったようです。  他の人が同じページを更新してしまったようです。
416  </p><p>  </p><p>
417  以下に、あなたの編集したテキストがありますので、  以下に、あなたの編集したテキストがありますので、
418  あなたの編集内容が失われないように、  あなたの編集内容が失われないように、
419  いますぐ、メモ帳などにコピー&ペーストしてください。  いますぐ、メモ帳などにコピー&ペーストしてください。
420  </p><p>  </p><p>
421  コピー&ペーストが済んでから、  コピー&ペーストが済んでから、
422  最新の内容を見て再度編集し直してください。  最新の内容を見て再度編集し直してください。
423  最新の内容は  最新の内容は
424  <a target="_blank" href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>  <a target="_blank" href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>
425  で見ることができます。  で見ることができます。
426  </p>  </p>
427  EOD  EOD
428          # &print_toolbar($page_name);          # &print_toolbar($page_name);
429          &print_preview_buttons($page_name, &escape($form{mymsg}), $form{mydigest});          &print_preview_buttons($page_name, &escape($form{mymsg}), $form{mydigest});
430          &print_footer;          &print_footer;
431          return;          return;
432      }      }
433    
434      # diff生成      # diff生成
435      {      {
436          &opendiff;          &opendiff;
437          my @msg1 = split(/\n/, &get_page($page_name));          my @msg1 = split(/\n/, &get_page($page_name));
438          my @msg2 = split(/\n/, $form{mymsg});          my @msg2 = split(/\n/, $form{mymsg});
439          $msgrefA = \@msg1;          $msgrefA = \@msg1;
440          $msgrefB = \@msg2;          $msgrefB = \@msg2;
441          &diff_check;          &diff_check;
442          $diffbase{$form{mypage}} = $diff_text;          $diffbase{$form{mypage}} = $diff_text;
443          $diff_text = '';          $diff_text = '';
444          &closediff;          &closediff;
445      }      }
446    
447      &print_header($page_name);      &print_header($page_name);
448      &set_page($page_name, $form{mymsg});      &set_page($page_name, $form{mymsg});
449      if ($form{mymsg}) {      if ($form{mymsg}) {
450          print qq|<h1>$IconTag${page_name}を更新しました</h1>\n|;          print qq|<h1>$IconTag${page_name}を更新しました</h1>\n|;
451          &print_toolbar($page_name);          &print_toolbar($page_name);
452          print &convert_html(&get_page($page_name));          print &convert_html(&get_page($page_name));
453      } else {      } else {
454          print qq|<h1>$IconTag${page_name}を削除しました</h1>\n|;          print qq|<h1>$IconTag${page_name}を削除しました</h1>\n|;
455          &print_toolbar($page_name);          &print_toolbar($page_name);
456          print qq|<p>${page_name}を削除しました。</p>\n|;          print qq|<p>${page_name}を削除しました。</p>\n|;
457      }      }
458      &print_footer;      &print_footer;
459      # 更新されたのでタッチしておく。      # 更新されたのでタッチしておく。
460      if ($touchfile) {      if ($touchfile) {
461          open(FILE, "> $touchfile");          open(FILE, "> $touchfile");
462          print FILE "\n";          print FILE "\n";
463          close(FILE);          close(FILE);
464      }      }
465  }  }
466    
467  # ページの変更点  # ページの変更点
468  sub do_diff {  sub do_diff {
469      if (not &is_editable($form{mypage})) {      if (not &is_editable($form{mypage})) {
470          # 編集不可ページは表示のみ          # 編集不可ページは表示のみ
471          &do_read;          &do_read;
472          return;          return;
473      }      }
474      &opendiff;      &opendiff;
475      &print_header($form{mypage} . 'の変更点');      &print_header($form{mypage} . 'の変更点');
476      print qq|<h1>$IconTag <a href="$thisurl?mycmd=read&mypage=$form{mypage}">$form{mypage}</a>の変更点</h1>\n|;      print qq|<h1>$IconTag <a href="$thisurl?mycmd=read&mypage=$form{mypage}">$form{mypage}</a>の変更点</h1>\n|;
477      &print_toolbar();      &print_toolbar();
478      $_ = &escape($diffbase{$form{mypage}});      $_ = &escape($diffbase{$form{mypage}});
479      print <<"EOD";      print <<"EOD";
480  <ul>  <ul>
481  <li>追加された行は<ins>青色</ins>です。  <li>追加された行は<ins>青色</ins>です。
482  <li>削除された行は<del>赤色</del>です。  <li>削除された行は<del>赤色</del>です。
483  <li><a href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>へ行く。  <li><a href="$thisurl?mycmd=read;mypage=$form{mypage}">$form{mypage}</a>へ行く。
484  </ul>  </ul>
485  <hr />  <hr />
486  EOD  EOD
487      print qq|<pre>\n|;      print qq|<pre>\n|;
488      foreach (split(/\n/, $_)) {      foreach (split(/\n/, $_)) {
489          if (/^\+(.*)/) {          if (/^\+(.*)/) {
490              print qq|<ins>$1</ins>\n|;              print qq|<ins>$1</ins>\n|;
491          } elsif (/^\-(.*)/) {          } elsif (/^\-(.*)/) {
492              print qq|<del>$1</del>\n|;              print qq|<del>$1</del>\n|;
493          } elsif (/^\=(.*)/) {          } elsif (/^\=(.*)/) {
494              print qq|$1\n|;              print qq|$1\n|;
495          } else {          } else {
496              print qq|??? $_\n|;              print qq|??? $_\n|;
497          }          }
498      }      }
499      print qq|</pre>\n|;      print qq|</pre>\n|;
500      &print_footer;      &print_footer;
501      &closediff;      &closediff;
502  }  }
503    
504  sub opendiff {  sub opendiff {
505      if ($dbmopen) {      if ($dbmopen) {
506          if (!dbmopen(%diffbase, $diffdbname, 0666)) {          if (!dbmopen(%diffbase, $diffdbname, 0666)) {
507              &print_error("(dbmopen) $diffdbname が作れません。");              &print_error("(dbmopen) $diffdbname が作れません。");
508          }          }
509      } else {      } else {
510          if (!tie(%diffbase, "YukiWikiDB", $diffdbname)) {          if (!tie(%diffbase, "YukiWikiDB", $diffdbname)) {
511              &print_error("(tie error)");              &print_error("(tie error)");
512          }          }
513      }      }
514  }  }
515    
516  sub closediff {  sub closediff {
517      if ($dbmopen) {      if ($dbmopen) {
518          dbmclose(%diffbase);          dbmclose(%diffbase);
519      } else {      } else {
520          untie(%diffbase);          untie(%diffbase);
521      }      }
522  }  }
523    
524  # フォームからの情報を連想配列 %form に入れる  # フォームからの情報を連想配列 %form に入れる
525  # &init_form('euc');  # &init_form('euc');
526  sub init_form {  sub init_form {
527      my ($charcode) = @_;      my ($charcode) = @_;
528      my $query;      my $query;
529      if ($ENV{REQUEST_METHOD} =~ /^post$/i) {      if ($ENV{REQUEST_METHOD} =~ /^post$/i) {
530          read(STDIN, $query, $ENV{CONTENT_LENGTH});          read(STDIN, $query, $ENV{CONTENT_LENGTH});
531      } else {      } else {
532          $query = $ENV{QUERY_STRING};          $query = $ENV{QUERY_STRING};
533      }      }
534      my @assocarray = split(/[&;]/, $query);      my @assocarray = split(/[&;]/, $query);
535      foreach my $assoc (@assocarray) {      foreach my $assoc (@assocarray) {
536          my ($property, $value) = split(/=/, $assoc);          my ($property, $value) = split(/=/, $assoc);
537          $value =~ tr/+/ /;          $value =~ tr/+/ /;
538          $value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;          $value =~ s/%([A-Fa-f0-9][A-Fa-f0-9])/pack("C", hex($1))/eg;
539          &jcode::convert(\$value, $charcode);          &jcode::convert(\$value, $charcode);
540          $form{$property} = $value;          $form{$property} = $value;
541      }      }
542  }  }
543    
544  # エラーページを出力する  # エラーページを出力する
545  sub print_error {  sub print_error {
546      my ($msg) = @_;      my ($msg) = @_;
547      &print_header('Error');      &print_header('Error');
548      print "<h1>$IconTag$msg</h1></body></html>";      print "<h1>$IconTag$msg</h1></body></html>";
549      exit(0);      exit(0);
550  }  }
551    
552  sub escape {  sub escape {
553      my ($line) = shift;      my ($line) = shift;
554      $line =~ s|<|&lt;|g;      $line =~ s|<|&lt;|g;
555      $line =~ s|>|&gt;|g;      $line =~ s|>|&gt;|g;
556      $line =~ s|"|&quot;|g;      $line =~ s|"|&quot;|g;
557      # $line =~ s|\&|&amp;|g;      # $line =~ s|\&|&amp;|g;
558      return $line;      return $line;
559  }  }
560    
561  sub inline {  sub inline {
562      my ($line) = shift;      my ($line) = shift;
563      $line = &escape($line);      $line = &escape($line);
564      $line =~ s|'''([^']+?)'''|<strong>$1</strong>|g;      $line =~ s|'''([^']+?)'''|<strong>$1</strong>|g;
565      $line =~ s|''([^']+?)''|<em>$1</em>|g;                $line =~ s|''([^']+?)''|<em>$1</em>|g;          
566      $line =~ s!      $line =~ s!
567         (         (
568           (?:&lt;(?:mailto|http|https|ftp|urn):[\x21-\x7E]*)&gt;           (?:&lt;(?:mailto|http|https|ftp|urn):[\x21-\x7E]*)&gt;
569         #| (?:$WikiName)                         # LocalLinkLikeThis         #| (?:$WikiName)                         # LocalLinkLikeThis
570         | (?:$BracketName)                      # [[日本語リンク]]         | (?:$BracketName)                      # [[日本語リンク]]
571         )         )
572              !              !
573                  &make_link($1)                  &make_link($1)
574              !gex;              !gex;
575      return $line;      return $line;
576  }  }
577    
578  # ページのタイトルからページの内容を得る  # ページのタイトルからページの内容を得る
579  sub get_page {  sub get_page {
580      my $page_name = shift;      my $page_name = shift;
581      return $database{$page_name};      return $database{$page_name};
582  }  }
583    
584  # ページの内容を与える  # ページの内容を与える
585  # &set_page($title, $txt)  # &set_page($title, $txt)
586  sub set_page {  sub set_page {
587      # ページを更新する      # ページを更新する
588      my $title = $_[0];      my $title = $_[0];
589      $database{$title} = $_[1];      $database{$title} = $_[1];
590      # 空ページなら削除する      # 空ページなら削除する
591      unless ($database{$title}) {      unless ($database{$title}) {
592          delete $database{$title};          delete $database{$title};
593      }      }
594      # RecentChangesを更新する      # RecentChangesを更新する
595      my $delim = ' - ';      my $delim = ' - ';
596      my @pages = split(/\n/, $database{$whatsnew});      my @pages = split(/\n/, $database{$whatsnew});
597      my $datestr = &get_current_datestr;      my $datestr = &get_current_datestr;
598      unshift(@pages, qq|-$datestr$delim$title|);      unshift(@pages, qq|-$datestr$delim$title|);
599      # 同一ページの更新は最新のもののみにし、      # 同一ページの更新は最新のもののみにし、
600      # 存在しないページはスキップする。      # 存在しないページはスキップする。
601      my %count;      my %count;
602      my @newpages;      my @newpages;
603      foreach my $line (@pages) {      foreach my $line (@pages) {
604          my ($prefix, $title) = split(/$delim/, $line);          my ($prefix, $title) = split(/$delim/, $line);
605          $count{$title}++;          $count{$title}++;
606          if ($count{$title} == 1 and exists($database{$title})) {          if ($count{$title} == 1 and exists($database{$title})) {
607              push(@newpages, qq|$prefix - $title|);              push(@newpages, qq|$prefix - $title|);
608          }          }
609      }      }
610      # ここで本当に更新      # ここで本当に更新
611      $database{$whatsnew} = join("\n", splice(@newpages, 0, $maxnew));      $database{$whatsnew} = join("\n", splice(@newpages, 0, $maxnew));
612  }  }
613    
614  # ページのヘッダを出力  # ページのヘッダを出力
615  sub print_header {  sub print_header {
616      my $title = shift;      my $title = shift;
617      print <<"EOD";      print <<"EOD";
618  Content-type: text/html  Content-type: text/html
619    
620  <html><head>  <html><head>
621  <title>$title</title>  <title>$title</title>
622  <style type="text/css">  <style type="text/css">
623  $style  $style
624  </style>  </style>
625  </head>  </head>
626  <body>  <body>
627  EOD  EOD
628  }  }
629    
630  # ツールバーを出力  # ツールバーを出力
631  sub print_toolbar {  sub print_toolbar {
632      my $page_name = shift;      my $page_name = shift;
633      my $percent_name = &encode_percent($page_name);      my $percent_name = &encode_percent($page_name);
634      my $editlink = '';      my $editlink = '';
635      if ($page_name ne '' and &is_editable($page_name)) {      if ($page_name ne '' and &is_editable($page_name)) {
636          $editlink = <<"EOD";          $editlink = <<"EOD";
637  <a href="$thisurl?mycmd=edit;mypage=$percent_name">編集</a> |  <a href="$thisurl?mycmd=edit;mypage=$percent_name">編集</a> |
638  <a href="$thisurl?mycmd=diff;mypage=$percent_name">差分</a> |  <a href="$thisurl?mycmd=diff;mypage=$percent_name">差分</a> |
639  EOD  EOD
640      }      }
641      print <<"EOD";      print <<"EOD";
642  <p>  <p>
643   [   [
644  <a href="$thisurl?mycmd=read;mypage=$toppage">トップ</a> |  <a href="$thisurl?mycmd=read;mypage=$toppage">トップ</a> |
645  <a href="$thisurl?mycmd=list">一覧</a> |  <a href="$thisurl?mycmd=list">一覧</a> |
646  $editlink  $editlink
647  <a href="$thisurl?mycmd=search">単語検索</a> |  <a href="$thisurl?mycmd=search">単語検索</a> |
648  <a href="$thisurl?mycmd=read;mypage=$whatsnew">最終更新</a>  <a href="$thisurl?mycmd=read;mypage=$whatsnew">最終更新</a>
649   ]   ]
650  </p>  </p>
651  EOD  EOD
652  }  }
653    
654  # ページのフッタを出力  # ページのフッタを出力
655  sub print_footer {  sub print_footer {
656      print <<"EOD";      print <<"EOD";
657  <address>  <address>
658  <a href="http://www.hyuki.com/yukiwiki/">YukiWiki</a> 1.6.6 Copyright (C) 2000,2001 by <a href="http://www.hyuki.com/">Hiroshi Yuki.</a>  <a href="http://www.hyuki.com/yukiwiki/">YukiWiki</a> 1.6.6 Copyright (C) 2000,2001 by <a href="http://www.hyuki.com/">Hiroshi Yuki.</a>
659  + <a href="$modifierlink">$modifier</a> ${version}.  + <a href="$modifierlink">$modifier</a> ${version}.
660  [<a href="/" title="このサーバーの首頁">/</a>  [<a href="/" title="このサーバーの首頁">/</a>
661  <a href="/map" title="このサーバーの案内">地図</a>  <a href="/map" title="このサーバーの案内">地図</a>
662  <a href="/search/" title="このサーバーの検索">検索</a>]  <a href="/search/" title="このサーバーの検索">検索</a>]
663  </address>  </address>
664  </body></html>  </body></html>
665  EOD  EOD
666  }  }
667    
668  # URLやページの名前からリンクを作る  # URLやページの名前からリンクを作る
669  sub make_link {  sub make_link {
670      my $name = shift;      my $name = shift;
671      $name =~ s/^&lt;(.*)&gt;$/$1/;      $name =~ s/^&lt;(.*)&gt;$/$1/;
672      $name =~ s/^\[\[(.*)\]\]$/$1/;      $name =~ s/^\[\[(.*)\]\]$/$1/;
673      if ($name =~ /^(http|https|ftp).*?(\.png|\.jpeg|\.jpg)?$/) {      if ($name =~ /^(http|https|ftp).*?(\.png|\.jpeg|\.jpg)?$/) {
674          if ($2) {          if ($2) {
675              return qq|<a href="$name"><img border="0" src="$name" /></a>|;              return qq|<a href="$name"><img border="0" src="$name" /></a>|;
676          } else {          } else {
677              return qq|&lt;<a href="$name">$name</a>&gt;|;              return qq|&lt;<a href="$name">$name</a>&gt;|;
678          }          }
679      } elsif ($name =~ /^mailto:(.*)/) {      } elsif ($name =~ /^mailto:(.*)/) {
680          my $address = $1;          my $address = $1;
681          return qq|&lt;<a href="$name">$address</a>&gt;|;          return qq|&lt;<a href="$name">$address</a>&gt;|;
682      } elsif ($name =~ /^urn:[0-9A-Za-z_:-]+/) {      } elsif ($name =~ /^urn:[0-9A-Za-z_:-]+/) {
683          return qq|&lt;<a href="/uri-res/N2L?${name}">$name</a>&gt;|;          return qq|&lt;<a href="/uri-res/N2L?${name}">$name</a>&gt;|;
684      } else {      } else {
685        my $name2 = $name; $name2 =~ tr/\x20/-/;        my $name2 = $name; $name2 =~ tr/\x20/-/;
686        if ($database{$name2}) {        if ($database{$name2}) {
687          my $percent_name = &encode_percent($name2);          my $percent_name = &encode_percent($name2);
688          return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;          return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;
689        } elsif ($database{'[['.$name2.']]'}) {        } elsif ($database{'[['.$name2.']]'}) {
690          my $percent_name = &encode_percent('[['.$name2.']]');          my $percent_name = &encode_percent('[['.$name2.']]');
691          return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;          return qq|<a href="$thisurl?mycmd=read;mypage=$percent_name" class="wiki">$name</a>|;
692        } else {        } else {
693          my $percent_name = &encode_percent($name2);          my $percent_name = &encode_percent($name2);
694          return qq|<a href="$thisurl?mycmd=edit;mypage=$percent_name" class="wiki newpage">$name?</a>|;          return qq|<a href="$thisurl?mycmd=edit;mypage=$percent_name" class="wiki newpage">$name?</a>|;
695        }        }
696      }      }
697  }  }
698    
699  # %xx の形式にエンコードする  # %xx の形式にエンコードする
700  # これは、  # これは、
701  # http://www.hyuki.com/yukiwiki/yukiwiki.cgi?mycmd=read&mypage=%3C%8C%8B%8F%E9%8D_%3E  # http://www.hyuki.com/yukiwiki/yukiwiki.cgi?mycmd=read&mypage=%3C%8C%8B%8F%E9%8D_%3E
702  # という形式のために使われる。  # という形式のために使われる。
703  # '<結城浩>' → '%3C%8C%8B%8F%E9%8D_%3E'  # '<結城浩>' → '%3C%8C%8B%8F%E9%8D_%3E'
704  sub encode_percent {  sub encode_percent {
705      my $name = shift;      my $name = shift;
706      my $encoded = '';      my $encoded = '';
707      foreach my $ch (split(//, $name)) {      foreach my $ch (split(//, $name)) {
708          if ($ch =~ /[A-Za-z0-9_]/) {          if ($ch =~ /[A-Za-z0-9_]/) {
709              $encoded .= $ch;              $encoded .= $ch;
710          } else {          } else {
711              $encoded .= '%' . sprintf("%02X", ord($ch));              $encoded .= '%' . sprintf("%02X", ord($ch));
712          }          }
713      }      }
714      return $encoded;      return $encoded;
715  }  }
716    
717  # テキスト本体をHTMLに変換する  # テキスト本体をHTMLに変換する
718  sub convert_html {  sub convert_html {
719      my ($txt) = shift;      my ($txt) = shift;
720      my (@txt) = split(/\n/, $txt);      my (@txt) = split(/\n/, $txt);
721      foreach (@txt) {      foreach (@txt) {
722          chomp;          chomp;
723          if (/^\*\*(.*)/) {          if (/^\*\*(.*)/) {
724              push(@result, splice(@saved), '<h3>' . &inline($1) . '</h3>');              push(@result, splice(@saved), '<h3>' . &inline($1) . '</h3>');
725          } elsif (/^\*(.*)/) {          } elsif (/^\*(.*)/) {
726              push(@result, splice(@saved), '<h2>' . &inline($1) . '</h2>');              push(@result, splice(@saved), '<h2>' . &inline($1) . '</h2>');
727          } elsif (/^----/) {          } elsif (/^----/) {
728              push(@result, splice(@saved), '<hr>');              push(@result, splice(@saved), '<hr>');
729          } elsif (/^(-{1,3})(.*)/) {          } elsif (/^(-{1,3})(.*)/) {
730              &back_push('ul', length($1));              &back_push('ul', length($1));
731              push(@result, '<li>' . &inline($2) . '</li>');              push(@result, '<li>' . &inline($2) . '</li>');
732          } elsif (/^:([^:]+):(.*)/) {          } elsif (/^:([^:]+):(.*)/) {
733              &back_push('dl', 1);              &back_push('dl', 1);
734              push(@result, '<dt>' . &inline($1) . '</dt>', '<dd>' . &inline($2) . '</dd>');              push(@result, '<dt>' . &inline($1) . '</dt>', '<dd>' . &inline($2) . '</dd>');
735          } elsif (/^(>{1,3})(.*)/) {          } elsif (/^(>{1,3})(.*)/) {
736              &back_push('blockquote', length($1));              &back_push('blockquote', length($1));
737              push(@result, &inline($2));              push(@result, &inline($2));
738          } elsif (/^\s*$/) {          } elsif (/^\s*$/) {
739              push(@result, splice(@saved));              push(@result, splice(@saved));
740              unshift(@saved, "</p>");              unshift(@saved, "</p>");
741              push(@result, "<p>");              push(@result, "<p>");
742          } elsif (/^(\s+.*)$/) {          } elsif (/^(\s+.*)$/) {
743              &back_push('pre', 1);              &back_push('pre', 1);
744              push(@result, &escape($1)); # Not &inline, but &escape              push(@result, &escape($1)); # Not &inline, but &escape
745          } else {          } else {
746              push(@result, &inline($_));              push(@result, &inline($_));
747          }          }
748      }      }
749      push(@result, splice(@saved));      push(@result, splice(@saved));
750      return join("\n", @result);      return join("\n", @result);
751  }  }
752    
753  # &back_push($tag, $count)  # &back_push($tag, $count)
754  # $tagのタグを$levelレベルまで詰める。  # $tagのタグを$levelレベルまで詰める。
755  sub back_push {  sub back_push {
756      my ($tag, $level) = @_;      my ($tag, $level) = @_;
757      while (@saved > $level) {      while (@saved > $level) {
758          push(@result, shift(@saved));          push(@result, shift(@saved));
759      }      }
760      if ($saved[0] ne "</$tag>") {      if ($saved[0] ne "</$tag>") {
761          push(@result, splice(@saved));          push(@result, splice(@saved));
762      }      }
763      while (@saved < $level) {      while (@saved < $level) {
764          unshift(@saved, "</$tag>");          unshift(@saved, "</$tag>");
765          push(@result, "<$tag>");          push(@result, "<$tag>");
766      }      }
767  }  }
768    
769  # 編集可能ページか?  # 編集可能ページか?
770  sub is_editable {  sub is_editable {
771      my ($pagename) = @_;      my ($pagename) = @_;
772      foreach (@uneditable) {      foreach (@uneditable) {
773          if ($pagename eq $_) {          if ($pagename eq $_) {
774              return 0;              return 0;
775          }          }
776      }      }
777      if (&is_valid_name($pagename)) {      if (&is_valid_name($pagename)) {
778          return 1;          return 1;
779      }      }
780      return 0;      return 0;
781  }  }
782    
783  # Validな名前か?  # Validな名前か?
784  sub is_valid_name {  sub is_valid_name {
785      my ($pagename) = @_;      my ($pagename) = @_;
786      if ($pagename =~ /^$WikiName$/) {      if ($pagename =~ /^$WikiName$/) {
787          return 1;          return 1;
788      } elsif ($pagename =~ /^$BracketName$/) {      } elsif ($pagename =~ /^$BracketName$/) {
789          return 1;          return 1;
790      } else {      } else {
791          return 0;          return 0;
792      }      }
793  }  }
794    
795  # 現在時刻の文字列を得る  # 現在時刻の文字列を得る
796  sub get_current_datestr {  sub get_current_datestr {
797      my (@wdays) = ( "日", "月", "火", "水", "木", "金", "土" );      my (@wdays) = ( "日", "月", "火", "水", "木", "金", "土" );
798      my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time);      my ($sec, $min, $hour, $mday, $mon, $year, $wday) = localtime(time);
799      return sprintf("%4d-%02d-%02d (%s) %02d:%02d:%02d",      return sprintf("%4d-%02d-%02d (%s) %02d:%02d:%02d",
800          $year + 1900, $mon + 1, $mday, $wdays[$wday], $hour, $min, $sec);          $year + 1900, $mon + 1, $mday, $wdays[$wday], $hour, $min, $sec);
801  }  }
802    
803  # URL?SomePageや、  # URL?SomePageや、
804  # URL?[[結城浩]]の形式だった場合、(not yet)  # URL?[[結城浩]]の形式だった場合、(not yet)
805  # 強制的にmycmdをreadにして$formの内容を設定する。  # 強制的にmycmdをreadにして$formの内容を設定する。
806  sub normalize_form {  sub normalize_form {
807      foreach my $key (keys %form) {      foreach my $key (keys %form) {
808          if ($key =~ /^$WikiName$/) {          if ($key =~ /^$WikiName$/) {
809              $form{mycmd} = 'read';              $form{mycmd} = 'read';
810              $form{mypage} = $1;              $form{mypage} = $1;
811              last;              last;
812          } elsif ($key =~ /^$BracketName$/) {          } elsif ($key =~ /^$BracketName$/) {
813              $form{mycmd} = 'read';              $form{mycmd} = 'read';
814              $form{mypage} = $1;              $form{mypage} = $1;
815              last;              last;
816          }          }
817      }      }
818  }  }
819    
820  # 変換テストを行なうときのサンプル  # 変換テストを行なうときのサンプル
821  sub print_sample {  sub print_sample {
822      my $txt = &convert_html(<<"EOD");      my $txt = &convert_html(<<"EOD");
823  *大見出し1  *大見出し1
824  **小見出し1-1  **小見出し1-1
825  -項目1  -項目1
826  -項目2  -項目2
827  -項目3  -項目3
828  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
829  段落1段落1段落1段落1段落1段落''強調''1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落''強調''1段落1段落1段落1段落1段落1
830  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
831    
832  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
833  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
834  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
835  **小見出し1-2  **小見出し1-2
836  :用語1:いろいろ書いた解説文1と''強調単語''  :用語1:いろいろ書いた解説文1と''強調単語''
837  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
838  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
839  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
840  :用語2:いろいろ書いた解説文2  :用語2:いろいろ書いた解説文2
841  :用語3:いろいろ書いた解説文3  :用語3:いろいろ書いた解説文3
842  ----  ----
843  *大見出し2  *大見出し2
844  **小見出し2-1  **小見出し2-1
845  &lt;http://suika.fam.cx/&gt;  &lt;http://suika.fam.cx/&gt;
846  **小見出し2-2  **小見出し2-2
847    
848  [[結城浩]]  [[結城浩]]
849    
850  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1段落1
851  段落1段落1段落1段落'''強調'''1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落'''強調'''1段落1段落1段落1段落1段落1段落1段落1
852  段落1段落1段落1段落'''''強い強調'''''1段落1段落1段落1段落1段落1段落1段落1段落1  段落1段落1段落1段落'''''強い強調'''''1段落1段落1段落1段落1段落1段落1段落1段落1
853  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
854  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
855  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2  >段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2段落2
856    
857  レベル0レベル0レベル0レベル0レベル0レベル0  レベル0レベル0レベル0レベル0レベル0レベル0
858    
859  >レベル1  >レベル1
860  >レベル1  >レベル1
861  >レベル1  >レベル1
862  >>レベル2  >>レベル2
863  >>レベル2  >>レベル2
864  >>レベル2  >>レベル2
865  >>>レベル3  >>>レベル3
866  -はろ1  -はろ1
867  --はろ2  --はろ2
868  ろろろろ2  ろろろろ2
869  ---はろ3  ---はろ3
870  --はろ2  --はろ2
871  ---はろ3  ---はろ3
872  --はろ2  --はろ2
873  ---はろ3  ---はろ3
874  >>>レベル3  >>>レベル3
875  >>>レベル3  >>>レベル3
876  >>>レベル3  >>>レベル3
877  EOD  EOD
878      print $txt;      print $txt;
879      exit;      exit;
880  }  }
881    
882  sub diff_check {  sub diff_check {
883      traverse_sequences(      traverse_sequences(
884              $msgrefA, $msgrefB,              $msgrefA, $msgrefB,
885              {              {
886                  MATCH => \&df_match,                  MATCH => \&df_match,
887                  DISCARD_A => \&df_delete,                  DISCARD_A => \&df_delete,
888                  DISCARD_B => \&df_add,                  DISCARD_B => \&df_add,
889              }              }
890      );      );
891      &diff_flush;      &diff_flush;
892  }  }
893    
894  sub diff_flush {  sub diff_flush {
895      $diff_text .= join('', map { "-$_\n" } splice(@diff_deleted));      $diff_text .= join('', map { "-$_\n" } splice(@diff_deleted));
896      $diff_text .= join('', map { "+$_\n" } splice(@diff_added));      $diff_text .= join('', map { "+$_\n" } splice(@diff_added));
897  }  }
898    
899  sub df_match {  sub df_match {
900      my ($a, $b) = @_;      my ($a, $b) = @_;
901      &diff_flush;      &diff_flush;
902      $diff_text .= "=$msgrefA->[$a]\n";      $diff_text .= "=$msgrefA->[$a]\n";
903  }  }
904    
905  sub df_delete {  sub df_delete {
906      my ($a, $b) = @_;      my ($a, $b) = @_;
907      push(@diff_deleted, $msgrefA->[$a]);      push(@diff_deleted, $msgrefA->[$a]);
908  }  }
909    
910  sub df_add {  sub df_add {
911      my ($a, $b) = @_;      my ($a, $b) = @_;
912      push(@diff_added, $msgrefB->[$b]);      push(@diff_added, $msgrefB->[$b]);
913  }  }
914    
915  sub calc_message_digest {   # You have to use MD5...  sub calc_message_digest {   # You have to use MD5...
916      my $text = shift;      my $text = shift;
917      my @text = split(//, $text);      my @text = split(//, $text);
918      my $len = length($text);      my $len = length($text);
919      my $checksum = 0;      my $checksum = 0;
920      foreach (@text) {      foreach (@text) {
921          $checksum += ord($_);          $checksum += ord($_);
922          $checksum = ($checksum * 2) % 65536 + (($checksum & 32768) ? 1 : 0); # 16bit rotate          $checksum = ($checksum * 2) % 65536 + (($checksum & 32768) ? 1 : 0); # 16bit rotate
923      }      }
924      return "$len:$checksum";      return "$len:$checksum";
925  }  }
926    
927  # Definition of YukiWikiDB  # Definition of YukiWikiDB
928  package YukiWikiDB;  package YukiWikiDB;
929    
930  my $debug = 1;  my $debug = 1;
931    
932  # Constructor  # Constructor
933  sub new {  sub new {
934      return shift->TIEHASH(@_);      return shift->TIEHASH(@_);
935  }  }
936    
937  # tying  # tying
938  sub TIEHASH {  sub TIEHASH {
939      my ($class, $dbname) = @_;      my ($class, $dbname) = @_;
940      my $self = {      my $self = {
941          dir => $dbname,          dir => $dbname,
942          keys => [],          keys => [],
943      };      };
944      if (not -d $self->{dir}) {      if (not -d $self->{dir}) {
945          if (!mkdir($self->{dir}, 0777)) {          if (!mkdir($self->{dir}, 0777)) {
946              print "mkdir(" . $self->{dir} . ") fail\n" if ($debug);              print "mkdir(" . $self->{dir} . ") fail\n" if ($debug);
947              return undef;              return undef;
948          }          }
949      }      }
950      return bless($self, $class);      return bless($self, $class);
951  }  }
952    
953  # Store  # Store
954  sub STORE {  sub STORE {
955      my ($self, $key, $val) = @_;      my ($self, $key, $val) = @_;
956      my $file = &make_filename($self, $key);      my $file = &make_filename($self, $key);
957      if (open(FILE,"> $file")) {      if (open(FILE,"> $file")) {
958          binmode(FILE);          binmode(FILE);
959          print FILE $val;          print FILE $val;
960          close(FILE);          close(FILE);
961          return $self->{$key} = $val;          return $self->{$key} = $val;
962      } else {      } else {
963          print "$file create error.";          print "$file create error.";
964      }      }
965  }  }
966    
967  # Fetch  # Fetch
968  sub FETCH {  sub FETCH {
969      my ($self, $key) = @_;      my ($self, $key) = @_;
970      my $file = &make_filename($self, $key);      my $file = &make_filename($self, $key);
971      if (open(FILE, $file)) {      if (open(FILE, $file)) {
972          local $/;          local $/;
973          $self->{$key} = <FILE>;          $self->{$key} = <FILE>;
974          close(FILE);          close(FILE);
975      }      }
976      return $self->{$key};      return $self->{$key};
977  }  }
978    
979  # Exists  # Exists
980  sub EXISTS {  sub EXISTS {
981      my ($self, $key) = @_;      my ($self, $key) = @_;
982      my $file = &make_filename($self, $key);      my $file = &make_filename($self, $key);
983      return -e($file);      return -e($file);
984  }  }
985    
986  # Delete  # Delete
987  sub DELETE {  sub DELETE {
988      my ($self, $key) = @_;      my ($self, $key) = @_;
989      my $file = &make_filename($self, $key);      my $file = &make_filename($self, $key);
990      unlink $file;      unlink $file;
991      return delete $self->{$key};      return delete $self->{$key};
992  }  }
993    
994  sub FIRSTKEY {  sub FIRSTKEY {
995      my ($self) = @_;      my ($self) = @_;
996      opendir(DIR, $self->{dir}) or die $self->{dir};      opendir(DIR, $self->{dir}) or die $self->{dir};
997      @{$self->{keys}} = grep /\.txt$/, readdir(DIR);      @{$self->{keys}} = grep /\.txt$/, readdir(DIR);
998      foreach my $name (@{$self->{keys}}) {      foreach my $name (@{$self->{keys}}) {
999          $name =~ s/\.txt$//;          $name =~ s/\.txt$//;
1000          $name =~ s/[0-9A-F][0-9A-F]/pack("C", hex($&))/eg;          $name =~ s/[0-9A-F][0-9A-F]/pack("C", hex($&))/eg;
1001      }      }
1002      return shift @{$self->{keys}};      return shift @{$self->{keys}};
1003  }  }
1004    
1005  sub NEXTKEY {  sub NEXTKEY {
1006      my ($self) = @_;      my ($self) = @_;
1007      return shift @{$self->{keys}};      return shift @{$self->{keys}};
1008  }  }
1009    
1010  sub make_filename {  sub make_filename {
1011      my ($self, $key) = @_;      my ($self, $key) = @_;
1012      my $enkey = '';      my $enkey = '';
1013      foreach my $ch (split(//, $key)) {      foreach my $ch (split(//, $key)) {
1014          $enkey .= sprintf("%02X", ord($ch));          $enkey .= sprintf("%02X", ord($ch));
1015      }      }
1016      return $self->{dir} . "/$enkey.txt";      return $self->{dir} . "/$enkey.txt";
1017  }  }
1018  __END__  __END__
1019    
1020  =head1 NAME  =head1 NAME
1021    
1022  YukiWiki - 自由にページを追加・削除・編集できるWebページ構築CGI  YukiWiki - 自由にページを追加・削除・編集できるWebページ構築CGI
1023    
1024      Copyright (C) 2000,2001 by Hiroshi Yuki.      Copyright (C) 2000,2001 by Hiroshi Yuki.
1025      結城浩 <hyuki@hyuki.com>      結城浩 <hyuki@hyuki.com>
1026      http://www.hyuki.com/      http://www.hyuki.com/
1027      http://www.hyuki.com/yukiwiki/      http://www.hyuki.com/yukiwiki/
1028    
1029  =head1 SYNOPSIS  =head1 SYNOPSIS
1030    
1031      http://www.hyuki.com/yukiwiki/yukiwiki.cgi      http://www.hyuki.com/yukiwiki/yukiwiki.cgi
1032    
1033  =head1 DESCRIPTION  =head1 DESCRIPTION
1034    
1035  YukiWiki(結城ウィキィ)は参加者が自由にページを追加・削除・編集できる  YukiWiki(結城ウィキィ)は参加者が自由にページを追加・削除・編集できる
1036  不思議なWebページ群を作るCGIです。  不思議なWebページ群を作るCGIです。
1037  Webで動作する掲示板とちょっと似ていますが、  Webで動作する掲示板とちょっと似ていますが、
1038  Web掲示板が単にメッセージを追加するだけなのに対して、  Web掲示板が単にメッセージを追加するだけなのに対して、
1039  YukiWikiは、Webページ全体を自由に変更することができます。  YukiWikiは、Webページ全体を自由に変更することができます。
1040    
1041  YukiWikiは、Cunningham & CunninghamのWikiWikiWebの  YukiWikiは、Cunningham & CunninghamのWikiWikiWebの
1042  仕様を参考にして独自に作られました。  仕様を参考にして独自に作られました。
1043    
1044  YukiWikiはPerlで書かれたCGIスクリプトとして実現されていますので、  YukiWikiはPerlで書かれたCGIスクリプトとして実現されていますので、
1045  Perlが動作するWebサーバならば比較的容易に設置できます。  Perlが動作するWebサーバならば比較的容易に設置できます。
1046    
1047  あとはdbmopenが使える環境ならば設置できます(Version 1.5.0以降ならdbmopenが使えなくても設置できます)。  あとはdbmopenが使える環境ならば設置できます(Version 1.5.0以降ならdbmopenが使えなくても設置できます)。
1048    
1049    
1050  YukiWikiはフリーソフトです。  YukiWikiはフリーソフトです。
1051  ご自由にお使いください。  ご自由にお使いください。
1052    
1053  =head1 設置方法  =head1 設置方法
1054    
1055  =head2 入手  =head2 入手
1056    
1057  YukiWikiの最新版は、  YukiWikiの最新版は、
1058  http://www.hyuki.com/yukiwiki/  http://www.hyuki.com/yukiwiki/
1059  から入手できます。  から入手できます。
1060    
1061  =head2 ファイル一覧  =head2 ファイル一覧
1062    
1063      readme.txt      ドキュメント      readme.txt      ドキュメント
1064      yukiwiki.cgi    YukiWiki本体      yukiwiki.cgi    YukiWiki本体
1065      yukiwiki.gif    ロゴ(カラー版)      yukiwiki.gif    ロゴ(カラー版)
1066      yukimono.gif    ロゴ(モノクロ版)      yukimono.gif    ロゴ(モノクロ版)
1067      jcode.pl        漢字コードライブラリ      jcode.pl        漢字コードライブラリ
1068    
1069  =head2 インストール  =head2 インストール
1070    
1071  =over 4  =over 4
1072    
1073  =item 1.  =item 1.
1074    
1075  アーカイブを解く。  アーカイブを解く。
1076    
1077  =item 2.  =item 2.
1078    
1079  yukiwiki.cgiのはじめの方にある設定を確認します。  yukiwiki.cgiのはじめの方にある設定を確認します。
1080  通常は何もしなくてよいが、  通常は何もしなくてよいが、
1081  はじめは$touchfileを''にした方がよいでしょう。  はじめは$touchfileを''にした方がよいでしょう。
1082    
1083  =item 3.  =item 3.
1084    
1085  yukiwiki.cgiとjcode.plを同じところに設置します。  yukiwiki.cgiとjcode.plを同じところに設置します。
1086    
1087  =item 4.  =item 4.
1088    
1089  サイズ0のyukiwiki.dbというファイルを設置します。  サイズ0のyukiwiki.dbというファイルを設置します。
1090  (Perlシステムによってはyukiwiki.pag, yukiwiki.dir)  (Perlシステムによってはyukiwiki.pag, yukiwiki.dir)
1091    
1092  =item 5.  =item 5.
1093    
1094  yukiwiki.cgiにブラウザからアクセスします。  yukiwiki.cgiにブラウザからアクセスします。
1095    
1096  =back  =back
1097    
1098  =head2 パーミッション  =head2 パーミッション
1099    
1100          ファイル        パーミッション      転送モード          ファイル        パーミッション      転送モード
1101          yukiwiki.cgi    755                 ASCII          yukiwiki.cgi    755                 ASCII
1102          yukiwiki.gif    644                 BINARY          yukiwiki.gif    644                 BINARY
1103          yukimono.gif    644                 BINARY          yukimono.gif    644                 BINARY
1104          jcode.pl        644                 ASCII          jcode.pl        644                 ASCII
1105    
1106      $dbmopen = 1; にした場合:      $dbmopen = 1; にした場合:
1107          yukiwiki.db     666                 BINARY          yukiwiki.db     666                 BINARY
1108          (yukiwiki.pag, yukiwiki.dirの場合もあり)          (yukiwiki.pag, yukiwiki.dirの場合もあり)
1109    
1110      $dbmopen = 0; にした場合: (カレントディレクトリを777にしておく)      $dbmopen = 0; にした場合: (カレントディレクトリを777にしておく)
1111          .               777                 (転送しない)          .               777                 (転送しない)
1112    
1113  =head1 データのバックアップ方法  =head1 データのバックアップ方法
1114    
1115  $dbmopen = 1;の場合は、  $dbmopen = 1;の場合は、
1116  データはすべてyukiwiki.db(.dir, .pag)に入る。  データはすべてyukiwiki.db(.dir, .pag)に入る。
1117  これをバックアップすればよい。  これをバックアップすればよい。
1118    
1119  $dbmopen = 0;の場合は、  $dbmopen = 0;の場合は、
1120  yukiwikiというディレクトリができるので、  yukiwikiというディレクトリができるので、
1121  これ以下をバックアップすればよい。  これ以下をバックアップすればよい。
1122    
1123  =head1 新しいページの作り方  =head1 新しいページの作り方
1124    
1125  =over 4  =over 4
1126    
1127  =item 1.  =item 1.
1128    
1129  まず、適当なページ(例えばFrontPage)を選び、  まず、適当なページ(例えばFrontPage)を選び、
1130  ページの下にある「編集」リンクをたどります。  ページの下にある「編集」リンクをたどります。
1131    
1132  =item 2.  =item 2.
1133    
1134  するとテキスト入力ができる状態になるので、  するとテキスト入力ができる状態になるので、
1135  そこにNewPageのような単語  そこにNewPageのような単語
1136  (大文字小文字混在している英文字列)  (大文字小文字混在している英文字列)
1137  を書いて「保存」します。  を書いて「保存」します。
1138    
1139  =item 3.  =item 3.
1140    
1141  保存すると、FrontPageのページが書き換わり、  保存すると、FrontPageのページが書き換わり、
1142  あなたが書いたNewPageという文字列の後ろに ? というリンクが表示されます。  あなたが書いたNewPageという文字列の後ろに ? というリンクが表示されます。
1143  この ? はそのページがまだ存在しないことを示す印です。  この ? はそのページがまだ存在しないことを示す印です。
1144    
1145  =item 4.  =item 4.
1146    
1147  その ? をクリックすると新しいページNewPageができますので、  その ? をクリックすると新しいページNewPageができますので、
1148  あなたの好きな文章をその新しいページに書いて保存します。  あなたの好きな文章をその新しいページに書いて保存します。
1149    
1150  =item 5.  =item 5.
1151    
1152  NewPageページができるとFrontPageの ? は消えて、リンクとなります。  NewPageページができるとFrontPageの ? は消えて、リンクとなります。
1153    
1154  =back  =back
1155    
1156  =head1 テキスト整形のルール  =head1 テキスト整形のルール
1157    
1158  =over 4  =over 4
1159    
1160  =item *  =item *
1161    
1162  連続した複数行はフィルされて表示されます。  連続した複数行はフィルされて表示されます。
1163    
1164  =item *  =item *
1165    
1166  空行は段落C<< <p> >>の区切りとなります。  空行は段落C<< <p> >>の区切りとなります。
1167    
1168  =item *  =item *
1169    
1170  HTMLのタグは書けません。  HTMLのタグは書けません。
1171    
1172  =item *  =item *
1173    
1174  B<''ボールド''>のようにシングルクォート二つではさむと、  B<''ボールド''>のようにシングルクォート二つではさむと、
1175  ボールドC<< <b> >>になります。  ボールドC<< <b> >>になります。
1176    
1177  =item *  =item *
1178    
1179  B<'''イタリック'''>のようにシングルクォート三つではさむと、  B<'''イタリック'''>のようにシングルクォート三つではさむと、
1180  イタリックC<< <i> >>になります。  イタリックC<< <i> >>になります。
1181    
1182  =item *  =item *
1183    
1184  B<---->のようにマイナス4つがあると、  B<---->のようにマイナス4つがあると、
1185  水平線C<< <hr> >>になります。  水平線C<< <hr> >>になります。
1186    
1187  =item *  =item *
1188    
1189  行をB<*>ではじめると、  行をB<*>ではじめると、
1190  大見出しC<< <h2> >>になります。  大見出しC<< <h2> >>になります。
1191    
1192  =item *  =item *
1193    
1194  行をB<**>ではじめると、  行をB<**>ではじめると、
1195  小見出しC<< <h3> >>になります。  小見出しC<< <h3> >>になります。
1196    
1197  =item *  =item *
1198    
1199  行をマイナス-ではじめると、  行をマイナス-ではじめると、
1200  箇条書きC<< <ul> >>になります。  箇条書きC<< <ul> >>になります。
1201  マイナスの数が増えるとレベルが下がります(3レベルまで)  マイナスの数が増えるとレベルが下がります(3レベルまで)
1202    
1203      -項目1      -項目1
1204      --項目1-1      --項目1-1
1205      --項目1-2      --項目1-2
1206      -項目2      -項目2
1207      -項目3      -項目3
1208      --項目3-1      --項目3-1
1209      ---項目3-1-1      ---項目3-1-1
1210      ---項目3-1-2      ---項目3-1-2
1211      --項目3-2      --項目3-2
1212    
1213  =item *  =item *
1214    
1215  コロンを使うと、  コロンを使うと、
1216  用語と解説文のリストC<< <dl> >>が書けます。  用語と解説文のリストC<< <dl> >>が書けます。
1217    
1218      :用語1:いろいろ書いた解説文1      :用語1:いろいろ書いた解説文1
1219      :用語2:いろいろ書いた解説文2      :用語2:いろいろ書いた解説文2
1220      :用語3:いろいろ書いた解説文3      :用語3:いろいろ書いた解説文3
1221    
1222  =item *  =item *
1223    
1224  リンク  リンク
1225    
1226  =over 4  =over 4
1227    
1228  =item *  =item *
1229    
1230  LinkToSomePageやFrontPageのように、  LinkToSomePageやFrontPageのように、
1231  英単語の最初の一文字を大文字にしたものが  英単語の最初の一文字を大文字にしたものが
1232  二つ以上連続したものはYukiWikiのページ名となり、  二つ以上連続したものはYukiWikiのページ名となり、
1233  それが文章中に含まれるとリンクになります。  それが文章中に含まれるとリンクになります。
1234    
1235  =item *  =item *
1236    
1237  http://www.hyuki.com/ のようなURLは自動的にリンクになります。  http://www.hyuki.com/ のようなURLは自動的にリンクになります。
1238    
1239  =item *  =item *
1240    
1241  二重の大かっこ[[ ]]でくくった文字列も、  二重の大かっこ[[ ]]でくくった文字列も、
1242  YukiWikiのページ名になります。  YukiWikiのページ名になります。
1243  大かっこの中にはスペースを含めてはいけません。  大かっこの中にはスペースを含めてはいけません。
1244  日本語も使えます。  日本語も使えます。
1245    
1246  =back  =back
1247    
1248  =item *  =item *
1249    
1250  行頭がスペースやタブで始まっていると、  行頭がスペースやタブで始まっていると、
1251  それは整形済みの段落C<< <pre> >>として扱われます。  それは整形済みの段落C<< <pre> >>として扱われます。
1252  プログラムの表示などに使うと便利です。  プログラムの表示などに使うと便利です。
1253    
1254    
1255  =item *  =item *
1256    
1257  行を > ではじめると、  行を > ではじめると、
1258  引用文C<< <blockquote> >>が書けます。  引用文C<< <blockquote> >>が書けます。
1259  >の数が多いとインデントが深くなります(3レベルまで)。  >の数が多いとインデントが深くなります(3レベルまで)。
1260    
1261      >文章      >文章
1262      >文章      >文章
1263      >>さらなる引用      >>さらなる引用
1264      >文章      >文章
1265    
1266  =back  =back
1267    
1268  =head1 更新履歴  =head1 更新履歴
1269    
1270  =over 4  =over 4
1271    
1272  =item *  =item *
1273    
1274  2001年10月20日、Version 1.6.6。  2001年10月20日、Version 1.6.6。
1275    
1276  更新の衝突対策。  更新の衝突対策。
1277  元ページの簡単なチェックサムを取っておき、  元ページの簡単なチェックサムを取っておき、
1278  更新前にチェックサムを比較する。  更新前にチェックサムを比較する。
1279  修正個所はdigestという文字列を検索すれば分かる。  修正個所はdigestという文字列を検索すれば分かる。
1280  本来はMD5などでちゃんとやった方がいいのだけれど。  本来はMD5などでちゃんとやった方がいいのだけれど。
1281    
1282  衝突時に表示されるメッセージなどは「極悪」さんのページを参考にした。  衝突時に表示されるメッセージなどは「極悪」さんのページを参考にした。
1283    
1284  =item *  =item *
1285    
1286  2001年10月17日、Version 1.6.5。  2001年10月17日、Version 1.6.5。
1287    
1288  プレビュー画面で、更新ボタンを押したときに送信される  プレビュー画面で、更新ボタンを押したときに送信される
1289  メッセージの内容をinput要素のtype="hidden"を使って埋め込むのをやめる。  メッセージの内容をinput要素のtype="hidden"を使って埋め込むのをやめる。
1290  代わりに、textarea要素を使う。  代わりに、textarea要素を使う。
1291  再プレビュー用にmyspecial_を導入。でもきれいな対策ではない。  再プレビュー用にmyspecial_を導入。でもきれいな対策ではない。
1292    
1293  =item *  =item *
1294    
1295  2001年8月30日、Version 1.6.4。  2001年8月30日、Version 1.6.4。
1296    
1297  URLでダイレクトにページ名を指定しても、  URLでダイレクトにページ名を指定しても、
1298  $WikiNameと$BracketName以外のページを作れないようにした。  $WikiNameと$BracketName以外のページを作れないようにした。
1299  (is_valid_nameとis_editable参照)。  (is_valid_nameとis_editable参照)。
1300    
1301  =item *  =item *
1302    
1303  2001年8月30日、Version 1.6.3。  2001年8月30日、Version 1.6.3。
1304    
1305  RecentChangesを編集・再編集不可とした。  RecentChangesを編集・再編集不可とした。
1306  編集不可ページは@uneditableにページ名を入れる。  編集不可ページは@uneditableにページ名を入れる。
1307    
1308  =item *  =item *
1309    
1310  2001年2月25日、Version 1.6.1, 1.6.2。  2001年2月25日、Version 1.6.1, 1.6.2。
1311    
1312  差分機能のバグ修正。  差分機能のバグ修正。
1313  do_previewで'>'が扱えないバグを修正  do_previewで'>'が扱えないバグを修正
1314  (ユーザからの指摘)。  (ユーザからの指摘)。
1315    
1316  =item *  =item *
1317    
1318  2001年2月22日、Version 1.6.0。  2001年2月22日、Version 1.6.0。
1319  差分機能を実装した。  差分機能を実装した。
1320    
1321  =item *  =item *
1322    
1323  2001年2月19日、Version 1.5.4。  2001年2月19日、Version 1.5.4。
1324  画像ファイルへのリンクは画像にしてみた。  画像ファイルへのリンクは画像にしてみた。
1325    
1326  =item *  =item *
1327    
1328  2001年2月19日、Version 1.5.3。  2001年2月19日、Version 1.5.3。
1329  RecentChangesの中に削除したページがあるのをやめた。  RecentChangesの中に削除したページがあるのをやめた。
1330  use strict;で引っかかる部分を少し整理(完全ではない)。  use strict;で引っかかる部分を少し整理(完全ではない)。
1331    
1332  =item *  =item *
1333    
1334  2001年2月16日、Version 1.5.2。  2001年2月16日、Version 1.5.2。
1335  textareaに表示およびプレビューする前に < や > を &lt; や &gt; に変換した  textareaに表示およびプレビューする前に < や > を &lt; や &gt; に変換した
1336  (do_preview, editpage, print_preview_buttons)。  (do_preview, editpage, print_preview_buttons)。
1337    
1338  =item *  =item *
1339    
1340  2000年12月27日、Version 1.5.1。  2000年12月27日、Version 1.5.1。
1341  プレビュー画面を整理した。  プレビュー画面を整理した。
1342    
1343  =item *  =item *
1344    
1345  2000年12月22日、Version 1.5.0。  2000年12月22日、Version 1.5.0。
1346  全体的にずいぶん書き直した。  全体的にずいぶん書き直した。
1347  一覧を別途作成するようにした(do_list)。  一覧を別途作成するようにした(do_list)。
1348  書き込む前に確認画面を出すようにした(do_preview)。  書き込む前に確認画面を出すようにした(do_preview)。
1349  テキストの書き方を編集画面に入れた(do_edit, do_reedit)。  テキストの書き方を編集画面に入れた(do_edit, do_reedit)。
1350  WhatsNew→RecentChanges、TopPage→FrontPageに変更した。  WhatsNew→RecentChanges、TopPage→FrontPageに変更した。
1351    
1352  =item *  =item *
1353    
1354  2000年12月20日、Version 1.1.0。  2000年12月20日、Version 1.1.0。
1355  tieを利用して、dbmopenが使えない場合でも動作するように修正。  tieを利用して、dbmopenが使えない場合でも動作するように修正。
1356  利用者の1人である「極悪」さんから  利用者の1人である「極悪」さんから
1357  送っていただいたコードを元にしています。  送っていただいたコードを元にしています。
1358    
1359  =item *  =item *
1360    
1361  2000年9月5日、Version 1.0.2。  2000年9月5日、Version 1.0.2。
1362   <body color=...> → <body bgcolor=...>   <body color=...> → <body bgcolor=...>
1363  利用者からの指摘による。感謝。  利用者からの指摘による。感謝。
1364    
1365  =item *  =item *
1366    
1367  2000年8月6日、Version 1.0.1を公開。  2000年8月6日、Version 1.0.1を公開。
1368  C MAGAZINE(ソフトバンクパブリッシング)  C MAGAZINE(ソフトバンクパブリッシング)
1369  2000年10月号連載記事向け公開版。  2000年10月号連載記事向け公開版。
1370  [[ ]] の最後が「望」のようにシフトJISで  [[ ]] の最後が「望」のようにシフトJISで
1371  0x5Dになる場合の回避を行なった。  0x5Dになる場合の回避を行なった。
1372    
1373  =item *  =item *
1374    
1375  2000年8月5日、Version 1.0.0を公開。  2000年8月5日、Version 1.0.0を公開。
1376    
1377  =item *  =item *
1378    
1379  2000年7月23日、Version 0.82を公開。  2000年7月23日、Version 0.82を公開。
1380  編集時のリンクミス。  編集時のリンクミス。
1381  <textarea>の属性変更。  <textarea>の属性変更。
1382    
1383  =item *  =item *
1384    
1385  2000年7月22日、Version 0.81を公開。  2000年7月22日、Version 0.81を公開。
1386  ロゴを組み込む。  ロゴを組み込む。
1387    
1388  =item *  =item *
1389    
1390  2000年7月21日、Version 0.80を公開。  2000年7月21日、Version 0.80を公開。
1391  PODをCGI中に書き込む。  PODをCGI中に書き込む。
1392    
1393  =item *  =item *
1394    
1395  2000年7月19日、Version 0.70を公開。  2000年7月19日、Version 0.70を公開。
1396  '''イタリック'''や、--、---、>>、>>>などを実装。  '''イタリック'''や、--、---、>>、>>>などを実装。
1397    
1398  =item *  =item *
1399    
1400  2000年7月18日、Version 0.60を公開。  2000年7月18日、Version 0.60を公開。
1401  *太字*を''太字''に変更  *太字*を''太字''に変更
1402    
1403  =item *  =item *
1404    
1405  2000年7月17日、Version 0.50を公開。  2000年7月17日、Version 0.50を公開。
1406    
1407  =item *  =item *
1408    
1409  2000年7月17日、さらにいろいろ追加する。  2000年7月17日、さらにいろいろ追加する。
1410    
1411  =item *  =item *
1412    
1413  2000年7月16日、いろいろ追加。  2000年7月16日、いろいろ追加。
1414    
1415  =item *  =item *
1416    
1417  2000年7月15日、公開。  2000年7月15日、公開。
1418    
1419  =back  =back
1420    
1421  =head1 TODO  =head1 TODO
1422    
1423      - テキスト表示モード      - テキスト表示モード
1424      - Charsetを明示。      - Charsetを明示。
1425      - textarea中の閉じタグ対応      - textarea中の閉じタグ対応
1426      - メニューの英語表記付記      - メニューの英語表記付記
1427      - プレビューのボタンで、mymsgをinputのvalueに入れているが、改行をそのままvalueにいれてはいけないのではないか。      - プレビューのボタンで、mymsgをinputのvalueに入れているが、改行をそのままvalueにいれてはいけないのではないか。
1428      - 「再編集」の機能はブラウザの back で充分ではないか。プレビューはもっとシンプルに。      - 「再編集」の機能はブラウザの back で充分ではないか。プレビューはもっとシンプルに。
1429      - ページタイトル(Wikiname)が検索にかかるようにする。      - ページタイトル(Wikiname)が検索にかかるようにする。
1430      - InterWiki風の機能「URLを隠しつつ、リンクを張る」      - InterWiki風の機能「URLを隠しつつ、リンクを張る」
1431    
1432  =head1 作者  =head1 作者
1433    
1434      Copyright (C) 2000 by Hiroshi Yuki.      Copyright (C) 2000 by Hiroshi Yuki.
1435      結城浩 <hyuki@hyuki.com>      結城浩 <hyuki@hyuki.com>
1436      http://www.hyuki.com/      http://www.hyuki.com/
1437      http://www.hyuki.com/yukiwiki/      http://www.hyuki.com/yukiwiki/
1438    
1439  質問、意見、バグ報告は hyuki@hyuki.com にメールしてください。  質問、意見、バグ報告は hyuki@hyuki.com にメールしてください。
1440    
1441  =head1 配布条件  =head1 配布条件
1442    
1443  YukiWikiは、  YukiWikiは、
1444  GNU General Public Licenseにて公開します。  GNU General Public Licenseにて公開します。
1445    
1446  YukiWikiはフリーソフトです。  YukiWikiはフリーソフトです。
1447  ご自由にお使いください。  ご自由にお使いください。
1448  自分好みのYukiWikiが作れるようにシンプルにしてあります。  自分好みのYukiWikiが作れるようにシンプルにしてあります。
1449    
1450  =head1 謝辞  =head1 謝辞
1451    
1452  本家のWikiWikiを作ったCunningham & Cunningham, Inc.に  本家のWikiWikiを作ったCunningham & Cunningham, Inc.に
1453  感謝します。  感謝します。
1454    
1455  YukiWikiを楽しんで使ってくださる  YukiWikiを楽しんで使ってくださる
1456  ネット上の方々に感謝します。  ネット上の方々に感謝します。
1457    
1458  YukiWikiのロゴをデザインしてくださった橋本礼奈さん  YukiWikiのロゴをデザインしてくださった橋本礼奈さん
1459  http://city.hokkai.or.jp/~reina/  http://city.hokkai.or.jp/~reina/
1460  に感謝します。  に感謝します。
1461    
1462  tieを使った版の元になるコードを送ってくださった  tieを使った版の元になるコードを送ってくださった
1463  「極悪」さんに感謝します。  「極悪」さんに感謝します。
1464    
1465  =head1 参照リンク  =head1 参照リンク
1466    
1467  =over 4  =over 4
1468    
1469  =item *  =item *
1470    
1471  YukiWikiホームページ  YukiWikiホームページ
1472  http://www.hyuki.com/yukiwiki/  http://www.hyuki.com/yukiwiki/
1473    
1474  =item *  =item *
1475    
1476  本家のWikiWiki  本家のWikiWiki
1477  http://c2.com/cgi/wiki?WikiWikiWeb  http://c2.com/cgi/wiki?WikiWikiWeb
1478    
1479  =item *  =item *
1480    
1481  本家のWikiWikiの作者(Cunningham & Cunningham, Inc.)  本家のWikiWikiの作者(Cunningham & Cunningham, Inc.)
1482  http://c2.com/  http://c2.com/
1483    
1484  =item *  =item *
1485    
1486  YukiWikiのロゴデザインをしてくださった橋本礼奈さんのページ  YukiWikiのロゴデザインをしてくださった橋本礼奈さんのページ
1487  http://city.hokkai.or.jp/~reina/  http://city.hokkai.or.jp/~reina/
1488    
1489  =back  =back
1490    
1491  =cut  =cut

Legend:
Removed from v.1.8  
changed lines
  Added in v.1.9

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24