OSDN Git Service

5ab954cf778195da1002f89e471db5cff06c5494
[pukiwiki/pukiwiki.git] / make_link.php
1 <?php
2 /////////////////////////////////////////////////
3 // PukiWiki - Yet another WikiWikiWeb clone.
4 //
5 // $Id: make_link.php,v 1.37 2003/05/13 07:56:12 arino Exp $
6 //
7
8 // ¥ê¥ó¥¯¤òÉղ乤ë
9 function make_link($string,$page = '')
10 {
11         global $vars;
12         static $converter;
13         
14         if (!isset($converter))
15         {
16                 $converter = new InlineConverter();
17         }
18         $_converter = $converter; // copy
19         return $_converter->convert($string, ($page != '') ? $page : $vars['page']);
20 }
21 //¥¤¥ó¥é¥¤¥óÍ×ÁǤòÃÖ´¹¤¹¤ë
22 class InlineConverter
23 {
24         var $converters; // as array()
25         var $pattern;
26         var $pos;
27         var $result;
28         
29         function InlineConverter($converters=NULL,$excludes=NULL)
30         {
31                 if ($converters === NULL)
32                 {
33                         $converters = array(
34                                 'plugin',        // ¥¤¥ó¥é¥¤¥ó¥×¥é¥°¥¤¥ó
35                                 'note',          // Ãí¼á 
36                                 'url',           // URL
37                                 'url_interwiki', // URL (interwiki definition)
38                                 'mailto',        // mailto:
39                                 'interwikiname', // InterWikiName
40                                 'autolink',      // AutoLink
41                                 'bracketname',   // BracketName
42                                 'wikiname',      // WikiName
43                         );
44                 }
45                 if ($excludes !== NULL)
46                 {
47                         $converters = array_diff($converters,$excludes);
48                 }
49                 $this->converters = array();
50                 $patterns = array();
51                 $start = 3; // $this->pattern¤Î¥µ¥Ö¥Ñ¥¿¡¼¥ó2¸Ä¤ò´Þ¤à
52                 
53                 foreach ($converters as $name)
54                 {
55                         $classname = "Link_$name";
56                         $converter = new $classname($start);
57                         $pattern = $converter->get_pattern();
58                         if ($pattern === FALSE)
59                         {
60                                 continue;
61                         }
62                         $patterns[] = "(\n$pattern\n)";
63                         $this->converters[$start] = $converter;
64                         $start += $converter->get_count();
65                         $start++;
66                 }
67                 $this->pattern = '(.*?)('.join('|',$patterns).')'; // $start += 2
68         }
69         function convert($string,$page)
70         {
71                 $this->page = $page;
72                 $this->result = '';
73                 
74                 $string = preg_replace_callback("/{$this->pattern}/x",array(&$this,'replace'),$string);
75                 
76                 return $this->result.htmlspecialchars($string);
77         }
78         function replace($arr)
79         {
80                 $obj = $this->get_converter($arr);
81                 
82                 $this->result .= ($obj !== NULL and $obj->set($arr,$this->page) !== FALSE) ?
83                         htmlspecialchars($arr[1]).$obj->toString() : htmlspecialchars($arr[0]);
84                 
85                 return ''; //½èÍýºÑ¤ß¤ÎÉôʬ¤òʸ»úÎ󤫤éºï½ü¤¹¤ë
86         }
87         function get_objects($string,$page)
88         {
89                 preg_match_all("/{$this->pattern}/x",$string,$matches,PREG_SET_ORDER);
90                 
91                 $arr = array();
92                 foreach ($matches as $match)
93                 {
94                         $obj = $this->get_converter($match);
95                         if ($obj->set($match,$page) !== FALSE)
96                         {
97                                 $arr[] = $obj; // copy
98                         }
99                 }
100                 return $arr;
101         }
102         function &get_converter(&$arr)
103         {
104                 foreach (array_keys($this->converters) as $start)
105                 {
106                         if ($arr[$start] != '')
107                         {
108                                 return $this->converters[$start];
109                         }
110                 }
111                 return NULL;
112         }
113 }
114 //¥¤¥ó¥é¥¤¥óÍ×Áǽ¸¹ç¤Î¥Ù¡¼¥¹¥¯¥é¥¹
115 class Link
116 {
117         var $start;   // ³ç¸Ì¤ÎÀèƬÈÖ¹æ(0¥ª¥ê¥¸¥ó)
118         var $text;    // ¥Þ¥Ã¥Á¤·¤¿Ê¸»úÎóÁ´ÂÎ
119
120         var $type;
121         var $page;
122         var $name;
123         var $alias;
124
125         // constructor
126         function Link($start)
127         {
128                 $this->start = $start;
129         }
130         // ¥Þ¥Ã¥Á¤Ë»ÈÍѤ¹¤ë¥Ñ¥¿¡¼¥ó¤òÊÖ¤¹
131         function get_pattern()
132         {
133         }
134         // »ÈÍѤ·¤Æ¤¤¤ë³ç¸Ì¤Î¿ô¤òÊÖ¤¹ ((?:...)¤ò½ü¤¯)
135         function get_count()
136         {
137         }
138         // ¥Þ¥Ã¥Á¤·¤¿¥Ñ¥¿¡¼¥ó¤òÀßÄꤹ¤ë
139         function set($arr,$page)
140         {
141         }
142         // Ê¸»úÎó¤ËÊÑ´¹¤¹¤ë
143         function toString()
144         {
145         }
146         
147         //private
148         // ¥Þ¥Ã¥Á¤·¤¿ÇÛÎ󤫤顢¼«Ê¬¤ËɬÍפÊÉôʬ¤À¤±¤ò¼è¤ê½Ð¤¹
149         function splice($arr)
150         {
151                 $count = $this->get_count() + 1;
152                 $arr = array_splice($arr,$this->start,$count);
153                 while (count($arr) < $count)
154                 {
155                         $arr[] = '';
156                 }
157                 $this->text = $arr[0];
158                 return $arr;
159         }
160         // ´ðËܥѥé¥á¡¼¥¿¤òÀßÄꤹ¤ë
161         function setParam($page,$name,$type='',$alias='')
162         {
163                 static $converter = NULL;
164                 
165                 $this->page = $page;
166                 $this->name = $name;
167                 $this->type = $type;
168                 if ($type != 'InterWikiName' and preg_match('/\.(gif|png|jpe?g)$/i',$alias))
169                 {
170                         $alias = "<img src=\"$alias\" alt=\"$name\" />";
171                 }
172                 else if ($alias != '')
173                 {
174                         if ($converter === NULL)
175                         {
176                                 $converter = new InlineConverter(array('plugin'));
177                         }
178                         $alias = make_line_rules($converter->convert($alias,$page));
179                 }
180                 $this->alias = $alias;
181                 
182                 return TRUE;
183         }
184 }
185 // ¥¤¥ó¥é¥¤¥ó¥×¥é¥°¥¤¥ó
186 class Link_plugin extends Link
187 {
188         var $param,$body;
189         
190         function Link_plugin($start)
191         {
192                 parent::Link($start);
193         }
194         function get_pattern()
195         {
196                 return <<<EOD
197 &(\w+) # (1) plugin name
198 (?:
199  \(
200   ([^)]*)  # (2) parameter
201  \)
202 )?
203 (?:
204  \{
205   (.*)     # (3) body
206  \}
207 )?
208 ;
209 EOD;
210         }
211         function get_count()
212         {
213                 return 3;
214         }
215         function set($arr,$page)
216         {
217                 $arr = $this->splice($arr);
218                 
219                 $name = $arr[1];
220                 $this->param = $arr[2];
221                 $this->body = ($arr[3] == '') ? '' : make_link($arr[3]);
222                 
223                 if (!exist_plugin_inline($name))
224                 {
225                         return FALSE;
226                 }
227                 
228                 return parent::setParam($page,$name,'plugin');
229         }
230         function toString()
231         {
232                 return $this->make_inline($this->name,$this->param,$this->body);
233         }
234         function make_inline($func,$param,$body)
235         {
236                 //&hoge(){...}; &fuga(){...}; ¤Îbody¤¬'...}; &fuga(){...'¤È¤Ê¤ë¤Î¤Ç¡¢Á°¸å¤Ëʬ¤±¤ë
237                 $after = '';
238                 if (preg_match("/^ ((?!};).*?) }; (.*?) &amp; (\w+) (?: \( ([^()]*) \) )? { (.+)$/x",$body,$matches))
239                 {
240                         $body = $matches[1];
241                         $after = $matches[2].$this->make_inline($matches[3],$matches[4],$matches[5]);
242                 }
243                 
244                 // ¥×¥é¥°¥¤¥ó¸Æ¤Ó½Ð¤·
245                 if (exist_plugin_inline($func))
246                 {
247                         $str = do_plugin_inline($func,$param,$body);
248                         if ($str !== FALSE) //À®¸ù
249                         {
250                                 return $str.$after;
251                         }
252                 }
253                 
254                 // ¥×¥é¥°¥¤¥ó¤¬Â¸ºß¤·¤Ê¤¤¤«¡¢ÊÑ´¹¤Ë¼ºÇÔ
255                 return htmlspecialchars($this->text);
256         }
257 }
258 // Ãí¼á
259 class Link_note extends Link
260 {
261         function Link_note($start)
262         {
263                 parent::Link($start);
264         }
265         function get_pattern()
266         {
267                 return <<<EOD
268 \(\(    # open paren
269  (      # (1) note body
270   (?:
271    (?>  # once-only 
272     (?:
273      (?!\(\()(?!\)\)(?:[^\)]|$)).
274     )+
275    )
276    |
277    (?R) # or recursive of me
278   )*
279  )
280 \)\)
281 EOD;
282         }
283         function get_count()
284         {
285                 return 1;
286         }
287         function set($arr,$page)
288         {
289                 global $foot_explain;
290                 static $note_id = 0;
291                 
292                 $arr = $this->splice($arr);
293                 
294                 $id = ++$note_id;
295                 $note = make_line_rules(make_link($arr[1]));
296                 
297                 $foot_explain[$id] = <<<EOD
298 <a id="notefoot_$id" href="#notetext_$id" class="note_super">*$id</a>
299 <span class="small">$note</span>
300 <br />
301 EOD;
302                 $name = "<a id=\"notetext_$id\" href=\"#notefoot_$id\" class=\"note_super\">*$id</a>";
303                 
304                 return parent::setParam($page,$name);
305         }
306         function toString()
307         {
308                 return $this->name;
309         }
310 }
311 // url
312 class Link_url extends Link
313 {
314         function Link_url($start)
315         {
316                 parent::Link($start);
317         }
318         function get_pattern()
319         {
320                 $s1 = $this->start + 1;
321                 return <<<EOD
322 (\[\[         # (1) open bracket
323  ([^\]]+)     # (2) alias
324  (?:&gt;|>|:) # '&gt;' or '>' or ':'
325 )?
326 (             # (3) url
327  (?:https?|ftp|news):\/\/[!~*'();\/?:\@&=+\$,%#\w.-]+
328 )
329 (?($s1)\]\])  # close bracket
330 EOD;
331         }
332         function get_count()
333         {
334                 return 3;
335         }
336         function set($arr,$page)
337         {
338                 $arr = $this->splice($arr);
339                 
340                 $name = $arr[3];
341                 $alias = ($arr[2] == '') ? $arr[3] : $arr[2];
342                 return parent::setParam($page,$name,'url',$alias);
343                 
344         }
345         function toString()
346         {
347                 return "<a href=\"{$this->name}\">{$this->alias}</a>";
348         }
349 }
350 // url (InterWiki definition type)
351 class Link_url_interwiki extends Link
352 {
353         function Link_url_interwiki($start)
354         {
355                 parent::Link($start);
356         }
357         function get_pattern()
358         {
359                 return <<<EOD
360 \[       # open bracket
361 (        # (1) url
362  (?:(?:https?|ftp|news):\/\/|\.\.?\/)[!~*'();\/?:\@&=+\$,%#\w.-]*
363 )
364 \s
365 ([^\]]+) # (2) alias
366 \]       # close bracket
367 EOD;
368         }
369         function get_count()
370         {
371                 return 2;
372         }
373         function set($arr,$page)
374         {
375                 $arr = $this->splice($arr);
376                 
377                 $name = $arr[1];
378                 $alias = $arr[2];
379                 
380                 return parent::setParam($page,$name,'url',$alias);
381         }
382         function toString()
383         {
384                 return "<a href=\"{$this->name}\">{$this->alias}</a>";
385         }
386 }
387 //mailto:
388 class Link_mailto extends Link
389 {
390         var $is_image,$image;
391         
392         function Link_mailto($start)
393         {
394                 parent::Link($start);
395         }
396         function get_pattern()
397         {
398                 $s1 = $this->start + 1;
399                 return <<<EOD
400 (?:\[\[([^\]]+)(?:&gt;|>|:))? # (1) alias
401  ([\w.-]+@[\w-]+\.[\w.-]+)    # (2) mailto
402 (?($s1)\]\])                  # close bracket if (1)
403 EOD;
404         }
405         function get_count()
406         {
407                 return 2;
408         }
409         function set($arr,$page)
410         {
411                 $arr = $this->splice($arr);
412                 
413                 $name = $arr[2];
414                 $alias = ($arr[1] == '') ? $arr[2] : $arr[1];
415                 
416                 return parent::setParam($page,$name,'mailto',$alias);
417         }
418         function toString()
419         {
420                 return "<a href=\"mailto:{$this->name}\">{$this->alias}</a>";
421         }
422 }
423 //InterWikiName
424 class Link_interwikiname extends Link
425 {
426         var $anchor = '';
427         
428         function Link_interwikiname($start)
429         {
430                 parent::Link($start);
431         }
432         function get_pattern()
433         {
434                 $s1 = $this->start + 1;
435                 $s3 = $this->start + 3;
436                 $s5 = $this->start + 5;
437                 return <<<EOD
438 \[\[                    # open bracket
439 (?:
440  (\[\[)?                # (1) open bracket
441  ([^\[\]]+)             # (2) alias
442  (?:&gt;|>)             # '&gt;' or '>'
443 )?
444 (?:
445  (\[\[)?                # (3) open bracket
446  (\[*?[^\s\]]+?\]*?)    # (4) InterWiki
447  (                      # (5)
448   (?($s1)\]\]           #  close bracket if (1)
449   |(?($s3)\]\])         #   or (3)
450   )
451  )?
452  (?<!&gt;)
453  (\:(?:(?<!&gt;|>).)*?) # (6) param
454  (?($s5) |              # if !(5)
455   (?($s1)\]\]           #  close bracket if (1)
456   |(?($s3)\]\])         #   or (3)
457   )
458  )
459 )?
460 \]\]                    # close bracket
461 EOD;
462         }
463         function get_count()
464         {
465                 return 6;
466         }
467         function set($arr,$page)
468         {
469                 $arr = $this->splice($arr);
470                 
471                 $param = $arr[6];
472                 if (preg_match('/^([^#]+)(#[A-Za-z][\w-]*)$/',$param,$matches))
473                 {
474                         $this->anchor = $matches[2];
475                         $param = $matches[1];
476                 }
477                 $name = rawurlencode('[['.$arr[4].$param.']]');
478                 $alias = ($arr[2] != '') ? $arr[2] : $arr[4].$arr[6];
479                 
480                 return parent::setParam($page,$name,'InterWikiName',$alias);
481         }
482         function toString()
483         {
484                 global $script; //,$interwiki_target;
485                 
486                 return "<a href=\"$script?{$this->name}{$this->anchor}\">{$this->alias}</a>";
487         }
488 }
489 // BracketName
490 class Link_bracketname extends Link
491 {
492         var $anchor,$refer;
493         
494         function Link_bracketname($start)
495         {
496                 parent::Link($start);
497         }
498         function get_pattern()
499         {
500                 global $WikiName,$BracketName;
501                 
502                 $s1 = $this->start + 1;
503                 $s3 = $this->start + 3;
504                 $s7 = $this->start + 7;
505                 return <<<EOD
506 \[\[                     # open bracket
507 (?:
508  (\[\[)?                 # (1) open bracket
509  ([^\[\]]+)              # (2) alias
510  (?:&gt;|>)              # '&gt;' or '>'
511 )?
512 (\[\[)?                  # (3) open bracket
513 (                        # (4) PageName
514  ($WikiName)             # (5) WikiName
515  |
516  ($BracketName)          # (6) BracketName
517 )?
518 (                        # (7)
519  (?($s1)\]\]             #  close bracket if (1)
520   |(?($s3)\]\])          #   or (3)
521  )
522 )
523 (\#(?:[a-zA-Z][\w-]*)?)? # (8) anchor
524 (?($s7)|                 # if !(7)
525  (?($s1)\]\]             #  close bracket if (1)
526   |(?($s3)\]\])          #   or (3)
527  )
528 )
529 \]\]                     # close bracket
530 EOD;
531         }
532         function get_count()
533         {
534                 return 8;
535         }
536         function set($arr,$page)
537         {
538                 global $WikiName,$BracketName;
539                 
540                 $arr = $this->splice($arr);
541                 
542                 $alias = $arr[2];
543                 $name = $arr[4];
544                 $this->anchor = $arr[8];
545                 
546                 if ($name == '' and $this->anchor == '')
547                 {
548                         return FALSE;
549                 }
550                 if ($name != '' and preg_match("/^$WikiName$/",$name))
551                 {
552                         return parent::setParam($page,$name,'pagename',$alias);
553                 }
554                 if ($alias == '')
555                 {
556                         $alias = $name.$this->anchor;
557                 }
558                 if ($name == '' and $this->anchor == '')
559                 {
560                         return FALSE;
561                 }
562                 
563                 $name = get_fullname($name,$page);
564                 
565                 if ($name != '' and !preg_match("/^($WikiName)|($BracketName)$/",$name))
566                 {
567                         return FALSE;
568                 }
569                 return parent::setParam($page,$name,'pagename',$alias);
570         }
571         function toString()
572         {
573                 return make_pagelink(
574                         $this->name,
575                         $this->alias,
576                         $this->anchor,
577                         $this->page
578                 );
579         }
580 }
581 // WikiName
582 class Link_wikiname extends Link
583 {
584         function Link_wikiname($start)
585         {
586                 parent::Link($start);
587         }
588         function get_pattern()
589         {
590                 global $WikiName,$nowikiname;
591                 
592                 return $nowikiname ? FALSE : "($WikiName)";
593         }
594         function get_count()
595         {
596                 return 1;
597         }
598         function set($arr,$page)
599         {
600                 $arr = $this->splice($arr);
601                 $name = $alias = $arr[0];
602                 return parent::setParam($page,$name,'pagename',$alias);
603         }
604         function toString()
605         {
606                 return make_pagelink(
607                         $this->name,
608                         $this->alias,
609                         '',
610                         $this->page
611                 );
612         }
613 }
614 // AutoLink
615 class Link_autolink extends Link
616 {
617         var $forceignorepages = array();
618         
619         function Link_autolink($start)
620         {
621                 parent::Link($start);
622         }
623         function get_pattern()
624         {
625                 global $autolink;
626                 static $auto,$forceignorepages;
627                 
628                 if (!$autolink or !file_exists(CACHE_DIR.'autolink.dat'))
629                 {
630                         return FALSE;
631                 }
632                 if (!isset($auto)) // and/or !isset($forceignorepages)
633                 {
634                         @list($auto,$forceignorepages) = file(CACHE_DIR.'autolink.dat');
635                         $forceignorepages = explode("\t",$forceignorepages);
636                 }
637                 $this->forceignorepages = $forceignorepages;
638                 return "($auto)";
639         }
640         function get_count()
641         {
642                 return 1;
643         }
644         function set($arr,$page)
645         {
646                 global $WikiName;
647                 
648                 $arr = $this->splice($arr);
649                 $name = $alias = $arr[0];
650                 // Ìµ»ë¥ê¥¹¥È¤Ë´Þ¤Þ¤ì¤Æ¤¤¤ë¡¢¤¢¤ë¤¤¤Ï¸ºß¤·¤Ê¤¤¥Ú¡¼¥¸¤ò¼Î¤Æ¤ë
651                 if (in_array($name,$this->forceignorepages) or !is_page($name))
652                 {
653                         return FALSE;
654                 }
655                 return parent::setParam($page,$name,'pagename',$alias);
656         }
657         function toString()
658         {
659                 return make_pagelink(
660                         $this->name,
661                         $this->alias,
662                         '',
663                         $this->page
664                 );
665         }
666 }
667 // ¥Ú¡¼¥¸Ì¾¤Î¥ê¥ó¥¯¤òºîÀ®
668 function make_pagelink($page,$alias='',$anchor='',$refer='')
669 {
670         global $script,$vars,$show_title,$show_passage,$link_compact,$related;
671         
672         $s_page = htmlspecialchars(strip_bracket($page));
673         $s_alias = ($alias == '') ? $s_page : $alias;
674         
675         if ($page == '')
676         {
677                 return "<a href=\"$anchor\">$s_alias</a>";
678         }
679         
680         $r_page = rawurlencode($page);
681         $r_refer = ($refer == '') ? '' : '&amp;refer='.rawurlencode($refer);
682         
683         if (!array_key_exists($page,$related) and $page != $vars['page'] and is_page($page))
684         {
685                 $related[$page] = get_filetime($page);
686         }
687         
688         if (is_page($page))
689         {
690                 $passage = get_pg_passage($page,FALSE);
691                 $title = $link_compact ? '' : " title=\"$s_page$passage\"";
692                 return "<a href=\"$script?$r_page$anchor\"$title>$s_alias</a>";
693         }
694         else
695         {
696                 return $link_compact ?
697                         "$s_alias<a href=\"$script?cmd=edit&amp;page=$r_page$r_refer\">?</a>" :
698                         "<span class=\"noexists\">$s_alias<a href=\"$script?cmd=edit&amp;page=$r_page$r_refer\">?</a></span>";
699         }
700 }
701 // ÁêÂл²¾È¤òŸ³«
702 function get_fullname($name,$refer)
703 {
704         global $defaultpage;
705         
706         if ($name == './')
707         {
708                 return $refer;
709         }
710         
711         if (substr($name,0,2) == './')
712         {
713                 $arrn = preg_split('/\//',$name,-1,PREG_SPLIT_NO_EMPTY);
714                 $arrn[0] = $refer;
715                 return join('/',$arrn);
716         }
717         
718         if (substr($name,0,3) == '../')
719         {
720                 $arrn = preg_split('/\//',$name,-1,PREG_SPLIT_NO_EMPTY);
721                 $arrp = preg_split('/\//',$refer,-1,PREG_SPLIT_NO_EMPTY);
722                 
723                 while (count($arrn) > 0 and $arrn[0] == '..')
724                 {
725                         array_shift($arrn);
726                         array_pop($arrp);
727                 }
728                 $name = count($arrp) ? join('/',array_merge($arrp,$arrn)) :
729                         (count($arrn) ? "$defaultpage/".join('/',$arrn) : $defaultpage);
730         }
731         return $name;
732 }
733 ?>