function parse_indents($text) { global $MaxNesting; static $last_prefix = ''; // Last line's prefix. static $steal = ''; // Stealing the next line? // Locate the indent prefix characters. preg_match('/^([:\\*#]*)(;([^:]*):)?(.*\\n?$)/', $text, $result); if ($result[2] != '') { $result[1] = $result[1] . ';'; } // No list on last line, no list on this line. Bail out: if ($steal == '' && $last_prefix == '' && $result[1] == '') { return $text; } // Common case fast. // Remember lengths of strings. $last_len = strlen($last_prefix); $prefix_len = strlen($result[1]); if ($steal == '') { $text = $result[4]; $fixup = ''; // Loop through and look for prefix characters in common with the // previous line. for ($i = 0; $i < $MaxNesting && ($i < $last_len || $i < $prefix_len); $i++) { // If equal, continue. if ($i < $last_len && $i < $prefix_len && $last_prefix[$i] == $result[1][$i]) { continue; } // If we've gone deeper than the previous line, we're done. if ($i >= $last_len) { break; } // If last line goes further than us, end its dangling lists. if ($i >= $prefix_len || $last_prefix[$i] != $result[1][$i]) { for ($j = $i; $j < $MaxNesting && $j < $last_len; $j++) { $fixup = entity_listitem($last_prefix[$j], 'end') . entity_list($last_prefix[$j], 'end') . $fixup; } break; } } // End the preceding line's list item if we're starting another one // at the same level. if ($i > 0 && $i >= $prefix_len) { $fixup = $fixup . entity_listitem($last_prefix[$i - 1], 'end'); } // Start fresh new lists for this line as needed. // We start all but the last one as *indents* (definition lists) // instead of what they really may appear as, since their function is // really just to indent. for (; $i < $MaxNesting - 1 && $i + 1 < $prefix_len; $i++) { $result[1][$i] = ':'; // Pretend to be an indent. $fixup = $fixup . entity_list(':', 'start') . entity_listitem(':', 'start'); } if ($i < $prefix_len) { $fixup = $fixup . entity_list($result[1][$i], 'start'); } // Start the list *item*. if ($result[2] != '') { $fixup = $fixup . new_entity(array('term_item_start')) . $result[3] . new_entity(array('term_item_end')); } if ($result[1] != '') { $text = entity_listitem(substr($result[1], -1), 'start') . $text; } $text = $fixup . $text; $last_prefix = $result[1]; } if ($steal != '' || $result[1] != '') { // Check if a previous line used a trailing '\' to "steal" us; // i.e., to insert a new line while continuing with the same list item. if ($steal == '') { $steal = substr($result[1], -1); } // Check if *we* have a trailing '\' to "steal" the next line. If not, // end ourselves right here. if (preg_match('/(^|[^\\\\])(\\\\\\\\)*\\\\$/', $text)) { $text = preg_replace('/\\\\$/', "\n", $text); } else { $text = str_replace("\n", '', $text); $steal = ''; } } return $text; }
function view_macro_titlesearch($args) { // Description of TitleSearch macro: // [[TitleSearch {options} search-pattern]] // This macro searches for page-titles matching the searchpattern, // and presents it according to options. The pattern may include // ^ or $ to lock it against start or end, and otherwise it must // only contain alpha-characters (or '/'). The special pattern '*' // matches every title. // // Legal options, capitalized unique prefix: // Class : Sets the class of the list used for results // STyle : Sets the style attribute // Delimiter : Choose delimiter between text entries // Index : Divides the list according to first character, // or first character after value of option // Oneline/List : Indicates to use line/list-markup // // Examples: [[TitleSearch Pages$]] // [[TitleSearch *]] // [[TitleSearch {c=prelist} ^Tavi]] // [[TitleSearch {i=5} ^Tavi]] global $pagestore, $AlphaPtn; // Check for CurlyOptions, and split them preg_match("/^(?:\\s*{([^]]*)})?\\s*(.*)\$/", $args, $arg); $options = split(',', $arg[1]); $search = $arg[2]; // Some defaults $useDelim = ''; // Empty delimiter at the start, changed by options // Parse options foreach ($options as $opt) { list($name, $value) = split('=', $opt); $name = strtolower($name); if (preg_match("/^st/", $name)) { // STyle - Adds a style-attribute $style = $value; $listAttr = "style=\"{$value}\" "; } else { if ($name[0] == 'c') { // Class - Adds a class-attribute $listAttr = "class=\"{$value}\" "; if ($value == "prelist") { $useDelim = ''; } } else { if ($name[0] == 'd') { // Delimiter - Changes the delimiter used $useDelim = $value; } else { if ($name[0] == 'o') { // Oneline - use line-markup $useList = false; } else { if ($name[0] == 'l') { // List - use list-markup $useList = true; } else { if ($name[0] == 'i') { // Index - Use heading to divide index $showIndex = true; $level = 2; if (is_numeric($value)) { $indexCharNo = $value - 1; } else { $indexCharNo = 0; } } } } } } } } // Check for illegal characters to make search pattern safer against exploits if ($search == '*') { // Match every title $pattern = "."; } else { if (!preg_match("/^\\^?(\\/|{$AlphaPtn})+\\\$?\$/", $search)) { // Search can be locked at ^start and/or end$, and elsewise only // contain Alpha-characters, digits not included return "[[TitleSearch {$args}]]"; } else { // $search validates, so use as is $pattern = $search; } } if (!isset($useList) or !$useList) { $useList = false; $useDelim = $useDelim ? $useDelim : ', '; } if ($showIndex) { $lastIndexChar = ''; } else { if ($useList) { $text = entity_list("*", 'start', $listAttr); } } // Loop through all pagetitles $list = $pagestore->allpages(); foreach ($list as $page) { if (preg_match("|{$pattern}|", $page[1])) { if ($showIndex && $lastIndexChar != $page[1][$indexCharNo]) { if ($lastIndexChar != '') { // End previous list if ($useDelim) { $text = preg_replace("/" . preg_quote($useDelim) . "\$/", "\n", $text); } if ($useList) { $text .= entity_list("*", "end"); } } // Add index-header $text .= new_entity(array('head_start', $level)) . substr($page[1], 0, $indexCharNo + 1) . new_entity(array('head_end', $level)); if ($useList) { // Start list again $text .= entity_list("*", 'start', $listAttr); } $lastIndexChar = $page[1][$indexCharNo]; } if ($useList) { $text .= entity_listitem("*", "start"); } $text .= sprintf("%s" . $useDelim, html_ref($page[1], $page[1])); } } if ($useDelim) { $text = preg_replace("/" . preg_quote($useDelim) . "\$/", "\n", $text); } if ($useList) { $text .= entity_list("*", "end"); } return parse_elements($text); }
function parse_indents($text) { global $MaxNesting; static $indentPrevLevel = -1; static $indentPrefixString = ''; static $indentPrevLineIsBlank = 0; static $indentStealLine = 0; static $pending_p = ''; // Indentation increase of more than on level will be corrected to only one. $auto_fix_indent_leap = 1; // this value is boolean // Fix notation for ordered list, changes: // - '[0-9].' to '#' // - '[a-z].' to INDENTS_TYPE_A, i.e. ascii 182 (266 in octal) // - 'ii.' to INDENTS_TYPE_I, i.e. ascii 187 (273 in octal) $text = preg_replace('/^(\\s*)[0-9]{1,2}\\.(.+\\n?$)/', '\\1#\\2', $text); $text = preg_replace('/^(\\s*)[a-z]\\.(.+\\n?$)/', '\\1' . INDENTS_TYPE_A . '\\2', $text); $text = preg_replace('/^(\\s*)ii\\.(.+\\n?$)/', '\\1' . INDENTS_TYPE_I . '\\2', $text); // Fix notation for citation $cite_pattern = '^(\\s*>)+'; if (preg_match("/{$cite_pattern}/", $text, $matches)) { $auto_fix_indent_leap = 0; $cite_blank_line = preg_match("/{$cite_pattern}\\s*\$/", $text); $cite_level = preg_match_all('/>/', $matches[0], $dummy); $text = preg_replace("/{$cite_pattern}/", str_repeat(' ', $cite_level - 1) . '>', $text); if ($cite_blank_line) { $text = preg_replace('/^(.*)$/', '\\1<p>', $text); } } // Locate the indent prefix characters. $matched = preg_match('/^(\\s*)([:\\-\\*#>\\266\\273])([^:\\-\\*#>].*\\n?)$/', $text, $result); if (!$matched) { preg_match('/^(\\s*)(:)(-.*\\n?)$/', $text, $result); } if (array_key_exists(1, $result) && isset($result[1])) { $indentSpaces = $result[1]; } if (array_key_exists(2, $result) && isset($result[2])) { $indentChar = $result[2]; } if (array_key_exists(3, $result) && isset($result[3])) { $indentText = $result[3]; } // No list on last line, no list on this line. Bail out: if ($indentPrevLevel == -1 && !isset($indentChar) && !$indentStealLine) { return $text; } // Common case fast. $isBlankLine = trim($text) == '' ? 1 : 0; if (!$indentStealLine) { if (isset($indentChar)) { if ($auto_fix_indent_leap) { $indentCurLevel = min($indentPrevLevel + 1, strlen($indentSpaces), $MaxNesting); } else { $indentCurLevel = min(strlen($indentSpaces), $MaxNesting); } $fixup = ''; if ($indentCurLevel > $indentPrevLevel) { // add any pending <p> $fixup .= $pending_p; $pending_p = ''; if ($auto_fix_indent_leap) { $fixup .= entity_list($indentChar, 'start'); } else { $fixup .= str_repeat(entity_list($indentChar, 'start'), $indentCurLevel - $indentPrevLevel); } } else { // close previously openend levels, until current level for ($i = $indentPrevLevel; $i > $indentCurLevel; $i--) { $fixup .= entity_listitem($indentPrefixString[$i], 'end') . entity_list($indentPrefixString[$i], 'end'); } // close previous list item $fixup .= entity_listitem($indentPrefixString[$indentCurLevel], 'end'); // if the indent type ([:#-*]) is different from previous at same level if ($indentPrefixString[$indentCurLevel] != $indentChar) { $fixup .= entity_list($indentPrefixString[$indentCurLevel], 'end'); } // add any pending <p> $fixup .= $pending_p; $pending_p = ''; // if the indent type ([:#-*]) is different from previous at same level if ($indentPrefixString[$indentCurLevel] != $indentChar) { $fixup .= entity_list($indentChar, 'start'); } } // open new list item $fixup .= entity_listitem($indentChar, 'start'); $text = $fixup . $indentText; if ($auto_fix_indent_leap || $indentCurLevel <= $indentPrevLevel) { $indentPrefixString = substr($indentPrefixString, 0, $indentCurLevel) . $indentChar; } else { $indentPrefixString = substr($indentPrefixString, 0, $indentPrevLevel + 1) . str_repeat($indentChar, $indentCurLevel - $indentPrevLevel); } $indentPrevLevel = $indentCurLevel; } else { // Note: Every parsing functions is called at the end of the page with // an empty string, i.e. without a carriage return, since some stateful // parsers need to perform final processing. This is the case for // indents and this is why $text is tested against ''. if ($isBlankLine && $text != '') { $text = $indentPrevLineIsBlank ? '' : '<p>'; } else { if ($indentPrevLineIsBlank || $text == '') { // Check if there's leading spaces, telling to stay in the same // list item but to start a new paragraph. // If no leading spaces, the indents are completely closed. $i = 0; // just in case... if (preg_match('/^\\s*/', $text, $leadingSpaces)) { $i = strlen($leadingSpaces[0]); } if ($i < strlen($indentPrefixString)) { // We're at a lower nesting level, end dangling lists up // to the nesting level specified by the leading spaces // and add any pending <p> after the last list end. for ($j = $i; $j < strlen($indentPrefixString); $j++) { $text = entity_listitem($indentPrefixString[$j], 'end') . entity_list($indentPrefixString[$j], 'end') . $pending_p . $text; $pending_p = ''; } $indentPrefixString = substr($indentPrefixString, 0, $i); $indentPrevLevel = $i - 1; } else { // $indentChar is set here to keep the line stealing working $indentChar = ' '; $text = $pending_p . $text; $pending_p = ''; } } else { $text = ' ' . trim($text); $indentChar = ' '; // same as above } } } } $indentPrevLineIsBlank = $isBlankLine; // Check if *we* have a trailing '\' to "steal" the next line. if (isset($indentChar) || isset($indentStealLine)) { if (preg_match('/(^|[^\\\\])(\\\\\\\\)*\\\\$/', $text)) { $text = preg_replace('/\\\\$/', "\n", $text); $indentStealLine = 1; } else { $indentStealLine = 0; } } // holds any single <p> and prints it later, see above if ($text == '<p>') { $pending_p = $text; $text = ''; } return $text; }