/** * ソースからHTML生成 */ function srcToHTML(&$string, &$lang, &$option, $end = null, $begin = 1) { // テーブルジャンプ用ハッシュ $switchHash = array(); $capital = 0; // 大文字小文字を区別しない $mkoutline = $option['outline']; $mknumber = $option['number']; // 改行 $switchHash["\n"] = $this->cont['PLUGIN_CODE_CARRIAGERETURN']; $switchHash['\\'] = $this->cont['PLUGIN_CODE_ESCAPE']; // 識別子開始文字 for ($i = ord('a'); $i <= ord('z'); ++$i) { $switchHash[chr($i)] = $this->cont['PLUGIN_CODE_IDENTIFIRE']; } for ($i = ord('A'); $i <= ord('Z'); ++$i) { $switchHash[chr($i)] = $this->cont['PLUGIN_CODE_IDENTIFIRE']; } $switchHash['_'] = $this->cont['PLUGIN_CODE_IDENTIFIRE']; // 文字列開始文字 $switchHash['"'] = $this->cont['PLUGIN_CODE_STRING_LITERAL']; // 言語定義ファイル読み込み include dirname(__FILE__) . '/keyword.' . $lang . '.php'; // 文字->html変換用ハッシュ $htmlHash = array('"' => '"', '\'' => ''', '<' => '<', '>' => '>', '&' => '&'); $spaceHash = array("\t" => $this->cont['PLUGIN_CODE_WIDTHOFTAB'], ' ' => ' '); $html = ''; $str_len = strlen($string); $str_pos = 0; $num_of_line = $begin; // 行数をカウント $this->nestlevel = 1; // ネスト $this->blockno = 0; // 何番目のブロックか?IDをユニークにするために用いる $terminate = array(); // ブロック終端文字 $str_continue = 0; // ブロックの種類 $startline = 1; // 行頭判定 //$indentlevel = 0; // インデントの深さ // 最初の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc while ($code !== false) { switch ($switchHash[$code]) { case $this->cont['PLUGIN_CODE_CARRIAGERETURN']: // 改行 $startline = 1; if ($str_continue == $this->cont['PLUGIN_CODE_STRING_LITERAL']) { $result = ltrim(substr($string, $str_pos)); $code = $result[0]; switch ($switchHash[$code]) { case $this->cont['PLUGIN_CODE_STRING_LITERAL']: case $this->cont['PLUGIN_CODE_NONESCAPE_LITERAL']: case $this->cont['PLUGIN_CODE_PAIR_LITERAL']: case $this->cont['PLUGIN_CODE_STRING_CONCAT']: break; default: $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } } else { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } } ++$num_of_line; $html .= "\n"; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_ESCAPE']: $startline = 0; if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } // 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") { ++$num_of_line; } } // htmlに追加 $html .= htmlspecialchars($start . $result, ENT_QUOTES); // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_COMMENT']: case $this->cont['PLUGIN_CODE_HEAD_COMMENT']: // 行頭に限る // コメント --$str_pos; $result = substr($string, $str_pos); foreach ($code_comment[$code] as $pattern) { if (($switchHash[$code] == $this->cont['PLUGIN_CODE_COMMENT'] || $startline) && preg_match($pattern[0], $result)) { if ($pattern[2] === 'reg') { if (preg_match($pattern[1], $result, $match)) { $result = $match[0]; $str_pos += strlen($result); } else { // 見つからないときは終わりまで $str_pos = $str_len; } } else { $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); } } // ライン数カウント $commentlines = substr_count($result, "\n"); if ($pattern[1] == "\n") { if ($str_continue != $this->cont['PLUGIN_CODE_COMMENT']) { if ($str_continue != 0) { $this->endRegion($num_of_line - 1); $html .= '</span>'; } if ($option['comment'] && $startline) { $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'comment">'; $str_continue = $this->cont['PLUGIN_CODE_COMMENT']; } } ++$num_of_line; $startline = 1; } else { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } if ($option['comment']) { $is_comment = 0; if ($commentlines >= 1) { if (!isset($this->outline[$num_of_line])) { $this->outline[$num_of_line] = array(); } $this->beginRegion($num_of_line); $num_of_line += $commentlines; $this->endRegion($num_of_line); } } else { $num_of_line += $commentlines; } $startline = 0; } // htmlに追加 $result = str_replace('\\t', $this->cont['PLUGIN_CODE_WIDTHOFTAB'], htmlspecialchars($result, ENT_QUOTES)); if ($option['link']) { $result = preg_replace('/(s?https?:\\/\\/|ftp:\\/\\/|mailto:)([-_.!~*()a-zA-Z0-9;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $_id = $commentlines ? ' id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '"' : ''; $html .= '<span' . $_id . ' class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'comment">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 3; } } // コメントではない ++$str_pos; break; case $this->cont['PLUGIN_CODE_COMMENT_WORD']: // 文字列から始まるコメント // 出来る限り長く識別子を得る --$str_pos; $result = substr($string, $str_pos); foreach ($code_comment[$code] as $pattern) { if (preg_match($pattern[0], $result)) { if ($pattern[2] === 'reg') { if (preg_match($pattern[1], $result, $match)) { $result = $match[0]; $str_pos += strlen($result); } else { // 見つからないときは終わりまで $str_pos = $str_len; } } else { $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); } } if ($str_continue != $this->cont['PLUGIN_CODE_COMMENT']) { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } if ($option['comment'] && $startline) { $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'comment">'; $str_continue = $this->cont['PLUGIN_CODE_COMMENT']; } } ++$num_of_line; $startline = 1; // htmlに追加 $result = str_replace('\\t', $this->cont['PLUGIN_CODE_WIDTHOFTAB'], htmlspecialchars($result, ENT_QUOTES)); if ($option['link']) { $result = preg_replace('/(s?https?:\\/\\/|ftp:\\/\\/|mailto:)([-_.!~*()a-zA-Z0-9;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'comment">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 3; } } ++$str_pos; // コメントでなければ文字列 (break を使わない) // コメントでなければ文字列 (break を使わない) case $this->cont['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 ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } // begin outline if ($option['block'] && isset($outline_def[$result])) { $status = $outline_def[$result]; if ($option['outline'] && !$status[1]) { $state = 0; } else { $state = 1; } $display = $state ? '' : 'none'; if ($status[2] != 'startline' || $startline != 0) { $this->beginRegion($num_of_line, $state); $terminate[$this->nestlevel] = $status[0]; $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>' . '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '_img" style="display:none;"></span>' . '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'block" style="display:' . $diplay . '">'; } else { $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>'; } } else { if ($result == $terminate[$this->nestlevel]) { $this->endRegion($num_of_line); $html .= '</span>'; $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>'; //$pos1 = strpos($result2, "\n"); // end outline } else { if ($index != '') { $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>'; } else { $html .= $result; } } } $startline = 0; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['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 ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } // begin outline if ($option['block'] && isset($outline_def[$result])) { $status = $outline_def[$result]; if ($option['outline'] && !$status[1]) { $state = 0; } else { $state = 1; } $display = $state ? '' : 'none'; $this->beginRegion($num_of_line, $state); $terminate[$this->nestlevel] = $status[0]; $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>' . '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '_img" style="display:none;"></span>' . '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'block" style="display:' . $display . '">'; } else { if ($result == $terminate[$this->nestlevel]) { $result2 = substr($string, $str_pos); $html .= '</span>'; $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>'; //$pos1 = strpos($result2, "\n"); $this->endRegion($num_of_line); // end outline } else { if ($index != '') { $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . $code_css[$index - 1] . '">' . $result . '</span>'; } else { $html .= $result; } } } $startline = 0; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['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); if ($str_continue != $this->cont['PLUGIN_CODE_STRING_LITERAL']) { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } if ($option['literal'] && $startline) { $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">'; $str_continue = $this->cont['PLUGIN_CODE_STRING_LITERAL']; } } // ライン数カウント $num_of_line += substr_count($result, "\n"); $startline = 0; // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) { $result = preg_replace('/(s?https?:\\/\\/|ftp:\\/\\/|mailto:)([-_.!~*()a-zA-Z0-9;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['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); if ($str_continue != $this->cont['PLUGIN_CODE_STRING_LITERAL']) { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } if ($option['literal'] && $startline) { $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">'; $str_continue = $this->cont['PLUGIN_CODE_STRING_LITERAL']; } } // ライン数カウント $num_of_line += substr_count($result, "\n"); $startline = 0; // htmlに追加 $result = htmlspecialchars($result, ENT_QUOTES); if ($option['link']) { $result = preg_replace('/(s?https?:\\/\\/|ftp:\\/\\/|mailto:)([-_.!~*()a-zA-Z0-9;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_PAIR_LITERAL']: $startline = 0; // 対記号で囲まれた文字列リテラルを得る 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); if ($str_continue != $this->cont['PLUGIN_CODE_STRING_LITERAL']) { if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } if ($option['literal']) { $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 //$html .= '<span id="'.$this->cont['PLUGIN_CODE_HEADER'].$this->id_number._.$this->blockno.'_img" display="none"></span>'; $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">'; $str_continue = $this->cont['PLUGIN_CODE_STRING_LITERAL']; } } // ライン数カウント $num_of_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;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'string">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_STRING_CONCAT']: $startline = 0; $result = $htmlHash[$code]; if ($result) { $html .= $result; } else { $html .= $code; } // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_FORMULA']: $startline = 0; if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } // 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;\\/:@?=+$,%#]|&)+/', '<a href="$0">$0</a>', $result); } $html .= '<span class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'formula">' . $result . '</span>'; // 次の検索用に読み込み if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_BLOCK_START']: $startline = 0; if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } $html .= $code; if ($option['block']) { // outline 表示用開始文字 {, ( $this->beginRegion($num_of_line); // アウトラインが閉じた時に表示する画像を埋め込む場所 $html .= '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '_img" style="display:none;"></span>' . '<span id="' . $this->cont['PLUGIN_CODE_HEADER'] . $this->id_number . _ . $this->blockno . '" class="' . $this->cont['PLUGIN_CODE_HEADER'] . 'block">'; } if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; case $this->cont['PLUGIN_CODE_BLOCK_END']: $startline = 0; if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; } if ($option['block']) { // outline 表示終了文字 }, ) $this->endRegion($num_of_line); $html .= '</span>'; } $html .= $code; if ($str_len == $str_pos) { $code = false; } else { $code = $string[$str_pos++]; } // getc continue 2; } // switch // その他の文字 $result = $spaceHash[$code]; if ($result) { $html .= $result; } else { $startline = 0; if ($str_continue != 0) { $this->endRegion($num_of_line); $html .= '</span>'; $str_continue = 0; } $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); } if (is_null($end)) { $end = substr_count($html, "\n") + $begin - 1; } $html = array('src' => $html, 'number' => '', 'outline' => '', 'blocknum' => $this->blockno); if ($option['outline']) { return $this->makeOutline($html, $option['number'], $end, $begin); } // 最後に改行を削除したため -1 if ($option['number']) { $html['number'] = xpwiki_plugin_code::_plugin_code_makeNumber($end, $begin); } return $html; }
function plugin_pre_init() { parent::plugin_code_init(); }