source = $source; $this->language = $language; $this->language_path = ( substr($path, strlen($path) - 1, 1) == '/' ) ? $path : $path . '/'; $this->load_language(); } // // Error methods // /** * method: error * ------------- * Returns an error message associated with the last GeSHi operation */ function error() { return $this->error; } // // Setters // /** * method: set_source * ------------------ * Sets the source code for this object */ function set_source ( $source ) { $this->source = $source; } /** * method: set_language * -------------------- * Sets the language for this object */ function set_language ( $language ) { $this->language = $language; // Load the language for parsing $this->load_language(); } /** * method: set_language_path * ------------------------- * Sets the path to the directory containing the language files. NOTE * that this path is relative to the directory of the script that included * geshi.php, NOT geshi.php itself. */ function set_language_path ( $path ) { $this->language_path = ( substr($path, strlen($path) - 1, 1) == '/' ) ? $path : $path . '/'; } /** * method: set_header_type * ----------------------- * Sets the type of header to be used. If GESHI_HEADER_DIV is used, * the code is surrounded in a
is used - less source, but less control. Default * is GESHI_HEADER_PRE */ function set_header_type ( $type ) { $this->header_type = $type; } /** * method: set_overall_style * ------------------------- * Sets the styles for the code that will be outputted * when this object is parsed. The style should be a * string of valid stylesheet declarations */ function set_overall_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->overall_style .= $style; } else { $this->overall_style = $style; } } /** * method: set_overall_class * ------------------------- * Sets the overall classname for this block of code. This * class can then be used in a stylesheet to style this object's * output */ function set_overall_class ( $class ) { $this->overall_class = $class; } /** * method: set_overall_id * ---------------------- * Sets the overall id for this block of code. This id can then * be used in a stylesheet to style this object's output */ function set_overall_id ( $id ) { $this->overall_id = $id; } /** * method: enable_classes * ---------------------- * Sets whether CSS classes should be used to highlight the source. Default * is off, calling this method with no arguments will turn it on */ function enable_classes ( $flag = true ) { $this->use_classes = ( $flag ) ? true : false; } /** * method: set_line_style * ---------------------- * Sets the styles for the line numbers. This should be a string * containing valid stylesheet declarations. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_line_style ( $style1, $style2 = '', $preserve_defaults = false ) { if ( is_bool($style2) ) { $preserve_defaults = $style2; $style2 = ''; } if ( $preserve_defaults ) { $this->line_style1 .= $style1; $this->line_style2 .= $style2; } else { $this->line_style1 = $style1; $this->line_style2 = $style2; } } /** * method: enable_line_numbers * --------------------------- * Sets whether line numbers should be displayed. False = not displayed, * 1 = displayed, 2 = every nth line a different class. Default is for * no line numbers to be used */ function enable_line_numbers ( $flag, $nth_row = 5 ) { $this->line_numbers = $flag; $this->line_nth_row = $nth_row; } /** * method: set_keyword_group_style * ------------------------------- * Sets the style for a keyword group. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_keyword_group_style ( $key, $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['KEYWORDS'][$key] .= $style; } else { $this->language_data['STYLES']['KEYWORDS'][$key] = $style; } } /** * method: set_keyword_group_highlighting * -------------------------------------- * Turns highlighting on/off for a keyword group */ function set_keyword_group_highlighting ( $key, $flag = true ) { $this->lexic_permissions['KEYWORDS'][$key] = ( $flag ) ? true : false; } /** * method: set_comments_style * -------------------------- * Sets the styles for comment groups. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_comments_style ( $key, $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['COMMENTS'][$key] .= $style; } else { $this->language_data['STYLES']['COMMENTS'][$key] = $style; } } /** * method: set_comments_highlighting * --------------------------------- * Turns highlighting on/off for comment groups */ function set_comments_highlighting ( $key, $flag = true ) { $this->lexic_permissions['COMMENTS'][$key] = ( $flag ) ? true : false; } /** * method: set_escape_characters_style * ----------------------------------- * Sets the styles for escaped characters. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_escape_characters_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['ESCAPE_CHAR'][0] .= $style; } else { $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style; } } /** * method: set_escape_characters_highlighting * ------------------------------------------ * Turns highlighting on/off for escaped characters */ function set_escape_characters_highlighting ( $flag = true ) { $this->lexic_permissions['ESCAPE_CHAR'] = ( $flag ) ? true : false; } /** * method: set_brackets_style * -------------------------- * Sets the styles for brackets. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority * * This method is DEPRECATED: use set_symbols_style instead */ function set_brackets_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['BRACKETS'][0] .= $style; } else { $this->language_data['STYLES']['BRACKETS'][0] = $style; } } /** * method: set_brackets_highlighting * --------------------------------- * Turns highlighting on/off for brackets * * This method is DEPRECATED: use set_symbols_highlighting instead */ function set_brackets_highlighting ( $flag ) { $this->lexic_permissions['BRACKETS'] = ( $flag ) ? true : false; } /** * method: set_symbols_style * -------------------------- * Sets the styles for symbols. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_symbols_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['SYMBOLS'][0] .= $style; } else { $this->language_data['STYLES']['SYMBOLS'][0] = $style; } // For backward compatibility $this->set_brackets_style ( $style, $preserve_defaults ); } /** * method: set_symbols_highlighting * --------------------------------- * Turns highlighting on/off for symbols */ function set_symbols_highlighting ( $flag ) { $this->lexic_permissions['SYMBOLS'] = ( $flag ) ? true : false; // For backward compatibility $this->set_brackets_highlighting ( $flag ); } /** * method: set_strings_style * ------------------------- * Sets the styles for strings. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_strings_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['STRINGS'][0] .= $style; } else { $this->language_data['STYLES']['STRINGS'][0] = $style; } } /** * method: set_strings_highlighting * -------------------------------- * Turns highlighting on/off for strings */ function set_strings_highlighting ( $flag ) { $this->lexic_permissions['STRINGS'] = ( $flag ) ? true : false; } /** * method: set_numbers_style * ------------------------- * Sets the styles for numbers. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_numbers_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['NUMBERS'][0] .= $style; } else { $this->language_data['STYLES']['NUMBERS'][0] = $style; } } /** * method: set_numbers_highlighting * -------------------------------- * Turns highlighting on/off for numbers */ function set_numbers_highlighting ( $flag ) { $this->lexic_permissions['NUMBERS'] = ( $flag ) ? true : false; } /** * method: set_methods_style * ------------------------- * Sets the styles for methods. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_methods_style ( $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['METHODS'][0] .= $style; } else { $this->language_data['STYLES']['METHODS'][0] = $style; } } /** * method: set_methods_highlighting * -------------------------------- * Turns highlighting on/off for methods */ function set_methods_highlighting ( $flag ) { $this->lexic_permissions['METHODS'] = ( $flag ) ? true : false; } /** * method: set_regexps_style * ------------------------- * Sets the styles for regexps. If $preserve_defaults is * true, then styles are merged with the default styles, with the * user defined styles having priority */ function set_regexps_style ( $key, $style, $preserve_defaults = false ) { if ( $preserve_defaults ) { $this->language_data['STYLES']['REGEXPS'][$key] .= $style; } else { $this->language_data['STYLES']['REGEXPS'][$key] = $style; } } /** * method: set_regexps_highlighting * -------------------------------- * Turns highlighting on/off for regexps */ function set_regexps_highlighting ( $key, $flag ) { $this->lexic_permissions['REGEXPS'][$key] = ( $flag ) ? true : false; } /** * method: set_case_sensitivity * ---------------------------- * Sets whether a set of keywords are checked for in a case sensitive manner */ function set_case_sensitivity ( $key, $case ) { $this->language_data['CASE_SENSITIVE'][$key] = ( $case ) ? true : false; } /** * method: set_case_keywords * ------------------------- * Sets the case that keywords should use when found. Use the constants: * GESHI_CAPS_NO_CHANGE: leave keywords as-is * GESHI_CAPS_UPPER: convert all keywords to uppercase where found * GESHI_CAPS_LOWER: convert all keywords to lowercase where found * Method added in 1.0.1 */ function set_case_keywords ( $case ) { $this->language_data['CASE_KEYWORDS'] = $case; } /** * method: set_tab_width * --------------------- * Sets how many spaces a tab is substituted for * This method will probably be re-engineered later to allow customisability * in the maximum and minimum number of tabs without mutulating data fields. */ function set_tab_width ( $width ) { if ( $width > $this->max_tabs ) $width = $this->max_tabs; if ( $width < $this->min_tabs ) $width = $this->min_tabs; $this->tab_width = $width; } /** * method: enable_strict_mode * -------------------------- * Enables/disables strict highlighting. Default is off, calling this * method without parameters will turn it on. See documentation * for more details on strict mode and where to use it */ function enable_strict_mode ( $mode = true ) { $this->strict_mode = ( $mode ) ? true : false; // Turn on strict mode no matter what if language should always // be in strict mode if ( $this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS ) { $this->strict_mode = true; } // Turn off strict mode no matter what if language should never // be in strict mode elseif ( $this->language_data['STRICT_MODE_APPLIES'] == GESHI_NEVER ) { $this->strict_mode = false; } } /** * method: disable_highlighting * ---------------------------- * Disables all highlighting */ function disable_highlighting () { foreach ( $this->language_data['KEYWORDS'] as $key => $words ) { $this->lexic_permissions['KEYWORDS'][$key] = false; } foreach ( $this->language_data['COMMENT_SINGLE'] as $key => $comment ) { $this->lexic_permissions['COMMENTS'][$key] = false; } // Multiline comments $this->lexic_permissions['COMMENTS']['MULTI'] = false; // Escape characters $this->lexic_permissions['ESCAPE_CHAR'] = false; // Brackets $this->lexic_permissions['BRACKETS'] = false; // Strings $this->lexic_permissions['STRINGS'] = false; // Numbers $this->lexic_permissions['NUMBERS'] = false; // Methods $this->lexic_permissions['METHODS'] = false; // Symbols $this->lexic_permissions['SYMBOLS'] = false; // Regexps foreach ( $this->language_data['REGEXPS'] as $key => $regexp ) { $this->lexic_permissions['REGEXPS'][$key] = false; } } /** * method: enable_highlighting * --------------------------- * Enables all highlighting */ function enable_highlighting () { foreach ( $this->language_data['KEYWORDS'] as $key => $words ) { $this->lexic_permissions['KEYWORDS'][$key] = true; } foreach ( $this->language_data['COMMENT_SINGLE'] as $key => $comment ) { $this->lexic_permissions['COMMENTS'][$key] = true; } // Multiline comments $this->lexic_permissions['COMMENTS']['MULTI'] = true; // Escape characters $this->lexic_permissions['ESCAPE_CHAR'] = true; // Brackets $this->lexic_permissions['BRACKETS'] = true; // Strings $this->lexic_permissions['STRINGS'] = true; // Numbers $this->lexic_permissions['NUMBERS'] = true; // Methods $this->lexic_permissions['METHODS'] = true; // Symbols $this->lexic_permissions['SYMBOLS'] = true; // Regexps foreach ( $this->language_data['REGEXPS'] as $key => $regexp ) { $this->lexic_permissions['REGEXPS'][$key] = true; } } /** * method: add_keyword * ------------------- * Adds a keyword to a keyword group for highlighting */ function add_keyword( $key, $word ) { $this->language_data['KEYWORDS'][$key][] = $word; } /** * method: remove_keyword * ---------------------- * Removes a keyword from a keyword group */ function remove_keyword ( $key, $word ) { $this->language_data['KEYWORDS'][$key] = array_diff($this->language_data['KEYWORDS'][$key], array($word)); } /** * method: add_keyword_group * ------------------------- * Creates a new keyword group */ function add_keyword_group ( $key, $styles, $case_sensitive = true, $words = array() ) { if ( !is_array($words) ) { $words = array($words); } $this->language_data['KEYWORDS'][$key] = $words; $this->lexic_permissions['KEYWORDS'][$key] = true; $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive; $this->language_data['STYLES']['KEYWORDS'][$key] = $styles; } /** * method: remove_keyword_group * ---------------------------- * Removes a keyword group */ function remove_keyword_group ( $key ) { unset($this->language_data['KEYWORDS'][$key]); unset($this->lexic_permissions['KEYWORDS'][$key]); unset($this->language_data['CASE_SENSITIVE'][$key]); unset($this->language_data['STYLES']['KEYWORDS'][$key]); } /** * method: parse_code() * -------------------- * Returns the code in $this->source, highlighted and surrounded by the * nessecary HTML. This should only be called ONCE, cos it's SLOW! * If you want to highlight the same source multiple times, you're better * off doing a whole lot of str_replaces to replace the s */ function parse_code() { // Firstly, if there is an error, we won't highlight // FUTURE: maybe an option to try to force highlighting anyway? if ( $this->error ) { $result = $this->header(); if ( $this->header_type != GESHI_HEADER_PRE ) { $result .= $this->indent(htmlentities($this->source)); } else { $result .= htmlentities($this->source); } return $result . $this->footer(); } // Add a space for regular expression matching and line numbers $code = ' ' . $this->source; // Replace all newlines to a common form. $code = str_replace("\r\n", "\n", $code); $code = str_replace("\r", "\n", $code); // Initialise various stuff $length = strlen($code); $STRING_OPEN = ''; $CLOSE_STRING = false; $ESCAPE_CHAR_OPEN = false; $COMMENT_MATCHED = false; // Turn highlighting on if strict mode doesn't apply to this language $HIGHLIGHTING_ON = ( $this->strict_mode ) ? '' : true; // Whether to highlight inside a block of code $HIGHLIGHT_INSIDE_STRICT = false; $stuff_to_parse = ''; $result = ''; if ( $this->strict_mode ) { // Break the source into bits. Each bit will be a portion of the code // within script delimiters - for example, HTML between < and > $parts = array(0 => array(0 => '')); $k = 0; for ( $i = 0; $i < $length; $i++ ) { $char = substr($code, $i, 1); if ( !$HIGHLIGHTING_ON ) { foreach ( $this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters ) { foreach ( $delimiters as $open => $close ) { // Get the next little bit for this opening string $check = substr($code, $i, strlen($open)); // If it matches... if ( $check == $open ) { // We start a new block with the highlightable // code in it $HIGHLIGHTING_ON = $open; $i += strlen($open) - 1; ++$k; $char = $open; $parts[$k][0] = $char; // No point going around again... break(2); } } } } else { foreach ( $this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters ) { foreach ( $delimiters as $open => $close ) { if ( $open == $HIGHLIGHTING_ON ) { // Found the closing tag break(2); } } } // We check code from our current position BACKWARDS. This is so // the ending string for highlighting can be included in the block $check = substr($code, $i - strlen($close) + 1, strlen($close)); if ( $check == $close ) { $HIGHLIGHTING_ON = ''; // Add the string to the rest of the string for this part $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; ++$k; $parts[$k][0] = ''; $char = ''; } } $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; } $HIGHLIGHTING_ON = ''; } else { // Not strict mode - simply dump the source into // the array at index 1 (the first highlightable block) $parts = array( 1 => array( 0 => '', 1 => $code ) ); } // Now we go through each part. We know that even-indexed parts are // code that shouldn't be highlighted, and odd-indexed parts should // be highlighted foreach ( $parts as $key => $data ) { $part = $data[1]; // If this block should be highlighted... if ( $key % 2 ) { if ( $this->strict_mode ) { // Find the class key for this block of code foreach ( $this->language_data['SCRIPT_DELIMITERS'] as $script_key => $script_data ) { foreach ( $script_data as $open => $close ) { if ( $data[0] == $open ) { break(2); } } } if ( $this->language_data['STYLES']['SCRIPT'][$script_key] != '' ) { // Add a span element around the source to // highlight the overall source block if ( !$this->use_classes && $this->language_data['STYLES']['SCRIPT'][$script_key] != '' ) { $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"'; } else { $attributes = ' class="sc' . $script_key . '"'; } $result .= ""; } } if ( !$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key] ) { // Now, highlight the code in this block. This code // is really the engine of GeSHi (along with the method // parse_non_string_part). $length = strlen($part); for ( $i = 0; $i < $length; $i++ ) { // Get the next char $char = substr($part, $i, 1); // Is this a match of a string delimiter? if ( $char == $STRING_OPEN ) { if ( ($this->lexic_permissions['ESCAPE_CHAR'] && $ESCAPE_CHAR_OPEN) || ($this->lexic_permissions['STRINGS'] && !$ESCAPE_CHAR_OPEN) ) { $char .= ''; } if ( !$ESCAPE_CHAR_OPEN ) { $STRING_OPEN = ''; $CLOSE_STRING = true; } $ESCAPE_CHAR_OPEN = false; } // Is this the start of a new string? elseif ( in_array( $char, $this->language_data['QUOTEMARKS'] ) && ($STRING_OPEN == '') ) { $STRING_OPEN = $char; if ( $this->lexic_permissions['STRINGS'] ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; } else { $attributes = ' class="st0"'; } $char = "" . $char; } $result .= $this->parse_non_string_part( $stuff_to_parse ); $stuff_to_parse = ''; } // Is this an escape char? elseif ( ($char == $this->language_data['ESCAPE_CHAR']) && ($STRING_OPEN != '') ) { if ( !$ESCAPE_CHAR_OPEN ) { $ESCAPE_CHAR_OPEN = true; if ( $this->lexic_permissions['ESCAPE_CHAR'] ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"'; } else { $attributes = ' class="es0"'; } $char = "" . $char; } } else { $ESCAPE_CHAR_OPEN = false; if ( $this->lexic_permissions['ESCAPE_CHAR'] ) { $char .= ''; } } } elseif ( $ESCAPE_CHAR_OPEN ) { if ( $this->lexic_permissions['ESCAPE_CHAR'] ) { $char .= ''; } $ESCAPE_CHAR_OPEN = false; $test_str = $char; } elseif ( $STRING_OPEN == '' ) { // Is this a single line comment? foreach ( $this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark ) { $com_len = strlen($comment_mark); $test_str = substr( $part, $i, $com_len ); if ( $this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] ) { $match = ( $comment_mark == $test_str ); } else { $match = ( strtolower($comment_mark) == strtolower($test_str) ); } if ( $match ) { $COMMENT_MATCHED = true; if ( $this->lexic_permissions['COMMENTS'][$comment_key] ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment_key] . '"'; } else { $attributes = ' class="co' . $comment_key . '"'; } $test_str = "" . htmlentities($this->change_case($test_str)); } else { $test_str = htmlentities($test_str); } $close_pos = strpos( $part, "\n", $i ); if ( $close_pos === false ) { $close_pos = strlen($part); } $test_str .= htmlentities(substr($part, $i + $com_len, $close_pos - $i - $com_len)); if ( $this->lexic_permissions['COMMENTS'][$comment_key] ) { $test_str .= ""; } $test_str .= "\n"; $i = $close_pos; // parse the rest $result .= $this->parse_non_string_part( $stuff_to_parse ); $stuff_to_parse = ''; break; } } // Otherwise, is this a multiline comment? if ( !$COMMENT_MATCHED ) { foreach ( $this->language_data['COMMENT_MULTI'] as $open => $close ) { $com_len = strlen($open); $test_str = substr( $part, $i, $com_len ); if ( $open == $test_str ) { $COMMENT_MATCHED = true; if ( $this->lexic_permissions['COMMENTS']['MULTI'] ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS']['MULTI'] . '"'; } else { $attributes = ' class="coMULTI"'; } $test_str = "" . htmlentities($test_str); } else { $test_str = htmlentities($test_str); } $close_pos = strpos( $part, $close, $i + strlen($close) ); if ( $close_pos === false ) { $close_pos = strlen($part); } $test_str .= htmlentities(substr($part, $i + $com_len, $close_pos - $i)); if ( $this->lexic_permissions['COMMENTS']['MULTI'] ) { $test_str .= ''; } $i = $close_pos + $com_len - 1; // parse the rest $result .= $this->parse_non_string_part( $stuff_to_parse ); $stuff_to_parse = ''; break; } } } } // Otherwise, convert it to HTML form elseif ( $STRING_OPEN != '' ) { $char = htmlentities($char); } // Where are we adding this char? if ( !$COMMENT_MATCHED ) { if ( ($STRING_OPEN == '') && !$CLOSE_STRING ) { $stuff_to_parse .= $char; } else { $result .= $char; $CLOSE_STRING = false; } } else { $result .= $test_str; $COMMENT_MATCHED = false; } } // Parse the last bit $result .= $this->parse_non_string_part( $stuff_to_parse ); $stuff_to_parse = ''; } else { $result .= htmlentities($part); } // Close the that surrounds the block if ( $this->strict_mode ) { $result .= ''; } } // Else not a block to highlight else { $result .= htmlentities($part); } } // Parse the last stuff (redundant?) $result .= $this->parse_non_string_part( $stuff_to_parse ); // Lop off the very first space $result = substr($result, 1); // Replace \n with correct endline // I'm removing this in 1.0.1, any complaints and I'll // put it back in //$result = str_replace("\n", "\r\n", $result); // Indentation if ( $this->header_type != GESHI_HEADER_PRE ) { $result = $this->indent($result); } // Add line numbers if ( $this->line_numbers != GESHI_NO_LINE_NUMBERS ) { $result = $this->add_line_numbers($result); } return $this->header() . $result . $this->footer(); } /** * method: indent * -------------- * Swaps out spaces and tabs for HTML indentation. Not needed if * the code is in a pre block... */ function indent ( $result ) { $result = str_replace(' ', ' ', $result); $result = str_replace(' ', ' ', $result); $result = str_replace("\n ", "\n ", $result); $result = str_replace("\t", $this->get_tab_replacement(), $result); $result = nl2br($result); return $result; } /** * method: change_case * ------------------- * Changes the case of a keyword for those languages where a change is asked for */ function change_case ( $instr ) { if ( $this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_UPPER ) { return strtoupper($instr); } elseif ( $this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_LOWER ) { return strtolower($instr); } return $instr; } function parse_non_string_part ( &$stuff_to_parse ) { $stuff_to_parse = ' ' . quotemeta(htmlentities($stuff_to_parse)); // This var will disappear in the future $func = '$this->change_case'; // // Regular expressions // foreach ( $this->language_data['REGEXPS'] as $key => $regexp ) { if ( $this->lexic_permissions['REGEXPS'][$key] ) { $stuff_to_parse = preg_replace( "#(" . $regexp . ")#", "<|!REG3XP$key!>\\1|>", $stuff_to_parse); } } // // Highlight numbers. This regexp sucks... anyone with a regexp that WORKS // here wins a cookie if they send it to me. At the moment there's two doing // almost exactly the same thing, except the second one prevents a number // being highlighted twice (eg\n"; } /** * method: get_stylesheet * ---------------------- * Returns a stylesheet for the highlighted code. If $economy mode * is true, we only return the stylesheet declarations that matter for * this code block instead of the whole thing */ function get_stylesheet ( $economy_mode = true ) { // If there's an error, chances are that the language file // won't have populated the language data file, so we can't // risk getting a stylesheet... if ( $this->error ) { return ''; } // First, work out what the selector should be. If there's an ID, // that should be used, the same for a class. Otherwise, a selector // of '' means that these styles will be applied anywhere $selector = ( $this->overall_id != '' ) ? "#{$this->overall_id} " : ''; $selector = ( $selector == '' && $this->overall_class != '' ) ? ".{$this->overall_class} " : $selector; // Header of the stylesheet if ( !$economy_mode ) { $stylesheet = "/** * GeSHi Dynamically Generated Stylesheet * -------------------------------------- * Dynamically generated stylesheet for {$this->language} * CSS class: {$this->overall_class}, CSS id: {$this->overall_id} * GeSHi (c) Oracle 2004 (http://qbnz.com/highlighter) */\n"; } else { $stylesheet = '/* GeSHi (c) Oracle 2004 (http://qbnz.com/highlighter) */' . "\r\n"; } if ( $this->overall_class != '' && $this->overall_style != '' && $this->overall_id == '' ) { $stylesheet .= ".{$this->overall_class} { {$this->overall_style} }\n"; } if ( $this->overall_id != '' && $this->overall_style != '' ) { $stylesheet .= "#{$this->overall_id} { {$this->overall_style} }\n"; } if ( !$economy_mode || ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->line_style1 != '') ) { $stylesheet .= "$selector.li1 { {$this->line_style1} }\n"; } if ( !$economy_mode || ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $this->line_style2 != '') ) { $stylesheet .= "$selector.li2 { {$this->line_style2} }\n"; } foreach ( $this->language_data['STYLES']['KEYWORDS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && (!$this->lexic_permissions['KEYWORDS'][$group] || $styles == '')) ) { $stylesheet .= "$selector.kw$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['COMMENTS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['COMMENTS'][$group]) ) { $stylesheet .= "$selector.co$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['ESCAPE_CHAR'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['ESCAPE_CHAR']) ) { $stylesheet .= "$selector.es$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['SYMBOLS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['BRACKETS']) ) { $stylesheet .= "$selector.br$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['STRINGS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['STRINGS']) ) { $stylesheet .= "$selector.st$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['NUMBERS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['NUMBERS']) ) { $stylesheet .= "$selector.nu$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['METHODS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['METHODS']) ) { $stylesheet .= "$selector.me$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['SCRIPT'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') /*&& !($economy_mode && !$this->lexic_permissions['SCRIPT'])*/ ) { $stylesheet .= "$selector.sc$group { $styles }\n"; } } foreach ( $this->language_data['STYLES']['REGEXPS'] as $group => $styles ) { if ( !$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && !$this->lexic_permissions['REGEXPS'][$group]) ) { $stylesheet .= "$selector.re$group { $styles }\n"; } } return $stylesheet; } } // end class GeSHi ?>) // Put /NUM!/ in for the styles, which gets replaced at the end. // if ( $this->lexic_permissions['NUMBERS'] && preg_match("#[0-9]#", $stuff_to_parse ) ) { $stuff_to_parse = preg_replace("#([^a-zA-Z0-9\#])([0-9]+)([^a-zA-Z0-9])#", "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse); $stuff_to_parse = preg_replace("#([^a-zA-Z0-9\#>])([0-9]+)([^a-zA-Z0-9])#", "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse); } // Highlight keywords // if there is a couple of alpha symbols there *might* be a keyword if ( preg_match("#[a-zA-Z]{2,}#", $stuff_to_parse) ) { foreach ( $this->language_data['KEYWORDS'] as $k => $keywordset ) { if ( $this->lexic_permissions['KEYWORDS'][$k] ) { foreach ( $keywordset as $keyword ) { $keyword = quotemeta($keyword); // // This replacement checks the word is on it's own (except if brackets etc // are next to it), then highlights it. We don't put the color=" for the span // in just yet - otherwise languages with the keywords "color" or "or" have // a fit. // if ( false !== stristr($stuff_to_parse, $keyword ) ) { $stuff_to_parse .= ' '; // Might make a more unique string for putting the number in soon // Basically, we don't put the styles in yet because then the styles themselves will // get highlighted if the language has a CSS keyword in it (like CSS, for example ;)) $styles = "/$k/"; $keyword = quotemeta($keyword); if ( $this->language_data['CASE_SENSITIVE'][$k] ) { $stuff_to_parse = preg_replace("#([^a-zA-Z0-9\$_\|\.\#;>])($keyword)([^a-zA-Z0-9_<\|%\-&])#e", "'\\1<|$styles>' . $func('\\2') . '|>\\3'", $stuff_to_parse); } else { // Change the case of the word. $stuff_to_parse = preg_replace("#([^a-zA-Z0-9\$_\|\.\#;>])($keyword)([^a-zA-Z0-9_<\|%\-&])#ie", "'\\1<|$styles>' . $func('\\2') . '|>\\3'", $stuff_to_parse); } $stuff_to_parse = substr($stuff_to_parse, 0, strlen($stuff_to_parse) - 1); } } } } } // // Now that's all done, replace /[number]/ with the correct styles // foreach ( $this->language_data['KEYWORDS'] as $k => $kws ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['KEYWORDS'][$k] . '"'; } else { $attributes = ' class="kw' . $k . '"'; } $stuff_to_parse = str_replace("/$k/", $attributes, $stuff_to_parse); } // Put number styles in if ( !$this->use_classes && $this->lexic_permissions['NUMBERS'] ) { $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][0] . '"'; } else { $attributes = ' class="nu0"'; } $stuff_to_parse = str_replace('/NUM!/', $attributes, $stuff_to_parse); // // Highlight methods and fields in objects // if ( $this->lexic_permissions['METHODS'] && $this->language_data['OOLANG'] && (false !== stristr($stuff_to_parse, $this->language_data['OBJECT_SPLITTER'])) ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][0] . '"'; } else { $attributes = ' class="me0"'; } $stuff_to_parse = preg_replace("#(" . quotemeta($this->language_data['OBJECT_SPLITTER']) . "[\s]*)([a-zA-Z\*\(][a-zA-Z0-9_\*]*)#", "\\1<|$attributes>\\2|>", $stuff_to_parse); } // // Highlight brackets. Yes, I've tried adding a semi-colon to this list. // You try it, and see what happens ;) // TODO: Fix lexic permissions not converting entities if shouldn't // be highlighting regardless // if ( $this->lexic_permissions['BRACKETS'] ) { $code_entities_match = array('[', ']', '(', ')', '{', '}'); if ( !$this->use_classes ) { $code_entities_replace = array( '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">[|>', '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">]|>', '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">(|>', '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">)|>', '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">{|>', '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">}|>', ); } else { $code_entities_replace = array( '<| class="br0">[|>', '<| class="br0">]|>', '<| class="br0">(|>', '<| class="br0">)|>', '<| class="br0">{|>', '<| class="br0">}|>', ); } $stuff_to_parse = str_replace( $code_entities_match, $code_entities_replace, $stuff_to_parse ); } // // Add class/style for regexps // foreach ( $this->language_data['REGEXPS'] as $key => $regexp ) { if ( $this->lexic_permissions['REGEXPS'][$key] ) { if ( !$this->use_classes ) { $attributes = ' style="' . $this->language_data['STYLES']['REGEXPS'][$key] . '"'; } else { $attributes = ' class="re' . $key . '"'; } $stuff_to_parse = str_replace("!REG3XP$key!", $attributes, $stuff_to_parse); } } // // NOW we add the span thingy ;) // $stuff_to_parse = str_replace("<|", "", '', $stuff_to_parse ); return substr(stripslashes($stuff_to_parse), 1); } /** * method: load_language * --------------------- * Gets language information and stores it for later use */ function load_language () { if ( !file_exists($this->language_path . $this->language . '.php') ) { $this->error = GESHI_ERROR_NO_SUCH_LANG; return; } include($this->language_path . $this->language.'.php'); // Perhaps some checking might be added here later to check that // $language data is a valid thing but maybe not $this->language_data = $language_data; // Set strict mode if should be set if ( $this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS ) { $this->strict_mode = true; } // Set permissions for all lexics to true // so they'll be highlighted by default $this->enable_highlighting(); // Set default class for CSS $this->overall_class = $this->language; } /** * method: get_tab_replacement * --------------------------- * Gets the replacement string for tabs in the source code. Useful for * HTML highlighting, where tabs don't mean anything to a browser. */ function get_tab_replacement () { $i = 0; $result = ''; while ( $i < $this->tab_width ) { $i++; if ( $i % 2 == 0 ) { $result .= ' '; } else { $result .= ' '; } } return $result; } /** * method: add_line_numbers * ------------------------ * Adds line numbers to source. Parameter to be removed later */ function add_line_numbers ( $source ) { $line_container1 = 'use_classes ) { $line_container1 .= " class=\"li1\""; } elseif ( $this->line_style1 != '' ) { $line_container1 .= " style=\"{$this->line_style1}\""; } $line_container1 .= '>%s%s%s%s'; if ( $this->line_numbers == GESHI_FANCY_LINE_NUMBERS ) { $line_container2 = 'use_classes ) { $line_container2 .= " class=\"li2\""; } elseif ( $this->line_style2 != '' ) { $line_container2 .= " style=\"{$this->line_style2}\""; } $line_container2 .= '>%s%s%s%s'; } // Has to be \r\n if I add \r back in later... $lines = explode("\n", $source); $LINES = count($lines); $length = strlen($LINES); // Create a string with non-breaking spaces in it to indent code // from the line numbers. // TODO: Able to specify number of spaces extra for $indentation? $spaces = ''; for ( $i = 0; $i < $length; $i++ ) { $spaces .= ' '; } $indentation = ' '; // The value used to decide whether the second class is used $stopper = $this->line_nth_row - 1; // Put the line numbers into each line. for ( $i = 0; $i < $LINES; $i++ ) { if ( $this->line_numbers == GESHI_FANCY_LINE_NUMBERS ) { $container = ( $i % $this->line_nth_row != $stopper ) ? 'line_container1' : 'line_container2'; } else { $container = 'line_container1'; } $lines[$i] = sprintf($$container, $i + 1, substr($spaces, 0, ($length - strlen($i + 1)) * 6), $indentation, $lines[$i] ); } return implode("\n", $lines); } /** * method: header * -------------- * Creates the header for the code block (with correct attributes) */ function header () { $attributes = ''; if ( $this->overall_class != '' && $this->use_classes ) { $attributes .= " class=\"{$this->overall_class}\""; } if ( $this->overall_id != '' ) { $attributes .= " id=\"{$this->overall_id}\""; } if ( $this->overall_style != '' && !$this->use_classes ) { $attributes .= ' style="' . $this->overall_style . '"'; } if ( $this->header_type == GESHI_HEADER_DIV ) { return " 5 "; } return "\n"; } return ""; } /** * method: footer * -------------- * Returns the footer for the code block */ function footer () { if ( $this->header_type == GESHI_HEADER_DIV ) { return "