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