*
* 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['number'])
$data['number'] = '';
else
$data['number'] = null;
if ($option['outline'])
$data['outline'] = '';
$html .= '';
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 .= ''."\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 .= ''."\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 .= '';
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 .= '';
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 = $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 .= '';
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 .= ''
.'';
$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 .= '';
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 .= '';
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 .= '';
// 次の検索用に読み込み
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 .= ''."\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 .= '';
// 次の検索用に読み込み
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 .= '';
// 次の検索用に読み込み
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 .= '';
//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 .= '';
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 .= '';
// 次の検索用に読み込み
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 .= '';
// 次の検索用に読み込み
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 .= '';
// 次の検索用に読み込み
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 .= '';
// 次の検索用に読み込み
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.''
.''.$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 .= '';
$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'] .= ''."\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 .= '';
} else {
if ($number !== null)
$html .= '';
if ($outline !== null)
$html .= '';
$html .= ''
. '
';
}
return $html;
}
?>