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