[ Index ]
 

Code source de PRADO 3.0.6

Accédez au Source d'autres logiciels libresSoutenez Angelica Josefina !

title

Body

[fermer]

/framework/3rdParty/geshi/ -> geshi.php (source)

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


Généré le : Sun Feb 25 21:07:04 2007 par Balluche grâce à PHPXref 0.7