OSDN Git Service

BugTrack/2396 Support Page redurection rules on make_pagelink()
[pukiwiki/pukiwiki.git] / lib / make_link.php
1 <?php
2 // PukiWiki - Yet another WikiWikiWeb clone.
3 // make_link.php
4 // Copyright
5 //   2003-2016 PukiWiki Development Team
6 //   2001-2002 Originally written by yu-ji
7 // License: GPL v2 or (at your option) any later version
8 //
9 // Hyperlink-related functions
10
11 // Hyperlink decoration
12 function make_link($string, $page = '')
13 {
14         global $vars;
15         static $converter;
16
17         if (! isset($converter)) $converter = new InlineConverter();
18
19         $clone = $converter->get_clone($converter);
20
21         return $clone->convert($string, ($page != '') ? $page : $vars['page']);
22 }
23
24 // Converters of inline element
25 class InlineConverter
26 {
27         var $converters; // as array()
28         var $pattern;
29         var $pos;
30         var $result;
31
32         function get_clone($obj) {
33                 static $clone_func;
34
35                 if (! isset($clone_func)) {
36                         if (version_compare(PHP_VERSION, '5.0.0', '<')) {
37                                 $clone_func = create_function('$a', 'return $a;');
38                         } else {
39                                 $clone_func = create_function('$a', 'return clone $a;');
40                         }
41                 }
42                 return $clone_func($obj);
43         }
44
45         function __clone() {
46                 $converters = array();
47                 foreach ($this->converters as $key=>$converter) {
48                         $converters[$key] = $this->get_clone($converter);
49                 }
50                 $this->converters = $converters;
51         }
52
53         function InlineConverter($converters = NULL, $excludes = NULL)
54         {
55                 $this->__construct($converters, $excludes);
56         }
57         function __construct($converters = NULL, $excludes = NULL)
58         {
59                 if ($converters === NULL) {
60                         $converters = array(
61                                 'plugin',        // Inline plugins
62                                 'note',          // Footnotes
63                                 'url',           // URLs
64                                 'url_interwiki', // URLs (interwiki definition)
65                                 'mailto',        // mailto: URL schemes
66                                 'interwikiname', // InterWikiNames
67                                 'autolink',      // AutoLinks
68                                 'bracketname',   // BracketNames
69                                 'wikiname',      // WikiNames
70                                 'autolink_a',    // AutoLinks(alphabet)
71                         );
72                 }
73
74                 if ($excludes !== NULL)
75                         $converters = array_diff($converters, $excludes);
76
77                 $this->converters = $patterns = array();
78                 $start = 1;
79
80                 foreach ($converters as $name) {
81                         $classname = 'Link_' . $name;
82                         $converter = new $classname($start);
83                         $pattern   = $converter->get_pattern();
84                         if ($pattern === FALSE) continue;
85
86                         $patterns[] = '(' . "\n" . $pattern . "\n" . ')';
87                         $this->converters[$start] = $converter;
88                         $start += $converter->get_count();
89                         ++$start;
90                 }
91                 $this->pattern = join('|', $patterns);
92         }
93
94         function convert($string, $page)
95         {
96                 $this->page   = $page;
97                 $this->result = array();
98
99                 $string = preg_replace_callback('/' . $this->pattern . '/x',
100                         array(& $this, 'replace'), $string);
101
102                 $arr = explode("\x08", make_line_rules(htmlsc($string)));
103                 $retval = '';
104                 while (! empty($arr)) {
105                         $retval .= array_shift($arr) . array_shift($this->result);
106                 }
107                 return $retval;
108         }
109
110         function replace($arr)
111         {
112                 $obj = $this->get_converter($arr);
113
114                 $this->result[] = ($obj !== NULL && $obj->set($arr, $this->page) !== FALSE) ?
115                         $obj->toString() : make_line_rules(htmlsc($arr[0]));
116
117                 return "\x08"; // Add a mark into latest processed part
118         }
119
120         function get_objects($string, $page)
121         {
122                 $matches = $arr = array();
123                 preg_match_all('/' . $this->pattern . '/x', $string, $matches, PREG_SET_ORDER);
124                 foreach ($matches as $match) {
125                         $obj = $this->get_converter($match);
126                         if ($obj->set($match, $page) !== FALSE) {
127                                 $arr[] = $this->get_clone($obj);
128                                 if ($obj->body != '')
129                                         $arr = array_merge($arr, $this->get_objects($obj->body, $page));
130                         }
131                 }
132                 return $arr;
133         }
134
135         function & get_converter(& $arr)
136         {
137                 foreach (array_keys($this->converters) as $start) {
138                         if ($arr[$start] == $arr[0])
139                                 return $this->converters[$start];
140                 }
141                 return NULL;
142         }
143 }
144
145 // Base class of inline elements
146 class Link
147 {
148         var $start;   // Origin number of parentheses (0 origin)
149         var $text;    // Matched string
150
151         var $type;
152         var $page;
153         var $name;
154         var $body;
155         var $alias;
156
157         function Link($start)
158         {
159                 $this->__construct($start);
160         }
161         function __construct($start)
162         {
163                 $this->start = $start;
164         }
165
166         // Return a regex pattern to match
167         function get_pattern() {}
168
169         // Return number of parentheses (except (?:...) )
170         function get_count() {}
171
172         // Set pattern that matches
173         function set($arr, $page) {}
174
175         function toString() {}
176
177         // Private: Get needed parts from a matched array()
178         function splice($arr)
179         {
180                 $count = $this->get_count() + 1;
181                 $arr   = array_pad(array_splice($arr, $this->start, $count), $count, '');
182                 $this->text = $arr[0];
183                 return $arr;
184         }
185
186         // Set basic parameters
187         function setParam($page, $name, $body, $type = '', $alias = '')
188         {
189                 static $converter = NULL;
190
191                 $this->page = $page;
192                 $this->name = $name;
193                 $this->body = $body;
194                 $this->type = $type;
195                 if (! PKWK_DISABLE_INLINE_IMAGE_FROM_URI &&
196                         is_url($alias) && preg_match('/\.(gif|png|jpe?g)$/i', $alias)) {
197                         $alias = '<img src="' . htmlsc($alias) . '" alt="' . $name . '" />';
198                 } else if ($alias != '') {
199                         if ($converter === NULL)
200                                 $converter = new InlineConverter(array('plugin'));
201
202                         $alias = make_line_rules($converter->convert($alias, $page));
203
204                         // BugTrack/669: A hack removing anchor tags added by AutoLink
205                         $alias = preg_replace('#</?a[^>]*>#i', '', $alias);
206                 }
207                 $this->alias = $alias;
208
209                 return TRUE;
210         }
211 }
212
213 // Inline plugins
214 class Link_plugin extends Link
215 {
216         var $pattern;
217         var $plain,$param;
218
219         function Link_plugin($start)
220         {
221                 $this->__construct($start);
222         }
223         function __construct($start)
224         {
225                 parent::__construct($start);
226         }
227
228         function get_pattern()
229         {
230                 $this->pattern = <<<EOD
231 &
232 (      # (1) plain
233  (\w+) # (2) plugin name
234  (?:
235   \(
236    ((?:(?!\)[;{]).)*) # (3) parameter
237   \)
238  )?
239 )
240 EOD;
241                 return <<<EOD
242 {$this->pattern}
243 (?:
244  \{
245   ((?:(?R)|(?!};).)*) # (4) body
246  \}
247 )?
248 ;
249 EOD;
250         }
251
252         function get_count()
253         {
254                 return 4;
255         }
256
257         function set($arr, $page)
258         {
259                 list($all, $this->plain, $name, $this->param, $body) = $this->splice($arr);
260
261                 // Re-get true plugin name and patameters (for PHP 4.1.2)
262                 $matches = array();
263                 if (preg_match('/^' . $this->pattern . '/x', $all, $matches)
264                         && $matches[1] != $this->plain) 
265                         list(, $this->plain, $name, $this->param) = $matches;
266
267                 return parent::setParam($page, $name, $body, 'plugin');
268         }
269
270         function toString()
271         {
272                 $body = ($this->body == '') ? '' : make_link($this->body);
273                 $str = FALSE;
274
275                 // Try to call the plugin
276                 if (exist_plugin_inline($this->name))
277                         $str = do_plugin_inline($this->name, $this->param, $body);
278
279                 if ($str !== FALSE) {
280                         return $str; // Succeed
281                 } else {
282                         // No such plugin, or Failed
283                         $body = (($body == '') ? '' : '{' . $body . '}') . ';';
284                         return make_line_rules(htmlsc('&' . $this->plain) . $body);
285                 }
286         }
287 }
288
289 // Footnotes
290 class Link_note extends Link
291 {
292         function Link_note($start)
293         {
294                 $this->__construct($start);
295         }
296         function __construct($start)
297         {
298                 parent::__construct($start);
299         }
300
301         function get_pattern()
302         {
303                 return <<<EOD
304 \(\(
305  ((?>(?=\(\()(?R)|(?!\)\)).)*) # (1) note body
306 \)\)
307 EOD;
308         }
309
310         function get_count()
311         {
312                 return 1;
313         }
314
315         function set($arr, $page)
316         {
317                 global $foot_explain, $vars;
318                 static $note_id = 0;
319
320                 list(, $body) = $this->splice($arr);
321
322                 if (PKWK_ALLOW_RELATIVE_FOOTNOTE_ANCHOR) {
323                         $script = '';
324                 } else {
325                         $script = get_script_uri() . '?' . pagename_urlencode($page);
326                 }
327
328                 $id   = ++$note_id;
329                 $note = make_link($body);
330                 $page = isset($vars['page']) ? rawurlencode($vars['page']) : '';
331
332                 // Footnote
333                 $foot_explain[$id] = '<a id="notefoot_' . $id . '" href="' .
334                         $script . '#notetext_' . $id . '" class="note_super">*' .
335                         $id . '</a>' . "\n" .
336                         '<span class="small">' . $note . '</span><br />';
337
338                 // A hyperlink, content-body to footnote
339                 if (! is_numeric(PKWK_FOOTNOTE_TITLE_MAX) || PKWK_FOOTNOTE_TITLE_MAX <= 0) {
340                         $title = '';
341                 } else {
342                         $title = strip_tags($note);
343                         $count = mb_strlen($title, SOURCE_ENCODING);
344                         $title = mb_substr($title, 0, PKWK_FOOTNOTE_TITLE_MAX, SOURCE_ENCODING);
345                         $abbr  = (mb_strlen($title) < $count) ? '...' : '';
346                         $title = ' title="' . $title . $abbr . '"';
347                 }
348                 $name = '<a id="notetext_' . $id . '" href="' . $script .
349                         '#notefoot_' . $id . '" class="note_super"' . $title .
350                         '>*' . $id . '</a>';
351
352                 return parent::setParam($page, $name, $body);
353         }
354
355         function toString()
356         {
357                 return $this->name;
358         }
359 }
360
361 // URLs
362 class Link_url extends Link
363 {
364         function Link_url($start)
365         {
366                 $this->__construct($start);
367         }
368         function __construct($start)
369         {
370                 parent::__construct($start);
371         }
372
373         function get_pattern()
374         {
375                 $s1 = $this->start + 1;
376                 return <<<EOD
377 ((?:\[\[))?       # (1) open bracket
378 ((?($s1)          # (2) alias
379 ((?:(?!\]\]).)+)  # (3) alias name
380  (?:>|:)
381 ))?
382 (                 # (4) url
383  (?:(?:https?|ftp|news):\/\/|mailto:)[\w\/\@\$()!?&%#:;.,~'=*+-]+
384 )
385 (?($s1)\]\])      # close bracket
386 EOD;
387         }
388
389         function get_count()
390         {
391                 return 4;
392         }
393
394         function set($arr, $page)
395         {
396                 list(, , , $alias, $name) = $this->splice($arr);
397                 return parent::setParam($page, htmlsc($name),
398                         '', 'url', $alias == '' ? $name : $alias);
399         }
400
401         function toString()
402         {
403                 if (FALSE) {
404                         $rel = '';
405                 } else {
406                         $rel = ' rel="nofollow"';
407                 }
408                 return '<a href="' . $this->name . '"' . $rel . '>' . $this->alias . '</a>';
409         }
410 }
411
412 // URLs (InterWiki definition on "InterWikiName")
413 class Link_url_interwiki extends Link
414 {
415         function Link_url_interwiki($start)
416         {
417                 $this->__construct($start);
418         }
419         function __construct($start)
420         {
421                 parent::__construct($start);
422         }
423
424         function get_pattern()
425         {
426                 return <<<EOD
427 \[       # open bracket
428 (        # (1) url
429  (?:(?:https?|ftp|news):\/\/|\.\.?\/)[!~*'();\/?:\@&=+\$,%#\w.-]*
430 )
431 \s
432 ([^\]]+) # (2) alias
433 \]       # close bracket
434 EOD;
435         }
436
437         function get_count()
438         {
439                 return 2;
440         }
441
442         function set($arr, $page)
443         {
444                 list(, $name, $alias) = $this->splice($arr);
445                 return parent::setParam($page, htmlsc($name), '', 'url', $alias);
446         }
447
448         function toString()
449         {
450                 return '<a href="' . $this->name . '" rel="nofollow">' . $this->alias . '</a>';
451         }
452 }
453
454 // mailto: URL schemes
455 class Link_mailto extends Link
456 {
457         var $is_image, $image;
458
459         function Link_mailto($start)
460         {
461                 $this->__construct($start);
462         }
463         function __construct($start)
464         {
465                 parent::__construct($start);
466         }
467
468         function get_pattern()
469         {
470                 $s1 = $this->start + 1;
471                 return <<<EOD
472 (?:
473  \[\[
474  ((?:(?!\]\]).)+)(?:>|:)  # (1) alias
475 )?
476 ([\w.-]+@[\w-]+\.[\w.-]+) # (2) mailto
477 (?($s1)\]\])              # close bracket if (1)
478 EOD;
479         }
480
481         function get_count()
482         {
483                 return 2;
484         }
485
486         function set($arr, $page)
487         {
488                 list(, $alias, $name) = $this->splice($arr);
489                 return parent::setParam($page, $name, '', 'mailto', $alias == '' ? $name : $alias);
490         }
491         
492         function toString()
493         {
494                 return '<a href="mailto:' . $this->name . '" rel="nofollow">' . $this->alias . '</a>';
495         }
496 }
497
498 // InterWikiName-rendered URLs
499 class Link_interwikiname extends Link
500 {
501         var $url    = '';
502         var $param  = '';
503         var $anchor = '';
504
505         function Link_interwikiname($start)
506         {
507                 $this->__construct($start);
508         }
509         function __construct($start)
510         {
511                 parent::__construct($start);
512         }
513
514         function get_pattern()
515         {
516                 $s2 = $this->start + 2;
517                 $s5 = $this->start + 5;
518                 return <<<EOD
519 \[\[                  # open bracket
520 (?:
521  ((?:(?!\]\]).)+)>    # (1) alias
522 )?
523 (\[\[)?               # (2) open bracket
524 ((?:(?!\s|:|\]\]).)+) # (3) InterWiki
525 (?<! > | >\[\[ )      # not '>' or '>[['
526 :                     # separator
527 (                     # (4) param
528  (\[\[)?              # (5) open bracket
529  (?:(?!>|\]\]).)+
530  (?($s5)\]\])         # close bracket if (5)
531 )
532 (?($s2)\]\])          # close bracket if (2)
533 \]\]                  # close bracket
534 EOD;
535         }
536
537         function get_count()
538         {
539                 return 5;
540         }
541
542         function set($arr, $page)
543         {
544                 global $script;
545
546                 list(, $alias, , $name, $this->param) = $this->splice($arr);
547
548                 $matches = array();
549                 if (preg_match('/^([^#]+)(#[A-Za-z][\w-]*)$/', $this->param, $matches))
550                         list(, $this->param, $this->anchor) = $matches;
551
552                 $url = get_interwiki_url($name, $this->param);
553                 $this->url = ($url === FALSE) ?
554                         $script . '?' . pagename_urlencode('[[' . $name . ':' . $this->param . ']]') :
555                         htmlsc($url);
556
557                 return parent::setParam(
558                         $page,
559                         htmlsc($name . ':' . $this->param),
560                         '',
561                         'InterWikiName',
562                         $alias == '' ? $name . ':' . $this->param : $alias
563                 );
564         }
565
566         function toString()
567         {
568                 return '<a href="' . $this->url . $this->anchor . '" title="' .
569                         $this->name . '" rel="nofollow">' . $this->alias . '</a>';
570         }
571 }
572
573 // BracketNames
574 class Link_bracketname extends Link
575 {
576         var $anchor, $refer;
577
578         function Link_bracketname($start)
579         {
580                 $this->__construct($start);
581         }
582         function __construct($start)
583         {
584                 parent::__construct($start);
585         }
586
587         function get_pattern()
588         {
589                 global $WikiName, $BracketName;
590
591                 $s2 = $this->start + 2;
592                 return <<<EOD
593 \[\[                     # Open bracket
594 (?:((?:(?!\]\]).)+)>)?   # (1) Alias
595 (\[\[)?                  # (2) Open bracket
596 (                        # (3) PageName
597  (?:$WikiName)
598  |
599  (?:$BracketName)
600 )?
601 (\#(?:[a-zA-Z][\w-]*)?)? # (4) Anchor
602 (?($s2)\]\])             # Close bracket if (2)
603 \]\]                     # Close bracket
604 EOD;
605         }
606
607         function get_count()
608         {
609                 return 4;
610         }
611
612         function set($arr, $page)
613         {
614                 global $WikiName;
615
616                 list(, $alias, , $name, $this->anchor) = $this->splice($arr);
617                 if ($name == '' && $this->anchor == '') return FALSE;
618
619                 if ($name == '' || ! preg_match('/^' . $WikiName . '$/', $name)) {
620                         if ($alias == '') $alias = $name . $this->anchor;
621                         if ($name != '') {
622                                 $name = get_fullname($name, $page);
623                                 if (! is_pagename($name)) return FALSE;
624                         }
625                 }
626
627                 return parent::setParam($page, $name, '', 'pagename', $alias);
628         }
629
630         function toString()
631         {
632                 return make_pagelink(
633                         $this->name,
634                         $this->alias,
635                         $this->anchor,
636                         $this->page
637                 );
638         }
639 }
640
641 // WikiNames
642 class Link_wikiname extends Link
643 {
644         function Link_wikiname($start)
645         {
646                 $this->__construct($start);
647         }
648         function __construct($start)
649         {
650                 parent::__construct($start);
651         }
652
653         function get_pattern()
654         {
655                 global $WikiName, $nowikiname;
656
657                 return $nowikiname ? FALSE : '(' . $WikiName . ')';
658         }
659
660         function get_count()
661         {
662                 return 1;
663         }
664
665         function set($arr, $page)
666         {
667                 list($name) = $this->splice($arr);
668                 return parent::setParam($page, $name, '', 'pagename', $name);
669         }
670
671         function toString()
672         {
673                 return make_pagelink(
674                         $this->name,
675                         $this->alias,
676                         '',
677                         $this->page
678                 );
679         }
680 }
681
682 // AutoLinks
683 class Link_autolink extends Link
684 {
685         var $forceignorepages = array();
686         var $auto;
687         var $auto_a; // alphabet only
688
689         function Link_autolink($start)
690         {
691                 $this->__construct($start);
692         }
693         function __construct($start)
694         {
695                 global $autolink;
696
697                 parent::__construct($start);
698
699                 if (! $autolink || ! file_exists(CACHE_DIR . 'autolink.dat'))
700                         return;
701
702                 @list($auto, $auto_a, $forceignorepages) = file(CACHE_DIR . 'autolink.dat');
703                 $this->auto   = $auto;
704                 $this->auto_a = $auto_a;
705                 $this->forceignorepages = explode("\t", trim($forceignorepages));
706         }
707
708         function get_pattern()
709         {
710                 return isset($this->auto) ? '(' . $this->auto . ')' : FALSE;
711         }
712
713         function get_count()
714         {
715                 return 1;
716         }
717
718         function set($arr, $page)
719         {
720                 global $WikiName;
721
722                 list($name) = $this->splice($arr);
723
724                 // Ignore pages listed, or Expire ones not found
725                 if (in_array($name, $this->forceignorepages) || ! is_page($name))
726                         return FALSE;
727
728                 return parent::setParam($page, $name, '', 'pagename', $name);
729         }
730
731         function toString()
732         {
733                 return make_pagelink($this->name, $this->alias, '', $this->page, TRUE);
734         }
735 }
736
737 class Link_autolink_a extends Link_autolink
738 {
739         function Link_autolink_a($start)
740         {
741                 $this->__construct($start);
742         }
743         function __construct($start)
744         {
745                 parent::__construct($start);
746         }
747
748         function get_pattern()
749         {
750                 return isset($this->auto_a) ? '(' . $this->auto_a . ')' : FALSE;
751         }
752 }
753
754 // Make hyperlink for the page
755 function make_pagelink($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE)
756 {
757         global $script, $vars, $link_compact, $related, $_symbol_noexists;
758
759         $s_page = htmlsc(strip_bracket($page));
760         $s_alias = ($alias == '') ? $s_page : $alias;
761
762         if ($page == '') return '<a href="' . $anchor . '">' . $s_alias . '</a>';
763
764         $r_page  = pagename_urlencode($page);
765         $r_refer = ($refer == '') ? '' : '&amp;refer=' . rawurlencode($refer);
766
767         if (! isset($related[$page]) && $page !== $vars['page'] && is_page($page))
768                 $related[$page] = get_filetime($page);
769
770         if ($isautolink || is_page($page)) {
771                 // Hyperlink to the page
772                 if ($link_compact) {
773                         $title   = '';
774                 } else {
775                         $title   = ' title="' . $s_page . get_pg_passage($page, FALSE) . '"';
776                 }
777
778                 // AutoLink marker
779                 if ($isautolink) {
780                         $al_left  = '<!--autolink-->';
781                         $al_right = '<!--/autolink-->';
782                 } else {
783                         $al_left = $al_right = '';
784                 }
785
786                 return $al_left . '<a ' . 'href="' . $script . '?' . $r_page . $anchor .
787                         '"' . $title . '>' . $s_alias . '</a>' . $al_right;
788         } else {
789                 // Support Page redirection
790                 $redirect_page = get_pagename_on_redirect($page);
791                 if ($redirect_page !== false) {
792                         return make_pagelink($redirect_page, $s_alias);
793                 }
794                 // Dangling link
795                 if (PKWK_READONLY) return $s_alias; // No dacorations
796
797                 $retval = $s_alias . '<a href="' .
798                         $script . '?cmd=edit&amp;page=' . $r_page . $r_refer . '">' .
799                         $_symbol_noexists . '</a>';
800
801                 if ($link_compact) {
802                         return $retval;
803                 } else {
804                         return '<span class="noexists">' . $retval . '</span>';
805                 }
806         }
807 }
808
809 // Resolve relative / (Unix-like)absolute path of the page
810 function get_fullname($name, $refer)
811 {
812         global $defaultpage;
813
814         // 'Here'
815         if ($name == '' || $name == './') return $refer;
816
817         // Absolute path
818         if ($name{0} == '/') {
819                 $name = substr($name, 1);
820                 return ($name == '') ? $defaultpage : $name;
821         }
822
823         // Relative path from 'Here'
824         if (substr($name, 0, 2) == './') {
825                 $arrn    = preg_split('#/#', $name, -1, PREG_SPLIT_NO_EMPTY);
826                 $arrn[0] = $refer;
827                 return join('/', $arrn);
828         }
829
830         // Relative path from dirname()
831         if (substr($name, 0, 3) == '../') {
832                 $arrn = preg_split('#/#', $name,  -1, PREG_SPLIT_NO_EMPTY);
833                 $arrp = preg_split('#/#', $refer, -1, PREG_SPLIT_NO_EMPTY);
834
835                 while (! empty($arrn) && $arrn[0] == '..') {
836                         array_shift($arrn);
837                         array_pop($arrp);
838                 }
839                 $name = ! empty($arrp) ? join('/', array_merge($arrp, $arrn)) :
840                         (! empty($arrn) ? $defaultpage . '/' . join('/', $arrn) : $defaultpage);
841         }
842
843         return $name;
844 }
845
846 // Render an InterWiki into a URL
847 function get_interwiki_url($name, $param)
848 {
849         global $WikiName, $interwiki;
850         static $interwikinames;
851         static $encode_aliases = array('sjis'=>'SJIS', 'euc'=>'EUC-JP', 'utf8'=>'UTF-8');
852
853         if (! isset($interwikinames)) {
854                 $interwikinames = $matches = array();
855                 foreach (get_source($interwiki) as $line)
856                         if (preg_match('/\[(' . '(?:(?:https?|ftp|news):\/\/|\.\.?\/)' .
857                             '[!~*\'();\/?:\@&=+\$,%#\w.-]*)\s([^\]]+)\]\s?([^\s]*)/',
858                             $line, $matches))
859                                 $interwikinames[$matches[2]] = array($matches[1], $matches[3]);
860         }
861
862         if (! isset($interwikinames[$name])) return FALSE;
863
864         list($url, $opt) = $interwikinames[$name];
865
866         // Encoding
867         switch ($opt) {
868
869         case '':    /* FALLTHROUGH */
870         case 'std': // Simply URL-encode the string, whose base encoding is the internal-encoding
871                 $param = rawurlencode($param);
872                 break;
873
874         case 'asis': /* FALLTHROUGH */
875         case 'raw' : // Truly as-is
876                 break;
877
878         case 'yw': // YukiWiki
879                 if (! preg_match('/' . $WikiName . '/', $param))
880                         $param = '[[' . mb_convert_encoding($param, 'SJIS', SOURCE_ENCODING) . ']]';
881                 break;
882
883         case 'moin': // MoinMoin
884                 $param = str_replace('%', '_', rawurlencode($param));
885                 break;
886
887         default:
888                 // Alias conversion of $opt
889                 if (isset($encode_aliases[$opt])) $opt = & $encode_aliases[$opt];
890
891                 // Encoding conversion into specified encode, and URLencode
892                 if (strpos($url, '$1') === FALSE && substr($url, -1) === '?') {
893                         // PukiWiki site
894                         $param = pagename_urlencode(mb_convert_encoding($param, $opt, SOURCE_ENCODING));
895                 } else {
896                         $param = rawurlencode(mb_convert_encoding($param, $opt, SOURCE_ENCODING));
897                 }
898         }
899
900         // Replace or Add the parameter
901         if (strpos($url, '$1') !== FALSE) {
902                 $url = str_replace('$1', $param, $url);
903         } else {
904                 $url .= $param;
905         }
906
907         $len = strlen($url);
908         if ($len > 512) die_message('InterWiki URL too long: ' . $len . ' characters');
909
910         return $url;
911 }