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