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

Contents of /suikawiki/script/wiki.cgi

Parent Directory Parent Directory | Revision Log Revision Log


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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24