# Source for file geshi.php

Documentation is available at geshi.php

    1     <?php
    2     /**
    3      * GeSHi - Generic Syntax Highlighter
    4      *
    5      * The GeSHi class for Generic Syntax Highlighting. Please refer to the documentation
    6      * at http://qbnz.com/highlighter/documentation.php for more information about how to
    7      * use this class.
    8      *
    9      * For changes, release notes, TODOs etc, see the relevant files in the docs/ directory
    10     *
    11     *   This file is part of GeSHi.
    12     *
    13     *  GeSHi is free software; you can redistribute it and/or modify
    14     *  it under the terms of the GNU General Public License as published by
    15     *  the Free Software Foundation; either version 2 of the License, or
    16     *  (at your option) any later version.
    17     *
    18     *  GeSHi is distributed in the hope that it will be useful,
    19     *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    20     *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    21     *  GNU General Public License for more details.
    22     *
    23     *  You should have received a copy of the GNU General Public License
    24     *  along with GeSHi; if not, write to the Free Software
    25     *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    26     *
    27     * @package   core
    28     * @author    Nigel McNie <nigel@geshi.org>
    29     * @copyright Copyright &copy; 2004, 2005, Nigel McNie
    30     * @license   http://gnu.org/copyleft/gpl.html GNU GPL
    31     * @version   $Id: fsource_core__geshi.php.html,v 1.1 2006/01/23 09:27:28 wangcs Exp $
    32     *
    33     */
    34
    35    //
    36    // GeSHi Constants
    37    // You should use these constant names in your programs instead of
    38    // their values - you never know when a value may change in a future
    39    // version
    40    //
    41
    42    /** The version of this GeSHi file */
    42
    43    define('GESHI_VERSION', '1.0.7.3');
    44
    45    /** For the future (though this may never be realised) */
    45
    46    define('GESHI_OUTPUT_HTML', 0);
    47
    48    /** Set the correct directory separator */
    48
    49    define('GESHI_DIR_SEPARATOR', ('WIN' != substr(PHP_OS, 0, 3)) ? '/' : '\\');
    50
    51    // Define the root directory for the GeSHi code tree
    52    if (!defined('GESHI_ROOT')) {
    53        /** The root directory for GeSHi */
    53
    54        define('GESHI_ROOT', dirname(__FILE__) . GESHI_DIR_SEPARATOR);
    55    }
    56    /** The language file directory for GeSHi
    57        @access private */
    58    define('GESHI_LANG_ROOT', GESHI_ROOT . 'geshi' . GESHI_DIR_SEPARATOR);
    59
    60
    61    // Line numbers - use with enable_line_numbers()
    62    /** Use no line numbers when building the result */
    62
    63    define('GESHI_NO_LINE_NUMBERS', 0);
    64    /** Use normal line numbers when building the result */
    64
    65    define('GESHI_NORMAL_LINE_NUMBERS', 1);
    66    /** Use fancy line numbers when building the result */
    66
    67    define('GESHI_FANCY_LINE_NUMBERS', 2);
    68
    69    // Container HTML type
    70    /** Use nothing to surround the source */
    70
    71    define('GESHI_HEADER_NONE', 0);
    72    /** Use a "div" to surround the source */
    72
    73    define('GESHI_HEADER_DIV', 1);
    74    /** Use a "pre" to surround the source */
    74
    75    define('GESHI_HEADER_PRE', 2);
    76
    77    // Capatalisation constants
    78    /** Lowercase keywords found */
    78
    79    define('GESHI_CAPS_NO_CHANGE', 0);
    80    /** Uppercase keywords found */
    80
    81    define('GESHI_CAPS_UPPER', 1);
    82    /** Leave keywords found as the case that they are */
    82
    83    define('GESHI_CAPS_LOWER', 2);
    84
    85    // Link style constants
    86    /** Links in the source in the :link state */
    86
    87    define('GESHI_LINK', 0);
    88    /** Links in the source in the :hover state */
    88
    89    define('GESHI_HOVER', 1);
    90    /** Links in the source in the :active state */
    90
    91    define('GESHI_ACTIVE', 2);
    92    /** Links in the source in the :visited state */
    92
    93    define('GESHI_VISITED', 3);
    94
    95    // Important string starter/finisher
    96    // Note that if you change these, they should be as-is: i.e., don't
    97    // write them as if they had been run through htmlentities()
    98    /** The starter for important parts of the source */
    98
    99    define('GESHI_START_IMPORTANT', '<BEGIN GeSHi>');
    100   /** The ender for important parts of the source */
    100
    101   define('GESHI_END_IMPORTANT', '<END GeSHi>');
    102
    103   /**#@+
    104    *  @access private
    105    */
    106   // When strict mode applies for a language
    107   /** Strict mode never applies (this is the most common) */
    107
    108   define('GESHI_NEVER', 0);
    109   /** Strict mode *might* apply, and can be enabled or
    110       disabled by {@link GeSHi::enable_strict_mode()} */
    111   define('GESHI_MAYBE', 1);
    112   /** Strict mode always applies */
    112
    113   define('GESHI_ALWAYS', 2);
    114
    115   // Advanced regexp handling constants, used in language files
    116   /** The key of the regex array defining what to search for */
    116
    117   define('GESHI_SEARCH', 0);
    118   /** The key of the regex array defining what bracket group in a
    119       matched search to use as a replacement */
    120   define('GESHI_REPLACE', 1);
    121   /** The key of the regex array defining any modifiers to the regular expression */
    121
    122   define('GESHI_MODIFIERS', 2);
    123   /** The key of the regex array defining what bracket group in a
    124       matched search to put before the replacement */
    125   define('GESHI_BEFORE', 3);
    126   /** The key of the regex array defining what bracket group in a
    127       matched search to put after the replacement */
    128   define('GESHI_AFTER', 4);
    129
    130   /** Used in language files to mark comments */
    130
    131   define('GESHI_COMMENTS', 0);
    132
    133   // Error detection - use these to analyse faults
    134   /** No sourcecode to highlight was specified */
    134
    135   define('GESHI_ERROR_NO_INPUT', 1);
    136   /** The language specified does not exist */
    136
    137   define('GESHI_ERROR_NO_SUCH_LANG', 2);
    138   /** GeSHi could not open a file for reading (generally a language file) */
    138
    139   define('GESHI_ERROR_FILE_NOT_READABLE', 3);
    140   /** The header type passed to {@link GeSHi::set_header_type()} was invalid */
    140
    141   define('GESHI_ERROR_INVALID_HEADER_TYPE', 4);
    142   /** The line number type passed to {@link GeSHi::enable_line_numbers()} was invalid */
    142
    143   define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5);
    144   /**#@-*/
    145
    146
    147   /**
    148    * The GeSHi Class.
    149    *
    150    * Please refer to the documentation for GeSHi 1.0.X that is available
    151    * at http://qbnz.com/highlighter/documentation.php for more information
    152    * about how to use this class.
    153    *
    154    * @package   core
    155    * @author    Nigel McNie <nigel@geshi.org>
    156    * @copyright Copyright &copy; 2004, 2005 Nigel McNie
    157    */
    158   class GeSHi
    159   {
    160       /**#@+
    161        * @access private
    162        */
    163       /**
    164        * The source code to highlight
    165        * @var string
    166        */
    167       var $source = '';
    168
    169       /**
    170        * The language to use when highlighting
    171        * @var string
    172        */
    173       var $language = '';
    174
    175       /**
    176        * The data for the language used
    177        * @var array
    178        */
    179       var $language_data = array();
    180
    181       /**
    182        * The path to the language files
    183        * @var string
    184        */
    185       var $language_path = GESHI_LANG_ROOT;
    186
    187       /**
    188        * The error message associated with an error
    189        * @var string
    190        * @todo check err reporting works
    191        */
    192       var $error = false;
    193
    194       /**
    195        * Possible error messages
    196        * @var array
    197        */
    198       var $error_messages = array(
    199           GESHI_ERROR_NO_INPUT => 'No source code inputted',
    200           GESHI_ERROR_NO_SUCH_LANG => 'GeSHi could not find the language {LANGUAGE} (using path {PATH})',
    201           GESHI_ERROR_FILE_NOT_READABLE => 'The file specified for load_from_file was not readable',
    202           GESHI_ERROR_INVALID_HEADER_TYPE => 'The header type specified is invalid',
    203           GESHI_ERROR_INVALID_LINE_NUMBER_TYPE => 'The line number type specified is invalid'
    204       );
    205
    206       /**
    207        * Whether highlighting is strict or not
    208        * @var boolean
    209        */
    210       var $strict_mode = false;
    211
    212       /**
    213        * Whether to use CSS classes in output
    214        * @var boolean
    215        */
    216       var $use_classes = false;
    217
    218       /**
    219        * The type of header to use. Can be one of the following
    220        * values:
    221        *
    222        * <ul>
    223        *   <li><b>GESHI_HEADER_PRE</b>: Source is outputted in
    224        *   a &lt;pre&gt; HTML element.</li>
    225        *   <li><b>GESHI_HEADER_DIV</b>: Source is outputted in
    226        *   a &lt;div&gt; HTML element.</li>
    227        * </ul>
    228        *
    229        * @var int
    230        */
    231       var $header_type = GESHI_HEADER_PRE;
    232
    233       /**
    234        * Array of permissions for which lexics should be highlighted
    235        * @var array
    236        */
    237       var $lexic_permissions = array(
    238           'KEYWORDS' =>    array(),
    239           'COMMENTS' =>    array('MULTI' => true),
    240           'REGEXPS' =>     array(),
    241           'ESCAPE_CHAR' => true,
    242           'BRACKETS' =>    true,
    243           'SYMBOLS' =>     true,
    244           'STRINGS' =>     true,
    245           'NUMBERS' =>     true,
    246           'METHODS' =>     true,
    247           'SCRIPT' =>      true
    248       );
    249
    250       /**
    251        * The time it took to parse the code
    252        * @var double
    253        */
    254       var $time = 0;
    255
    256       /**
    257        * The content of the header block
    258        * @var string
    259        */
    260       var $header_content = '';
    261
    262       /**
    263        * The content of the footer block
    264        * @var string
    265        */
    266       var $footer_content = '';
    267
    268       /**
    269        * The style of the header block
    270        * @var string
    271        */
    272       var $header_content_style = '';
    273
    274       /**
    275        * The style of the footer block
    276        * @var string
    277        */
    278       var $footer_content_style = '';
    279
    280       /**
    281        * The styles for hyperlinks in the code
    282        * @var array
    283        */
    284       var $link_styles = array();
    285
    286       /**
    287        * Whether important blocks should be recognised or not
    288        * @var boolean
    289        * @todo REMOVE THIS FUNCTIONALITY!
    290        */
    291       var $enable_important_blocks = false;
    292
    293       /**
    294        * Styles for important parts of the code
    295        * @var string
    296        * @todo As above - rethink the whole idea of important blocks as it is buggy and
    297        *  will be hard to implement in 1.2
    298        */
    299       var $important_styles = 'font-weight: bold; color: red;'; // Styles for important parts of the code
    300
    301       /**
    302        * Whether CSS IDs should be added to the code
    303        * @var boolean
    304        */
    305       var $add_ids = false;
    306
    307       /**
    308        * Lines that should be highlighted extra
    309        * @var array
    310        */
    311       var $highlight_extra_lines = array();
    312
    313       /**
    314        * Styles of extra-highlighted lines
    315        * @var string
    316        */
    317       var $highlight_extra_lines_style = 'color: #cc0; background-color: #ffc;';
    318
    319       /**
    320        * Number at which line numbers should start at
    321        * @var int
    322        * @todo Warning documentation about XHTML compliance
    323        */
    324       var $line_numbers_start = 1;
    325
    326       /**
    327        * The overall style for this code block
    328        * @var string
    329   	 */
    330       var $overall_style = '';
    331
    332       /**
    333        *  The style for the actual code
    334        * @var string
    335        */
    336       var $code_style = 'font-family: \'Courier New\', Courier, monospace; font-weight: normal;';
    337
    338       /**
    339        * The overall class for this code block
    340        * @var string
    341        */
    342       var $overall_class = '';
    343
    344       /**
    345        * The overall ID for this code block
    346        * @var string
    347        */
    348       var $overall_id = '';
    349
    350       /**
    351        * Line number styles
    352        * @var string
    353        */
    354       var $line_style1 = 'font-family: \'Courier New\', Courier, monospace; color: black; font-weight: normal; font-style: normal;';
    355
    356       /**
    357        * Line number styles for fancy lines
    358        * @var string
    359        */
    360       var $line_style2 = 'font-weight: bold;';
    361
    362       /**
    363        * Flag for how line nubmers are displayed
    364        * @var boolean
    365        */
    366       var $line_numbers = GESHI_NO_LINE_NUMBERS;
    367
    368       /**
    369        * The "nth" value for fancy line highlighting
    370        * @var int
    371        */
    372       var $line_nth_row = 0;
    373
    374       /**
    375        * The size of tab stops
    376        * @var int
    377   	 */
    378       var $tab_width = 8;
    379
    380       /**
    381        * Default target for keyword links
    382        * @var string
    383        */
    384       var $link_target = '';
    385
    386       /**
    387        * The encoding to use for entity encoding
    388        * @var string
    389        */
    390       var $encoding = 'ISO-8859-1';
    391
    392       /**
    393        * Unused (planned for future)
    394        * @var int
    395   	 */
    396       var $output_format = GESHI_OUTPUT_HTML;
    397
    398       /**#@-*/
    399
    400       /**
    401   	 * Creates a new GeSHi object, with source and language
    402        *
    403        * @param string The source code to highlight
    404        * @param string The language to highlight the source with
    405        * @param string The path to the language file directory. <b>This
    406        *                is deprecated!</b> I've backported the auto path
    407        *                detection from the 1.1.X dev branch, so now it
    408        *                should be automatically set correctly. If you have
    409        *                renamed the language directory however, you will
    410        *                still need to set the path using this parameter or
    411        *                {@link GeSHi::set_language_path()}
    412        * @since 1.0.0
    413   	 */
    414       function GeSHi ($source, $language, $path = '')
    415       {
    416           $this->set_source($source);
    417           $this->set_language_path($path);
    418           $this->set_language($language);
    419       }
    420
    421       /**
    422   	 * Returns an error message associated with the last GeSHi operation,
    423   	 * or false if no error has occured
    424        *
    425        * @return string|falseAn error message if there has been an error, else false
    426        * @since  1.0.0
    427   	 */
    428       function error ()
    429       {
    430           if ($this->error) {
    431               $msg = $this->error_messages[$this->error];
    432               $debug_tpl_vars = array(
    433                   '{LANGUAGE}' => $this->language,
    434                   '{PATH}' => $this->
    435   language_path            );
    436               foreach ($debug_tpl_vars as $tpl => $var) {
    437                   $msg = str_replace($tpl, $var, $msg);
    438               }
    439               return "<br /><strong>GeSHi Error:</strong> $msg (code $this->error)<br />";
    440           }
    441           return false;
    442       }
    443
    444       /**
    445   	 * Gets a human-readable language name (thanks to Simon Patterson
    446   	 * for the idea :))
    447        *
    448        * @return string The name for the current language
    449        * @since  1.0.2
    450   	 */
    451       function get_language_name ()
    452       {
    453           if (GESHI_ERROR_NO_SUCH_LANG == $this->_error) {
    454               return $this->language_data['LANG_NAME'] . ' (Unknown Language)';
    455           }
    456           return $this->language_data['LANG_NAME'];
    457       }
    458
    459       /**
    460   	 * Sets the source code for this object
    461        *
    462        * @param string The source code to highlight
    463        * @since 1.0.0
    464   	 */
    465       function set_source ($source)
    466       {
    467           if ('' == trim($source)) {
    468               $this->error = GESHI_ERROR_NO_INPUT;
    469           }
    470           $this->source = $source;
    471       }
    472
    473       /**
    474   	 * Sets the language for this object
    475        *
    476        * @param string The name of the language to use
    477        * @since 1.0.0
    478   	 */
    479       function set_language ($language)
    480       {
    481           $this->error = false;
    482           $this->strict_mode = GESHI_NEVER;
    483
    484           $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language);
    485           $this->language = strtolower($language);
    486
    487           $file_name = $this->language_path . $this->language . '.php';
    488           if (!is_readable($file_name)) {
    489               $this->error = GESHI_ERROR_NO_SUCH_LANG;
    490               return;
    491           }
    492           // Load the language for parsing
    493           $this->load_language($file_name);
    494       }
    495
    496       /**
    497   	 * Sets the path to the directory containing the language files. Note
    498   	 * that this path is relative to the directory of the script that included
    499   	 * geshi.php, NOT geshi.php itself.
    500        *
    501        * @param string The path to the language directory
    502        * @since 1.0.0
    503        * @deprecated The path to the language files should now be automatically
    504        *              detected, so this method should no longer be needed. The
    505        *              1.1.X branch handles manual setting of the path differently
    506        *              so this method will disappear in 1.2.0.
    507   	 */
    508       function set_language_path ($path)
    509       {
    510           if ($path) {
    511             $this->language_path = ('/' == substr($path, strlen($path) - 1, 1)) ? $path : $path . '/';
    512           }
    513       }
    514
    515       /**
    516   	 * Sets the type of header to be used.
    517        *
    518        * If GESHI_HEADER_DIV is used, the code is surrounded in a "div".This
    519        * means more source code but more control over tab width and line-wrapping.
    520        * GESHI_HEADER_PRE means that a "pre" is used - less source, but less
    521        * control. Default is GESHI_HEADER_PRE.
    522        *
    523        * From 1.0.7.2, you can use GESHI_HEADER_NONE to specify that no header code
    524        * should be outputted.
    525        *
    526        * @param int The type of header to be used
    527        * @since 1.0.0
    528   	 */
    529       function set_header_type ($type)
    530       {
    531           if (GESHI_HEADER_DIV != $type && GESHI_HEADER_PRE != $type && GESHI_HEADER_NONE != $type) {
    532               $this->error = GESHI_ERROR_INVALID_HEADER_TYPE;
    533               return;
    534           }
    535           $this->header_type = $type;
    536       }
    537
    538       /**
    539   	 * Sets the styles for the code that will be outputted
    540   	 * when this object is parsed. The style should be a
    541   	 * string of valid stylesheet declarations
    542        *
    543        * @param string  The overall style for the outputted code block
    544        * @param boolean Whether to merge the styles with the current styles or not
    545        * @since 1.0.0
    546   	 */
    547       function set_overall_style ($style, $preserve_defaults = false)
    548       {
    549           if (!$preserve_defaults) {
    550               $this->overall_style = $style;
    551           } else {
    552               $this->overall_style .= $style;
    553           }
    554       }
    555
    556       /**
    557   	 * Sets the overall classname for this block of code. This
    558   	 * class can then be used in a stylesheet to style this object's
    559   	 * output
    560        *
    561        * @param string The class name to use for this block of code
    562        * @since 1.0.0
    563   	 */
    564       function set_overall_class ($class)
    565       {
    566           $this->overall_class = $class;
    567       }
    568
    569       /**
    570   	 * Sets the overall id for this block of code. This id can then
    571   	 * be used in a stylesheet to style this object's output
    572        *
    573        * @param string The ID to use for this block of code
    574        * @since 1.0.0
    575   	 */
    576       function set_overall_id ($id)
    577       {
    578           $this->overall_id = $id;
    579       }
    580
    581       /**
    582        * Sets whether CSS classes should be used to highlight the source. Default
    583        * is off, calling this method with no arguments will turn it on
    584        *
    585        * @param boolean Whether to turn classes on or not
    586        * @since 1.0.0
    587        */
    588       function enable_classes ($flag = true)
    589       {
    590           $this->use_classes = ($flag) ? true : false;
    591       }
    592
    593       /**
    594   	 * Sets the style for the actual code. This should be a string
    595   	 * containing valid stylesheet declarations. If $preserve_defaults is
    596   	 * true, then styles are merged with the default styles, with the
    597   	 * user defined styles having priority
    598   	 *
    599   	 * Note: Use this method to override any style changes you made to
    600   	 * the line numbers if you are using line numbers, else the line of
    601   	 * code will have the same style as the line number! Consult the
    602   	 * GeSHi documentation for more information about this.
    603        *
    604        * @param string  The style to use for actual code
    605        * @param boolean Whether to merge the current styles with the new styles
    606   	 */
    607       function set_code_style ($style, $preserve_defaults = false)
    608       {
    609           if (!$preserve_defaults) {
    610               $this->code_style = $style;
    611           } else {
    612               $this->code_style .= $style;
    613           }
    614       }
    615
    616       /**
    617   	 * Sets the styles for the line numbers.
    618        *
    619        * @param string The style for the line numbers that are "normal"
    620        * @param string|booleanIf a string, this is the style of the line
    621        *         numbers that are "fancy", otherwise if boolean then this
    622        *         defines whether the normal styles should be merged with the
    623        *         new normal styles or not
    624        * @param boolean If set, is the flag for whether to merge the "fancy"
    625        *         styles with the current styles or not
    626        * @since 1.0.2
    627   	 */
    628       function set_line_style ($style1, $style2 = '', $preserve_defaults = false)
    629       {
    630           if (is_bool($style2)) {
    631               $preserve_defaults = $style2;
    632               $style2 = '';
    633           }
    634           if (!$preserve_defaults) {
    635               $this->line_style1 = $style1;
    636               $this->line_style2 = $style2;
    637           } else {
    638               $this->line_style1 .= $style1;
    639               $this->line_style2 .= $style2;
    640           }
    641       }
    642
    643       /**
    644   	 * Sets whether line numbers should be displayed.
    645        *
    646        * Valid values for the first parameter are:
    647        *
    648        * <ul>
    649        *   <li><b>GESHI_NO_LINE_NUMBERS</b>: Line numbers will not be displayed</li>
    650   	 *   <li><b>GESHI_NORMAL_LINE_NUMBERS</b>: Line numbers will be displayed</li>
    651        *   <li><b>GESHI_FANCY_LINE_NUMBERS</b>: Fancy line numbers will be displayed</li>
    652        * </ul>
    653        *
    654        * For fancy line numbers, the second parameter is used to signal which lines
    655        * are to be fancy. For example, if the value of this parameter is 5 then every
    656        * 5th line will be fancy.
    657        *
    658        * @param int How line numbers should be displayed
    659        * @param int Defines which lines are fancy
    660        * @since 1.0.0
    661   	 */
    662       function enable_line_numbers ($flag, $nth_row = 5)
    663       {
    664           if (GESHI_NO_LINE_NUMBERS != $flag && GESHI_NORMAL_LINE_NUMBERS != $flag
    665               && GESHI_FANCY_LINE_NUMBERS != $flag) {
    666               $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE;
    667           }
    668           $this->line_numbers = $flag;
    669           $this->line_nth_row = $nth_row;
    670       }
    671
    672       /**
    673   	 * Sets the style for a keyword group. If $preserve_defaults is
    674   	 * true, then styles are merged with the default styles, with the
    675   	 * user defined styles having priority
    676        *
    677        * @param int     The key of the keyword group to change the styles of
    678        * @param string  The style to make the keywords
    679        * @param boolean Whether to merge the new styles with the old or just
    680        *                 to overwrite them
    681        * @since 1.0.0
    682   	 */
    683       function set_keyword_group_style ($key, $style, $preserve_defaults = false)
    684       {
    685           if (!$preserve_defaults) {
    686               $this->language_data['STYLES']['KEYWORDS'][$key] = $style;
    687           } else {
    688               $this->language_data['STYLES']['KEYWORDS'][$key] .= $style;
    689           }
    690       }
    691
    692       /**
    693   	 * Turns highlighting on/off for a keyword group
    694        *
    695        * @param int     The key of the keyword group to turn on or off
    696        * @param boolean Whether to turn highlighting for that group on or off
    697        * @since 1.0.0
    698   	 */
    699       function set_keyword_group_highlighting ( $key, $flag = true )
    700       {
    701           $this->lexic_permissions['KEYWORDS'][$key] = ($flag) ? true : false;
    702       }
    703
    704       /**
    705   	 * Sets the styles for comment groups.  If $preserve_defaults is
    706   	 * true, then styles are merged with the default styles, with the
    707   	 * user defined styles having priority
    708        *
    709        * @param int     The key of the comment group to change the styles of
    710        * @param string  The style to make the comments
    711        * @param boolean Whether to merge the new styles with the old or just
    712        *                 to overwrite them
    713        * @since 1.0.0
    714   	 */
    715       function set_comments_style ($key, $style, $preserve_defaults = false)
    716       {
    717           if (!$preserve_defaults) {
    718               $this->language_data['STYLES']['COMMENTS'][$key] = $style;
    719           } else {
    720               $this->language_data['STYLES']['COMMENTS'][$key] .= $style;
    721           }
    722       }
    723
    724       /**
    725   	 * Turns highlighting on/off for comment groups
    726        *
    727        * @param int     The key of the comment group to turn on or off
    728        * @param boolean Whether to turn highlighting for that group on or off
    729        * @since 1.0.0
    730   	 */
    731       function set_comments_highlighting ($key, $flag = true)
    732       {
    733           $this->lexic_permissions['COMMENTS'][$key] = ($flag) ? true : false;
    734       }
    735
    736       /**
    737   	 * Sets the styles for escaped characters. If $preserve_defaults is
    738   	 * true, then styles are merged with the default styles, with the
    739   	 * user defined styles having priority
    740        *
    741        * @param string  The style to make the escape characters
    742        * @param boolean Whether to merge the new styles with the old or just
    743        *                 to overwrite them
    744        * @since 1.0.0
    745   	 */
    746       function set_escape_characters_style ($style, $preserve_defaults = false)
    747       {
    748           if (!$preserve_defaults) {
    749               $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style;
    750           } else {
    751               $this->language_data['STYLES']['ESCAPE_CHAR'][0] .= $style;
    752           }
    753       }
    754
    755       /**
    756   	 * Turns highlighting on/off for escaped characters
    757        *
    758        * @param boolean Whether to turn highlighting for escape characters on or off
    759        * @since 1.0.0
    760   	 */
    761       function set_escape_characters_highlighting ($flag = true)
    762       {
    763           $this->lexic_permissions['ESCAPE_CHAR'] = ($flag) ? true : false;
    764       }
    765
    766       /**
    767   	 * Sets the styles for brackets. If $preserve_defaults is
    768   	 * true, then styles are merged with the default styles, with the
    769   	 * user defined styles having priority
    770   	 *
    771   	 * This method is DEPRECATED: use set_symbols_style instead.
    772   	 * This method will be removed in 1.2.X
    773        *
    774        * @param string  The style to make the brackets
    775        * @param boolean Whether to merge the new styles with the old or just
    776        *                 to overwrite them
    777        * @since 1.0.0
    778        * @deprecated In favour of set_symbols_style
    779   	 */
    780       function set_brackets_style ($style, $preserve_defaults = false)
    781       {
    782           if (!$preserve_defaults) {
    783               $this->language_data['STYLES']['BRACKETS'][0] = $style;
    784           } else {
    785               $this->language_data['STYLES']['BRACKETS'][0] .= $style;
    786           }
    787       }
    788
    789       /**
    790   	 * Turns highlighting on/off for brackets
    791   	 *
    792   	 * This method is DEPRECATED: use set_symbols_highlighting instead.
    793   	 * This method will be remove in 1.2.X
    794        *
    795        * @param boolean Whether to turn highlighting for brackets on or off
    796        * @since 1.0.0
    797        * @deprecated In favour of set_symbols_highlighting
    798   	 */
    799       function set_brackets_highlighting ($flag)
    800       {
    801           $this->lexic_permissions['BRACKETS'] = ($flag) ? true : false;
    802       }
    803
    804       /**
    805   	 * Sets the styles for symbols. If $preserve_defaults is
    806   	 * true, then styles are merged with the default styles, with the
    807   	 * user defined styles having priority
    808        *
    809        * @param string  The style to make the symbols
    810        * @param boolean Whether to merge the new styles with the old or just
    811        *                 to overwrite them
    812        * @since 1.0.1
    813   	 */
    814       function set_symbols_style ($style, $preserve_defaults = false)
    815       {
    816           if (!$preserve_defaults) {
    817               $this->language_data['STYLES']['SYMBOLS'][0] = $style;
    818           } else {
    819               $this->language_data['STYLES']['SYMBOLS'][0] .= $style;
    820           }
    821           // For backward compatibility
    822           $this->set_brackets_style ($style, $preserve_defaults);
    823       }
    824
    825       /**
    826   	 * Turns highlighting on/off for symbols
    827        *
    828        * @param boolean Whether to turn highlighting for symbols on or off
    829        * @since 1.0.0
    830   	 */
    831       function set_symbols_highlighting ($flag)
    832       {
    833           $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false;
    834           // For backward compatibility
    835           $this->set_brackets_highlighting ($flag);
    836       }
    837
    838       /**
    839   	 * Sets the styles for strings. If $preserve_defaults is
    840   	 * true, then styles are merged with the default styles, with the
    841   	 * user defined styles having priority
    842        *
    843        * @param string  The style to make the escape characters
    844        * @param boolean Whether to merge the new styles with the old or just
    845        *                 to overwrite them
    846        * @since 1.0.0
    847   	 */
    848       function set_strings_style ($style, $preserve_defaults = false)
    849       {
    850           if (!$preserve_defaults) {
    851               $this->language_data['STYLES']['STRINGS'][0] = $style;
    852           } else {
    853               $this->language_data['STYLES']['STRINGS'][0] .= $style;
    854           }
    855       }
    856
    857       /**
    858   	 * Turns highlighting on/off for strings
    859        *
    860        * @param boolean Whether to turn highlighting for strings on or off
    861        * @since 1.0.0
    862   	 */
    863       function set_strings_highlighting ($flag)
    864       {
    865           $this->lexic_permissions['STRINGS'] = ($flag) ? true : false;
    866       }
    867
    868       /**
    869   	 * Sets the styles for numbers. If $preserve_defaults is
    870   	 * true, then styles are merged with the default styles, with the
    871   	 * user defined styles having priority
    872        *
    873        * @param string  The style to make the numbers
    874        * @param boolean Whether to merge the new styles with the old or just
    875        *                 to overwrite them
    876        * @since 1.0.0
    877   	 */
    878       function set_numbers_style ($style, $preserve_defaults = false)
    879       {
    880           if (!$preserve_defaults) {
    881               $this->language_data['STYLES']['NUMBERS'][0] = $style;
    882           } else {
    883               $this->language_data['STYLES']['NUMBERS'][0] .= $style;
    884           }
    885       }
    886
    887       /**
    888   	 * Turns highlighting on/off for numbers
    889        *
    890        * @param boolean Whether to turn highlighting for numbers on or off
    891        * @since 1.0.0
    892   	 */
    893       function set_numbers_highlighting ($flag)
    894       {
    895           $this->lexic_permissions['NUMBERS'] = ($flag) ? true : false;
    896       }
    897
    898       /**
    899   	 * Sets the styles for methods. $key is a number that references the
    900   	 * appropriate "object splitter" - see the language file for the language
    901   	 * you are highlighting to get this number. If $preserve_defaults is
    902   	 * true, then styles are merged with the default styles, with the
    903   	 * user defined styles having priority
    904        *
    905        * @param int     The key of the object splitter to change the styles of
    906        * @param string  The style to make the methods
    907        * @param boolean Whether to merge the new styles with the old or just
    908        *                 to overwrite them
    909        * @since 1.0.0
    910   	 */
    911       function set_methods_style ($key, $style, $preserve_defaults = false)
    912       {
    913           if (!$preserve_defaults) {
    914               $this->language_data['STYLES']['METHODS'][$key] = $style;
    915           } else {
    916               $this->language_data['STYLES']['METHODS'][$key] .= $style;
    917           }
    918       }
    919
    920       /**
    921   	 * Turns highlighting on/off for methods
    922        *
    923        * @param boolean Whether to turn highlighting for methods on or off
    924        * @since 1.0.0
    925   	 */
    926       function set_methods_highlighting ($flag)
    927       {
    928           $this->lexic_permissions['METHODS'] = ($flag) ? true : false;
    929       }
    930
    931       /**
    932   	 * Sets the styles for regexps. If $preserve_defaults is
    933   	 * true, then styles are merged with the default styles, with the
    934   	 * user defined styles having priority
    935        *
    936        * @param string  The style to make the regular expression matches
    937        * @param boolean Whether to merge the new styles with the old or just
    938        *                 to overwrite them
    939        * @since 1.0.0
    940   	 */
    941       function set_regexps_style ($key, $style, $preserve_defaults = false)
    942       {
    943           if (!$preserve_defaults) {
    944               $this->language_data['STYLES']['REGEXPS'][$key] = $style;
    945           } else {
    946               $this->language_data['STYLES']['REGEXPS'][$key] .= $style;
    947           }
    948       }
    949
    950       /**
    951   	 * Turns highlighting on/off for regexps
    952        *
    953        * @param int     The key of the regular expression group to turn on or off
    954        * @param boolean Whether to turn highlighting for the regular expression group on or off
    955        * @since 1.0.0
    956   	 */
    957       function set_regexps_highlighting ($key, $flag)
    958       {
    959           $this->lexic_permissions['REGEXPS'][$key] = ($flag) ? true : false;
    960       }
    961
    962       /**
    963   	 * Sets whether a set of keywords are checked for in a case sensitive manner
    964        *
    965        * @param int The key of the keyword group to change the case sensitivity of
    966        * @param boolean Whether to check in a case sensitive manner or not
    967        * @since 1.0.0
    968   	 */
    969       function set_case_sensitivity ($key, $case)
    970       {
    971           $this->language_data['CASE_SENSITIVE'][$key] = ($case) ? true : false;
    972       }
    973
    974       /**
    975   	 * Sets the case that keywords should use when found. Use the constants:
    976        *
    977        * <ul>
    978   	 *   <li><b>GESHI_CAPS_NO_CHANGE</b>: leave keywords as-is</li>
    979   	 *   <li><b>GESHI_CAPS_UPPER</b>: convert all keywords to uppercase where found</li>
    980   	 *   <li><b>GESHI_CAPS_LOWER</b>: convert all keywords to lowercase where found</li>
    981        * </ul>
    982        *
    983        * @param int A constant specifying what to do with matched keywords
    984        * @since 1.0.1
    985        * @todo  Error check the passed value
    986   	 */
    987       function set_case_keywords ($case)
    988       {
    989           $this->language_data['CASE_KEYWORDS'] = $case;
    990       }
    991
    992       /**
    993   	 * Sets how many spaces a tab is substituted for
    994        *
    995        * Widths below zero are ignored
    996        *
    997        * @param int The tab width
    998        * @since 1.0.0
    999   	 */
    1000      function set_tab_width ($width)
    1001      {
    1002          $this->tab_width = intval($width);
    1003      }
    1004
    1005      /**
    1006  	 * Enables/disables strict highlighting. Default is off, calling this
    1007  	 * method without parameters will turn it on. See documentation
    1008  	 * for more details on strict mode and where to use it.
    1009       *
    1010       * @param boolean Whether to enable strict mode or not
    1011       * @since 1.0.0
    1012  	 */
    1013      function enable_strict_mode ($mode = true)
    1014      {
    1015          if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) {
    1016            $this->strict_mode = ($mode) ? true : false;
    1017          }
    1018      }
    1019
    1020      /**
    1021  	 * Disables all highlighting
    1022       *
    1023       * @since 1.0.0
    1024       * @todo Rewrite with an array traversal
    1025  	 */
    1026      function disable_highlighting ()
    1027      {
    1028          foreach ($this->lexic_permissions as $key => $value) {
    1029              if (is_array($value)) {
    1030                  foreach ($value as $k => $v) {
    1031                      $this->lexic_permissions[$key][$k] = false;
    1032                  }
    1033              } else {
    1034                  $this->lexic_permissions[$key] = false;
    1035              }
    1036          }
    1037          // Context blocks
    1038          $this->enable_important_blocks = false;
    1039      }
    1040
    1041      /**
    1042  	 * Enables all highlighting
    1043       *
    1044       * @since 1.0.0
    1045       * @todo  Rewrite with array traversal
    1046  	 */
    1047      function enable_highlighting ()
    1048      {
    1049          foreach ($this->lexic_permissions as $key => $value) {
    1050              if (is_array($value)) {
    1051                  foreach ($value as $k => $v) {
    1052                      $this->lexic_permissions[$key][$k] = true;
    1053                  }
    1054              } else {
    1055                  $this->lexic_permissions[$key] = true;
    1056              }
    1057          }
    1058          // Context blocks
    1059          $this->enable_important_blocks = true;
    1060      }
    1061
    1062      /**
    1063  	 * Given a file extension, this method returns either a valid geshi language
    1064  	 * name, or the empty string if it couldn't be found
    1065       *
    1066       * @param string The extension to get a language name for
    1067       * @param array  A lookup array to use instead of the default
    1068       * @since 1.0.5
    1069       * @todo Re-think about how this method works (maybe make it private and/or make it
    1070       *        a extension->lang lookup?)
    1071       * @todo static?
    1072  	 */
    1073      function get_language_name_from_extension ( $extension, $lookup = array() )
    1074      {
    1075          if ( !$lookup )
    1076          {
    1077              $lookup = array(
    1078                  'actionscript' => array('as'),
    1079                  'ada' => array('a', 'ada', 'adb', 'ads'),
    1080                  'apache' => array('conf'),
    1081                  'asm' => array('ash', 'asm'),
    1082                  'asp' => array('asp'),
    1083                  'bash' => array('sh'),
    1084                  'c' => array('c'),
    1085                  'c_mac' => array('c'),
    1086                  'caddcl' => array(),
    1087                  'cadlisp' => array(),
    1088                  'cpp' => array('cpp'),
    1089                  'csharp' => array(),
    1090                  'css' => array('css'),
    1091                  'delphi' => array('dpk', 'dpr'),
    1092                  'html4strict' => array('html', 'htm'),
    1093                  'java' => array('java'),
    1094                  'javascript' => array('js'),
    1095                  'lisp' => array('lisp'),
    1096                  'lua' => array('lua'),
    1097                  'mpasm' => array(),
    1098                  'nsis' => array(),
    1099                  'objc' => array(),
    1100                  'oobas' => array(),
    1101                  'oracle8' => array(),
    1102                  'pascal' => array('pas'),
    1103                  'perl' => array('pl', 'pm'),
    1104                  'php' => array('php', 'php5', 'phtml', 'phps'),
    1105                  'python' => array('py'),
    1106                  'qbasic' => array('bi'),
    1107                  'smarty' => array(),
    1108                  'vb' => array('bas'),
    1109                  'vbnet' => array(),
    1110                  'visualfoxpro' => array(),
    1111                  'xml' => array('xml')
    1112              );
    1113          }
    1114
    1115          foreach ($lookup as $lang => $extensions) {
    1116              foreach ($extensions as $ext) {
    1117                  if ($ext == $extension) {
    1118                      return $lang;
    1119                  }
    1120              }
    1121          }
    1122          return '';
    1123      }
    1124
    1125      /**
    1126  	 * Given a file name, this method loads its contents in, and attempts
    1127  	 * to set the language automatically. An optional lookup table can be
    1128  	 * passed for looking up the language name. If not specified a default
    1129       * table is used
    1130  	 *
    1131  	 * The language table is in the form
    1132  	 * <pre>array(
    1133  	 *   'lang_name' => array('extension', 'extension', ...),
    1134  	 *   'lang_name' ...
    1135  	 * );</pre>
    1136       *
    1137       * @todo Complete rethink of this and above method
    1138       * @since 1.0.5
    1139  	 */
    1140      function load_from_file ($file_name, $lookup = array())
    1141      {
    1142          if (is_readable($file_name)) {
    1143              $this->set_source(implode('', file($file_name)));
    1144              $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup));
    1145          } else {
    1146              $this->error = GESHI_ERROR_FILE_NOT_READABLE;
    1147          }
    1148      }
    1149
    1150      /**
    1151  	 * Adds a keyword to a keyword group for highlighting
    1152       *
    1153       * @param int    The key of the keyword group to add the keyword to
    1154       * @param string The word to add to the keyword group
    1155       * @since 1.0.0
    1156  	 */
    1157      function add_keyword ($key, $word)
    1158      {
    1159          $this->language_data['KEYWORDS'][$key][] = $word;
    1160      }
    1161
    1162      /**
    1163  	 * Removes a keyword from a keyword group
    1164       *
    1165       * @param int    The key of the keyword group to remove the keyword from
    1166       * @param string The word to remove from the keyword group
    1167       * @since 1.0.0
    1168  	 */
    1169      function remove_keyword ($key, $word)
    1170      {
    1171          $this->language_data['KEYWORDS'][$key] =
    1172              array_diff($this->language_data['KEYWORDS'][$key], array($word));
    1173      }
    1174
    1175      /**
    1176  	 * Creates a new keyword group
    1177       *
    1178       * @param int    The key of the keyword group to create
    1179       * @param string The styles for the keyword group
    1180       * @param boolean Whether the keyword group is case sensitive ornot
    1181       * @param array  The words to use for the keyword group
    1182       * @since 1.0.0
    1183  	 */
    1184      function add_keyword_group ( $key, $styles, $case_sensitive = true, $words = array() )
    1185      {
    1186          $words = (array) $words;
    1187          $this->language_data['KEYWORDS'][$key] = $words;
    1188          $this->lexic_permissions['KEYWORDS'][$key] = true;
    1189          $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive;
    1190          $this->language_data['STYLES']['KEYWORDS'][$key] = $styles;
    1191      }
    1192
    1193      /**
    1194  	 * Removes a keyword group
    1195       *
    1196       * @param int    The key of the keyword group to remove
    1197       * @since 1.0.0
    1198  	 */
    1199      function remove_keyword_group ($key)
    1200      {
    1201          unset($this->language_data['KEYWORDS'][$key]);
    1202          unset($this->lexic_permissions['KEYWORDS'][$key]);
    1203          unset($this->language_data['CASE_SENSITIVE'][$key]);
    1204          unset($this->language_data['STYLES']['KEYWORDS'][$key]);
    1205      }
    1206
    1207      /**
    1208  	 * Sets the content of the header block
    1209       *
    1210       * @param string The content of the header block
    1211       * @since 1.0.2
    1212  	 */
    1213      function set_header_content ($content)
    1214      {
    1215          $this->header_content = $content;
    1216      }
    1217
    1218      /**
    1219  	 * Sets the content of the footer block
    1220       *
    1221       * @param string The content of the footer block
    1222       * @since 1.0.2
    1223  	 */
    1224      function set_footer_content ($content)
    1225      {
    1226          $this->footer_content = $content;
    1227      }
    1228
    1229      /**
    1230  	 * Sets the style for the header content
    1231       *
    1232       * @param string The style for the header content
    1233       * @since 1.0.2
    1234  	 */
    1235      function set_header_content_style ($style)
    1236      {
    1237          $this->header_content_style = $style;
    1238      }
    1239
    1240      /**
    1241  	 * Sets the style for the footer content
    1242       *
    1243       * @param string The style for the footer content
    1244       * @since 1.0.2
    1245  	 */
    1246      function set_footer_content_style ($style)
    1247      {
    1248          $this->footer_content_style = $style;
    1249      }
    1250
    1251      /**
    1252  	 * Sets the base URL to be used for keywords
    1253       *
    1254       * @param int The key of the keyword group to set the URL for
    1255       * @param string The URL to set for the group. If {FNAME} is in
    1256       *                the url somewhere, it is replaced by the keyword
    1257       *                that the URL is being made for
    1258       * @since 1.0.2
    1259  	 */
    1260      function set_url_for_keyword_group ($group, $url)
    1261      {
    1262          $this->language_data['URLS'][$group] = $url;
    1263      }
    1264
    1265      /**
    1266  	 * Sets styles for links in code
    1267       *
    1268       * @param int A constant that specifies what state the style is being
    1269       *             set for - e.g. :hover or :visited
    1270       * @param string The styles to use for that state
    1271       * @since 1.0.2
    1272  	 */
    1273      function set_link_styles ($type, $styles)
    1274      {
    1275          $this->link_styles[$type] = $styles;
    1276      }
    1277
    1278      /**
    1279       * Sets the target for links in code
    1280       *
    1281       * @param string The target for links in the code, e.g. _blank
    1282       * @since 1.0.3
    1283       */
    1284      function set_link_target ( $target )
    1285      {
    1286          if (!$target) {
    1287              $this->link_target = '';
    1288          } else {
    1289              $this->link_target = ' target="' . $target . '" ';
    1290          }
    1291      }
    1292
    1293      /**
    1294  	 * Sets styles for important parts of the code
    1295       *
    1296       * @param string The styles to use on important parts of the code
    1297       * @since 1.0.2
    1298  	 */
    1299      function set_important_styles ($styles)
    1300      {
    1301          $this->important_styles = $styles;
    1302      }
    1303
    1304      /**
    1305  	 * Sets whether context-important blocks are highlighted
    1306       *
    1307       * @todo REMOVE THIS SHIZ FROM GESHI!
    1308  	 */
    1309      function enable_important_blocks ( $flag )
    1310      {
    1311          $this->enable_important_blocks = ( $flag ) ? true : false;
    1312      }
    1313
    1314      /**
    1315  	 * Whether CSS IDs should be added to each line
    1316       *
    1317       * @param boolean If true, IDs will be added to each line.
    1318       * @since 1.0.2
    1319  	 */
    1320      function enable_ids ($flag = true)
    1321      {
    1322          $this->add_ids = ($flag) ? true : false;
    1323      }
    1324
    1325      /**
    1326  	 * Specifies which lines to highlight extra
    1327       *
    1328       * @param mixed An array of line numbers to highlight, or just a line
    1329       *               number on its own.
    1330       * @since 1.0.2
    1331       * @todo  Some data replication here that could be cut down on
    1332  	 */
    1333      function highlight_lines_extra ($lines)
    1334      {
    1335          if (is_array($lines)) {
    1336              foreach ($lines as $line) {
    1337                  $this->highlight_extra_lines[intval($line)] = intval($line);
    1338              }
    1339          } else {
    1340              $this->highlight_extra_lines[intval($lines)] = intval($lines);
    1341          }
    1342      }
    1343
    1344      /**
    1345  	 * Sets the style for extra-highlighted lines
    1346       *
    1347       * @param string The style for extra-highlighted lines
    1348       * @since 1.0.2
    1349  	 */
    1350      function set_highlight_lines_extra_style ($styles)
    1351      {
    1352          $this->highlight_extra_lines_style = $styles;
    1353      }
    1354
    1355      /**
    1356  	 * Sets what number line numbers should start at. Should
    1357  	 * be a positive integer, and will be converted to one.
    1358       *
    1359       * <b>Warning:</b> Using this method will add the "start"
    1360       * attribute to the &lt;ol&gt; that is used for line numbering.
    1361       * This is <b>not</b> valid XHTML strict, so if that's what you
    1362       * care about then don't use this method. Firefox is getting
    1363       * support for the CSS method of doing this in 1.1 and Opera
    1364       * has support for the CSS method, but (of course) IE doesn't
    1365       * so it's not worth doing it the CSS way yet.
    1366       *
    1367       * @param int The number to start line numbers at
    1368       * @since 1.0.2
    1369  	 */
    1370      function start_line_numbers_at ($number)
    1371      {
    1372          $this->line_numbers_start = abs(intval($number));
    1373      }
    1374
    1375      /**
    1376  	 * Sets the encoding used for htmlspecialchars(), for international
    1377  	 * support.
    1378       *
    1379       * @param string The encoding to use for the source
    1380       * @since 1.0.3
    1381  	 */
    1382      function set_encoding ($encoding)
    1383      {
    1384          if ($encoding) {
    1385            $this->encoding = $encoding;
    1386          }
    1387      }
    1388
    1389      /**
    1390  	 * Returns the code in $this->source, highlighted and surrounded by the
    1391  	 * nessecary HTML.
    1392       *
    1393       * This should only be called ONCE, cos it's SLOW! If you want to highlight
    1394       * the same source multiple times, you're better off doing a whole lot of
    1395       * str_replaces to replace the &lt;span&gt;s
    1396       *
    1397       * @since 1.0.0
    1398  	 */
    1399      function parse_code ()
    1400      {
    1401          // Start the timer
    1402          $start_time = microtime();
    1403
    1404          // Firstly, if there is an error, we won't highlight
    1405          if ($this->error) {
    1406              $result = $this->header();
    1407              if ($this->header_type != GESHI_HEADER_PRE) {
    1408                  $result .= $this->indent(@htmlspecialchars($this->source, ENT_COMPAT, $this->encoding));
    1409              } else {
    1410                  $result .= @htmlspecialchars($this->source, ENT_COMPAT, $this->encoding);
    1411              }
    1412              // Stop Timing
    1413              $this->set_time($start_time, microtime());
    1414              return $result . $this->footer();
    1415          }
    1416
    1417          // Add spaces for regular expression matching and line numbers
    1418          $code = ' ' . $this->source . ' ';
    1419          // Replace all newlines to a common form.
    1420          $code = str_replace("\r\n", "\n", $code);
    1421          $code = str_replace("\r", "\n", $code);
    1422
    1423          // Initialise various stuff
    1424          $length           = strlen($code);
    1425          $STRING_OPEN      = '';
    1426          $CLOSE_STRING     = false;
    1427          $ESCAPE_CHAR_OPEN = false;
    1428          $COMMENT_MATCHED  = false;
    1429          // Turn highlighting on if strict mode doesn't apply to this language
    1430          $HIGHLIGHTING_ON  = ( !$this->strict_mode ) ? true : '';
    1431          // Whether to highlight inside a block of code
    1432          $HIGHLIGHT_INSIDE_STRICT = false;
    1433          $stuff_to_parse   = '';
    1434          $result           = '';
    1435
    1436          // "Important" selections are handled like multiline comments
    1437          // @todo GET RID OF THIS SHIZ
    1438          if ($this->enable_important_blocks) {
    1439              $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT;
    1440          }
    1441
    1442          if ($this->strict_mode) {
    1443              // Break the source into bits. Each bit will be a portion of the code
    1444              // within script delimiters - for example, HTML between < and >
    1445              $parts = array(0 => array(0 => ''));
    1446              $k = 0;
    1447              for ($i = 0; $i < $length; $i++) {
    1448                  $char = substr($code, $i, 1);
    1449                  if (!$HIGHLIGHTING_ON) {
    1450                      foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) {
    1451                          foreach ($delimiters as $open => $close) {
    1452                              // Get the next little bit for this opening string
    1453                              $check = substr($code, $i, strlen($open));
    1454                              // If it matches...
    1455                              if ($check == $open) {
    1456                                  // We start a new block with the highlightable
    1457                                  // code in it
    1458                                  $HIGHLIGHTING_ON = $open;
    1459                                  $i += strlen($open) - 1;
    1460                                  $char = $open;
    1461                                  $parts[++$k][0] = $char;
    1462
    1463                                  // No point going around again...
    1464                                  break(2);
    1465                              }
    1466                          }
    1467                      }
    1468                  } else {
    1469                      foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) {
    1470                          foreach ($delimiters as $open => $close) {
    1471                              if ($open == $HIGHLIGHTING_ON) {
    1472                                  // Found the closing tag
    1473                                  break(2);
    1474                              }
    1475                          }
    1476                      }
    1477                      // We check code from our current position BACKWARDS. This is so
    1478                      // the ending string for highlighting can be included in the block
    1479                      $check = substr($code, $i - strlen($close) + 1, strlen($close));
    1480                      if ($check == $close) {
    1481                          $HIGHLIGHTING_ON = '';
    1482                          // Add the string to the rest of the string for this part
    1483                          $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char;
    1484                          $parts[++$k][0] = '';
    1485                          $char = '';
    1486                      }
    1487                  }
    1488                  $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char;
    1489              }
    1490              $HIGHLIGHTING_ON = '';
    1491          } else {
    1492              // Not strict mode - simply dump the source into
    1493              // the array at index 1 (the first highlightable block)
    1494              $parts = array(
    1495                  1 => array(
    1496                      0 => '',
    1497                      1 => $code
    1498                  )
    1499              );
    1500          }
    1501
    1502          // Now we go through each part. We know that even-indexed parts are
    1503          // code that shouldn't be highlighted, and odd-indexed parts should
    1504          // be highlighted
    1505          foreach ($parts as $key => $data) {
    1506              $part = $data[1];
    1507              // If this block should be highlighted...
    1508              if ($key % 2) {
    1509                  if ($this->strict_mode) {
    1510                      // Find the class key for this block of code
    1511                      foreach ($this->language_data['SCRIPT_DELIMITERS'] as $script_key => $script_data) {
    1512                          foreach ($script_data as $open => $close) {
    1513                              if ($data[0] == $open) {
    1514                                  break(2);
    1515                              }
    1516                          }
    1517                      }
    1518
    1519                      if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' &&
    1520                          $this->lexic_permissions['SCRIPT']) {
    1521                          // Add a span element around the source to
    1522                          // highlight the overall source block
    1523                          if (!$this->use_classes &&
    1524                              $this->language_data['STYLES']['SCRIPT'][$script_key] != '') {
    1525                              $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"';
    1526                          } else {
    1527                              $attributes = ' class="sc' . $script_key . '"';
    1528                          }
    1529                          $result .= "<span$attributes>";
    1530                      }
    1531                  }
    1532
    1533                  if (!$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]) {
    1534                      // Now, highlight the code in this block. This code
    1535                      // is really the engine of GeSHi (along with the method
    1536                      // parse_non_string_part).
    1537                      $length = strlen($part);
    1538                      for ($i = 0; $i < $length; $i++) {
    1539                          // Get the next char
    1540                          $char = substr($part, $i, 1);
    1541                          // Is this char the newline and line numbers being used?
    1542                          if (($this->line_numbers != GESHI_NO_LINE_NUMBERS
    1543                              || count($this->highlight_extra_lines) > 0)
    1544                              && $char == "\n") {
    1545                              // If so, is there a string open? If there is, we should end it before
    1546                              // the newline and begin it again (so when <li>s are put in the source
    1547                              // remains XHTML compliant)
    1548                              // note to self: This opens up possibility of config files specifying
    1549                              // that languages can/cannot have multiline strings???
    1550                              if ($STRING_OPEN) {
    1551                                  if (!$this->use_classes) {
    1552                                      $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"';
    1553                                  } else {
    1554                                      $attributes = ' class="st0"';
    1555                                  }
    1556                                  $char = '</span>' . $char . "<span$attributes>";
    1557                              }
    1558                          } elseif ($char == $STRING_OPEN) {
    1559                              // A match of a string delimiter
    1560                              if (($this->lexic_permissions['ESCAPE_CHAR'] && $ESCAPE_CHAR_OPEN) ||
    1561                                  ($this->lexic_permissions['STRINGS'] && !$ESCAPE_CHAR_OPEN)) {
    1562                                  $char .= '</span>';
    1563                              }
    1564                              if (!$ESCAPE_CHAR_OPEN) {
    1565                                  $STRING_OPEN = '';
    1566                                  $CLOSE_STRING = true;
    1567                              }
    1568                              $ESCAPE_CHAR_OPEN = false;
    1569                          } elseif (in_array($char, $this->language_data['QUOTEMARKS']) &&
    1570                              ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) {
    1571                              // The start of a new string
    1572                              $STRING_OPEN = $char;
    1573                              if (!$this->use_classes) {
    1574                                  $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"';
    1575                              } else {
    1576                                  $attributes = ' class="st0"';
    1577                              }
    1578                              $char = "<span$attributes>" . $char;
    1579
    1580                              $result .= $this->parse_non_string_part( $stuff_to_parse );
    1581                              $stuff_to_parse = '';
    1582                          } elseif (($char == $this->language_data['ESCAPE_CHAR']) && ($STRING_OPEN != '')) {
    1583                              // An escape character
    1584                              if (!$ESCAPE_CHAR_OPEN) {
    1585                                  $ESCAPE_CHAR_OPEN = true;
    1586                                  if ($this->lexic_permissions['ESCAPE_CHAR']) {
    1587                                      if (!$this->use_classes) {
    1588                                          $attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"';
    1589                                      } else {
    1590                                          $attributes = ' class="es0"';
    1591                                      }
    1592                                      $char = "<span$attributes>" . $char;
    1593                                  }
    1594                              } else {
    1595                                  $ESCAPE_CHAR_OPEN = false;
    1596                                  if ($this->lexic_permissions['ESCAPE_CHAR']) {
    1597                                      $char .= '</span>';
    1598                                  }
    1599                              }
    1600                          } elseif ($ESCAPE_CHAR_OPEN) {
    1601                              if ($this->lexic_permissions['ESCAPE_CHAR']) {
    1602                                  $char .= '</span>';
    1603                              }
    1604                              $ESCAPE_CHAR_OPEN = false;
    1605                              $test_str = $char;
    1606                          } elseif ($STRING_OPEN == '') {
    1607                              // Is this a multiline comment?
    1608                              foreach ($this->language_data['COMMENT_MULTI'] as $open => $close) {
    1609                                  $com_len = strlen($open);
    1610                                  $test_str = substr( $part, $i, $com_len );
    1611                                  $test_str_match = $test_str;
    1612                                  if ($open == $test_str) {
    1613                                      $COMMENT_MATCHED = true;
    1614                                      //@todo If remove important do remove here
    1615                                      if ($this->lexic_permissions['COMMENTS']['MULTI'] ||
    1616                                          $test_str == GESHI_START_IMPORTANT) {
    1617                                          if ($test_str != GESHI_START_IMPORTANT) {
    1618                                              if (!$this->use_classes) {
    1619                                                  $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS']['MULTI'] . '"';
    1620                                              } else {
    1621                                                  $attributes = ' class="coMULTI"';
    1622                                              }
    1623                                              $test_str = "<span$attributes>" . @htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
    1624                                          } else {
    1625                                              if (!$this->use_classes) {
    1626                                                  $attributes = ' style="' . $this->important_styles . '"';
    1627                                              } else {
    1628                                                  $attributes = ' class="imp"';
    1629                                              }
    1630                                              // We don't include the start of the comment if it's an
    1631                                              // "important" part
    1632                                              $test_str = "<span$attributes>";
    1633                                          }
    1634                                      } else {
    1635                                          $test_str = @htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
    1636                                      }
    1637
    1638                                      $close_pos = strpos( $part, $close, $i + strlen($close) );
    1639
    1640                                      if ($close_pos === false) {
    1641                                          $close_pos = strlen($part);
    1642                                      }
    1643
    1644                                      // Short-cut through all the multiline code
    1645                                      $rest_of_comment = @htmlspecialchars(substr($part, $i + $com_len, $close_pos - $i), ENT_COMPAT, $this->encoding);
    1646                                      if (($this->lexic_permissions['COMMENTS']['MULTI'] ||
    1647                                          $test_str_match == GESHI_START_IMPORTANT) &&
    1648                                          ($this->line_numbers != GESHI_NO_LINE_NUMBERS ||
    1649                                          count($this->highlight_extra_lines) > 0)) {
    1650                                          // strreplace to put close span and open span around multiline newlines
    1651                                          $test_str .= str_replace("\n", "</span>\n<span$attributes>", $rest_of_comment);
    1652                                      } else {
    1653                                          $test_str .= $rest_of_comment;
    1654                                      }
    1655
    1656                                      if ($this->lexic_permissions['COMMENTS']['MULTI'] ||
    1657                                          $test_str_match == GESHI_START_IMPORTANT) {
    1658                                          $test_str .= '</span>';
    1659                                      }
    1660                                      $i = $close_pos + $com_len - 1;
    1661                                      // parse the rest
    1662                                      $result .= $this->parse_non_string_part($stuff_to_parse);
    1663                                      $stuff_to_parse = '';
    1664                                      break;
    1665                                  }
    1666                              }
    1667                              // If we haven't matched a multiline comment, try single-line comments
    1668                              if (!$COMMENT_MATCHED) {
    1669                                  foreach ($this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark) {
    1670                                      $com_len = strlen($comment_mark);
    1671                                      $test_str = substr($part, $i, $com_len);
    1672                                      if ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS]) {
    1673                                          $match = ($comment_mark == $test_str);
    1674                                      } else {
    1675                                          $match = (strtolower($comment_mark) == strtolower($test_str));
    1676                                      }
    1677                                      if ($match) {
    1678                                          $COMMENT_MATCHED = true;
    1679                                          if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
    1680                                              if (!$this->use_classes) {
    1681                                                  $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment_key] . '"';
    1682                                              } else {
    1683                                                  $attributes = ' class="co' . $comment_key . '"';
    1684                                              }
    1685                                              $test_str = "<span$attributes>" . @htmlspecialchars($this->change_case($test_str), ENT_COMPAT, $this->encoding);
    1686                                          } else {
    1687                                              $test_str = @htmlspecialchars($test_str, ENT_COMPAT, $this->encoding);
    1688                                          }
    1689                                          $close_pos = strpos($part, "\n", $i);
    1690                                          if ($close_pos === false) {
    1691                                              $close_pos = strlen($part);
    1692                                          }
    1693                                          $test_str .= @htmlspecialchars(substr($part, $i + $com_len, $close_pos - $i - $com_len), ENT_COMPAT, $this->encoding);
    1694                                          if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
    1695                                              $test_str .= "</span>";
    1696                                          }
    1697                                          $test_str .= "\n";
    1698                                          $i = $close_pos;
    1699                                          // parse the rest
    1700                                          $result .= $this->parse_non_string_part($stuff_to_parse);
    1701                                          $stuff_to_parse = '';
    1702                                          break;
    1703                                      }
    1704                                  }
    1705                              }
    1706                          } elseif ($STRING_OPEN != '') {
    1707                              // Otherwise, convert it to HTML form
    1708                              if (strtolower($this->encoding) == 'utf-8') {
    1709                                  //only escape <128 (we don't want to break multibyte chars)
    1710                                  if (ord($char) < 128) {
    1711                                      $char = @htmlspecialchars($char, ENT_COMPAT, $this->encoding);
    1712                                  }
    1713                              } else {
    1714                                  //encode everthing
    1715                                  $char = @htmlspecialchars($char, ENT_COMPAT, $this->encoding);
    1716                              }
    1717                          }
    1718                          // Where are we adding this char?
    1719                          if (!$COMMENT_MATCHED) {
    1720                              if (($STRING_OPEN == '') && !$CLOSE_STRING) {
    1721                                  $stuff_to_parse .= $char;
    1722                              } else {
    1723                                  $result .= $char;
    1724                                  $CLOSE_STRING = false;
    1725                              }
    1726                          } else {
    1727                              $result .= $test_str;
    1728                              $COMMENT_MATCHED = false;
    1729                          }
    1730                      }
    1731                      // Parse the last bit
    1732                      $result .= $this->parse_non_string_part($stuff_to_parse);
    1733                      $stuff_to_parse = '';
    1734                  } else {
    1735                      $result .= @htmlspecialchars($part, ENT_COMPAT, $this->encoding);
    1736                  }
    1737                  // Close the <span> that surrounds the block
    1738                  // Removed since the only time this is used is for php and it doesn't need a </span>
    1739                  /*if ($this->strict_mode && $this->lexic_permissions['SCRIPT']) {
    1740  					$result .= '</span>';
    1741  				}*/
    1742              } else {
    1743                  // Else not a block to highlight
    1744                  $result .= @htmlspecialchars($part, ENT_COMPAT, $this->encoding);
    1745              }
    1746          }
    1747
    1748          // Parse the last stuff (redundant?)
    1749          $result .= $this->parse_non_string_part($stuff_to_parse);
    1750
    1751          // Lop off the very first and last spaces
    1752          $result = substr($result, 1, strlen($result) - 1);
    1753
    1754          // Are we still in a string?
    1755          if ($STRING_OPEN) {
    1756              $result .= '</span>';
    1757          }
    1758
    1759          // We're finished: stop timing
    1760          $this->set_time($start_time, microtime());
    1761
    1762          return $this->finalise($result);
    1763      }
    1764
    1765      /**
    1766  	 * Swaps out spaces and tabs for HTML indentation. Not needed if
    1767  	 * the code is in a pre block...
    1768       *
    1769       * @param  string The source to indent
    1770       * @return string The source with HTML indenting applied
    1771       * @since  1.0.0
    1772       * @access private
    1773  	 */
    1774      function indent ($result)
    1775      {
    1776              /// Replace tabs with the correct number of spaces
    1777              if (false !== strpos($result, "\t")) {
    1778                  $lines = explode("\n", $result);
    1779                  foreach ($lines as $key => $line) {
    1780                      if (false === strpos($line, "\t")) {
    1781                          $lines[$key] = $line;
    1782                          continue;
    1783                      }//echo 'checking line ' . $key . '<br />';
    1784
    1785                      $pos = 0;
    1786                      $tab_width = $this->tab_width;
    1787                      $length = strlen($line);
    1788                      $result_line = '';
    1789
    1790                      //echo '<pre>line: ' . htmlspecialchars($line) . '</pre>';
    1791                      $IN_TAG = false;
    1792                      for ($i = 0; $i < $length; $i++) {
    1793                          $char = substr($line, $i, 1);
    1794                          // Simple engine to work out whether we're in a tag.
    1795                          // If we are we modify $pos. This is so we ignore HTML
    1796                          // in the line and only workout the tab replacement
    1797                          // via the actual content of the string
    1798                          // This test could be improved to include strings in the
    1799                          // html so that < or > would be allowed in user's styles
    1800                          // (e.g. quotes: '<' '>'; or similar)
    1801                          if ($IN_TAG && '>' == $char) {
    1802                              $IN_TAG = false;
    1803                              $result_line .= '>';
    1804                              ++$pos;
    1805                          } elseif (!$IN_TAG && '<' == $char) {
    1806                              $IN_TAG = true;
    1807                              $result_line .= '<';
    1808                              ++$pos;
    1809                          } elseif (!$IN_TAG && '&' == $char) {
    1810                              //echo "matched &amp; in line... ";
    1811                              $substr = substr($line, $i + 3, 4);
    1812                              //$substr_5 = substr($line, 5, 1);
    1813                              $posi = strpos($substr, ';');
    1814                              if (false !== $posi) {
    1815                                  //echo "found entity at $posi\n";
    1816                                  $pos += $posi + 3;
    1817                              }
    1818                              $result_line .= '&';
    1819                          } elseif (!$IN_TAG && "\t" == $char) {
    1820                              $str = '';
    1821                              // OPTIMISE - move $strs out. Make an array:
    1822                              // $tabs = array(
    1823                              //  1 => '&nbsp;',
    1824                              //  2 => '&nbsp; ',
    1825                              //  3 => '&nbsp; &nbsp;' etc etc
    1826                              // to use instead of building a string every time
    1827                              $strs = array(0 => '&nbsp;', 1 => ' ');
    1828                              //echo "building (pos=$pos i=$i) (" . ($i - $pos) . ") " . ($tab_width - (($i - $pos) % $tab_width)) . " spaces\n";
    1829                              for ($k = 0; $k < ($tab_width - (($i - $pos) % $tab_width)); $k++) $str .= $strs[$k % 2];
    1830                              $result_line .= $str;
    1831                              //$pos--;
    1832                              $pos++;
    1833                              //$pos -= $tab_width-1;
    1834
    1835                              if (false === strpos($line, "\t", $i + 1)) {
    1836                                  //$lines[$key] = $result_line;
    1837                                  //echo 'got here';
    1838                                  $result_line .= substr($line, $i + 1);
    1839                                  break;
    1840                              }
    1841                          } elseif ( $IN_TAG ) {
    1842                              ++$pos;
    1843                              $result_line .= $char;
    1844                          } else {
    1845                              $result_line .= $char;
    1846                              //++$pos;
    1847                          }
    1848                      }
    1849                      $lines[$key] = $result_line;
    1850                  }
    1851                  $result = implode("\n", $lines);
    1852              }
    1853          // Other whitespace
    1854          $result = str_replace('  ', '&nbsp; ', $result);
    1855          $result = str_replace('  ', ' &nbsp;', $result);
    1856          $result = str_replace("\n ", "\n&nbsp;", $result);
    1857
    1858          if ($this->line_numbers == GESHI_NO_LINE_NUMBERS) {
    1859              $result = nl2br($result);
    1860          }
    1861          return $result;
    1862      }
    1863
    1864      /**
    1865  	 * Changes the case of a keyword for those languages where a change is asked for
    1866       *
    1867       * @param  string The keyword to change the case of
    1868       * @return string The keyword with its case changed
    1869       * @since  1.0.0
    1870       * @access private
    1871  	 */
    1872      function change_case ($instr)
    1873      {
    1874          if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_UPPER) {
    1875              return strtoupper($instr);
    1876          } elseif ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_LOWER) {
    1877              return strtolower($instr);
    1878          }
    1879          return $instr;
    1880      }
    1881
    1882      /**
    1883  	 * Adds a url to a keyword where needed.
    1884  	 *
    1885       * @param  string The keyword to add the URL HTML to
    1886       * @param  int What group the keyword is from
    1887       * @param  boolean Whether to get the HTML for the start or end
    1888       * @return The HTML for either the start or end of the HTML &lt;a&gt; tag
    1889       * @since  1.0.2
    1890       * @access private
    1891       * @todo   Get rid of ender
    1892  	 */
    1893      function add_url_to_keyword ($keyword, $group, $start_or_end)
    1894      {
    1895          if (isset($this->language_data['URLS'][$group]) &&
    1896              $this->language_data['URLS'][$group] != '' &&
    1897              substr($keyword, 0, 5) != '&lt;/') {
    1898              // There is a base group for this keyword
    1899              if ($start_or_end == 'BEGIN') {
    1900                  // HTML workaround... not good form (tm) but should work for 1.0.X
    1901                  $keyword = ( substr($keyword, 0, 4) == '&lt;' ) ? substr($keyword, 4) : $keyword;
    1902                  $keyword = ( substr($keyword, -4) == '&gt;' ) ? substr($keyword, 0, strlen($keyword) - 4) : $keyword;
    1903                  if ($keyword != '') {
    1904                      $keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword);
    1905                      return '<|UR1|"' .
    1906                          str_replace(
    1907                              array('{FNAME}', '.'),
    1908                              array(@htmlspecialchars($keyword, ENT_COMPAT, $this->encoding), '<DOT>'),
    1909                              $this->language_data['URLS'][$group]
    1910                          ) . '">';
    1911                  }
    1912                  return '';
    1913              } else {
    1914                  return '</a>';
    1915              }
    1916          }
    1917      }
    1918
    1919      /**
    1920  	 * Takes a string that has no strings or comments in it, and highlights
    1921  	 * stuff like keywords, numbers and methods.
    1922       *
    1923       * @param string The string to parse for keyword, numbers etc.
    1924       * @since 1.0.0
    1925       * @access private
    1926       * @todo BUGGY! Why? Why not build string and return?
    1927  	 */
    1928      function parse_non_string_part (&$stuff_to_parse)
    1929      {
    1930          $stuff_to_parse = ' ' . quotemeta(@htmlspecialchars($stuff_to_parse, ENT_COMPAT, $this->encoding));
    1931          // These vars will disappear in the future
    1932          $func = '$this->change_case';
    1933          $func2 = '$this->add_url_to_keyword';
    1934
    1935          //
    1936          // Regular expressions
    1937          //
    1938          foreach ($this->language_data['REGEXPS'] as $key => $regexp) {
    1939              if ($this->lexic_permissions['REGEXPS'][$key]) {
    1940                  if (is_array($regexp)) {
    1941                      $stuff_to_parse = preg_replace(
    1942                          "#" .
    1943                          $regexp[GESHI_SEARCH] .
    1944                          "#{$regexp[GESHI_MODIFIERS]}",
    1945                          "{$regexp[GESHI_BEFORE]}<|!REG3XP$key!>{$regexp[GESHI_REPLACE]}|>{$regexp[GESHI_AFTER]}",
    1946                          $stuff_to_parse
    1947                      );
    1948                  } else {
    1949                      $stuff_to_parse = preg_replace( "#(" . $regexp . ")#", "<|!REG3XP$key!>\\1|>", $stuff_to_parse);
    1950                  }
    1951              }
    1952          }
    1953
    1954          //
    1955          // Highlight numbers. This regexp sucks... anyone with a regexp that WORKS
    1956          // here wins a cookie if they send it to me. At the moment there's two doing
    1957          // almost exactly the same thing, except the second one prevents a number
    1958          // being highlighted twice (eg <span...><span...>5</span></span>)
    1959          // Put /NUM!/ in for the styles, which gets replaced at the end.
    1960          //
    1961          if ($this->lexic_permissions['NUMBERS'] && preg_match('#[0-9]#', $stuff_to_parse )) {
    1962              $stuff_to_parse = preg_replace('#([^a-zA-Z0-9\#])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
    1963              $stuff_to_parse = preg_replace('#([^a-zA-Z0-9\#>])([0-9]+)([^a-zA-Z0-9])#', "\\1<|/NUM!/>\\2|>\\3", $stuff_to_parse);
    1964          }
    1965
    1966          // Highlight keywords
    1967          // if there is a couple of alpha symbols there *might* be a keyword
    1968          if (preg_match('#[a-zA-Z]{2,}#', $stuff_to_parse)) {
    1969              foreach ($this->language_data['KEYWORDS'] as $k => $keywordset) {
    1970                  if ($this->lexic_permissions['KEYWORDS'][$k]) {
    1971                      foreach ($keywordset as $keyword) {
    1972                          $keyword = quotemeta($keyword);
    1973                          //
    1974                          // This replacement checks the word is on it's own (except if brackets etc
    1975                          // are next to it), then highlights it. We don't put the color=" for the span
    1976                          // in just yet - otherwise languages with the keywords "color" or "or" have
    1977                          // a fit.
    1978                          //
    1979                          if (false !== stristr($stuff_to_parse, $keyword )) {
    1980                              $stuff_to_parse .= ' ';
    1981                              // Might make a more unique string for putting the number in soon
    1982                              // Basically, we don't put the styles in yet because then the styles themselves will
    1983                              // get highlighted if the language has a CSS keyword in it (like CSS, for example ;))
    1984                              $styles = "/$k/";
    1985                              $keyword = quotemeta($keyword);
    1986                              if ($this->language_data['CASE_SENSITIVE'][$k]) {
    1987                                  $stuff_to_parse = preg_replace(
    1988                                      "#([^a-zA-Z0-9\$_\|\#;>])($keyword)([^a-zA-Z0-9_<\|%\-&])#e",
    1989                                      "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END') . '\\3'",
    1990                                      $stuff_to_parse
    1991                                  );
    1992                              } else {
    1993                                  // Change the case of the word.
    1994                                  $stuff_to_parse = preg_replace(
    1995                                      "#([^a-zA-Z0-9\$_\|\#;>])($keyword)([^a-zA-Z0-9_<\|%\-&])#ie",
    1996                                      "'\\1' . $func2('\\2', '$k', 'BEGIN') . '<|$styles>' . $func('\\2') . '|>' . $func2('\\2', '$k', 'END') . '\\3'",
    1997                                      $stuff_to_parse
    1998                                  );
    1999                              }
    2000                              $stuff_to_parse = substr($stuff_to_parse, 0, strlen($stuff_to_parse) - 1);
    2001                          }
    2002                      }
    2003                  }
    2004              }
    2005          }
    2006
    2007          //
    2008          // Now that's all done, replace /[number]/ with the correct styles
    2009          //
    2010          foreach ($this->language_data['KEYWORDS'] as $k => $kws) {
    2011              if (!$this->use_classes) {
    2012                  $attributes = ' style="' . $this->language_data['STYLES']['KEYWORDS'][$k] . '"';
    2013              } else {
    2014                  $attributes = ' class="kw' . $k . '"';
    2015              }
    2016              $stuff_to_parse = str_replace("/$k/", $attributes, $stuff_to_parse);
    2017          }
    2018
    2019          // Put number styles in
    2020          if (!$this->use_classes && $this->lexic_permissions['NUMBERS']) {
    2021              $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][0] . '"';
    2022          } else {
    2023              $attributes = ' class="nu0"';
    2024          }
    2025          $stuff_to_parse = str_replace('/NUM!/', $attributes, $stuff_to_parse);
    2026
    2027          //
    2028          // Highlight methods and fields in objects
    2029          //
    2030          if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) {
    2031              foreach ($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) {
    2032                  if (false !== stristr($stuff_to_parse, $splitter)) {
    2033                      if (!$this->use_classes) {
    2034                          $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][$key] . '"';
    2035                      } else {
    2036                          $attributes = ' class="me' . $key . '"';
    2037                      }
    2038                      $stuff_to_parse = preg_replace("#(" . quotemeta($this->language_data['OBJECT_SPLITTERS'][$key]) . "[\s]*)([a-zA-Z\*\(][a-zA-Z0-9_\*]*)#", "\\1<|$attributes>\\2|>", $stuff_to_parse);
    2039                  }
    2040              }
    2041          }
    2042
    2043          //
    2044          // Highlight brackets. Yes, I've tried adding a semi-colon to this list.
    2045          // You try it, and see what happens ;)
    2046          // TODO: Fix lexic permissions not converting entities if shouldn't
    2047          // be highlighting regardless
    2048          //
    2049          if ($this->lexic_permissions['BRACKETS']) {
    2050              $code_entities_match = array('[', ']', '(', ')', '{', '}');
    2051              if (!$this->use_classes) {
    2052                  $code_entities_replace = array(
    2053                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#91;|>',
    2054                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#93;|>',
    2055                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#40;|>',
    2056                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#41;|>',
    2057                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#123;|>',
    2058                      '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#125;|>',
    2059                  );
    2060              } else {
    2061                  $code_entities_replace = array(
    2062                      '<| class="br0">&#91;|>',
    2063                      '<| class="br0">&#93;|>',
    2064                      '<| class="br0">&#40;|>',
    2065                      '<| class="br0">&#41;|>',
    2066                      '<| class="br0">&#123;|>',
    2067                      '<| class="br0">&#125;|>',
    2068                  );
    2069              }
    2070              $stuff_to_parse = str_replace( $code_entities_match,  $code_entities_replace, $stuff_to_parse );
    2071          }
    2072
    2073          //
    2074          // Add class/style for regexps
    2075          //
    2076          foreach ($this->language_data['REGEXPS'] as $key => $regexp) {
    2077              if ($this->lexic_permissions['REGEXPS'][$key]) {
    2078                  if (!$this->use_classes) {
    2079                      $attributes = ' style="' . $this->language_data['STYLES']['REGEXPS'][$key] . '"';
    2080                  } else {
    2081                      $attributes = ' class="re' . $key . '"';
    2082                  }
    2083                  $stuff_to_parse = str_replace("!REG3XP$key!", "$attributes", $stuff_to_parse);
    2084              }
    2085          }
    2086
    2087          // Replace <DOT> with . for urls
    2088          $stuff_to_parse = str_replace('<DOT>', '.', $stuff_to_parse);
    2089          // Replace <|UR1| with <a href= for urls also
    2090          if (isset($this->link_styles[GESHI_LINK])) {
    2091              if ($this->use_classes) {
    2092                  $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse);
    2093              } else {
    2094                  $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' style="' . $this->link_styles[GESHI_LINK] . '" href=', $stuff_to_parse);
    2095              }
    2096          } else {
    2097              $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse);
    2098          }
    2099
    2100          //
    2101          // NOW we add the span thingy ;)
    2102          //
    2103
    2104          $stuff_to_parse = str_replace('<|', '<span', $stuff_to_parse);
    2105          $stuff_to_parse = str_replace ( '|>', '</span>', $stuff_to_parse );
    2106
    2107          return substr(stripslashes($stuff_to_parse), 1);
    2108      }
    2109
    2110      /**
    2111  	 * Sets the time taken to parse the code
    2112       *
    2113       * @param microtime The time when parsing started
    2114       * @param microtime The time when parsing ended
    2115       * @since 1.0.2
    2116       * @access private
    2117  	 */
    2118      function set_time ($start_time, $end_time)
    2119      {
    2120          $start = explode(' ', $start_time);
    2121          $end = explode(' ', $end_time);
    2122          $this->time = $end[0] + $end[1] - $start[0] - $start[1];
    2123      }
    2124
    2125      /**
    2126  	 * Gets the time taken to parse the code
    2127       *
    2128       * @return double The time taken to parse the code
    2129       * @since  1.0.2
    2130  	 */
    2131      function get_time ()
    2132      {
    2133          return $this->time;
    2134      }
    2135
    2136      /**
    2137  	 * Gets language information and stores it for later use
    2138       *
    2139       * @access private
    2140       * @todo Needs to load keys for lexic permissions for keywords, regexps etc
    2141  	 */
    2142      function load_language ($file_name)
    2143      {
    2144          $language_data = array();
    2145          require $file_name;
    2146          // Perhaps some checking might be added here later to check that
    2147          // $language data is a valid thing but maybe not
    2148          $this->language_data = $language_data;
    2149          // Set strict mode if should be set
    2150          if ($this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS) {
    2151              $this->strict_mode = true;
    2152          }
    2153          // Set permissions for all lexics to true
    2154          // so they'll be highlighted by default
    2155          foreach ($this->language_data['KEYWORDS'] as $key => $words) {
    2156              $this->lexic_permissions['KEYWORDS'][$key] = true;
    2157          }
    2158          foreach ($this->language_data['COMMENT_SINGLE'] as $key => $comment) {
    2159              $this->lexic_permissions['COMMENTS'][$key] = true;
    2160          }
    2161          foreach ($this->language_data['REGEXPS'] as $key => $regexp) {
    2162              $this->lexic_permissions['REGEXPS'][$key] = true;
    2163          }
    2164          $this->enable_highlighting();
    2165          // Set default class for CSS
    2166          $this->overall_class = $this->language;
    2167      }
    2168
    2169      /**
    2170  	 * Takes the parsed code and various options, and creates the HTML
    2171  	 * surrounding it to make it look nice.
    2172       *
    2173       * @param  string The code already parsed
    2174       * @return string The code nicely finalised
    2175       * @since  1.0.0
    2176       * @access private
    2177  	 */
    2178      function finalise ($parsed_code)
    2179      {
    2180          // Remove end parts of important declarations
    2181          // This is BUGGY!! My fault for bad code: fix coming in 1.2
    2182          // @todo Remove this crap
    2183          if ($this->enable_important_blocks &&
    2184              (strstr($parsed_code, @htmlspecialchars(GESHI_START_IMPORTANT, ENT_COMPAT, $this->encoding)) === false)) {
    2185              $parsed_code = str_replace(@htmlspecialchars(GESHI_END_IMPORTANT, ENT_COMPAT, $this->encoding), '', $parsed_code);
    2186          }
    2187
    2188          // Add HTML whitespace stuff if we're using the <div> header
    2189          if ($this->header_type != GESHI_HEADER_PRE) {
    2190              $parsed_code = $this->indent($parsed_code);
    2191          }
    2192
    2193          // If we're using line numbers, we insert <li>s and appropriate
    2194          // markup to style them (otherwise we don't need to do anything)
    2195          if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
    2196              // If we're using the <pre> header, we shouldn't add newlines because
    2197              // the <pre> will line-break them (and the <li>s already do this for us)
    2198              $ls = ($this->header_type != GESHI_HEADER_PRE) ? "\n" : '';
    2199              // Get code into lines
    2200              $code = explode("\n", $parsed_code);
    2201              // Set vars to defaults for following loop
    2202              $parsed_code = '';
    2203              $i = 0;
    2204              // Foreach line...
    2205              foreach ($code as $line) {
    2206                  $line = ( $line ) ? $line : '&nbsp;';
    2207                  // If this is a "special line"...
    2208                  if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS &&
    2209                      $i % $this->line_nth_row == ($this->line_nth_row - 1)) {
    2210                      // Set the attributes to style the line
    2211                      if ($this->use_classes) {
    2212                          $attr = ' class="li2"';
    2213                          $def_attr = ' class="de2"';
    2214                      } else {
    2215                          $attr = ' style="' . $this->line_style2 . '"';
    2216                          // This style "covers up" the special styles set for special lines
    2217                          // so that styles applied to special lines don't apply to the actual
    2218                          // code on that line
    2219                          $def_attr = ' style="' . $this->code_style . '"';
    2220                      }
    2221                      // Span or div?
    2222                      $start = "<div$def_attr>";
    2223                      $end = '</div>';
    2224                  } else {
    2225                      if ($this->use_classes) {
    2226                          $attr = ' class="li1"';
    2227                          $def_attr = ' class="de1"';
    2228                      } else {
    2229                          $attr = ' style="' . $this->line_style1 . '"';
    2230                          $def_attr = ' style="' . $this->code_style . '"';
    2231                      }
    2232                      $start = "<div$def_attr>";
    2233                      $end = '</div>';
    2234                  }
    2235
    2236                  ++$i;
    2237                  // Are we supposed to use ids? If so, add them
    2238                  if ($this->add_ids) {
    2239                      $attr .= " id=\"{$this->overall_id}-{$i}\"";
    2240                  }
    2241                  if ($this->use_classes && in_array($i, $this->highlight_extra_lines)) {
    2242                      $attr .= " class=\"ln-xtra\"";
    2243                  }
    2244                  if (!$this->use_classes && in_array($i, $this->highlight_extra_lines)) {
    2245                      $attr .= " style=\"{$this->highlight_extra_lines_style}\"";
    2246                  }
    2247
    2248                  // Add in the line surrounded by appropriate list HTML
    2249                  $parsed_code .= "<li$attr>$start$line$end</li>$ls";
    2250              }
    2251          } else {
    2252              // No line numbers, but still need to handle highlighting lines extra.
    2253              // Have to use divs so the full width of the code is highlighted
    2254              $code = explode("\n", $parsed_code);
    2255              $parsed_code = '';
    2256              $i = 0;
    2257              foreach ($code as $line)
    2258              {
    2259                  // Make lines have at least one space in them if they're empty
    2260                  $line = ($line) ? $line : '&nbsp;';
    2261                  if (in_array(++$i, $this->highlight_extra_lines)) {
    2262                      if ($this->use_classes) {
    2263                          $parsed_code .= '<div class="ln-xtra">';
    2264                      } else {
    2265                          $parsed_code .= "<div style=\"{$this->highlight_extra_lines_style}\">";
    2266                      }
    2267                      $parsed_code .= $line . "</div>\n";
    2268                  } else {
    2269                      $parsed_code .= $line . "\n";
    2270                  }
    2271              }
    2272          }
    2273
    2274          // purge some unnecessary stuff
    2275          $parsed_code = preg_replace('#<span[^>]+>(\s*)</span>#', '\\1', $parsed_code);
    2276          $parsed_code = preg_replace('#<div[^>]+>(\s*)</div>#', '\\1', $parsed_code);
    2277
    2278          if ($this->header_type == GESHI_HEADER_PRE) {
    2279              // enforce line numbers when using pre
    2280              $parsed_code = str_replace('<li></li>', '<li>&nbsp;</li>', $parsed_code);
    2281          }
    2282
    2283          return $this->header() . chop($parsed_code) . $this->footer();
    2284      }
    2285
    2286      /**
    2287  	 * Creates the header for the code block (with correct attributes)
    2288       *
    2289       * @return string The header for the code block
    2290       * @since  1.0.0
    2291       * @access private
    2292  	 */
    2293      function header ()
    2294      {
    2295          // Get attributes needed
    2296          $attributes = $this->get_attributes();
    2297
    2298          $ol_attributes = '';
    2299
    2300          if ($this->line_numbers_start != 1) {
    2301              $ol_attributes .= ' start="' . $this->line_numbers_start . '"';
    2302          }
    2303
    2304          // Get the header HTML
    2305          $header = $this->format_header_content();
    2306
    2307          if (GESHI_HEADER_NONE == $this->header_type) {
    2308              return "$header<ol$ol_attributes>";
    2309          }
    2310
    2311          // Work out what to return and do it
    2312          if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
    2313              if ($this->header_type == GESHI_HEADER_PRE) {
    2314                  return "<pre$attributes>$header<ol$ol_attributes>";
    2315              } elseif ($this->header_type == GESHI_HEADER_DIV) {
    2316                  return "<div$attributes>$header<ol$ol_attributes>";
    2317              }
    2318          } else {
    2319              if ($this->header_type == GESHI_HEADER_PRE) {
    2320                  return "<pre$attributes>$header";
    2321              } elseif ($this->header_type == GESHI_HEADER_DIV) {
    2322                  return "<div$attributes>$header";
    2323              }
    2324          }
    2325      }
    2326
    2327      /**
    2328  	 * Returns the header content, formatted for output
    2329       *
    2330       * @return string The header content, formatted for output
    2331       * @since  1.0.2
    2332       * @access private
    2333  	 */
    2334      function format_header_content ()
    2335      {
    2336          $header = $this->header_content;
    2337          if ($header) {
    2338              if ($this->header_type == GESHI_HEADER_PRE) {
    2339                  $header = str_replace("\n", '', $header);
    2340              }
    2341              $header = $this->replace_keywords($header);
    2342
    2343              if ($this->use_classes) {
    2344                  $attr = ' class="head"';
    2345              } else {
    2346                  $attr = " style=\"{$this->header_content_style}\"";
    2347              }
    2348              return "<div$attr>$header</div>";
    2349          }
    2350      }
    2351
    2352      /**
    2353  	 * Returns the footer for the code block.
    2354       *
    2355       * @return string The footer for the code block
    2356       * @since  1.0.0
    2357       * @access private
    2358  	 */
    2359      function footer ()
    2360      {
    2361          $footer_content = $this->format_footer_content();
    2362
    2363          if (GESHI_HEADER_NONE == $this->header_type) {
    2364              return ($this->line_numbers != GESHI_NO_LINE_NUMBERS) ? '</ol>' . $footer_content
    2365                  : $footer_content;
    2366          }
    2367
    2368          if ($this->header_type == GESHI_HEADER_DIV) {
    2369              if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
    2370                  return "</ol>$footer_content</div>";
    2371              }
    2372              return "$footer_content</div>";
    2373          } else {
    2374              if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
    2375                  return "</ol>$footer_content</pre>";
    2376              }
    2377              return "$footer_content</pre>";
    2378          }
    2379      }
    2380
    2381      /**
    2382  	 * Returns the footer content, formatted for output
    2383       *
    2384       * @return string The footer content, formatted for output
    2385       * @since  1.0.2
    2386       * @access private
    2387  	 */
    2388      function format_footer_content ()
    2389      {
    2390          $footer = $this->footer_content;
    2391          if ($footer) {
    2392              if ($this->header_type == GESHI_HEADER_PRE) {
    2393                  $footer = str_replace("\n", '', $footer);;
    2394              }
    2395              $footer = $this->replace_keywords($footer);
    2396
    2397              if ($this->use_classes) {
    2398                  $attr = ' class="foot"';
    2399              } else {
    2400                  $attr = " style=\"{$this->footer_content_style}\">";
    2401              }
    2402              return "<div$attr>$footer</div>";
    2403          }
    2404      }
    2405
    2406      /**
    2407  	 * Replaces certain keywords in the header and footer with
    2408  	 * certain configuration values
    2409       *
    2410       * @param  string The header or footer content to do replacement on
    2411       * @return string The header or footer with replaced keywords
    2412       * @since  1.0.2
    2413       * @access private
    2414  	 */
    2415      function replace_keywords ($instr)
    2416      {
    2417          $keywords = $replacements = array();
    2418
    2419          $keywords[] = '<TIME>';
    2420          $replacements[] = number_format($this->get_time(), 3);
    2421
    2422          $keywords[] = '<LANGUAGE>';
    2423          $replacements[] = $this->language;
    2424
    2425          $keywords[] = '<VERSION>';
    2426          $replacements[] = GESHI_VERSION;
    2427
    2428          return str_replace($keywords, $replacements, $instr);
    2429      }
    2430
    2431      /**
    2432  	 * Gets the CSS attributes for this code
    2433       *
    2434       * @return The CSS attributes for this code
    2435       * @since  1.0.0
    2436       * @access private
    2437       * @todo   Document behaviour change - class is outputted regardless of whether we're using classes or not.
    2438       *         Same with style
    2439  	 */
    2440      function get_attributes ()
    2441      {
    2442          $attributes = '';
    2443
    2444          if ($this->overall_class != '') {
    2445              $attributes .= " class=\"{$this->overall_class}\"";
    2446          }
    2447          if ($this->overall_id != '') {
    2448              $attributes .= " id=\"{$this->overall_id}\"";
    2449          }
    2450          if ($this->overall_style != '') {
    2451              $attributes .= ' style="' . $this->overall_style . '"';
    2452          }
    2453          return $attributes;
    2454      }
    2455
    2456      /**
    2457  	 * Returns a stylesheet for the highlighted code. If $economy mode
    2458  	 * is true, we only return the stylesheet declarations that matter for
    2459  	 * this code block instead of the whole thing
    2460       *
    2461       * @param  boolean Whether to use economy mode or not
    2462       * @return string A stylesheet built on the data for the current language
    2463       * @since  1.0.0
    2464  	 */
    2465      function get_stylesheet ($economy_mode = true)
    2466      {
    2467          // If there's an error, chances are that the language file
    2468          // won't have populated the language data file, so we can't
    2469          // risk getting a stylesheet...
    2470          if ($this->error) {
    2471              return '';
    2472          }
    2473          // First, work out what the selector should be. If there's an ID,
    2474          // that should be used, the same for a class. Otherwise, a selector
    2475          // of '' means that these styles will be applied anywhere
    2476          $selector = ($this->overall_id != '') ? "#{$this->overall_id} " : '';
    2477          $selector = ($selector == '' && $this->overall_class != '') ? ".{$this->overall_class} " : $selector;
    2478
    2479          // Header of the stylesheet
    2480          if (!$economy_mode) {
    2481              $stylesheet = "/**\n * GeSHi Dynamically Generated Stylesheet\n * --------------------------------------\n * Dynamically generated stylesheet for {$this->language}\n * CSS class: {$this->overall_class}, CSS id: {$this->overall_id}\n * GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter)\n */\n";
    2482           } else {
    2483              $stylesheet = '/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */' . "\n";
    2484          }
    2485
    2486          // Set the <ol> to have no effect at all if there are line numbers
    2487          // (<ol>s have margins that should be destroyed so all layout is
    2488          // controlled by the set_overall_style method, which works on the
    2489          // <pre> or <div> container). Additionally, set default styles for lines
    2490          if (!$economy_mode || $this->line_numbers != GESHI_NO_LINE_NUMBERS) {
    2491              //$stylesheet .= "$selector, {$selector}ol, {$selector}ol li {margin: 0;}\n";
    2492              $stylesheet .= "$selector.de1, $selector.de2 {{$this->code_style}}\n";
    2493          }
    2494
    2495          // Add overall styles
    2496          if (!$economy_mode || $this->overall_style != '') {
    2497              $stylesheet .= "$selector {{$this->overall_style}}\n";
    2498          }
    2499
    2500          // Add styles for links
    2501          foreach ($this->link_styles as $key => $style) {
    2502              if (!$economy_mode || $key == GESHI_LINK && $style != '') {
    2503                  $stylesheet .= "{$selector}a:link {{$style}}\n";
    2504              }
    2505              if (!$economy_mode || $key == GESHI_HOVER && $style != '') {
    2506                  $stylesheet .= "{$selector}a:hover {{$style}}\n";
    2507              }
    2508              if (!$economy_mode || $key == GESHI_ACTIVE && $style != '') {
    2509                  $stylesheet .= "{$selector}a:active {{$style}}\n";
    2510              }
    2511              if (!$economy_mode || $key == GESHI_VISITED && $style != '') {
    2512                  $stylesheet .= "{$selector}a:visited {{$style}}\n";
    2513              }
    2514          }
    2515
    2516          // Header and footer
    2517          if (!$economy_mode || $this->header_content_style != '') {
    2518              $stylesheet .= "$selector.head {{$this->header_content_style}}\n";
    2519          }
    2520          if (!$economy_mode || $this->footer_content_style != '') {
    2521              $stylesheet .= "$selector.foot {{$this->footer_content_style}}\n";
    2522          }
    2523
    2524          // Styles for important stuff
    2525          if (!$economy_mode || $this->important_styles != '') {
    2526              $stylesheet .= "$selector.imp {{$this->important_styles}}\n";
    2527          }
    2528
    2529          // Styles for lines being highlighted extra
    2530          if (!$economy_mode || count($this->highlight_extra_lines)) {
    2531              $stylesheet .= "$selector.ln-xtra {{$this->highlight_extra_lines_style}}\n";
    2532          }
    2533
    2534          // Simple line number styles
    2535          if (!$economy_mode || ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->line_style1 != '')) {
    2536              $stylesheet .= "{$selector}li {{$this->line_style1}}\n";
    2537          }
    2538
    2539          // If there is a style set for fancy line numbers, echo it out
    2540          if (!$economy_mode || ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $this->line_style2 != '')) {
    2541              $stylesheet .= "{$selector}li.li2 {{$this->line_style2}}\n";
    2542          }
    2543
    2544          foreach ($this->language_data['STYLES']['KEYWORDS'] as $group => $styles) {
    2545              if (!$economy_mode || !($economy_mode && (!$this->lexic_permissions['KEYWORDS'][$group] || $styles == ''))) {
    2546                  $stylesheet .= "$selector.kw$group {{$styles}}\n";
    2547              }
    2548          }
    2549          foreach ($this->language_data['STYLES']['COMMENTS'] as $group => $styles) {
    2550              if (!$economy_mode || !($economy_mode && $styles == '') &&
    2551                  !($economy_mode && !$this->lexic_permissions['COMMENTS'][$group])) {
    2552                  $stylesheet .= "$selector.co$group {{$styles}}\n";
    2553              }
    2554          }
    2555          foreach ($this->language_data['STYLES']['ESCAPE_CHAR'] as $group => $styles) {
    2556              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2557                  !$this->lexic_permissions['ESCAPE_CHAR'])) {
    2558                  $stylesheet .= "$selector.es$group {{$styles}}\n";
    2559              }
    2560          }
    2561          foreach ($this->language_data['STYLES']['SYMBOLS'] as $group => $styles) {
    2562              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2563                  !$this->lexic_permissions['BRACKETS'])) {
    2564                  $stylesheet .= "$selector.br$group {{$styles}}\n";
    2565              }
    2566          }
    2567          foreach ($this->language_data['STYLES']['STRINGS'] as $group => $styles) {
    2568              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2569                  !$this->lexic_permissions['STRINGS'])) {
    2570                  $stylesheet .= "$selector.st$group {{$styles}}\n";
    2571              }
    2572          }
    2573          foreach ($this->language_data['STYLES']['NUMBERS'] as $group => $styles) {
    2574              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2575                  !$this->lexic_permissions['NUMBERS'])) {
    2576                  $stylesheet .= "$selector.nu$group {{$styles}}\n";
    2577              }
    2578          }
    2579          foreach ($this->language_data['STYLES']['METHODS'] as $group => $styles) {
    2580              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2581                  !$this->lexic_permissions['METHODS'])) {
    2582                  $stylesheet .= "$selector.me$group {{$styles}}\n";
    2583              }
    2584          }
    2585          foreach ($this->language_data['STYLES']['SCRIPT'] as $group => $styles) {
    2586              if (!$economy_mode || !($economy_mode && $styles == '')) {
    2587                  $stylesheet .= "$selector.sc$group {{$styles}}\n";
    2588              }
    2589          }
    2590          foreach ($this->language_data['STYLES']['REGEXPS'] as $group => $styles) {
    2591              if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode &&
    2592                  !$this->lexic_permissions['REGEXPS'][$group])) {
    2593                  $stylesheet .= "$selector.re$group {{$styles}}\n";
    2594              }
    2595          }
    2596
    2597          return $stylesheet;
    2598      }
    2599
    2600  } // End Class GeSHi
    2601
    2602
    2603  if (!function_exists('geshi_highlight')) {
    2604      /**
    2605       * Easy way to highlight stuff. Behaves just like highlight_string
    2606       *
    2607       * @param string The code to highlight
    2608       * @param string The language to highlight the code in
    2609       * @param string The path to the language files. You can leave this blank if you need
    2610       *               as from version 1.0.7 the path should be automatically detected
    2611       * @param boolean Whether to return the result or to echo
    2612       * @return string The code highlighted (if $return is true)
    2613       * @since 1.0.2
    2614       */
    2615      function geshi_highlight ($string, $language, $path, $return = false)
    2616      {
    2617          $geshi = new GeSHi($string, $language, $path);
    2618          $geshi->set_header_type(GESHI_HEADER_DIV);
    2619          if ($return) {
    2620              return str_replace('<div>', '<code>', str_replace('</div>', '</code>', $geshi->parse_code()));
    2621          }
    2622          echo str_replace('<div>', '<code>', str_replace('</div>', '</code>', $geshi->parse_code()));
    2623          if ($geshi->error()) {
    2624              return false;
    2625          }
    2626          return true;
    2627      }
    2628  }
    2629

Documentation generated on Thu, 22 Sep 2005 13:47:59 +1200 by phpDocumentor 1.2.3