OSDN Git Service

BugTrack/2525 Support make_link
[pukiwiki/pukiwiki.git] / lib / make_link.php
index b471bb4..b78cb18 100644 (file)
@@ -1,11 +1,31 @@
 <?php
-/////////////////////////////////////////////////
 // PukiWiki - Yet another WikiWikiWeb clone.
+// make_link.php
+// Copyright
+//   2003-2021 PukiWiki Development Team
+//   2001-2002 Originally written by yu-ji
+// License: GPL v2 or (at your option) any later version
 //
-// $Id: make_link.php,v 1.6 2004/11/23 09:39:42 henoheno Exp $
-//
+// Hyperlink-related functions
+
+// To get page exists or filetimes without accessing filesystem
+// Type: array (page => filetime)
+$_cached_page_filetime = null;
 
-// ¥ê¥ó¥¯¤òÉղ乤ë
+// Get filetime from cache
+function fast_get_filetime($page)
+{
+       global $_cached_page_filetime;
+       if (is_null($_cached_page_filetime)) {
+               return get_filetime($page);
+       }
+       if (isset($_cached_page_filetime[$page])) {
+               return $_cached_page_filetime[$page];
+       }
+       return get_filetime($page);
+}
+
+// Hyperlink decoration
 function make_link($string, $page = '')
 {
        global $vars;
@@ -18,7 +38,7 @@ function make_link($string, $page = '')
        return $clone->convert($string, ($page != '') ? $page : $vars['page']);
 }
 
