* * GPL * * code.inc.php Ver. 0.5.0 */ define('PLUGIN_CODE_HEADER', 'code_'); class CodeHighlight { function CodeHighlight() { // common define('PLUGIN_CODE_CODE_CANCEL', 0); // 指定を無効化する define('PLUGIN_CODE_IDENTIFIRE', 2); define('PLUGIN_CODE_SPECIAL_IDENTIFIRE', 3); define('PLUGIN_CODE_STRING_LITERAL', 5); define('PLUGIN_CODE_NONESCAPE_LITERAL', 7); define('PLUGIN_CODE_PAIR_LITERAL', 8); define('PLUGIN_CODE_ESCAPE', 10); define('PLUGIN_CODE_COMMENT', 11); define('PLUGIN_CODE_COMMENT_WORD', 12); // コメントが文字列で始まるもの define('PLUGIN_CODE_FORMULA', 14); // outline define('PLUGIN_CODE_BLOCK_START', 20); define('PLUGIN_CODE_BLOCK_END', 21); // 行指向用 define('PLUGIN_CODE_COMMENT_CHAR', 50); // 1文字でコメントと決定できるもの define('PLUGIN_CODE_COMMENT_WORD', 51); // コメントが文字列で始まるもの define('PLUGIN_CODE_HEAD_COMMENT', 52); // コメントが行頭だけのもの (1文字) // fortran define('PLUGIN_CODE_HEADW_COMMENT', 53); // コメントが行頭だけのもの // pukiwiki define('PLUGIN_CODE_CHAR_COMMENT', 54); // コメントが行頭だけかつ英字であるのもの (1文字) // fortran define('PLUGIN_CODE_IDENTIFIRE_CHAR', 60); // 1文字で命令が決定するもの define('PLUGIN_CODE_IDENTIFIRE_WORD', 61); // 命令が文字列で決定するもの define('PLUGIN_CODE_MULTILINE', 62); // 複数文字列への命令 define('PLUGIN_CODE_CARRIAGERETURN', 70); // 空行 define('PLUGIN_CODE_POST_IDENTIFIRE', 71); // 文末の語よって決まるルール } function highlight(& $lang, & $src, & $option) { static $id_number = 0; // プラグインが呼ばれた回数(IDに利用) ++$id_number; if (strlen($lang) > 16) $lang = ''; $option['number'] = (PLUGIN_CODE_NUMBER && ! $option['nonumber'] || $option['number']); $option['outline'] = (PLUGIN_CODE_OUTLINE && ! $option['nooutline'] || $option['outline']); $option['comment'] = (PLUGIN_CODE_COMMENT && ! $option['nocomment'] || $option['comment']); $option['link'] = (PLUGIN_CODE_LINK && ! $option['nolink'] || $option['link']); // mozillaの空白行対策 if($option['number'] || $option['outline']) { // ライン表示用補正 $src = preg_replace('/^$/m',' ',$src); } if (file_exists(PLUGIN_DIR.'code/keyword.'.$lang.'.php')) { // 言語定義ファイルが有る言語 $data = $this->srcToHTML($src, $lang, $id_number, $option); $src = '
'.$data['src'].'
'; } else if (file_exists(PLUGIN_DIR.'code/line.'.$lang.'.php')) { // 行指向解析設定ファイルが有る言語 $data = $this->lineToHTML($src, $lang, $id_number, $option); $src = '
'.$data['src'].'
'; } else { // PHP と 未定義言語 $option['outline'] = false; $option['comment'] = false; // 最後の余分な改行を削除 if ($src[strlen($src)-2] == ' ') $src = substr($src, 0, -2); else $src= substr($src, 0, -1); if ($option['number']) { // 行数を得る $num_of_line = substr_count($src, "\n"); if($src[strlen($src)-1]=="\n") $src=substr($src,0,-1); $data = array('number' => ''); $data['number'] = _plugin_code_makeNumber($num_of_line-1); } if ('php' == $lang) // PHPは標準機能を使う $src = '
'.$this->highlightPHP($src). '
'; else // 未定義言語 $src = '
' .htmlspecialchars($src). '
'; } $option['menu'] = (PLUGIN_CODE_MENU && ! $option['nomenu'] || $option['menu']); $option['menu'] = ($option['menu'] && ($option['outline'] || $option['comment'])); $menu = ''; if ($option['menu']) { // アイコンの設定 $menu .= '
'; if ($option['outline']) { // アウトラインのメニュー $_code_expand = _('Everything is expanded.'); $_code_short = _('Everything is shortened.'); $menu .= ''.$_code_expand.''; $menu .= ''.$_code_short.''."\n"; } if ($option['comment']){ // コメントの開閉ボタン $menu .= ''; $menu .= ''; } $menu .= '
'; } if ($option['number']) $data['number'] = '
'.$data['number'].'
'; else $data['number'] = null; if ($option['outline']) $data['outline'] = '
'.$data['outline'].'
'; $html .= '
' . $menu . _plugin_code_column($src, $data['number'], $data['outline']) . '
'; return $html; } /** * この関数は1行切り出す * 定型フォーマットを持つ言語用 */ function getline(& $string){ $line = ''; if(! $string[0]) return false; $pos = strpos($string, "\n"); // 改行まで切り出す if ($pos === false) { // 見つからないときは終わりまで $line = $string; $string = ''; } else { $line = substr($string, 0, $pos+1); $string = substr($string, $pos+1); } return $line; } /** * この関数は行頭の文字を判定して解析・変換する * 定型フォーマットを持つ言語用 */ function lineToHTML(& $string, & $lang, $id_number, & $option) { // テーブルジャンプ用ハッシュ $switchHash = Array(); $capital = false; // 大文字小文字を区別しない $option['outline'] = false; // outlineを使わない $mknumber = $option['number']; // 改行 $switchHash["\n"] = PLUGIN_CODE_CARRIAGERETURN; // エスケープ文字 $switchHash['\\'] = PLUGIN_CODE_ESCAPE; // 識別子開始文字 for ($i = ord('a'); $i <= ord('z'); ++$i) $switchHash[chr($i)] = PLUGIN_CODE_IDENTIFIRE; for ($i = ord('A'); $i <= ord('Z'); ++$i) $switchHash[chr($i)] = PLUGIN_CODE_IDENTIFIRE; $switchHash['_'] = PLUGIN_CODE_IDENTIFIRE; // 文字列開始文字 $switchHash['"'] = PLUGIN_CODE_STRING_LITERAL; $linemode = false; // 行内を解析するか否か $str_len = strlen($string); // 文字->html変換用ハッシュ $htmlHash = Array('"' => '"', '\'' => ''', '<' => '<', '>' => '>', '&' => '&', "\t" => PLUGIN_CODE_WIDTHOFTAB); // 言語定義ファイル読み込み include(PLUGIN_DIR.'code/line.'.$lang.'.php'); $html = ''; // 出力されるHTMLコード付きソース $num_of_line = 0; // 行数をカウント $commentnum = 0; // コメントのID番号 $line = $this->getline($string); while($line !== false) { ++$num_of_line; while ($line[strlen($line)-2] == '\\') { // 行末がエスケープ文字なら次の行も切り出す ++$num_of_line; $line .= $this->getline($string); } // 行頭文字の判定 switch ($switchHash[$line[0]]) { case PLUGIN_CODE_CHAR_COMMENT: case PLUGIN_CODE_HEAD_COMMENT: case PLUGIN_CODE_COMMENT_CHAR: // 行頭の1文字でコメントと判断できるもの // htmlに追加 ++$commentnum; $line = htmlspecialchars(substr($line,0,-1), ENT_QUOTES); if ($option['link']) $line = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); $html .= '' .$line.''."\n"; $line = $this->getline($string); // next line continue 2; case PLUGIN_CODE_HEADW_COMMENT: case PLUGIN_CODE_COMMENT_WORD: // 2文字以上のパターンから始まるコメント if (strncmp($line, $commentpattern, strlen($commentpattern)) == 0) { // htmlに追加 ++$commentnum; $line = htmlspecialchars(substr($line,0,-1), ENT_QUOTES); if ($option['link']) $line = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); $html .= '' .$line.''."\n"; $line = $this->getline($string); // next line continue 2; } // コメントではない break; case PLUGIN_CODE_IDENTIFIRE_CHAR: // 行頭の1文字が意味を持つもの $index = $code_keyword[$line[0]]; $line = htmlspecialchars($line, ENT_QUOTES); if ($option['link']) $line = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); if ($index != '') $html .= ''.$line.''; else $html .= $line; $line = $this->getline($string); // next line continue 2; case PLUGIN_CODE_IDENTIFIRE_WORD: if (strlen($line) < 2 && $line[0] == ' ') break; // 空行判定 // 行頭のパターンを調べる foreach ($code_identifire[$line[0]] as $pattern) { if (strncmp($line, $pattern, strlen($pattern)) == 0) { $index = $code_keyword[$pattern]; // htmlに追加 $line = htmlspecialchars($line, ENT_QUOTES); if ($option['link']) $line = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); if ($index != '') $html .= ''.$line.''; else $html .= $line; $line = $this->getline($string); // next line continue 3; } } // 行頭の1文字が意味を持つものか判定 $index = $code_keyword[$line[0]]; if ($index != '') { $line = htmlspecialchars($line, ENT_QUOTES); $html .= ''.$line.''; $line = $this->getline($string); // next line continue 2; } else // IDENTIFIREではない break; case PLUGIN_CODE_MULTILINE: // 複数行に渡って効果を持つ指定 $index = $code_keyword[$line[0]]; $src = $line; $line = $this->getline($string); while (in_array($line[0], $multilineEOL) === false && $line !== false) { // 効果の範囲内を取得する $src .= $line; ++$num_of_line; $line = $this->getline($string); } $src = htmlspecialchars($src, ENT_QUOTES); if ($option['link']) $src = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$src); if ($index != '') $html .= ''.$src.''; else $html .= $src; continue 2; case PLUGIN_CODE_POST_IDENTIFIRE: // 行中の特定のパターンを検索する // makeのターゲット用 識別子(アルファベットから始まっている) $str_pos = strpos($line, $post_identifire); if ($str_pos !== false) { $result = htmlspecialchars(substr($line, 0, $str_pos), ENT_QUOTES); $result2 = htmlspecialchars(substr($line, $str_pos+1), ENT_QUOTES); $html .= ''.$result.$post_identifire.'' .''.$result2.''; $line = $this->getline($string); // next line continue 2; } else // 該当しない break; default: // 行内を解析せずにHTMLに追加する (diff) if($linemode) { $line = htmlspecialchars($line, ENT_QUOTES); if ($option['link']) $html .= preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); $line = $this->getline($string); // next line continue 2; } } //switch // 行内の解析 1文字ずつ解析する $str_len = strlen($line); $str_pos = 0; if ($str_len == $str_pos) $code = false; else $code = $line[$str_pos++];// getc while($code !== false) { switch ($switchHash[$code]) { case PLUGIN_CODE_CHAR_COMMENT: // 行頭以外ではコメントにはならない (fortran) case PLUGIN_CODE_IDENTIFIRE: // 識別子(アルファベットから始まっている) // 出来る限り長く識別子を得る --$str_pos;// エラー処理したくないからpreg_matchで必ず見つかるようにする $result = substr($line, $str_pos); preg_match('/[A-Za-z0-9_\-]+/', $result, $matches); $str_pos += strlen($matches[0]); $result = $matches[0]; // htmlに追加 if($capital) $index = $code_keyword[strtolower($result)];// 大文字小文字を区別しない else $index = $code_keyword[$result]; $result = htmlspecialchars($result, ENT_QUOTES); if ($index != '') $html .= ''.$result.''; else $html .= $result; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $line[$str_pos++]; // getc continue 2; case PLUGIN_CODE_SPECIAL_IDENTIFIRE: // 特殊文字から始まる識別子 // 次の文字が英字か判定 if (! ctype_alpha($line[$str_pos])) break; $result = substr($line, $str_pos); preg_match('/[A-Za-z0-9_\-]+/', $result, $matches); $str_pos += strlen($matches[0]); $result = $code.$matches[0]; // htmlに追加 if($capital) $index = $code_keyword[strtolower($result)];// 大文字小文字を区別しない else $index = $code_keyword[$result]; $result = htmlspecialchars($result, ENT_QUOTES); if ($index != '') $html .= ''.$result.''; else $html .= $result; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $line[$str_pos++]; // getc continue 2; case PLUGIN_CODE_STRING_LITERAL: case PLUGIN_CODE_NONESCAPE_LITERAL: // 文字列リテラルを得る //現在エスケープする必要が無い $pos = $str_pos; $result = substr($line, $str_pos); $pos1 = strpos($result, $code); // 文字列終了文字検索 if ($pos1 === false) { // 文字列が終わらなかったので全部文字列とする $str_pos = $str_len; } else { $str_pos += $pos1 + 1; } $result = $code.substr($line, $pos, $str_pos - $pos); // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= ''.$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $line[$str_pos++]; // getc continue 2; case PLUGIN_CODE_COMMENT_CHAR: // 1文字で決まるコメント $line = substr($line, $str_pos-1, $str_len-$str_pos); ++$commentnum; $line = htmlspecialchars($line, ENT_QUOTES); if ($option['link']) $line = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$line); $html .= '' .$line.''."\n"; $line = $this->getline($string); // next line continue 3; } //switch // その他の文字 $result = $htmlHash[$code]; if ($result) $html .= $result; else $html .= $code; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $line[$str_pos++]; // getc }// while $line = $this->getline($string); // next line } // while // 最後の余分な改行を削除 if ($html[strlen($html)-2] == ' ') $html = substr($html, 0, -2); else $html = substr($html, 0, -1); $html = array( 'src' => $html, 'number' => '', 'outline' => '', 'commentnum' => $commentnum,); if($mknumber) $html['number'] = _plugin_code_makeNumber($num_of_line-2); // 最後に改行を削除したため -2 return $html; } /** * ソースからHTML生成 */ function srcToHTML(& $string, & $lang, $id_number, & $option) { // テーブルジャンプ用ハッシュ $switchHash = Array(); $capital = false; // 大文字小文字を区別しない $mkoutline = $option['outline']; $mknumber = $option['number']; // 改行 $switchHash["\n"] = PLUGIN_CODE_CARRIAGERETURN; $switchHash['\\'] = PLUGIN_CODE_ESCAPE; // 識別子開始文字 for ($i = ord('a'); $i <= ord('z'); ++$i) $switchHash[chr($i)] = PLUGIN_CODE_IDENTIFIRE; for ($i = ord('A'); $i <= ord('Z'); ++$i) $switchHash[chr($i)] = PLUGIN_CODE_IDENTIFIRE; $switchHash['_'] = PLUGIN_CODE_IDENTIFIRE; // 文字列開始文字 $switchHash['"'] = PLUGIN_CODE_STRING_LITERAL; // 言語定義ファイル読み込み include(PLUGIN_DIR.'code/keyword.'.$lang.'.php'); // 文字->html変換用ハッシュ $htmlHash = Array('"' => '"', '\'' => ''', '<' => '<', '>' => '>', '&' => '&', "\t" => PLUGIN_CODE_WIDTHOFTAB); $html = ''; $str_len = strlen($string); $str_pos = 0; $line = 0; // 行数をカウント // for outline $outline = Array();// $outline[lineno][nest] $outline[lineno][blockno]がある。 $nest = 1;// ネスト $blockno = 0;// 何番目のブロックか?IDをユニークにするために用いる $last_start = false;// 最後にブロック開始だったか、、、。 $commentno = 0; // 最初の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++];// getc while ($code !== false) { switch ($switchHash[$code]) { case PLUGIN_CODE_CARRIAGERETURN: // 改行 ++$line; $html .="\n"; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_ESCAPE: // escape charactor $start = $code; // 判定用にもう1文字読み込む if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc if (ctype_alnum($code)) { // 文字(変数)なら終端まで見付ける --$str_pos; // エラー処理したくないからpreg_matchで必ず見つかるようにする $result = substr($string, $str_pos); preg_match('/[A-Za-z0-9_]+/', $result, $matches); $str_pos += strlen($matches[0]); $result = $matches[0]; } else { // 記号なら1文字だけ切り出す $result = $code; if ($code == "\n") ++$line; } // htmlに追加 $html .= htmlspecialchars($start.$result, ENT_QUOTES); // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_COMMENT: // コメント --$str_pos; $result = substr($string, $str_pos); foreach($code_comment[$code] as $pattern) { if (preg_match($pattern[0], $result)) { //if (!strncmp($pattern[1], $result, $pattern[0])) { $pos = strpos($result, $pattern[1]); if ($pos === false) { // 見つからないときは終わりまで $str_pos = $str_len; //$result = $result; ってことで何もしない } else { $pos += $pattern[2]; $str_pos += $pos; $result = substr($result, 0, $pos); } // ライン数カウント $line+=substr_count($result,"\n"); ++$commentno; // htmlに追加 $result = str_replace('\t', PLUGIN_CODE_WIDTHOFTAB, htmlspecialchars($result, ENT_QUOTES)); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= '' .$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 3; } } // コメントではない ++$str_pos; break; case PLUGIN_CODE_COMMENT_WORD: // 文字列から始まるコメント // 出来る限り長く識別子を得る --$str_pos; $result = substr($string, $str_pos); foreach($code_comment[$code] as $pattern) { if (preg_match($pattern[0], $result)) { $pos = strpos($result, $pattern[1]); if ($pos === false) { // 見つからないときは終わりまで $str_pos = $str_len; //$result = $result; ってことで何もしない } else { $pos += $pattern[2]; $str_pos += $pos; $result = substr($result, 0, $pos); } // ライン数カウント $line+=substr_count($result,"\n"); ++$commentno; // htmlに追加 $result = str_replace('\t', PLUGIN_CODE_WIDTHOFTAB, htmlspecialchars($result, ENT_QUOTES)); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= '' .$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 3; } } ++$str_pos; // コメントでなければ文字列 break を使わない case PLUGIN_CODE_IDENTIFIRE: // 識別子(アルファベットから始まっている) // 出来る限り長く識別子を得る --$str_pos;// エラー処理したくないからpreg_matchで必ず見つかるようにする $result = substr($string, $str_pos); preg_match('/[A-Za-z0-9_\-]+/', $result, $matches); $str_pos += strlen($matches[0]); $result = $matches[0]; // htmlに追加 if($capital) $index = $code_keyword[strtolower($result)];// 大文字小文字を区別しない else $index = $code_keyword[$result]; $result = htmlspecialchars($result, ENT_QUOTES); if ($index != '') $html .= ''.$result.''; //else $html .= $start.$result.$end; else $html .= $result; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_SPECIAL_IDENTIFIRE: // 特殊文字から始まる識別子 // 次の文字が英字か判定 if (! ctype_alpha($string[$str_pos])) break; $result = substr($string, $str_pos); preg_match('/[A-Za-z0-9_\-]+/', $result, $matches); $str_pos += strlen($matches[0]); $result = $code.$matches[0]; // htmlに追加 if($capital) $index = $code_keyword[strtolower($result)];// 大文字小文字を区別しない else $index = $code_keyword[$result]; $result = htmlspecialchars($result, ENT_QUOTES); if ($index!='') $html .= ''.$result.''; else $html .= $result; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_STRING_LITERAL: // 文字列 // 文字列リテラルを得る $pos = $str_pos; do { $result = substr($string, $str_pos); $pos1 = strpos($result, $code); // 文字列終了文字検索 if ($pos1 === false) { // 文字列が終わらなかったので全部文字列とする $str_pos = $str_len-1; break; } $str_pos += $pos1 + 1; } while ($string[$str_pos-2] == '\\'); // 前の文字がエスケープ文字なら続ける $result = $code.substr($string, $pos, $str_pos - $pos); // ライン数カウント $line+=substr_count($result,"\n"); // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= ''.$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_NONESCAPE_LITERAL: // エスケープ文字と式展開を無視した文字列 // 文字列リテラルを得る $pos = $str_pos; $result = substr($string, $str_pos); $pos1 = strpos($result, $code); // 文字列終了文字検索 if ($pos1 === false) { // 文字列が終わらなかったので全部文字列とする $str_pos = $str_len-1; } else { $str_pos += $pos1 + 1; } $result = $code.substr($string, $pos, $str_pos - $pos); // ライン数カウント $line+=substr_count($result,"\n"); // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= ''.$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_PAIR_LITERAL: // 対記号で囲まれた文字列リテラルを得る PostScript $pos = $str_pos; do { $result = substr($string, $str_pos); $pos1 = strpos($result, $literal_delimiter); // 文字列終了文字検索 if ($pos1 === false) { // 文字列が終わらなかったので全部文字列とする $str_pos = $str_len-1; break; } $str_pos += $pos1 + 1; } while ($string[$str_pos-2] == '\\'); // 前の文字がエスケープ文字なら続ける $result = $code.substr($string, $pos, $str_pos - $pos); // ライン数カウント $line+=substr_count($result,"\n"); // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= ''.$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_FORMULA: // TeXの数式に使用 将来的には汎用性を持たせる $pos = $str_pos; $result = substr($string, $str_pos); $pos1 = strpos($result, $code); // 文字列終了文字検索 if ($pos1 === false) { // 文字列が終わらなかったので全部文字列とする $str_pos = $str_len-1; } else { $str_pos += $pos1 + 1; } $result = $code.substr($string, $pos, $str_pos - $pos); // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) $result = preg_replace('/(s?https?:\/\/|ftp:\/\/|mailto:)([-_.!~*()a-zA-Z0-9;\/:@?=+$,%#]|&)+/', '$0',$result); $html .= ''.$result.''; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_BLOCK_START: // outline 表示用開始文字 {, ( ++$blockno; ++$nest; //if(! array_key_exists($line,$outline)) { if(! isset($outline[$line])) { $outline[$line]=Array(); } array_push($outline[$line],Array('nest'=>$nest, 'blockno'=>$blockno)); // アウトラインが閉じた時に表示する画像を埋め込む場所 $html .= $code.'' .''; if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; case PLUGIN_CODE_BLOCK_END: // outline 表示終了文字 }, ) --$nest; //if(! array_key_exists($line,$outline)) { if(! isset($outline[$line])) { $outline[$line]=Array(); array_push($outline[$line],Array('nest'=>$nest,'blockno'=>0)); } else { $old = array_pop($outline[$line]); if($old['blockno']!=0 && ($nest+1) == $old['nest']) { } else { if(! is_null($old)) array_push($outline[$line],$old); array_push($outline[$line],Array('nest'=>$nest,'blockno'=>0)); } } $last_start = false; $html .= ''.$code; if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc continue 2; }// switch // その他の文字 $result = $htmlHash[$code]; if ($result) $html .= $result; else $html .= $code; // 次の検索用に読み込み if ($str_len == $str_pos) $code = false; else $code = $string[$str_pos++]; // getc }// while // 最後の余分な改行を削除 if ($html[strlen($html)-2] == ' ') $html = substr($html, 0, -2); else $html = substr($html, 0, -1); $html = array( 'src' => $html, 'number' => '', 'outline' => '', 'blocknum' => $blockno, 'commentnum' => $commentno, ); if($mkoutline) return $this->makeOutline($html,$line-1,$nest,$mknumber,$outline,$blockno,$id_number); if($mknumber) $html['number'] = _plugin_code_makeNumber($line-1); return $html; } /** * outline の形成 */ function makeOutline(& $html,$line,$nest,$mknumber,$tree,$blockno,$id_number) { while($nest>1) {// ネストがちゃんとしてなかった場合の対策 $html['src'] .= ''; --$nest; } $outline=''; $number=''; $nest=1; $linelen=$line+1; $str_len=max(3,strlen(''.$linelen)); for($i=0;$i<$linelen;++$i) { $plus=''; $plus1=''; $plus2=''; $minus=''; //if(array_key_exists($i,$tree)) { if(isset($tree[$i])) { while(true) { $array = array_shift($tree[$i]); if (is_null($array)) break; if ($nest<=$array['nest']) { $id=$id_number.'_'.$array['blockno']; if($plus=='') $plus = '-'; $plus1 .= ''; $plus2 .= ''; $nest=$array['nest']; } else { $nest=$array['nest']; $minus .= ''; } } } if($mknumber) { $number.= sprintf('%'.$str_len.'d',($i+1)).$minus.$plus2."\n"; } if($plus=='' && $minus == '') { if($nest==1) $outline.=' '; else $outline.='|'; } else if($plus!='' && $minus == '') { $outline.= $plus.$plus1; } else if($plus=='' && $minus != '') { $outline.= '!'.$minus; } else if($plus!='' && $minus != '') { $outline.= $plus.$minus.$plus1; } $outline.="\n"; } while ($nest>1) {// ネストがちゃんとしてなかった場合の対策 $number .= ''; $outline .= ''; --$nest; } $html['number'] = $number; $html['outline'] = $outline; return $html; } /** * PHPを標準関数でハイライトする */ function highlightPHP($src) { // phpタグが存在するか? $phptagf = false; if(! strstr($src,''; } ob_start(); //出力のバッファリングを有効に highlight_string($src); //phpは標準関数でハイライト $html = ob_get_contents(); //バッファの内容を得る ob_end_clean(); //バッファクリア? // phpタグを取り除く。 if ($phptagf) { $html = preg_replace('/<\?php (.*)?(]*>\?><\/font>|\?>)/m','$1',$html); } $html = str_replace(' ', ' ', $html); $html = str_replace("\n", '', $html); //$html内の"\n"を''で置き換える $html = str_replace('
', "\n", $html); //Vaild XHTML 1.1 Patch (thanks miko) $html = str_replace('', '', $html); return $html; } } /* pre.inc.php と共用 */ /** * 最終引数を解析する * 引数はプラグインへの最後の引数の内容 * 複数行引数の場合に真を返す */ function _plugin_code_multiline_argment(& $arg, & $data, & $option, $begin = 0, $end = null) { // 改行コード変換 $arg = str_replace("\r\n", "\n", $arg); $arg = strtr($arg,"\r", "\n"); // 最後の文字が改行でない場合は外部ファイル if ($arg[strlen($arg)-1] != "\n") { $params = _plugin_code_read_file_data($arg, $begin, $end); if (isset($params['_error']) && $params['_error'] != '') { $data['_error'] = '

'.$params['_error'].';

'; return false; } $data['data'] = $params['data']; if ($data['data'] == "\n" || $data['data'] == '' || $data['data'] == null) { $data['_error'] ='

file '.htmlspecialchars($params['title']).' is empty.

'; return false; } if (PLUGIN_CODE_FILE_ICON && !$option['noicon'] || $option['icon']) $icon = FILE_ICON; else $icon = ''; $data['title'] .= '
'.'' .$icon.$params['title'].'
'."\n"; } else { $data['data'] = $arg; return true; } return false; } /** * 引数に与えられたファイルの内容を文字列に変換して返す * 文字コードは PukiWikiと同一, 改行は \n である */ function _plugin_code_read_file_data(& $name, $begin = 0, $end = null) { global $vars; // 添付ファイルのあるページ: defaultは現在のページ名 $page = isset($vars['page']) ? $vars['page'] : ''; // 添付ファイルまでのパスおよび(実際の)ファイル名 $file = ''; $fname = $name; $is_url = is_url($fname); /* Chech file location */ if ($is_url) { // URL if (! PLUGIN_CODE_READ_URL) { $params['_error'] = 'Cannot assign URL'; return $params; } $url = htmlspecialchars($fname); $params['title'] = htmlspecialchars(preg_match('/([^\/]+)$/', $fname, $matches) ? $matches[1] : $url); } else { // 添付ファイル if (! is_dir(UPLOAD_DIR)) { $params['_error'] = 'No UPLOAD_DIR'; return $params; } $matches = array(); // ファイル名にページ名(ページ参照パス)が合成されているか // (Page_name/maybe-separated-with/slashes/ATTACHED_FILENAME) if (preg_match('#^(.+)/([^/]+)$#', $fname, $matches)) { if ($matches[1] == '.' || $matches[1] == '..') $matches[1] .= '/'; // Restore relative paths $fname = $matches[2]; $page = get_fullname(strip_bracket($matches[1]), $page); // strip is a compat $file = UPLOAD_DIR . encode($page) . '_' . encode($fname); $is_file = is_file($file); } else { // Simple single argument $file = UPLOAD_DIR . encode($page) . '_' . encode($fname); $is_file = is_file($file); } if (! $is_file) { $params['_error'] = htmlspecialchars('File not found: "' .$fname . '" at page "' . $page . '"'); return $params; } $params['title'] = htmlspecialchars($fname); $fname = $file; $url = $script . '?plugin=attach' . '&refer=' . rawurlencode($page) . '&openfile=' . rawurlencode($name); // Show its filename at the last } $params['url'] = $url; $params['info'] = get_date('Y/m/d H:i:s', filemtime($file) - LOCALZONE) . ' ' . sprintf('%01.1f', round(filesize($file)/1024, 1)) . 'KB'; /* Read file data */ $fdata = ''; $filelines = file($fname); if ($end === null) $end = count($filelines); for ($i=$begin; $i<=$end; ++$i) $fdata .= str_replace("\r\n", "\n", $filelines[$i]); $fdata = strtr($fdata, "\r", "\n"); $fdata = mb_convert_encoding($fdata, SOURCE_ENCODING, "auto"); // ファイルの最後を改行にする if($fdata[strlen($fdata)-1] != "\n") $fdata .= "\n"; $params['data'] = $fdata; return $params; } /** * オプション解析 * 引数に対応するキーをOnにする * キーをセットしたら"true"を返す */ function _plugin_code_check_argment(& $arg, & $option) { $arg = strtolower($arg); if (isset($option[$arg])) { $option[$arg] = true; return true; } return false; } /** * 範囲指定を解析 * 呼び出し側の変数を設定する * 範囲を設定したら"true"を返す */ function _plugin_code_get_region(& $option, & $begin, & $end) { if (false !== strpos($option, '-')) { $array = explode('-', $option); } else if (false !== strpos($option, '...')) { $array = explode('...', $option); } else { return false; } if (is_numeric ($array[0])) $begin = $array[0]; else $begin = 1; if (is_numeric ($array[1])) $end = $array[1]; else $end = null; return true; } /** * 行番号を作成する * 引数は行番号の範囲 * 整形された行番号を返す */ function _plugin_code_makeNumber($end, $begin=0) { $number=''; $str_len=max(3,strlen(''.$end)); for($i=$begin; $i<=$end; ++$i) { $number.= sprintf('%'.$str_len.'d',($i))."\n"; } return $number; } /** * 段組みして出力する * * 整形HTMLを返す */ function _plugin_code_column(& $text, $number=null, $outline=null) { if ($number === null && $outline === null) return $text; if (PLUGIN_CODE_TABLE) { $html .= ''; if ($number !== null) $html .= ''; if ($outline !== null) $html .= ''; $html .= '
'.$number.''.$outline.''.$text.'
'; } else { if ($number !== null) $html .= '
'.$number.'
'; if ($outline !== null) $html .= '
'.$outline.'
'; $html .= '
'.$text.'
' . '

'; } return $html; } ?>