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

Contents of /suikawiki/script/wiki.cgi

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.15 - (show annotations) (download)
Tue Apr 23 10:14:12 2002 UTC (22 years ago) by wakaba
Branch: MAIN
Branch point for: branch-1-6
Changes since 1.14: +5 -4 lines
2002-03-24  wakaba <w@suika.fam.cx>

	* wiki.cgi (inline): 
	- Ignore LinkLikeThis style anchor.
	- Allow [[space including anchor]].
	(make_link): Likewise.  Gets rid of angle branckets
	before make anchor.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24