-//¥¤¥ó¥é¥¤¥óÍ×ÁǤòÃÖ´¹¤¹¤ë
+// Converters of inline element
 class InlineConverter
 {
        var $converters; // as array()
@@ -27,16 +47,18 @@ class InlineConverter
        var $result;
 
        function get_clone($obj) {
-               static $clone_func;
-
-               if (! isset($clone_func)) {
+               static $clone_exists;
+               if (! isset($clone_exists)) {
                        if (version_compare(PHP_VERSION, '5.0.0', '<')) {
-                               $clone_func = create_function('$a', 'return $a;');
+                               $clone_exists = false;
                        } else {
-                               $clone_func = create_function('$a', 'return clone $a;');
+                               $clone_exists = true;
                        }
                }
-               return $clone_func($obj);
+               if ($clone_exists) {
+                       return clone ($obj);
+               }
+               return $obj;
        }
 
        function __clone() {
@@ -49,18 +71,24 @@ class InlineConverter
 
        function InlineConverter($converters = NULL, $excludes = NULL)
        {
+               $this->__construct($converters, $excludes);
+       }
+       function __construct($converters = NULL, $excludes = NULL)
+       {
                if ($converters === NULL) {
                        $converters = array(
-                               'plugin',        // ¥¤¥ó¥é¥¤¥ó¥×¥é¥°¥¤¥ó
-                               'note',          // Ãí¼á
-                               'url',           // URL
-                               'url_interwiki', // URL (interwiki definition)
-                               'mailto',        // mailto:
-                               'interwikiname', // InterWikiName
-                               'autolink',      // AutoLink
-                               'bracketname',   // BracketName
-                               'wikiname',      // WikiName
-                               'autolink_a',    // AutoLink(¥¢¥ë¥Õ¥¡¥Ù¥Ã¥È)
+                               'plugin',        // Inline plugins
+                               'note',          // Footnotes
+                               'url',           // URLs
+                               'url_interwiki', // URLs (interwiki definition)
+                               'mailto',        // mailto: URL schemes
+                               'interwikiname', // InterWikiNames
+                               'autoalias',     // AutoAlias
+                               'autolink',      // AutoLinks
+                               'bracketname',   // BracketNames
+                               'wikiname',      // WikiNames
+                               'autoalias_a',   // AutoAlias(alphabet)
+                               'autolink_a',    // AutoLinks(alphabet)
                        );
                }
 
@@ -71,12 +99,12 @@ class InlineConverter
                $start = 1;
 
                foreach ($converters as $name) {
-                       $classname = "Link_$name";
+                       $classname = 'Link_' . $name;
                        $converter = new $classname($start);
                        $pattern   = $converter->get_pattern();
                        if ($pattern === FALSE) continue;
 
-                       $patterns[] = "(\n$pattern\n)";
+                       $patterns[] = '(' . "\n" . $pattern . "\n" . ')';
                        $this->converters[$start] = $converter;
                        $start += $converter->get_count();
                        ++$start;
@@ -89,10 +117,10 @@ class InlineConverter
                $this->page   = $page;
                $this->result = array();
 
-               $string = preg_replace_callback("/{$this->pattern}/x",
+               $string = preg_replace_callback('/' . $this->pattern . '/x' . get_preg_u(),
                        array(& $this, 'replace'), $string);
 
-               $arr = explode("\x08", make_line_rules(htmlspecialchars($string)));
+               $arr = explode("\x08", make_line_rules(htmlsc($string)));
                $retval = '';
                while (! empty($arr)) {
                        $retval .= array_shift($arr) . array_shift($this->result);
@@ -105,16 +133,16 @@ class InlineConverter
                $obj = $this->get_converter($arr);
 
                $this->result[] = ($obj !== NULL && $obj->set($arr, $this->page) !== FALSE) ?
-                       $obj->toString() : make_line_rules(htmlspecialchars($arr[0]));
+                       $obj->toString() : make_line_rules(htmlsc($arr[0]));
 
-               return "\x08"; //½èÍýºÑ¤ß¤ÎÉôʬ¤Ë¥Þ¡¼¥¯¤òÆþ¤ì¤ë
+               return "\x08"; // Add a mark into latest processed part
        }
 
        function get_objects($string, $page)
        {
-               preg_match_all("/{$this->pattern}/x", $string, $matches, PREG_SET_ORDER);
-
-               $arr = array();
+               $matches = $arr = array();
+               preg_match_all('/' . $this->pattern . '/x' . get_preg_u(),
+                       $string, $matches, PREG_SET_ORDER);
                foreach ($matches as $match) {
                        $obj = $this->get_converter($match);
                        if ($obj->set($match, $page) !== FALSE) {
@@ -136,11 +164,11 @@ class InlineConverter
        }
 }
 
-//¥¤¥ó¥é¥¤¥óÍ×Áǽ¸¹ç¤Î¥Ù¡¼¥¹¥¯¥é¥¹
+// Base class of inline elements
 class Link
 {
-       var $start;   // ³ç¸Ì¤ÎÀèƬÈÖ¹æ(0¥ª¥ê¥¸¥ó)
-       var $text;    // ¥Þ¥Ã¥Á¤·¤¿Ê¸»úÎóÁ´ÂÎ
+       var $start;   // Origin number of parentheses (0 origin)
+       var $text;    // Matched string
 
        var $type;
        var $page;
@@ -148,34 +176,36 @@ class Link
        var $body;
        var $alias;
 
-       // constructor
        function Link($start)
        {
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
                $this->start = $start;
        }
 
-       // ¥Þ¥Ã¥Á¤Ë»ÈÍѤ¹¤ë¥Ñ¥¿¡¼¥ó¤òÊÖ¤¹
+       // Return a regex pattern to match
        function get_pattern() {}
 
-       // »ÈÍѤ·¤Æ¤¤¤ë³ç¸Ì¤Î¿ô¤òÊÖ¤¹ ((?:...)¤ò½ü¤¯)
+       // Return number of parentheses (except (?:...) )
        function get_count() {}
 
-       // ¥Þ¥Ã¥Á¤·¤¿¥Ñ¥¿¡¼¥ó¤òÀßÄꤹ¤ë
-       function set($arr,$page) {}
+       // Set pattern that matches
+       function set($arr, $page) {}
 
-       // Ê¸»úÎó¤ËÊÑ´¹¤¹¤ë
        function toString() {}
 
-       // Private
-       // ¥Þ¥Ã¥Á¤·¤¿ÇÛÎ󤫤顢¼«Ê¬¤ËɬÍפÊÉôʬ¤À¤±¤ò¼è¤ê½Ð¤¹
-       function splice($arr) {
+       // Private: Get needed parts from a matched array()
+       function splice($arr)
+       {
                $count = $this->get_count() + 1;
                $arr   = array_pad(array_splice($arr, $this->start, $count), $count, '');
                $this->text = $arr[0];
                return $arr;
        }
 
-       // ´ðËܥѥé¥á¡¼¥¿¤òÀßÄꤹ¤ë
+       // Set basic parameters
        function setParam($page, $name, $body, $type = '', $alias = '')
        {
                static $converter = NULL;
@@ -184,9 +214,9 @@ class Link
                $this->name = $name;
                $this->body = $body;
                $this->type = $type;
-               if (is_url($alias) && preg_match('/\.(gif|png|jpe?g)$/i', $alias)) {
-                       $alias = htmlspecialchars($alias);
-                       $alias = "<img src=\"$alias\" alt=\"$name\" />";
+               if (! PKWK_DISABLE_INLINE_IMAGE_FROM_URI &&
+                       is_url($alias) && preg_match('/\.(gif|png|jpe?g)$/i', $alias)) {
+                       $alias = '<img src="' . htmlsc($alias) . '" alt="' . $name . '" />';
                } else if ($alias != '') {
                        if ($converter === NULL)
                                $converter = new InlineConverter(array('plugin'));
@@ -202,7 +232,7 @@ class Link
        }
 }
 
-// ¥¤¥ó¥é¥¤¥ó¥×¥é¥°¥¤¥ó
+// Inline plugins
 class Link_plugin extends Link
 {
        var $pattern;
@@ -210,7 +240,11 @@ class Link_plugin extends Link
 
        function Link_plugin($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -246,45 +280,51 @@ EOD;
        {
                list($all, $this->plain, $name, $this->param, $body) = $this->splice($arr);
 
-               // ËÜÍè¤Î¥×¥é¥°¥¤¥ó̾¤ª¤è¤Ó¥Ñ¥é¥á¡¼¥¿¤ò¼èÆÀ¤·¤Ê¤ª¤¹ PHP4.1.2 (?R)Âкö
-               if (preg_match("/^{$this->pattern}/x", $all, $matches)
-                       && $matches[1] != $this->plain)
-               {
+               // Re-get true plugin name and patameters (for PHP 4.1.2)
+               $matches = array();
+               if (preg_match('/^' . $this->pattern . '/x' . get_preg_u(), $all, $matches)
+                       && $matches[1] != $this->plain) 
                        list(, $this->plain, $name, $this->param) = $matches;
-               }
+
                return parent::setParam($page, $name, $body, 'plugin');
        }
 
        function toString()
        {
                $body = ($this->body == '') ? '' : make_link($this->body);
+               $str = FALSE;
 
-               // ¥×¥é¥°¥¤¥ó¸Æ¤Ó½Ð¤·
-               if (exist_plugin_inline($this->name)) {
+               // Try to call the plugin
+               if (exist_plugin_inline($this->name))
                        $str = do_plugin_inline($this->name, $this->param, $body);
-                       if ($str !== FALSE) //À®¸ù
-                               return $str;
-               }
 
-               // ¥×¥é¥°¥¤¥ó¤¬Â¸ºß¤·¤Ê¤¤¤«¡¢ÊÑ´¹¤Ë¼ºÇÔ
-               $body = ($body == '') ? ';' : "\{$body};";
-               return make_line_rules(htmlspecialchars('&' . $this->plain) . $body);
+               if ($str !== FALSE) {
+                       return $str; // Succeed
+               } else {
+                       // No such plugin, or Failed
+                       $body = (($body == '') ? '' : '{' . $body . '}') . ';';
+                       return make_line_rules(htmlsc('&' . $this->plain) . $body);
+               }
        }
 }
 
-// Ãí¼á
+// Footnotes
 class Link_note extends Link
 {
        function Link_note($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
        {
                return <<<EOD
 \(\(
- ((?:(?R)|(?!\)\)).)*) # (1) note body
+ ((?>(?=\(\()(?R)|(?!\)\)).)*) # (1) note body
 \)\)
 EOD;
        }
@@ -296,20 +336,38 @@ EOD;
 
        function set($arr, $page)
        {
-               global $foot_explain;
+               global $foot_explain, $vars;
                static $note_id = 0;
 
                list(, $body) = $this->splice($arr);
 
+               if (PKWK_ALLOW_RELATIVE_FOOTNOTE_ANCHOR) {
+                       $script = '';
+               } else {
+                       $script = get_page_uri($page);
+               }
                $id   = ++$note_id;
                $note = make_link($body);
 
-               $foot_explain[$id] = <<<EOD
-<a id="notefoot_$id" href="#notetext_$id" class="note_super">*$id</a>
-<span class="small">$note</span>
-<br />
-EOD;
-               $name = "<a id=\"notetext_$id\" href=\"#notefoot_$id\" class=\"note_super\">*$id</a>";
+               // Footnote
+               $foot_explain[$id] = '<a id="notefoot_' . $id . '" href="' .
+                       $script . '#notetext_' . $id . '" class="note_super">*' .
+                       $id . '</a>' . "\n" .
+                       '<span class="small">' . $note . '</span><br />';
+
+               // A hyperlink, content-body to footnote
+               if (! is_numeric(PKWK_FOOTNOTE_TITLE_MAX) || PKWK_FOOTNOTE_TITLE_MAX <= 0) {
+                       $title = '';
+               } else {
+                       $title = strip_tags($note);
+                       $count = mb_strlen($title, SOURCE_ENCODING);
+                       $title = mb_substr($title, 0, PKWK_FOOTNOTE_TITLE_MAX, SOURCE_ENCODING);
+                       $abbr  = (PKWK_FOOTNOTE_TITLE_MAX < $count) ? '...' : '';
+                       $title = ' title="' . $title . $abbr . '"';
+               }
+               $name = '<a id="notetext_' . $id . '" href="' . $script .
+                       '#notefoot_' . $id . '" class="note_super"' . $title .
+                       '>*' . $id . '</a>';
 
                return parent::setParam($page, $name, $body);
        }
@@ -320,23 +378,28 @@ EOD;
        }
 }
 
-// url
+// URLs
 class Link_url extends Link
 {
        function Link_url($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
        {
                $s1 = $this->start + 1;
                return <<<EOD
-(\[\[             # (1) open bracket
- ((?:(?!\]\]).)+) # (2) alias
+((?:\[\[))?       # (1) open bracket
+((?($s1)          # (2) alias
+((?:(?!\]\]).)+)  # (3) alias name
  (?:>|:)
-)?
-(                 # (3) url
+))?
+(                 # (4) url
  (?:(?:https?|ftp|news):\/\/|mailto:)[\w\/\@\$()!?&%#:;.,~'=*+-]+
 )
 (?($s1)\]\])      # close bracket
@@ -345,28 +408,37 @@ EOD;
 
        function get_count()
        {
-               return 3;
+               return 4;
        }
 
        function set($arr, $page)
        {
-               list(, , $alias, $name) = $this->splice($arr);
-               return parent::setParam($page, htmlspecialchars($name),
+               list(, , $alias, $name) = $this->splice($arr);
+               return parent::setParam($page, htmlsc($name),
                        '', 'url', $alias == '' ? $name : $alias);
        }
 
        function toString()
        {
-               return "<a href=\"{$this->name}\">{$this->alias}</a>";
+               if (FALSE) {
+                       $rel = '';
+               } else {
+                       $rel = ' rel="nofollow"';
+               }
+               return '<a href="' . $this->name . '"' . $rel . '>' . $this->alias . '</a>';
        }
 }
 
-// url (InterWiki definition type)
+// URLs (InterWiki definition on "InterWikiName")
 class Link_url_interwiki extends Link
 {
        function Link_url_interwiki($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -390,23 +462,27 @@ EOD;
        function set($arr, $page)
        {
                list(, $name, $alias) = $this->splice($arr);
-               return parent::setParam($page, htmlspecialchars($name), '', 'url', $alias);
+               return parent::setParam($page, htmlsc($name), '', 'url', $alias);
        }
 
        function toString()
        {
-               return "<a href=\"{$this->name}\">{$this->alias}</a>";
+               return '<a href="' . $this->name . '" rel="nofollow">' . $this->alias . '</a>';
        }
 }
 
-//mailto:
+// mailto: URL schemes
 class Link_mailto extends Link
 {
        var $is_image, $image;
 
        function Link_mailto($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -435,11 +511,11 @@ EOD;
        
        function toString()
        {
-               return "<a href=\"mailto:{$this->name}\">{$this->alias}</a>";
+               return '<a href="mailto:' . $this->name . '" rel="nofollow">' . $this->alias . '</a>';
        }
 }
 
-//InterWikiName
+// InterWikiName-rendered URLs
 class Link_interwikiname extends Link
 {
        var $url    = '';
@@ -448,7 +524,11 @@ class Link_interwikiname extends Link
 
        function Link_interwikiname($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -481,21 +561,20 @@ EOD;
 
        function set($arr, $page)
        {
-               global $script;
-
                list(, $alias, , $name, $this->param) = $this->splice($arr);
 
+               $matches = array();
                if (preg_match('/^([^#]+)(#[A-Za-z][\w-]*)$/', $this->param, $matches))
                        list(, $this->param, $this->anchor) = $matches;
 
                $url = get_interwiki_url($name, $this->param);
                $this->url = ($url === FALSE) ?
-                       $script . '?' . rawurlencode('[[' . $name . ':' . $this->param . ']]') :
-                       htmlspecialchars($url);
+                       get_base_uri() . '?' . pagename_urlencode('[[' . $name . ':' . $this->param . ']]') :
+                       htmlsc($url);
 
                return parent::setParam(
                        $page,
-                       htmlspecialchars($name . ':' . $this->param),
+                       htmlsc($name . ':' . $this->param),
                        '',
                        'InterWikiName',
                        $alias == '' ? $name . ':' . $this->param : $alias
@@ -504,18 +583,23 @@ EOD;
 
        function toString()
        {
-               return "<a href=\"{$this->url}{$this->anchor}\" title=\"{$this->name}\">{$this->alias}</a>";
+               return '<a href="' . $this->url . $this->anchor . '" title="' .
+                       $this->name . '" rel="nofollow">' . $this->alias . '</a>';
        }
 }
 
-// BracketName
+// BracketNames
 class Link_bracketname extends Link
 {
        var $anchor, $refer;
 
        function Link_bracketname($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -524,17 +608,17 @@ class Link_bracketname extends Link
 
                $s2 = $this->start + 2;
                return <<<EOD
-\[\[                     # open bracket
-(?:((?:(?!\]\]).)+)>)?   # (1) alias
-(\[\[)?                  # (2) open bracket
+\[\[                     # Open bracket
+(?:((?:(?!\]\]).)+)>)?   # (1) Alias
+(\[\[)?                  # (2) Open bracket
 (                        # (3) PageName
  (?:$WikiName)
  |
  (?:$BracketName)
 )?
-(\#(?:[a-zA-Z][\w-]*)?)? # (4) anchor
-(?($s2)\]\])             # close bracket if (2)
-\]\]                     # close bracket
+(\#(?:[a-zA-Z][\w-]*)?)? # (4) Anchor
+(?($s2)\]\])             # Close bracket if (2)
+\]\]                     # Close bracket
 EOD;
        }
 
@@ -548,13 +632,10 @@ EOD;
                global $WikiName;
 
                list(, $alias, , $name, $this->anchor) = $this->splice($arr);
-
                if ($name == '' && $this->anchor == '') return FALSE;
 
-               if ($name == '' || ! preg_match("/^$WikiName$/", $name)) {
-
+               if ($name == '' || ! preg_match('/^' . $WikiName . '$/', $name)) {
                        if ($alias == '') $alias = $name . $this->anchor;
-
                        if ($name != '') {
                                $name = get_fullname($name, $page);
                                if (! is_pagename($name)) return FALSE;
@@ -575,12 +656,16 @@ EOD;
        }
 }
 
-// WikiName
+// WikiNames
 class Link_wikiname extends Link
 {
        function Link_wikiname($start)
        {
-               parent::Link($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -612,7 +697,7 @@ class Link_wikiname extends Link
        }
 }
 
-// AutoLink
+// AutoLinks
 class Link_autolink extends Link
 {
        var $forceignorepages = array();
@@ -621,9 +706,13 @@ class Link_autolink extends Link
 
        function Link_autolink($start)
        {
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
                global $autolink;
 
-               parent::Link($start);
+               parent::__construct($start);
 
                if (! $autolink || ! file_exists(CACHE_DIR . 'autolink.dat'))
                        return;
@@ -636,7 +725,7 @@ class Link_autolink extends Link
 
        function get_pattern()
        {
-               return isset($this->auto) ? "({$this->auto})" : FALSE;
+               return isset($this->auto) ? '(' . $this->auto . ')' : FALSE;
        }
 
        function get_count()
@@ -650,7 +739,7 @@ class Link_autolink extends Link
 
                list($name) = $this->splice($arr);
 
-               // Ìµ»ë¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤Æ¤¤¤ë¡¢¤¢¤ë¤¤¤Ï¸ºß¤·¤Ê¤¤¥Ú¡¼¥¸¤ò¼Î¤Æ¤ë
+               // Ignore pages listed, or Expire ones not found
                if (in_array($name, $this->forceignorepages) || ! is_page($name))
                        return FALSE;
 
@@ -659,12 +748,7 @@ class Link_autolink extends Link
 
        function toString()
        {
-               return make_pagelink(
-                       $this->name,
-                       $this->alias,
-                       '',
-                       $this->page
-               );
+               return make_pagelink($this->name, $this->alias, '', $this->page, TRUE);
        }
 }
 
@@ -672,7 +756,11 @@ class Link_autolink_a extends Link_autolink
 {
        function Link_autolink_a($start)
        {
-               parent::Link_autolink($start);
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
        }
 
        function get_pattern()
@@ -681,70 +769,179 @@ class Link_autolink_a extends Link_autolink
        }
 }
 
-// ¥Ú¡¼¥¸Ì¾¤Î¥ê¥ó¥¯¤òºîÀ®
-function make_pagelink($page, $alias = '', $anchor = '', $refer = '')
+// AutoAlias
+class Link_autoalias extends Link
 {
-       global $script, $vars, $show_title, $show_passage, $link_compact, $related;
-       global $_symbol_noexists;
+       var $forceignorepages = array();
+       var $auto;
+       var $auto_a; // alphabet only
+       var $alias;
 
-       $s_page = htmlspecialchars(strip_bracket($page));
-       $s_alias = ($alias == '') ? $s_page : $alias;
+       function Link_autoalias($start)
+       {
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               global $autoalias, $aliaspage;
+
+               parent::__construct($start);
 
-       if ($page == '') return "<a href=\"$anchor\">$s_alias</a>";
+               if (! $autoalias || ! file_exists(CACHE_DIR . PKWK_AUTOALIAS_REGEX_CACHE) || $this->page == $aliaspage)
+               {
+                       return;
+               }
 
-       $r_page  = rawurlencode($page);
-       $r_refer = ($refer == '') ? '' : '&amp;refer=' . rawurlencode($refer);
+               @list($auto, $auto_a, $forceignorepages) = file(CACHE_DIR . PKWK_AUTOALIAS_REGEX_CACHE);
+               $this->auto = $auto;
+               $this->auto_a = $auto_a;
+               $this->forceignorepages = explode("\t", trim($forceignorepages));
+               $this->alias = '';
+       }
+       function get_pattern()
+       {
+               return isset($this->auto) ? '(' . $this->auto . ')' : FALSE;
+       }
+       function get_count()
+       {
+               return 1;
+       }
+       function set($arr,$page)
+       {
+               list($name) = $this->splice($arr);
+               // Ignore pages listed
+               if (in_array($name, $this->forceignorepages) || get_autoalias_right_link($name) == '') {
+                       return FALSE;
+               }
+               return parent::setParam($page,$name,'','pagename',$name);
+       }
 
-       if (! isset($related[$page]) && $page != $vars['page'] && is_page($page))
-               $related[$page] = get_filetime($page);
+       function toString()
+       {
+               $this->alias = get_autoalias_right_link($this->name);
+               if ($this->alias != '') {
+                       $link = '[[' . $this->name . '>' . $this->alias . ']]';
+                       return make_link($link);
+               }
+               return '';
+       }
+}
 
-       if (is_page($page)) {
-               $passage = get_pg_passage($page, FALSE);
-               $title   = $link_compact ? '' : " title=\"$s_page$passage\"";
-               return "<a href=\"$script?$r_page$anchor\"$title>$s_alias</a>";
+class Link_autoalias_a extends Link_autoalias
+{
+       function Link_autoalias_a($start)
+       {
+               $this->__construct($start);
+       }
+       function __construct($start)
+       {
+               parent::__construct($start);
+       }
+       function get_pattern()
+       {
+               return isset($this->auto_a) ? '(' . $this->auto_a . ')' : FALSE;
+       }
+}
+
+// Make hyperlink for the page
+function make_pagelink($page, $alias = '', $anchor = '', $refer = '', $isautolink = FALSE)
+{
+       global $vars, $link_compact, $related, $_symbol_noexists;
+
+       $script = get_base_uri();
+       $s_page = htmlsc(strip_bracket($page));
+       $s_alias = ($alias == '') ? $s_page : $alias;
+
+       if ($page == '') return '<a href="' . $anchor . '">' . $s_alias . '</a>';
+
+       $page_filetime = fast_get_filetime($page);
+       $is_page = $page_filetime !== 0;
+       if (! isset($related[$page]) && $page !== $vars['page'] && $is_page) {
+               $related[$page] = $page_filetime;
+       }
+
+       if ($isautolink || $is_page) {
+               // Hyperlink to the page
+               $attrs = get_filetime_a_attrs($page_filetime);
+               // AutoLink marker
+               if ($isautolink) {
+                       $al_left  = '<!--autolink-->';
+                       $al_right = '<!--/autolink-->';
+               } else {
+                       $al_left = $al_right = '';
+               }
+               $title_attr_html = '';
+               if ($s_page !== $s_alias) {
+                       $title_attr_html = ' title="' . $s_page . '"';
+               }
+               return $al_left . '<a ' . 'href="' . get_page_uri($page) . $anchor .
+                       '"' . $title_attr_html . ' class="' .
+                       $attrs['class'] . '" data-mtime="' . $attrs['data_mtime'] .
+                       '">' . $s_alias . '</a>' . $al_right;
        } else {
-               $retval = "$s_alias<a href=\"$script?cmd=edit&amp;page=$r_page$r_refer\">$_symbol_noexists</a>";
-               if (! $link_compact)
-                       $retval = "<span class=\"noexists\">$retval</span>";
-               return $retval;
+               // Support Page redirection
+               $r_page  = rawurlencode($page);
+               $r_refer = ($refer == '') ? '' : '&amp;refer=' . rawurlencode($refer);
+               $redirect_page = get_pagename_on_redirect($page);
+               if ($redirect_page !== false) {
+                       return make_pagelink($redirect_page, $s_alias);
+               }
+               // Dangling link
+               if (PKWK_READONLY) return $s_alias; // No dacorations
+               $symbol_html = '';
+               if ($_symbol_noexists !== '') {
+                       $symbol_html = '<span style="user-select:none;">' .
+                               htmlsc($_symbol_noexists) . '</span>';
+               }
+               $href = $script . '?cmd=edit&amp;page=' . $r_page . $r_refer;
+               if ($link_compact && $_symbol_noexists != '') {
+                       $retval = '<a href="' . $href . '">' . $_symbol_noexists . '</a>';
+                       return $retval;
+               } else {
+                       $retval = '<a href="' . $href . '">' . $s_alias . '</a>';
+                       return '<span class="noexists">' . $retval . $symbol_html . '</span>';
+               }
        }
 }
 
-// ÁêÂл²¾È¤òŸ³«
+// Resolve relative / (Unix-like)absolute path of the page
 function get_fullname($name, $refer)
 {
        global $defaultpage;
 
-       if ($name == '') return $refer;
+       // 'Here'
+       if ($name == '' || $name == './') return $refer;
 
-       if ($name{0} == '/') {
+       // Absolute path
+       if ($name[0] == '/') {
                $name = substr($name, 1);
                return ($name == '') ? $defaultpage : $name;
        }
 
-       if ($name == './') return $refer;
+       // Relative path from 'Here'
        if (substr($name, 0, 2) == './') {
-               $arrn = preg_split('/\//', $name, -1, PREG_SPLIT_NO_EMPTY);
+               $arrn    = preg_split('#/#', $name, -1, PREG_SPLIT_NO_EMPTY);
                $arrn[0] = $refer;
                return join('/', $arrn);
        }
 
+       // Relative path from dirname()
        if (substr($name, 0, 3) == '../') {
-               $arrn = preg_split('/\//', $name,  -1, PREG_SPLIT_NO_EMPTY);
-               $arrp = preg_split('/\//', $refer, -1, PREG_SPLIT_NO_EMPTY);
+               $arrn = preg_split('#/#', $name,  -1, PREG_SPLIT_NO_EMPTY);
+               $arrp = preg_split('#/#', $refer, -1, PREG_SPLIT_NO_EMPTY);
 
                while (! empty($arrn) && $arrn[0] == '..') {
                        array_shift($arrn);
                        array_pop($arrp);
                }
                $name = ! empty($arrp) ? join('/', array_merge($arrp, $arrn)) :
-                       (! empty($arrn) ? "$defaultpage/" . join('/', $arrn) : $defaultpage);
+                       (! empty($arrn) ? $defaultpage . '/' . join('/', $arrn) : $defaultpage);
        }
 
        return $name;
 }
 
-// InterWikiName¤òŸ³«
+// Render an InterWiki into a URL
 function get_interwiki_url($name, $param)
 {
        global $WikiName, $interwiki;
@@ -753,33 +950,32 @@ function get_interwiki_url($name, $param)
 
        if (! isset($interwikinames)) {
                $interwikinames = $matches = array();
-               foreach (get_source($interwiki) as $line) {
-                       if (preg_match('/\[((?:(?:https?|ftp|news):\/\/|\.\.?\/)[!~*\'();\/?:\@&=+\$,%#\w.-]*)\s([^\]]+)\]\s?([^\s]*)/', $line, $matches))
+               foreach (get_source($interwiki) as $line)
+                       if (preg_match('/\[(' . '(?:(?:https?|ftp|news):\/\/|\.\.?\/)' .
+                           '[!~*\'();\/?:\@&=+\$,%#\w.-]*)\s([^\]]+)\]\s?([^\s]*)/',
+                           $line, $matches))
                                $interwikinames[$matches[2]] = array($matches[1], $matches[3]);
-               }
        }
 
        if (! isset($interwikinames[$name])) return FALSE;
 
        list($url, $opt) = $interwikinames[$name];
 
-       // Ê¸»ú¥¨¥ó¥³¡¼¥Ç¥£¥ó¥°
+       // Encoding
        switch ($opt) {
 
-       case '':
-       case 'std': // ÆâÉôʸ»ú¥¨¥ó¥³¡¼¥Ç¥£¥ó¥°¤Î¤Þ¤ÞURL¥¨¥ó¥³¡¼¥É
+       case '':    /* FALLTHROUGH */
+       case 'std': // Simply URL-encode the string, whose base encoding is the internal-encoding
                $param = rawurlencode($param);
                break;
 
-       case 'asis':
-       case 'raw':
-               // $param = htmlspecialchars($param);
+       case 'asis': /* FALLTHROUGH */
+       case 'raw' : // Truly as-is
                break;
 
        case 'yw': // YukiWiki
-               if (! preg_match("/$WikiName/", $param))
+               if (! preg_match('/' . $WikiName . '/', $param))
                        $param = '[[' . mb_convert_encoding($param, 'SJIS', SOURCE_ENCODING) . ']]';
-               // $param = htmlspecialchars($param);
                break;
 
        case 'moin': // MoinMoin
@@ -787,13 +983,19 @@ function get_interwiki_url($name, $param)
                break;
 
        default:
-               // ¥¨¥¤¥ê¥¢¥¹¤ÎÊÑ´¹
-               if (isset($encode_aliases[$opt])) $opt = $encode_aliases[$opt];
-               // »ØÄꤵ¤ì¤¿Ê¸»ú¥³¡¼¥É¤Ø¥¨¥ó¥³¡¼¥É¤·¤ÆURL¥¨¥ó¥³¡¼¥É
-               $param = rawurlencode(mb_convert_encoding($param, $opt, 'auto'));
+               // Alias conversion of $opt
+               if (isset($encode_aliases[$opt])) $opt = & $encode_aliases[$opt];
+
+               // Encoding conversion into specified encode, and URLencode
+               if (strpos($url, '$1') === FALSE && substr($url, -1) === '?') {
+                       // PukiWiki site
+                       $param = pagename_urlencode(mb_convert_encoding($param, $opt, SOURCE_ENCODING));
+               } else {
+                       $param = rawurlencode(mb_convert_encoding($param, $opt, SOURCE_ENCODING));
+               }
        }
 
-       // ¥Ñ¥é¥á¡¼¥¿¤òÃÖ´¹
+       // Replace or Add the parameter
        if (strpos($url, '$1') !== FALSE) {
                $url = str_replace('$1', $param, $url);
        } else {
@@ -805,4 +1007,90 @@ function get_interwiki_url($name, $param)
 
        return $url;
 }
-?>
+
+function get_autoticketlink_def_page()
+{
+       return 'AutoTicketLinkName';
+}
+
+/**
+ * Get AutoTicketLink - JIRA projects from AutoTiketLinkName page
+ */
+function get_ticketlink_jira_projects()
+{
+       $autoticketlink_def_page = get_autoticketlink_def_page();
+       $active_jira_base_url = null;
+       $jira_projects = array();
+       foreach (get_source($autoticketlink_def_page) as $line) {
+               if (substr($line, 0, 1) !== '-') {
+                       $active_jira_base_url = null;
+                       continue;
+               }
+               $m = null;
+               if (preg_match('/^-\s*(jira)\s+(https?:\/\/[!~*\'();\/?:\@&=+\$,%#\w.-]+)\s*$/', $line, $m)) {
+                       $active_jira_base_url = $m[2];
+               } else if (preg_match('/^--\s*([A-Z][A-Z0-9]{1,10}(?:_[A-Z0-9]{1,10}){0,2})(\s+(.+?))?\s*$/', $line, $m)) {
+                       if ($active_jira_base_url) {
+                               $project_key = $m[1];
+                               $title = isset($m[2]) ? $m[2] : '';
+                               array_push($jira_projects, array(
+                                       'key' => $m[1],
+                                       'title' => $title,
+                                       'base_url' => $active_jira_base_url,
+                               ));
+                       }
+               } else {
+                       $active_jira_base_url = null;
+               }
+       }
+       return $jira_projects;
+}
+
+function init_autoticketlink_def_page()
+{
+       $autoticketlink_def_page = get_autoticketlink_def_page();
+       if (is_page($autoticketlink_def_page)) {
+               return;
+       }
+       $body = <<<EOS
+#freeze
+* AutoTicketLink definition [#def]
+
+Reference: https://pukiwiki.osdn.jp/?AutoTicketLink
+
+ - jira https://site1.example.com/jira/browse/
+ -- AAA Project title \$1
+ -- BBB Project title \$1
+ - jira https://site2.example.com/jira/browse/
+ -- PROJECTA Site2 \$1
+
+ (Default definition) pukiwiki.ini.php
+ $ticket_jira_default_site = array(
+   'title' => 'My JIRA - \$1',
+   'base_url' => 'https://issues.example.com/jira/browse/',
+ );
+EOS;
+       page_write($autoticketlink_def_page, $body);
+}
+
+function init_autoalias_def_page()
+{
+       global $aliaspage; // 'AutoAliasName'
+       $autoticketlink_def_page = get_autoticketlink_def_page();
+       if (is_page($aliaspage)) {
+               return;
+       }
+       $body = <<<EOS
+#freeze
+*AutoAliasName [#qf9311bb]
+AutoAlias definition
+
+Reference: https://pukiwiki.osdn.jp/?AutoAlias
+
+* PukiWiki [#ee87d39e]
+-[[pukiwiki.official>https://pukiwiki.osdn.jp/]]
+-[[pukiwiki.dev>https://pukiwiki.osdn.jp/dev/]]
+EOS;
+       page_write($aliaspage, $body);
+       update_autoalias_cache_file();
+}