changeset 2 | 9e3258dfae15 |
parent 0 | 441963e5b07a |
child 3 | f3e2bbbd2155 |
1:c715631f809a | 2:9e3258dfae15 |
---|---|
25 * along with GeSHi; if not, write to the Free Software |
25 * along with GeSHi; if not, write to the Free Software |
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
27 * |
27 * |
28 * @package geshi |
28 * @package geshi |
29 * @subpackage core |
29 * @subpackage core |
30 * @author Nigel McNie <nigel@geshi.org> |
30 * @author Nigel McNie <nigel@geshi.org>, Benny Baumann <BenBE@omorphia.de> |
31 * @copyright (C) 2004 - 2007 Nigel McNie |
31 * @copyright (C) 2004 - 2007 Nigel McNie, (C) 2007 - 2008 Benny Baumann |
32 * @license http://gnu.org/copyleft/gpl.html GNU GPL |
32 * @license http://gnu.org/copyleft/gpl.html GNU GPL |
33 * |
33 * |
34 */ |
34 */ |
35 |
35 |
36 // |
36 // |
39 // their values - you never know when a value may change in a future |
39 // their values - you never know when a value may change in a future |
40 // version |
40 // version |
41 // |
41 // |
42 |
42 |
43 /** The version of this GeSHi file */ |
43 /** The version of this GeSHi file */ |
44 define('GESHI_VERSION', '1.0.7.20'); |
44 define('GESHI_VERSION', '1.0.8.2'); |
45 |
45 |
46 // Define the root directory for the GeSHi code tree |
46 // Define the root directory for the GeSHi code tree |
47 if (!defined('GESHI_ROOT')) { |
47 if (!defined('GESHI_ROOT')) { |
48 /** The root directory for GeSHi */ |
48 /** The root directory for GeSHi */ |
49 define('GESHI_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR); |
49 define('GESHI_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR); |
50 } |
50 } |
51 /** The language file directory for GeSHi |
51 /** The language file directory for GeSHi |
52 @access private */ |
52 @access private */ |
53 define('GESHI_LANG_ROOT', GESHI_ROOT . 'geshi' . DIRECTORY_SEPARATOR); |
53 define('GESHI_LANG_ROOT', GESHI_ROOT . 'geshi' . DIRECTORY_SEPARATOR); |
54 |
54 |
55 // Define if GeSHi should be paranoid about security |
|
56 if (!defined('GESHI_SECURITY_PARANOID')) { |
|
57 /** Tells GeSHi to be paranoid about security settings */ |
|
58 define('GESHI_SECURITY_PARANOID', false); |
|
59 } |
|
55 |
60 |
56 // Line numbers - use with enable_line_numbers() |
61 // Line numbers - use with enable_line_numbers() |
57 /** Use no line numbers when building the result */ |
62 /** Use no line numbers when building the result */ |
58 define('GESHI_NO_LINE_NUMBERS', 0); |
63 define('GESHI_NO_LINE_NUMBERS', 0); |
59 /** Use normal line numbers when building the result */ |
64 /** Use normal line numbers when building the result */ |
66 define('GESHI_HEADER_NONE', 0); |
71 define('GESHI_HEADER_NONE', 0); |
67 /** Use a "div" to surround the source */ |
72 /** Use a "div" to surround the source */ |
68 define('GESHI_HEADER_DIV', 1); |
73 define('GESHI_HEADER_DIV', 1); |
69 /** Use a "pre" to surround the source */ |
74 /** Use a "pre" to surround the source */ |
70 define('GESHI_HEADER_PRE', 2); |
75 define('GESHI_HEADER_PRE', 2); |
76 /** Use a pre to wrap lines when line numbers are enabled or to wrap the whole code. */ |
|
77 define('GESHI_HEADER_PRE_VALID', 3); |
|
78 /** |
|
79 * Use a "table" to surround the source: |
|
80 * |
|
81 * <table> |
|
82 * <thead><tr><td colspan="2">$header</td></tr></thead> |
|
83 * <tbody><tr><td><pre>$linenumbers</pre></td><td><pre>$code></pre></td></tr></tbody> |
|
84 * <tfooter><tr><td colspan="2">$footer</td></tr></tfoot> |
|
85 * </table> |
|
86 * |
|
87 * this is essentially only a workaround for Firefox, see sf#1651996 or take a look at |
|
88 * https://bugzilla.mozilla.org/show_bug.cgi?id=365805 |
|
89 * @note when linenumbers are disabled this is essentially the same as GESHI_HEADER_PRE |
|
90 */ |
|
91 define('GESHI_HEADER_PRE_TABLE', 4); |
|
71 |
92 |
72 // Capatalisation constants |
93 // Capatalisation constants |
73 /** Lowercase keywords found */ |
94 /** Lowercase keywords found */ |
74 define('GESHI_CAPS_NO_CHANGE', 0); |
95 define('GESHI_CAPS_NO_CHANGE', 0); |
75 /** Uppercase keywords found */ |
96 /** Uppercase keywords found */ |
100 */ |
121 */ |
101 // When strict mode applies for a language |
122 // When strict mode applies for a language |
102 /** Strict mode never applies (this is the most common) */ |
123 /** Strict mode never applies (this is the most common) */ |
103 define('GESHI_NEVER', 0); |
124 define('GESHI_NEVER', 0); |
104 /** Strict mode *might* apply, and can be enabled or |
125 /** Strict mode *might* apply, and can be enabled or |
105 disabled by {@link GeSHi::enable_strict_mode()} */ |
126 disabled by {@link GeSHi->enable_strict_mode()} */ |
106 define('GESHI_MAYBE', 1); |
127 define('GESHI_MAYBE', 1); |
107 /** Strict mode always applies */ |
128 /** Strict mode always applies */ |
108 define('GESHI_ALWAYS', 2); |
129 define('GESHI_ALWAYS', 2); |
109 |
130 |
110 // Advanced regexp handling constants, used in language files |
131 // Advanced regexp handling constants, used in language files |
126 define('GESHI_CLASS', 5); |
147 define('GESHI_CLASS', 5); |
127 |
148 |
128 /** Used in language files to mark comments */ |
149 /** Used in language files to mark comments */ |
129 define('GESHI_COMMENTS', 0); |
150 define('GESHI_COMMENTS', 0); |
130 |
151 |
152 /** Used to work around missing PHP features **/ |
|
153 define('GESHI_PHP_PRE_433', !(version_compare(PHP_VERSION, '4.3.3') === 1)); |
|
154 |
|
155 /** make sure we can call stripos **/ |
|
156 if (!function_exists('stripos')) { |
|
157 // the offset param of preg_match is not supported below PHP 4.3.3 |
|
158 if (GESHI_PHP_PRE_433) { |
|
159 /** |
|
160 * @ignore |
|
161 */ |
|
162 function stripos($haystack, $needle, $offset = null) { |
|
163 if (!is_null($offset)) { |
|
164 $haystack = substr($haystack, $offset); |
|
165 } |
|
166 if (preg_match('/'. preg_quote($needle, '/') . '/', $haystack, $match, PREG_OFFSET_CAPTURE)) { |
|
167 return $match[0][1]; |
|
168 } |
|
169 return false; |
|
170 } |
|
171 } |
|
172 else { |
|
173 /** |
|
174 * @ignore |
|
175 */ |
|
176 function stripos($haystack, $needle, $offset = null) { |
|
177 if (preg_match('/'. preg_quote($needle, '/') . '/', $haystack, $match, PREG_OFFSET_CAPTURE, $offset)) { |
|
178 return $match[0][1]; |
|
179 } |
|
180 return false; |
|
181 } |
|
182 } |
|
183 } |
|
184 |
|
185 /** some old PHP / PCRE subpatterns only support up to xxx subpatterns in |
|
186 regular expressions. Set this to false if your PCRE lib is up to date |
|
187 @see GeSHi->optimize_regexp_list() |
|
188 **/ |
|
189 define('GESHI_MAX_PCRE_SUBPATTERNS', 500); |
|
190 /** it's also important not to generate too long regular expressions |
|
191 be generous here... but keep in mind, that when reaching this limit we |
|
192 still have to close open patterns. 12k should do just fine on a 16k limit. |
|
193 @see GeSHi->optimize_regexp_list() |
|
194 **/ |
|
195 define('GESHI_MAX_PCRE_LENGTH', 12288); |
|
196 |
|
197 //Number format specification |
|
198 /** Basic number format for integers */ |
|
199 define('GESHI_NUMBER_INT_BASIC', 1); //Default integers \d+ |
|
200 /** Enhanced number format for integers like seen in C */ |
|
201 define('GESHI_NUMBER_INT_CSTYLE', 2); //Default C-Style \d+[lL]? |
|
202 /** Number format to highlight binary numbers with a suffix "b" */ |
|
203 define('GESHI_NUMBER_BIN_SUFFIX', 16); //[01]+[bB] |
|
204 /** Number format to highlight binary numbers with a prefix % */ |
|
205 define('GESHI_NUMBER_BIN_PREFIX_PERCENT', 32); //%[01]+ |
|
206 /** Number format to highlight binary numbers with a prefix 0b (C) */ |
|
207 define('GESHI_NUMBER_BIN_PREFIX_0B', 64); //0b[01]+ |
|
208 /** Number format to highlight octal numbers with a leading zero */ |
|
209 define('GESHI_NUMBER_OCT_PREFIX', 256); //0[0-7]+ |
|
210 /** Number format to highlight octal numbers with a suffix of o */ |
|
211 define('GESHI_NUMBER_OCT_SUFFIX', 512); //[0-7]+[oO] |
|
212 /** Number format to highlight hex numbers with a prefix 0x */ |
|
213 define('GESHI_NUMBER_HEX_PREFIX', 4096); //0x[0-9a-fA-F]+ |
|
214 /** Number format to highlight hex numbers with a suffix of h */ |
|
215 define('GESHI_NUMBER_HEX_SUFFIX', 8192); //[0-9][0-9a-fA-F]*h |
|
216 /** Number format to highlight floating-point numbers without support for scientific notation */ |
|
217 define('GESHI_NUMBER_FLT_NONSCI', 65536); //\d+\.\d+ |
|
218 /** Number format to highlight floating-point numbers without support for scientific notation */ |
|
219 define('GESHI_NUMBER_FLT_NONSCI_F', 131072); //\d+(\.\d+)?f |
|
220 /** Number format to highlight floating-point numbers with support for scientific notation (E) and optional leading zero */ |
|
221 define('GESHI_NUMBER_FLT_SCI_SHORT', 262144); //\.\d+e\d+ |
|
222 /** Number format to highlight floating-point numbers with support for scientific notation (E) and required leading digit */ |
|
223 define('GESHI_NUMBER_FLT_SCI_ZERO', 524288); //\d+(\.\d+)?e\d+ |
|
224 //Custom formats are passed by RX array |
|
225 |
|
131 // Error detection - use these to analyse faults |
226 // Error detection - use these to analyse faults |
132 /** No sourcecode to highlight was specified |
227 /** No sourcecode to highlight was specified |
133 * @deprecated |
228 * @deprecated |
134 */ |
229 */ |
135 define('GESHI_ERROR_NO_INPUT', 1); |
230 define('GESHI_ERROR_NO_INPUT', 1); |
136 /** The language specified does not exist */ |
231 /** The language specified does not exist */ |
137 define('GESHI_ERROR_NO_SUCH_LANG', 2); |
232 define('GESHI_ERROR_NO_SUCH_LANG', 2); |
138 /** GeSHi could not open a file for reading (generally a language file) */ |
233 /** GeSHi could not open a file for reading (generally a language file) */ |
139 define('GESHI_ERROR_FILE_NOT_READABLE', 3); |
234 define('GESHI_ERROR_FILE_NOT_READABLE', 3); |
140 /** The header type passed to {@link GeSHi::set_header_type()} was invalid */ |
235 /** The header type passed to {@link GeSHi->set_header_type()} was invalid */ |
141 define('GESHI_ERROR_INVALID_HEADER_TYPE', 4); |
236 define('GESHI_ERROR_INVALID_HEADER_TYPE', 4); |
142 /** The line number type passed to {@link GeSHi::enable_line_numbers()} was invalid */ |
237 /** The line number type passed to {@link GeSHi->enable_line_numbers()} was invalid */ |
143 define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5); |
238 define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5); |
144 /**#@-*/ |
239 /**#@-*/ |
145 |
240 |
146 |
241 |
147 /** |
242 /** |
150 * Please refer to the documentation for GeSHi 1.0.X that is available |
245 * Please refer to the documentation for GeSHi 1.0.X that is available |
151 * at http://qbnz.com/highlighter/documentation.php for more information |
246 * at http://qbnz.com/highlighter/documentation.php for more information |
152 * about how to use this class. |
247 * about how to use this class. |
153 * |
248 * |
154 * @package geshi |
249 * @package geshi |
155 * @author Nigel McNie <nigel@geshi.org> |
250 * @author Nigel McNie <nigel@geshi.org>, Benny Baumann <BenBE@omorphia.de> |
156 * @copyright (C) 2004 - 2007 Nigel McNie |
251 * @copyright (C) 2004 - 2007 Nigel McNie, (C) 2007 - 2008 Benny Baumann |
157 */ |
252 */ |
158 class GeSHi { |
253 class GeSHi { |
159 /**#@+ |
254 /**#@+ |
160 * @access private |
255 * @access private |
161 */ |
256 */ |
233 'KEYWORDS' => array(), |
328 'KEYWORDS' => array(), |
234 'COMMENTS' => array('MULTI' => true), |
329 'COMMENTS' => array('MULTI' => true), |
235 'REGEXPS' => array(), |
330 'REGEXPS' => array(), |
236 'ESCAPE_CHAR' => true, |
331 'ESCAPE_CHAR' => true, |
237 'BRACKETS' => true, |
332 'BRACKETS' => true, |
238 'SYMBOLS' => true, |
333 'SYMBOLS' => false, |
239 'STRINGS' => true, |
334 'STRINGS' => true, |
240 'NUMBERS' => true, |
335 'NUMBERS' => true, |
241 'METHODS' => true, |
336 'METHODS' => true, |
242 'SCRIPT' => true |
337 'SCRIPT' => true |
243 ); |
338 ); |
313 * @var array |
408 * @var array |
314 */ |
409 */ |
315 var $highlight_extra_lines = array(); |
410 var $highlight_extra_lines = array(); |
316 |
411 |
317 /** |
412 /** |
413 * Styles of lines that should be highlighted extra |
|
414 * @var array |
|
415 */ |
|
416 var $highlight_extra_lines_styles = array(); |
|
417 |
|
418 /** |
|
318 * Styles of extra-highlighted lines |
419 * Styles of extra-highlighted lines |
319 * @var string |
420 * @var string |
320 */ |
421 */ |
321 var $highlight_extra_lines_style = 'color: #cc0; background-color: #ffc;'; |
422 var $highlight_extra_lines_style = 'background-color: #ffc;'; |
322 |
423 |
323 /** |
424 /** |
324 * The line ending |
425 * The line ending |
325 * If null, nl2br() will be used on the result string. |
426 * If null, nl2br() will be used on the result string. |
326 * Otherwise, all instances of \n will be replaced with $line_ending |
427 * Otherwise, all instances of \n will be replaced with $line_ending |
327 * @var string |
428 * @var string |
328 */ |
429 */ |
329 var $line_ending = null; |
430 var $line_ending = null; |
330 |
431 |
331 /** |
432 /** |
332 * Number at which line numbers should start at |
433 * Number at which line numbers should start at |
333 * @var int |
434 * @var int |
334 */ |
435 */ |
336 |
437 |
337 /** |
438 /** |
338 * The overall style for this code block |
439 * The overall style for this code block |
339 * @var string |
440 * @var string |
340 */ |
441 */ |
341 var $overall_style = ''; |
442 var $overall_style = 'font-family:monospace;'; |
342 |
443 |
343 /** |
444 /** |
344 * The style for the actual code |
445 * The style for the actual code |
345 * @var string |
446 * @var string |
346 */ |
447 */ |
347 var $code_style = 'font-family: \'Courier New\', Courier, monospace; font-weight: normal;'; |
448 var $code_style = 'font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;'; |
348 |
449 |
349 /** |
450 /** |
350 * The overall class for this code block |
451 * The overall class for this code block |
351 * @var string |
452 * @var string |
352 */ |
453 */ |
360 |
461 |
361 /** |
462 /** |
362 * Line number styles |
463 * Line number styles |
363 * @var string |
464 * @var string |
364 */ |
465 */ |
365 var $line_style1 = 'font-family: \'Courier New\', Courier, monospace; color: black; font-weight: normal; font-style: normal;'; |
466 var $line_style1 = 'font-weight: normal; vertical-align:top;'; |
366 |
467 |
367 /** |
468 /** |
368 * Line number styles for fancy lines |
469 * Line number styles for fancy lines |
369 * @var string |
470 * @var string |
370 */ |
471 */ |
371 var $line_style2 = 'font-weight: bold;'; |
472 var $line_style2 = 'font-weight: bold; vertical-align:top;'; |
372 |
473 |
373 /** |
474 /** |
374 * Flag for how line nubmers are displayed |
475 * Style for line numbers when GESHI_HEADER_PRE_TABLE is chosen |
476 * @var string |
|
477 */ |
|
478 var $table_linenumber_style = 'width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;'; |
|
479 |
|
480 /** |
|
481 * Flag for how line numbers are displayed |
|
375 * @var boolean |
482 * @var boolean |
376 */ |
483 */ |
377 var $line_numbers = GESHI_NO_LINE_NUMBERS; |
484 var $line_numbers = GESHI_NO_LINE_NUMBERS; |
485 |
|
486 /** |
|
487 * Flag to decide if multi line spans are allowed. Set it to false to make sure |
|
488 * each tag is closed before and reopened after each linefeed. |
|
489 * @var boolean |
|
490 */ |
|
491 var $allow_multiline_span = true; |
|
378 |
492 |
379 /** |
493 /** |
380 * The "nth" value for fancy line highlighting |
494 * The "nth" value for fancy line highlighting |
381 * @var int |
495 * @var int |
382 */ |
496 */ |
386 * The size of tab stops |
500 * The size of tab stops |
387 * @var int |
501 * @var int |
388 */ |
502 */ |
389 var $tab_width = 8; |
503 var $tab_width = 8; |
390 |
504 |
391 /** |
505 /** |
392 * Should we use language-defined tab stop widths? |
506 * Should we use language-defined tab stop widths? |
393 * @var int |
507 * @var int |
394 */ |
508 */ |
395 var $use_language_tab_width = false; |
509 var $use_language_tab_width = false; |
396 |
510 |
397 /** |
511 /** |
398 * Default target for keyword links |
512 * Default target for keyword links |
399 * @var string |
513 * @var string |
400 */ |
514 */ |
401 var $link_target = ''; |
515 var $link_target = ''; |
402 |
516 |
403 /** |
517 /** |
404 * The encoding to use for entity encoding |
518 * The encoding to use for entity encoding |
405 * NOTE: no longer used |
519 * NOTE: Used with Escape Char Sequences to fix UTF-8 handling (cf. SF#2037598) |
406 * @var string |
520 * @var string |
407 */ |
521 */ |
408 var $encoding = 'ISO-8859-1'; |
522 var $encoding = 'utf-8'; |
409 |
523 |
410 /** |
524 /** |
411 * Should keywords be linked? |
525 * Should keywords be linked? |
412 * @var boolean |
526 * @var boolean |
413 */ |
527 */ |
414 var $keyword_links = true; |
528 var $keyword_links = true; |
529 |
|
530 /** |
|
531 * Currently loaded language file |
|
532 * @var string |
|
533 * @since 1.0.7.22 |
|
534 */ |
|
535 var $loaded_language = ''; |
|
536 |
|
537 /** |
|
538 * Wether the caches needed for parsing are built or not |
|
539 * |
|
540 * @var bool |
|
541 * @since 1.0.8 |
|
542 */ |
|
543 var $parse_cache_built = false; |
|
544 |
|
545 /** |
|
546 * Work around for Suhosin Patch with disabled /e modifier |
|
547 * |
|
548 * Note from suhosins author in config file: |
|
549 * <blockquote> |
|
550 * The /e modifier inside <code>preg_replace()</code> allows code execution. |
|
551 * Often it is the cause for remote code execution exploits. It is wise to |
|
552 * deactivate this feature and test where in the application it is used. |
|
553 * The developer using the /e modifier should be made aware that he should |
|
554 * use <code>preg_replace_callback()</code> instead |
|
555 * </blockquote> |
|
556 * |
|
557 * @var array |
|
558 * @since 1.0.8 |
|
559 */ |
|
560 var $_kw_replace_group = 0; |
|
561 var $_rx_key = 0; |
|
562 |
|
563 /** |
|
564 * some "callback parameters" for handle_multiline_regexps |
|
565 * |
|
566 * @since 1.0.8 |
|
567 * @access private |
|
568 * @var string |
|
569 */ |
|
570 var $_hmr_before = ''; |
|
571 var $_hmr_replace = ''; |
|
572 var $_hmr_after = ''; |
|
573 var $_hmr_key = 0; |
|
415 |
574 |
416 /**#@-*/ |
575 /**#@-*/ |
417 |
576 |
418 /** |
577 /** |
419 * Creates a new GeSHi object, with source and language |
578 * Creates a new GeSHi object, with source and language |
424 * is deprecated!</b> I've backported the auto path |
583 * is deprecated!</b> I've backported the auto path |
425 * detection from the 1.1.X dev branch, so now it |
584 * detection from the 1.1.X dev branch, so now it |
426 * should be automatically set correctly. If you have |
585 * should be automatically set correctly. If you have |
427 * renamed the language directory however, you will |
586 * renamed the language directory however, you will |
428 * still need to set the path using this parameter or |
587 * still need to set the path using this parameter or |
429 * {@link GeSHi::set_language_path()} |
588 * {@link GeSHi->set_language_path()} |
430 * @since 1.0.0 |
589 * @since 1.0.0 |
431 */ |
590 */ |
432 function GeSHi($source, $language, $path = '') { |
591 function GeSHi($source = '', $language = '', $path = '') { |
433 $this->set_source($source); |
592 if (!empty($source)) { |
593 $this->set_source($source); |
|
594 } |
|
595 if (!empty($language)) { |
|
596 $this->set_language($language); |
|
597 } |
|
434 $this->set_language_path($path); |
598 $this->set_language_path($path); |
435 $this->set_language($language); |
|
436 } |
599 } |
437 |
600 |
438 /** |
601 /** |
439 * Returns an error message associated with the last GeSHi operation, |
602 * Returns an error message associated with the last GeSHi operation, |
440 * or false if no error has occured |
603 * or false if no error has occured |
442 * @return string|false An error message if there has been an error, else false |
605 * @return string|false An error message if there has been an error, else false |
443 * @since 1.0.0 |
606 * @since 1.0.0 |
444 */ |
607 */ |
445 function error() { |
608 function error() { |
446 if ($this->error) { |
609 if ($this->error) { |
447 $msg = $this->error_messages[$this->error]; |
610 //Put some template variables for debugging here ... |
448 $debug_tpl_vars = array( |
611 $debug_tpl_vars = array( |
449 '{LANGUAGE}' => $this->language, |
612 '{LANGUAGE}' => $this->language, |
450 '{PATH}' => $this->language_path |
613 '{PATH}' => $this->language_path |
451 ); |
614 ); |
452 foreach ($debug_tpl_vars as $tpl => $var) { |
615 $msg = str_replace( |
453 $msg = str_replace($tpl, $var, $msg); |
616 array_keys($debug_tpl_vars), |
454 } |
617 array_values($debug_tpl_vars), |
455 return "<br /><strong>GeSHi Error:</strong> $msg (code $this->error)<br />"; |
618 $this->error_messages[$this->error]); |
619 |
|
620 return "<br /><strong>GeSHi Error:</strong> $msg (code {$this->error})<br />"; |
|
456 } |
621 } |
457 return false; |
622 return false; |
458 } |
623 } |
459 |
624 |
460 /** |
625 /** |
483 } |
648 } |
484 |
649 |
485 /** |
650 /** |
486 * Sets the language for this object |
651 * Sets the language for this object |
487 * |
652 * |
653 * @note since 1.0.8 this function won't reset language-settings by default anymore! |
|
654 * if you need this set $force_reset = true |
|
655 * |
|
488 * @param string The name of the language to use |
656 * @param string The name of the language to use |
489 * @since 1.0.0 |
657 * @since 1.0.0 |
490 */ |
658 */ |
491 function set_language($language) { |
659 function set_language($language, $force_reset = false) { |
660 if ($force_reset) { |
|
661 $this->loaded_language = false; |
|
662 } |
|
663 |
|
664 //Clean up the language name to prevent malicious code injection |
|
665 $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language); |
|
666 |
|
667 $language = strtolower($language); |
|
668 |
|
669 //Retreive the full filename |
|
670 $file_name = $this->language_path . $language . '.php'; |
|
671 if ($file_name == $this->loaded_language) { |
|
672 // this language is already loaded! |
|
673 return; |
|
674 } |
|
675 |
|
676 $this->language = $language; |
|
677 |
|
492 $this->error = false; |
678 $this->error = false; |
493 $this->strict_mode = GESHI_NEVER; |
679 $this->strict_mode = GESHI_NEVER; |
494 |
680 |
495 $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language); |
681 //Check if we can read the desired file |
496 $this->language = strtolower($language); |
|
497 |
|
498 $file_name = $this->language_path . $this->language . '.php'; |
|
499 if (!is_readable($file_name)) { |
682 if (!is_readable($file_name)) { |
500 $this->error = GESHI_ERROR_NO_SUCH_LANG; |
683 $this->error = GESHI_ERROR_NO_SUCH_LANG; |
501 return; |
684 return; |
502 } |
685 } |
686 |
|
503 // Load the language for parsing |
687 // Load the language for parsing |
504 $this->load_language($file_name); |
688 $this->load_language($file_name); |
505 } |
689 } |
506 |
690 |
507 /** |
691 /** |
515 * detected, so this method should no longer be needed. The |
699 * detected, so this method should no longer be needed. The |
516 * 1.1.X branch handles manual setting of the path differently |
700 * 1.1.X branch handles manual setting of the path differently |
517 * so this method will disappear in 1.2.0. |
701 * so this method will disappear in 1.2.0. |
518 */ |
702 */ |
519 function set_language_path($path) { |
703 function set_language_path($path) { |
704 if(strpos($path,':')) { |
|
705 //Security Fix to prevent external directories using fopen wrappers. |
|
706 if(DIRECTORY_SEPARATOR == "\\") { |
|
707 if(!preg_match('#^[a-zA-Z]:#', $path) || false !== strpos($path, ':', 2)) { |
|
708 return; |
|
709 } |
|
710 } else { |
|
711 return; |
|
712 } |
|
713 } |
|
714 if(preg_match('#[^/a-zA-Z0-9_\.\-\\\s:]#', $path)) { |
|
715 //Security Fix to prevent external directories using fopen wrappers. |
|
716 return; |
|
717 } |
|
718 if(GESHI_SECURITY_PARANOID && false !== strpos($path, '/.')) { |
|
719 //Security Fix to prevent external directories using fopen wrappers. |
|
720 return; |
|
721 } |
|
722 if(GESHI_SECURITY_PARANOID && false !== strpos($path, '..')) { |
|
723 //Security Fix to prevent external directories using fopen wrappers. |
|
724 return; |
|
725 } |
|
520 if ($path) { |
726 if ($path) { |
521 $this->language_path = ('/' == substr($path, strlen($path) - 1, 1)) ? $path : $path . '/'; |
727 $this->language_path = ('/' == $path[strlen($path) - 1]) ? $path : $path . '/'; |
522 $this->set_language($this->language); // otherwise set_language_path has no effect |
728 $this->set_language($this->language); // otherwise set_language_path has no effect |
523 } |
729 } |
524 } |
730 } |
525 |
731 |
526 /** |
732 /** |
527 * Sets the type of header to be used. |
733 * Sets the type of header to be used. |
536 * |
742 * |
537 * @param int The type of header to be used |
743 * @param int The type of header to be used |
538 * @since 1.0.0 |
744 * @since 1.0.0 |
539 */ |
745 */ |
540 function set_header_type($type) { |
746 function set_header_type($type) { |
541 if (GESHI_HEADER_DIV != $type && GESHI_HEADER_PRE != $type && GESHI_HEADER_NONE != $type) { |
747 //Check if we got a valid header type |
748 if (!in_array($type, array(GESHI_HEADER_NONE, GESHI_HEADER_DIV, |
|
749 GESHI_HEADER_PRE, GESHI_HEADER_PRE_VALID, GESHI_HEADER_PRE_TABLE))) { |
|
542 $this->error = GESHI_ERROR_INVALID_HEADER_TYPE; |
750 $this->error = GESHI_ERROR_INVALID_HEADER_TYPE; |
543 return; |
751 return; |
544 } |
752 } |
753 |
|
754 //Set that new header type |
|
545 $this->header_type = $type; |
755 $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 } |
756 } |
551 |
757 |
552 /** |
758 /** |
553 * Sets the styles for the code that will be outputted |
759 * Sets the styles for the code that will be outputted |
554 * when this object is parsed. The style should be a |
760 * when this object is parsed. The style should be a |
559 * @since 1.0.0 |
765 * @since 1.0.0 |
560 */ |
766 */ |
561 function set_overall_style($style, $preserve_defaults = false) { |
767 function set_overall_style($style, $preserve_defaults = false) { |
562 if (!$preserve_defaults) { |
768 if (!$preserve_defaults) { |
563 $this->overall_style = $style; |
769 $this->overall_style = $style; |
564 } |
770 } else { |
565 else { |
|
566 $this->overall_style .= $style; |
771 $this->overall_style .= $style; |
567 } |
772 } |
568 } |
773 } |
569 |
774 |
570 /** |
775 /** |
612 * code will have the same style as the line number! Consult the |
817 * code will have the same style as the line number! Consult the |
613 * GeSHi documentation for more information about this. |
818 * GeSHi documentation for more information about this. |
614 * |
819 * |
615 * @param string The style to use for actual code |
820 * @param string The style to use for actual code |
616 * @param boolean Whether to merge the current styles with the new styles |
821 * @param boolean Whether to merge the current styles with the new styles |
822 * @since 1.0.2 |
|
617 */ |
823 */ |
618 function set_code_style($style, $preserve_defaults = false) { |
824 function set_code_style($style, $preserve_defaults = false) { |
619 if (!$preserve_defaults) { |
825 if (!$preserve_defaults) { |
620 $this->code_style = $style; |
826 $this->code_style = $style; |
621 } |
827 } else { |
622 else { |
|
623 $this->code_style .= $style; |
828 $this->code_style .= $style; |
624 } |
829 } |
625 } |
830 } |
626 |
831 |
627 /** |
832 /** |
635 * @param boolean If set, is the flag for whether to merge the "fancy" |
840 * @param boolean If set, is the flag for whether to merge the "fancy" |
636 * styles with the current styles or not |
841 * styles with the current styles or not |
637 * @since 1.0.2 |
842 * @since 1.0.2 |
638 */ |
843 */ |
639 function set_line_style($style1, $style2 = '', $preserve_defaults = false) { |
844 function set_line_style($style1, $style2 = '', $preserve_defaults = false) { |
845 //Check if we got 2 or three parameters |
|
640 if (is_bool($style2)) { |
846 if (is_bool($style2)) { |
641 $preserve_defaults = $style2; |
847 $preserve_defaults = $style2; |
642 $style2 = ''; |
848 $style2 = ''; |
643 } |
849 } |
850 |
|
851 //Actually set the new styles |
|
644 if (!$preserve_defaults) { |
852 if (!$preserve_defaults) { |
645 $this->line_style1 = $style1; |
853 $this->line_style1 = $style1; |
646 $this->line_style2 = $style2; |
854 $this->line_style2 = $style2; |
647 } |
855 } else { |
648 else { |
|
649 $this->line_style1 .= $style1; |
856 $this->line_style1 .= $style1; |
650 $this->line_style2 .= $style2; |
857 $this->line_style2 .= $style2; |
651 } |
858 } |
652 } |
859 } |
653 |
860 |
673 && GESHI_FANCY_LINE_NUMBERS != $flag) { |
880 && GESHI_FANCY_LINE_NUMBERS != $flag) { |
674 $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE; |
881 $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE; |
675 } |
882 } |
676 $this->line_numbers = $flag; |
883 $this->line_numbers = $flag; |
677 $this->line_nth_row = $nth_row; |
884 $this->line_nth_row = $nth_row; |
885 } |
|
886 |
|
887 /** |
|
888 * Sets wether spans and other HTML markup generated by GeSHi can |
|
889 * span over multiple lines or not. Defaults to true to reduce overhead. |
|
890 * Set it to false if you want to manipulate the output or manually display |
|
891 * the code in an ordered list. |
|
892 * |
|
893 * @param boolean Wether multiline spans are allowed or not |
|
894 * @since 1.0.7.22 |
|
895 */ |
|
896 function enable_multiline_span($flag) { |
|
897 $this->allow_multiline_span = (bool) $flag; |
|
898 } |
|
899 |
|
900 /** |
|
901 * Get current setting for multiline spans, see GeSHi->enable_multiline_span(). |
|
902 * |
|
903 * @see enable_multiline_span |
|
904 * @return bool |
|
905 */ |
|
906 function get_multiline_span() { |
|
907 return $this->allow_multiline_span; |
|
678 } |
908 } |
679 |
909 |
680 /** |
910 /** |
681 * Sets the style for a keyword group. If $preserve_defaults is |
911 * Sets the style for a keyword group. If $preserve_defaults is |
682 * true, then styles are merged with the default styles, with the |
912 * true, then styles are merged with the default styles, with the |
687 * @param boolean Whether to merge the new styles with the old or just |
917 * @param boolean Whether to merge the new styles with the old or just |
688 * to overwrite them |
918 * to overwrite them |
689 * @since 1.0.0 |
919 * @since 1.0.0 |
690 */ |
920 */ |
691 function set_keyword_group_style($key, $style, $preserve_defaults = false) { |
921 function set_keyword_group_style($key, $style, $preserve_defaults = false) { |
922 //Set the style for this keyword group |
|
692 if (!$preserve_defaults) { |
923 if (!$preserve_defaults) { |
693 $this->language_data['STYLES']['KEYWORDS'][$key] = $style; |
924 $this->language_data['STYLES']['KEYWORDS'][$key] = $style; |
694 } |
925 } else { |
695 else { |
|
696 $this->language_data['STYLES']['KEYWORDS'][$key] .= $style; |
926 $this->language_data['STYLES']['KEYWORDS'][$key] .= $style; |
927 } |
|
928 |
|
929 //Update the lexic permissions |
|
930 if (!isset($this->lexic_permissions['KEYWORDS'][$key])) { |
|
931 $this->lexic_permissions['KEYWORDS'][$key] = true; |
|
697 } |
932 } |
698 } |
933 } |
699 |
934 |
700 /** |
935 /** |
701 * Turns highlighting on/off for a keyword group |
936 * Turns highlighting on/off for a keyword group |
720 * @since 1.0.0 |
955 * @since 1.0.0 |
721 */ |
956 */ |
722 function set_comments_style($key, $style, $preserve_defaults = false) { |
957 function set_comments_style($key, $style, $preserve_defaults = false) { |
723 if (!$preserve_defaults) { |
958 if (!$preserve_defaults) { |
724 $this->language_data['STYLES']['COMMENTS'][$key] = $style; |
959 $this->language_data['STYLES']['COMMENTS'][$key] = $style; |
725 } |
960 } else { |
726 else { |
|
727 $this->language_data['STYLES']['COMMENTS'][$key] .= $style; |
961 $this->language_data['STYLES']['COMMENTS'][$key] .= $style; |
728 } |
962 } |
729 } |
963 } |
730 |
964 |
731 /** |
965 /** |
750 * @since 1.0.0 |
984 * @since 1.0.0 |
751 */ |
985 */ |
752 function set_escape_characters_style($style, $preserve_defaults = false) { |
986 function set_escape_characters_style($style, $preserve_defaults = false) { |
753 if (!$preserve_defaults) { |
987 if (!$preserve_defaults) { |
754 $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style; |
988 $this->language_data['STYLES']['ESCAPE_CHAR'][0] = $style; |
755 } |
989 } else { |
756 else { |
|
757 $this->language_data['STYLES']['ESCAPE_CHAR'][0] .= $style; |
990 $this->language_data['STYLES']['ESCAPE_CHAR'][0] .= $style; |
758 } |
991 } |
759 } |
992 } |
760 |
993 |
761 /** |
994 /** |
783 * @deprecated In favour of set_symbols_style |
1016 * @deprecated In favour of set_symbols_style |
784 */ |
1017 */ |
785 function set_brackets_style($style, $preserve_defaults = false) { |
1018 function set_brackets_style($style, $preserve_defaults = false) { |
786 if (!$preserve_defaults) { |
1019 if (!$preserve_defaults) { |
787 $this->language_data['STYLES']['BRACKETS'][0] = $style; |
1020 $this->language_data['STYLES']['BRACKETS'][0] = $style; |
788 } |
1021 } else { |
789 else { |
|
790 $this->language_data['STYLES']['BRACKETS'][0] .= $style; |
1022 $this->language_data['STYLES']['BRACKETS'][0] .= $style; |
791 } |
1023 } |
792 } |
1024 } |
793 |
1025 |
794 /** |
1026 /** |
811 * user defined styles having priority |
1043 * user defined styles having priority |
812 * |
1044 * |
813 * @param string The style to make the symbols |
1045 * @param string The style to make the symbols |
814 * @param boolean Whether to merge the new styles with the old or just |
1046 * @param boolean Whether to merge the new styles with the old or just |
815 * to overwrite them |
1047 * to overwrite them |
1048 * @param int Tells the group of symbols for which style should be set. |
|
816 * @since 1.0.1 |
1049 * @since 1.0.1 |
817 */ |
1050 */ |
818 function set_symbols_style($style, $preserve_defaults = false) { |
1051 function set_symbols_style($style, $preserve_defaults = false, $group = 0) { |
1052 // Update the style of symbols |
|
819 if (!$preserve_defaults) { |
1053 if (!$preserve_defaults) { |
820 $this->language_data['STYLES']['SYMBOLS'][0] = $style; |
1054 $this->language_data['STYLES']['SYMBOLS'][$group] = $style; |
821 } |
1055 } else { |
822 else { |
1056 $this->language_data['STYLES']['SYMBOLS'][$group] .= $style; |
823 $this->language_data['STYLES']['SYMBOLS'][0] .= $style; |
1057 } |
824 } |
1058 |
825 // For backward compatibility |
1059 // For backward compatibility |
826 $this->set_brackets_style ($style, $preserve_defaults); |
1060 if (0 == $group) { |
1061 $this->set_brackets_style ($style, $preserve_defaults); |
|
1062 } |
|
827 } |
1063 } |
828 |
1064 |
829 /** |
1065 /** |
830 * Turns highlighting on/off for symbols |
1066 * Turns highlighting on/off for symbols |
831 * |
1067 * |
832 * @param boolean Whether to turn highlighting for symbols on or off |
1068 * @param boolean Whether to turn highlighting for symbols on or off |
833 * @since 1.0.0 |
1069 * @since 1.0.0 |
834 */ |
1070 */ |
835 function set_symbols_highlighting($flag) { |
1071 function set_symbols_highlighting($flag) { |
1072 // Update lexic permissions for this symbol group |
|
836 $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false; |
1073 $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false; |
1074 |
|
837 // For backward compatibility |
1075 // For backward compatibility |
838 $this->set_brackets_highlighting ($flag); |
1076 $this->set_brackets_highlighting ($flag); |
839 } |
1077 } |
840 |
1078 |
841 /** |
1079 /** |
849 * @since 1.0.0 |
1087 * @since 1.0.0 |
850 */ |
1088 */ |
851 function set_strings_style($style, $preserve_defaults = false) { |
1089 function set_strings_style($style, $preserve_defaults = false) { |
852 if (!$preserve_defaults) { |
1090 if (!$preserve_defaults) { |
853 $this->language_data['STYLES']['STRINGS'][0] = $style; |
1091 $this->language_data['STYLES']['STRINGS'][0] = $style; |
854 } |
1092 } else { |
855 else { |
|
856 $this->language_data['STYLES']['STRINGS'][0] .= $style; |
1093 $this->language_data['STYLES']['STRINGS'][0] .= $style; |
857 } |
1094 } |
858 } |
1095 } |
859 |
1096 |
860 /** |
1097 /** |
878 * @since 1.0.0 |
1115 * @since 1.0.0 |
879 */ |
1116 */ |
880 function set_numbers_style($style, $preserve_defaults = false) { |
1117 function set_numbers_style($style, $preserve_defaults = false) { |
881 if (!$preserve_defaults) { |
1118 if (!$preserve_defaults) { |
882 $this->language_data['STYLES']['NUMBERS'][0] = $style; |
1119 $this->language_data['STYLES']['NUMBERS'][0] = $style; |
883 } |
1120 } else { |
884 else { |
|
885 $this->language_data['STYLES']['NUMBERS'][0] .= $style; |
1121 $this->language_data['STYLES']['NUMBERS'][0] .= $style; |
886 } |
1122 } |
887 } |
1123 } |
888 |
1124 |
889 /** |
1125 /** |
910 * @since 1.0.0 |
1146 * @since 1.0.0 |
911 */ |
1147 */ |
912 function set_methods_style($key, $style, $preserve_defaults = false) { |
1148 function set_methods_style($key, $style, $preserve_defaults = false) { |
913 if (!$preserve_defaults) { |
1149 if (!$preserve_defaults) { |
914 $this->language_data['STYLES']['METHODS'][$key] = $style; |
1150 $this->language_data['STYLES']['METHODS'][$key] = $style; |
915 } |
1151 } else { |
916 else { |
|
917 $this->language_data['STYLES']['METHODS'][$key] .= $style; |
1152 $this->language_data['STYLES']['METHODS'][$key] .= $style; |
918 } |
1153 } |
919 } |
1154 } |
920 |
1155 |
921 /** |
1156 /** |
939 * @since 1.0.0 |
1174 * @since 1.0.0 |
940 */ |
1175 */ |
941 function set_regexps_style($key, $style, $preserve_defaults = false) { |
1176 function set_regexps_style($key, $style, $preserve_defaults = false) { |
942 if (!$preserve_defaults) { |
1177 if (!$preserve_defaults) { |
943 $this->language_data['STYLES']['REGEXPS'][$key] = $style; |
1178 $this->language_data['STYLES']['REGEXPS'][$key] = $style; |
944 } |
1179 } else { |
945 else { |
|
946 $this->language_data['STYLES']['REGEXPS'][$key] .= $style; |
1180 $this->language_data['STYLES']['REGEXPS'][$key] .= $style; |
947 } |
1181 } |
948 } |
1182 } |
949 |
1183 |
950 /** |
1184 /** |
976 * - GESHI_CAPS_UPPER: convert all keywords to uppercase where found |
1210 * - GESHI_CAPS_UPPER: convert all keywords to uppercase where found |
977 * - GESHI_CAPS_LOWER: convert all keywords to lowercase where found |
1211 * - GESHI_CAPS_LOWER: convert all keywords to lowercase where found |
978 * |
1212 * |
979 * @param int A constant specifying what to do with matched keywords |
1213 * @param int A constant specifying what to do with matched keywords |
980 * @since 1.0.1 |
1214 * @since 1.0.1 |
981 * @todo Error check the passed value |
|
982 */ |
1215 */ |
983 function set_case_keywords($case) { |
1216 function set_case_keywords($case) { |
984 $this->language_data['CASE_KEYWORDS'] = $case; |
1217 if (in_array($case, array( |
1218 GESHI_CAPS_NO_CHANGE, GESHI_CAPS_UPPER, GESHI_CAPS_LOWER))) { |
|
1219 $this->language_data['CASE_KEYWORDS'] = $case; |
|
1220 } |
|
985 } |
1221 } |
986 |
1222 |
987 /** |
1223 /** |
988 * Sets how many spaces a tab is substituted for |
1224 * Sets how many spaces a tab is substituted for |
989 * |
1225 * |
992 * @param int The tab width |
1228 * @param int The tab width |
993 * @since 1.0.0 |
1229 * @since 1.0.0 |
994 */ |
1230 */ |
995 function set_tab_width($width) { |
1231 function set_tab_width($width) { |
996 $this->tab_width = intval($width); |
1232 $this->tab_width = intval($width); |
1233 |
|
997 //Check if it fit's the constraints: |
1234 //Check if it fit's the constraints: |
998 if($this->tab_width < 1) { |
1235 if ($this->tab_width < 1) { |
999 //Return it to the default |
1236 //Return it to the default |
1000 $this->tab_width = 8; |
1237 $this->tab_width = 8; |
1001 } |
1238 } |
1002 } |
1239 } |
1003 |
1240 |
1004 /** |
1241 /** |
1005 * Sets whether or not to use tab-stop width specifed by language |
1242 * Sets whether or not to use tab-stop width specifed by language |
1006 * |
1243 * |
1007 * @param boolean Whether to use language-specific tab-stop widths |
1244 * @param boolean Whether to use language-specific tab-stop widths |
1008 */ |
1245 * @since 1.0.7.20 |
1009 function set_use_language_tab_width($use) { |
1246 */ |
1010 $this->use_language_tab_width = (bool) $use; |
1247 function set_use_language_tab_width($use) { |
1011 } |
1248 $this->use_language_tab_width = (bool) $use; |
1012 |
1249 } |
1013 /** |
1250 |
1014 * Returns the tab width to use, based on the current language and user |
1251 /** |
1015 * preference |
1252 * Returns the tab width to use, based on the current language and user |
1016 * |
1253 * preference |
1017 * @return int Tab width |
1254 * |
1018 */ |
1255 * @return int Tab width |
1019 function get_real_tab_width() { |
1256 * @since 1.0.7.20 |
1020 if (!$this->use_language_tab_width || !isset($this->language_data['TAB_WIDTH'])) { |
1257 */ |
1021 return $this->tab_width; |
1258 function get_real_tab_width() { |
1022 } else { |
1259 if (!$this->use_language_tab_width || |
1023 return $this->language_data['TAB_WIDTH']; |
1260 !isset($this->language_data['TAB_WIDTH'])) { |
1024 } |
1261 return $this->tab_width; |
1025 } |
1262 } else { |
1263 return $this->language_data['TAB_WIDTH']; |
|
1264 } |
|
1265 } |
|
1026 |
1266 |
1027 /** |
1267 /** |
1028 * Enables/disables strict highlighting. Default is off, calling this |
1268 * Enables/disables strict highlighting. Default is off, calling this |
1029 * method without parameters will turn it on. See documentation |
1269 * method without parameters will turn it on. See documentation |
1030 * for more details on strict mode and where to use it. |
1270 * for more details on strict mode and where to use it. |
1032 * @param boolean Whether to enable strict mode or not |
1272 * @param boolean Whether to enable strict mode or not |
1033 * @since 1.0.0 |
1273 * @since 1.0.0 |
1034 */ |
1274 */ |
1035 function enable_strict_mode($mode = true) { |
1275 function enable_strict_mode($mode = true) { |
1036 if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) { |
1276 if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) { |
1037 $this->strict_mode = ($mode) ? true : false; |
1277 $this->strict_mode = ($mode) ? GESHI_ALWAYS : GESHI_NEVER; |
1038 } |
1278 } |
1039 } |
1279 } |
1040 |
1280 |
1041 /** |
1281 /** |
1042 * Disables all highlighting |
1282 * Disables all highlighting |
1043 * |
1283 * |
1044 * @since 1.0.0 |
1284 * @since 1.0.0 |
1045 * @todo Rewrite with an array traversal |
1285 * @todo Rewrite with array traversal |
1286 * @deprecated In favour of enable_highlighting |
|
1046 */ |
1287 */ |
1047 function disable_highlighting() { |
1288 function disable_highlighting() { |
1289 $this->enable_highlighting(false); |
|
1290 } |
|
1291 |
|
1292 /** |
|
1293 * Enables all highlighting |
|
1294 * |
|
1295 * The optional flag parameter was added in version 1.0.7.21 and can be used |
|
1296 * to enable (true) or disable (false) all highlighting. |
|
1297 * |
|
1298 * @since 1.0.0 |
|
1299 * @param boolean A flag specifying whether to enable or disable all highlighting |
|
1300 * @todo Rewrite with array traversal |
|
1301 */ |
|
1302 function enable_highlighting($flag = true) { |
|
1303 $flag = $flag ? true : false; |
|
1048 foreach ($this->lexic_permissions as $key => $value) { |
1304 foreach ($this->lexic_permissions as $key => $value) { |
1049 if (is_array($value)) { |
1305 if (is_array($value)) { |
1050 foreach ($value as $k => $v) { |
1306 foreach ($value as $k => $v) { |
1051 $this->lexic_permissions[$key][$k] = false; |
1307 $this->lexic_permissions[$key][$k] = $flag; |
1052 } |
1308 } |
1053 } |
1309 } else { |
1054 else { |
1310 $this->lexic_permissions[$key] = $flag; |
1055 $this->lexic_permissions[$key] = false; |
1311 } |
1056 } |
1312 } |
1057 } |
1313 |
1058 // Context blocks |
1314 // Context blocks |
1059 $this->enable_important_blocks = false; |
1315 $this->enable_important_blocks = $flag; |
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 } |
1316 } |
1082 |
1317 |
1083 /** |
1318 /** |
1084 * Given a file extension, this method returns either a valid geshi language |
1319 * Given a file extension, this method returns either a valid geshi language |
1085 * name, or the empty string if it couldn't be found |
1320 * name, or the empty string if it couldn't be found |
1086 * |
1321 * |
1087 * @param string The extension to get a language name for |
1322 * @param string The extension to get a language name for |
1088 * @param array A lookup array to use instead of the default |
1323 * @param array A lookup array to use instead of the default one |
1089 * @since 1.0.5 |
1324 * @since 1.0.5 |
1090 * @todo Re-think about how this method works (maybe make it private and/or make it |
1325 * @todo Re-think about how this method works (maybe make it private and/or make it |
1091 * a extension->lang lookup?) |
1326 * a extension->lang lookup?) |
1092 * @todo static? |
1327 * @todo static? |
1093 */ |
1328 */ |
1094 function get_language_name_from_extension( $extension, $lookup = array() ) { |
1329 function get_language_name_from_extension( $extension, $lookup = array() ) { |
1095 if ( !$lookup ) { |
1330 if ( !is_array($lookup) || empty($lookup)) { |
1096 $lookup = array( |
1331 $lookup = array( |
1097 'actionscript' => array('as'), |
1332 'actionscript' => array('as'), |
1098 'ada' => array('a', 'ada', 'adb', 'ads'), |
1333 'ada' => array('a', 'ada', 'adb', 'ads'), |
1099 'apache' => array('conf'), |
1334 'apache' => array('conf'), |
1100 'asm' => array('ash', 'asm'), |
1335 'asm' => array('ash', 'asm', 'inc'), |
1101 'asp' => array('asp'), |
1336 'asp' => array('asp'), |
1102 'bash' => array('sh'), |
1337 'bash' => array('sh'), |
1338 'bf' => array('bf'), |
|
1103 'c' => array('c', 'h'), |
1339 'c' => array('c', 'h'), |
1104 'c_mac' => array('c', 'h'), |
1340 'c_mac' => array('c', 'h'), |
1105 'caddcl' => array(), |
1341 'caddcl' => array(), |
1106 'cadlisp' => array(), |
1342 'cadlisp' => array(), |
1107 'cdfg' => array('cdfg'), |
1343 'cdfg' => array('cdfg'), |
1108 'cpp' => array('cpp', 'h', 'hpp'), |
1344 'cobol' => array('cbl'), |
1109 'csharp' => array(), |
1345 'cpp' => array('cpp', 'hpp', 'C', 'H', 'CPP', 'HPP'), |
1346 'csharp' => array('cs'), |
|
1110 'css' => array('css'), |
1347 'css' => array('css'), |
1111 'delphi' => array('dpk', 'dpr'), |
1348 'd' => array('d'), |
1349 'delphi' => array('dpk', 'dpr', 'pp', 'pas'), |
|
1350 'diff' => array('diff', 'patch'), |
|
1351 'dos' => array('bat', 'cmd'), |
|
1352 'gettext' => array('po', 'pot'), |
|
1353 'gml' => array('gml'), |
|
1354 'gnuplot' => array('plt'), |
|
1355 'groovy' => array('groovy'), |
|
1356 'haskell' => array('hs'), |
|
1112 'html4strict' => array('html', 'htm'), |
1357 'html4strict' => array('html', 'htm'), |
1358 'ini' => array('ini', 'desktop'), |
|
1113 'java' => array('java'), |
1359 'java' => array('java'), |
1114 'javascript' => array('js'), |
1360 'javascript' => array('js'), |
1361 'klonec' => array('kl1'), |
|
1362 'klonecpp' => array('klx'), |
|
1363 'latex' => array('tex'), |
|
1115 'lisp' => array('lisp'), |
1364 'lisp' => array('lisp'), |
1116 'lua' => array('lua'), |
1365 'lua' => array('lua'), |
1366 'matlab' => array('m'), |
|
1117 'mpasm' => array(), |
1367 'mpasm' => array(), |
1368 'mysql' => array('sql'), |
|
1118 'nsis' => array(), |
1369 'nsis' => array(), |
1119 'objc' => array(), |
1370 'objc' => array(), |
1120 'oobas' => array(), |
1371 'oobas' => array(), |
1121 'oracle8' => array(), |
1372 'oracle8' => array(), |
1373 'oracle10' => array(), |
|
1122 'pascal' => array('pas'), |
1374 'pascal' => array('pas'), |
1123 'perl' => array('pl', 'pm'), |
1375 'perl' => array('pl', 'pm'), |
1124 'php' => array('php', 'php5', 'phtml', 'phps'), |
1376 'php' => array('php', 'php5', 'phtml', 'phps'), |
1377 'povray' => array('pov'), |
|
1378 'providex' => array('pvc', 'pvx'), |
|
1379 'prolog' => array('pl'), |
|
1125 'python' => array('py'), |
1380 'python' => array('py'), |
1126 'qbasic' => array('bi'), |
1381 'qbasic' => array('bi'), |
1382 'reg' => array('reg'), |
|
1383 'ruby' => array('rb'), |
|
1127 'sas' => array('sas'), |
1384 'sas' => array('sas'), |
1385 'scala' => array('scala'), |
|
1386 'scheme' => array('scm'), |
|
1387 'scilab' => array('sci'), |
|
1388 'smalltalk' => array('st'), |
|
1128 'smarty' => array(), |
1389 'smarty' => array(), |
1390 'tcl' => array('tcl'), |
|
1129 'vb' => array('bas'), |
1391 'vb' => array('bas'), |
1130 'vbnet' => array(), |
1392 'vbnet' => array(), |
1131 'visualfoxpro' => array(), |
1393 'visualfoxpro' => array(), |
1132 'xml' => array('xml') |
1394 'whitespace' => array('ws'), |
1395 'xml' => array('xml', 'svg'), |
|
1396 'z80' => array('z80', 'asm', 'inc') |
|
1133 ); |
1397 ); |
1134 } |
1398 } |
1135 |
1399 |
1136 foreach ($lookup as $lang => $extensions) { |
1400 foreach ($lookup as $lang => $extensions) { |
1137 foreach ($extensions as $ext) { |
1401 if (in_array($extension, $extensions)) { |
1138 if ($ext == $extension) { |
1402 return $lang; |
1139 return $lang; |
|
1140 } |
|
1141 } |
1403 } |
1142 } |
1404 } |
1143 return ''; |
1405 return ''; |
1144 } |
1406 } |
1145 |
1407 |
1153 * <pre>array( |
1415 * <pre>array( |
1154 * 'lang_name' => array('extension', 'extension', ...), |
1416 * 'lang_name' => array('extension', 'extension', ...), |
1155 * 'lang_name' ... |
1417 * 'lang_name' ... |
1156 * );</pre> |
1418 * );</pre> |
1157 * |
1419 * |
1420 * @param string The filename to load the source from |
|
1421 * @param array A lookup array to use instead of the default one |
|
1158 * @todo Complete rethink of this and above method |
1422 * @todo Complete rethink of this and above method |
1159 * @since 1.0.5 |
1423 * @since 1.0.5 |
1160 */ |
1424 */ |
1161 function load_from_file($file_name, $lookup = array()) { |
1425 function load_from_file($file_name, $lookup = array()) { |
1162 if (is_readable($file_name)) { |
1426 if (is_readable($file_name)) { |
1163 $this->set_source(implode('', file($file_name))); |
1427 $this->set_source(file_get_contents($file_name)); |
1164 $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup)); |
1428 $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup)); |
1165 } |
1429 } else { |
1166 else { |
|
1167 $this->error = GESHI_ERROR_FILE_NOT_READABLE; |
1430 $this->error = GESHI_ERROR_FILE_NOT_READABLE; |
1168 } |
1431 } |
1169 } |
1432 } |
1170 |
1433 |
1171 /** |
1434 /** |
1174 * @param int The key of the keyword group to add the keyword to |
1437 * @param int The key of the keyword group to add the keyword to |
1175 * @param string The word to add to the keyword group |
1438 * @param string The word to add to the keyword group |
1176 * @since 1.0.0 |
1439 * @since 1.0.0 |
1177 */ |
1440 */ |
1178 function add_keyword($key, $word) { |
1441 function add_keyword($key, $word) { |
1179 $this->language_data['KEYWORDS'][$key][] = $word; |
1442 if (!in_array($word, $this->language_data['KEYWORDS'][$key])) { |
1443 $this->language_data['KEYWORDS'][$key][] = $word; |
|
1444 |
|
1445 //NEW in 1.0.8 don't recompile the whole optimized regexp, simply append it |
|
1446 if ($this->parse_cache_built) { |
|
1447 $subkey = count($this->language_data['CACHED_KEYWORD_LISTS'][$key]) - 1; |
|
1448 $this->language_data['CACHED_KEYWORD_LISTS'][$key][$subkey] .= '|' . preg_quote($word, '/'); |
|
1449 } |
|
1450 } |
|
1180 } |
1451 } |
1181 |
1452 |
1182 /** |
1453 /** |
1183 * Removes a keyword from a keyword group |
1454 * Removes a keyword from a keyword group |
1184 * |
1455 * |
1185 * @param int The key of the keyword group to remove the keyword from |
1456 * @param int The key of the keyword group to remove the keyword from |
1186 * @param string The word to remove from the keyword group |
1457 * @param string The word to remove from the keyword group |
1458 * @param bool Wether to automatically recompile the optimized regexp list or not. |
|
1459 * Note: if you set this to false and @see GeSHi->parse_code() was already called once, |
|
1460 * for the current language, you have to manually call @see GeSHi->optimize_keyword_group() |
|
1461 * or the removed keyword will stay in cache and still be highlighted! On the other hand |
|
1462 * it might be too expensive to recompile the regexp list for every removal if you want to |
|
1463 * remove a lot of keywords. |
|
1187 * @since 1.0.0 |
1464 * @since 1.0.0 |
1188 */ |
1465 */ |
1189 function remove_keyword($key, $word) { |
1466 function remove_keyword($key, $word, $recompile = true) { |
1190 $this->language_data['KEYWORDS'][$key] = |
1467 $key_to_remove = array_search($word, $this->language_data['KEYWORDS'][$key]); |
1191 array_diff($this->language_data['KEYWORDS'][$key], array($word)); |
1468 if ($key_to_remove !== false) { |
1469 unset($this->language_data['KEYWORDS'][$key][$key_to_remove]); |
|
1470 |
|
1471 //NEW in 1.0.8, optionally recompile keyword group |
|
1472 if ($recompile && $this->parse_cache_built) { |
|
1473 $this->optimize_keyword_group($key); |
|
1474 } |
|
1475 } |
|
1192 } |
1476 } |
1193 |
1477 |
1194 /** |
1478 /** |
1195 * Creates a new keyword group |
1479 * Creates a new keyword group |
1196 * |
1480 * |
1200 * @param array The words to use for the keyword group |
1484 * @param array The words to use for the keyword group |
1201 * @since 1.0.0 |
1485 * @since 1.0.0 |
1202 */ |
1486 */ |
1203 function add_keyword_group($key, $styles, $case_sensitive = true, $words = array()) { |
1487 function add_keyword_group($key, $styles, $case_sensitive = true, $words = array()) { |
1204 $words = (array) $words; |
1488 $words = (array) $words; |
1489 if (empty($words)) { |
|
1490 // empty word lists mess up highlighting |
|
1491 return false; |
|
1492 } |
|
1493 |
|
1494 //Add the new keyword group internally |
|
1205 $this->language_data['KEYWORDS'][$key] = $words; |
1495 $this->language_data['KEYWORDS'][$key] = $words; |
1206 $this->lexic_permissions['KEYWORDS'][$key] = true; |
1496 $this->lexic_permissions['KEYWORDS'][$key] = true; |
1207 $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive; |
1497 $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive; |
1208 $this->language_data['STYLES']['KEYWORDS'][$key] = $styles; |
1498 $this->language_data['STYLES']['KEYWORDS'][$key] = $styles; |
1499 |
|
1500 //NEW in 1.0.8, cache keyword regexp |
|
1501 if ($this->parse_cache_built) { |
|
1502 $this->optimize_keyword_group($key); |
|
1503 } |
|
1209 } |
1504 } |
1210 |
1505 |
1211 /** |
1506 /** |
1212 * Removes a keyword group |
1507 * Removes a keyword group |
1213 * |
1508 * |
1214 * @param int The key of the keyword group to remove |
1509 * @param int The key of the keyword group to remove |
1215 * @since 1.0.0 |
1510 * @since 1.0.0 |
1216 */ |
1511 */ |
1217 function remove_keyword_group ($key) { |
1512 function remove_keyword_group ($key) { |
1513 //Remove the keyword group internally |
|
1218 unset($this->language_data['KEYWORDS'][$key]); |
1514 unset($this->language_data['KEYWORDS'][$key]); |
1219 unset($this->lexic_permissions['KEYWORDS'][$key]); |
1515 unset($this->lexic_permissions['KEYWORDS'][$key]); |
1220 unset($this->language_data['CASE_SENSITIVE'][$key]); |
1516 unset($this->language_data['CASE_SENSITIVE'][$key]); |
1221 unset($this->language_data['STYLES']['KEYWORDS'][$key]); |
1517 unset($this->language_data['STYLES']['KEYWORDS'][$key]); |
1518 |
|
1519 //NEW in 1.0.8 |
|
1520 unset($this->language_data['CACHED_KEYWORD_LISTS'][$key]); |
|
1521 } |
|
1522 |
|
1523 /** |
|
1524 * compile optimized regexp list for keyword group |
|
1525 * |
|
1526 * @param int The key of the keyword group to compile & optimize |
|
1527 * @since 1.0.8 |
|
1528 */ |
|
1529 function optimize_keyword_group($key) { |
|
1530 $this->language_data['CACHED_KEYWORD_LISTS'][$key] = |
|
1531 $this->optimize_regexp_list($this->language_data['KEYWORDS'][$key]); |
|
1222 } |
1532 } |
1223 |
1533 |
1224 /** |
1534 /** |
1225 * Sets the content of the header block |
1535 * Sets the content of the header block |
1226 * |
1536 * |
1304 * @since 1.0.3 |
1614 * @since 1.0.3 |
1305 */ |
1615 */ |
1306 function set_link_target($target) { |
1616 function set_link_target($target) { |
1307 if (!$target) { |
1617 if (!$target) { |
1308 $this->link_target = ''; |
1618 $this->link_target = ''; |
1309 } |
1619 } else { |
1310 else { |
1620 $this->link_target = ' target="' . $target . '"'; |
1311 $this->link_target = ' target="' . $target . '" '; |
|
1312 } |
1621 } |
1313 } |
1622 } |
1314 |
1623 |
1315 /** |
1624 /** |
1316 * Sets styles for important parts of the code |
1625 * Sets styles for important parts of the code |
1323 } |
1632 } |
1324 |
1633 |
1325 /** |
1634 /** |
1326 * Sets whether context-important blocks are highlighted |
1635 * Sets whether context-important blocks are highlighted |
1327 * |
1636 * |
1637 * @param boolean Tells whether to enable or disable highlighting of important blocks |
|
1328 * @todo REMOVE THIS SHIZ FROM GESHI! |
1638 * @todo REMOVE THIS SHIZ FROM GESHI! |
1329 * @deprecated |
1639 * @deprecated |
1640 * @since 1.0.2 |
|
1330 */ |
1641 */ |
1331 function enable_important_blocks($flag) { |
1642 function enable_important_blocks($flag) { |
1332 $this->enable_important_blocks = ( $flag ) ? true : false; |
1643 $this->enable_important_blocks = ( $flag ) ? true : false; |
1333 } |
1644 } |
1334 |
1645 |
1343 } |
1654 } |
1344 |
1655 |
1345 /** |
1656 /** |
1346 * Specifies which lines to highlight extra |
1657 * Specifies which lines to highlight extra |
1347 * |
1658 * |
1659 * The extra style parameter was added in 1.0.7.21. |
|
1660 * |
|
1348 * @param mixed An array of line numbers to highlight, or just a line |
1661 * @param mixed An array of line numbers to highlight, or just a line |
1349 * number on its own. |
1662 * number on its own. |
1663 * @param string A string specifying the style to use for this line. |
|
1664 * If null is specified, the default style is used. |
|
1665 * If false is specified, the line will be removed from |
|
1666 * special highlighting |
|
1350 * @since 1.0.2 |
1667 * @since 1.0.2 |
1351 * @todo Some data replication here that could be cut down on |
1668 * @todo Some data replication here that could be cut down on |
1352 */ |
1669 */ |
1353 function highlight_lines_extra($lines) { |
1670 function highlight_lines_extra($lines, $style = null) { |
1354 if (is_array($lines)) { |
1671 if (is_array($lines)) { |
1672 //Split up the job using single lines at a time |
|
1355 foreach ($lines as $line) { |
1673 foreach ($lines as $line) { |
1356 $this->highlight_extra_lines[intval($line)] = intval($line); |
1674 $this->highlight_lines_extra($line, $style); |
1357 } |
1675 } |
1358 } |
1676 } else { |
1359 else { |
1677 //Mark the line as being highlighted specially |
1360 $this->highlight_extra_lines[intval($lines)] = intval($lines); |
1678 $lines = intval($lines); |
1679 $this->highlight_extra_lines[$lines] = $lines; |
|
1680 |
|
1681 //Decide on which style to use |
|
1682 if ($style === null) { //Check if we should use default style |
|
1683 unset($this->highlight_extra_lines_styles[$lines]); |
|
1684 } else if ($style === false) { //Check if to remove this line |
|
1685 unset($this->highlight_extra_lines[$lines]); |
|
1686 unset($this->highlight_extra_lines_styles[$lines]); |
|
1687 } else { |
|
1688 $this->highlight_extra_lines_styles[$lines] = $style; |
|
1689 } |
|
1361 } |
1690 } |
1362 } |
1691 } |
1363 |
1692 |
1364 /** |
1693 /** |
1365 * Sets the style for extra-highlighted lines |
1694 * Sets the style for extra-highlighted lines |
1369 */ |
1698 */ |
1370 function set_highlight_lines_extra_style($styles) { |
1699 function set_highlight_lines_extra_style($styles) { |
1371 $this->highlight_extra_lines_style = $styles; |
1700 $this->highlight_extra_lines_style = $styles; |
1372 } |
1701 } |
1373 |
1702 |
1374 /** |
1703 /** |
1375 * Sets the line-ending |
1704 * Sets the line-ending |
1376 * |
1705 * |
1377 * @param string The new line-ending |
1706 * @param string The new line-ending |
1378 */ |
1707 * @since 1.0.2 |
1379 function set_line_ending($line_ending) { |
1708 */ |
1380 $this->line_ending = (string)$line_ending; |
1709 function set_line_ending($line_ending) { |
1381 } |
1710 $this->line_ending = (string)$line_ending; |
1711 } |
|
1382 |
1712 |
1383 /** |
1713 /** |
1384 * Sets what number line numbers should start at. Should |
1714 * Sets what number line numbers should start at. Should |
1385 * be a positive integer, and will be converted to one. |
1715 * be a positive integer, and will be converted to one. |
1386 * |
1716 * |
1411 * @param string The encoding to use for the source |
1741 * @param string The encoding to use for the source |
1412 * @since 1.0.3 |
1742 * @since 1.0.3 |
1413 */ |
1743 */ |
1414 function set_encoding($encoding) { |
1744 function set_encoding($encoding) { |
1415 if ($encoding) { |
1745 if ($encoding) { |
1416 $this->encoding = $encoding; |
1746 $this->encoding = strtolower($encoding); |
1417 } |
1747 } |
1418 } |
1748 } |
1419 |
1749 |
1420 /** |
1750 /** |
1421 * Turns linking of keywords on or off. |
1751 * Turns linking of keywords on or off. |
1422 * |
1752 * |
1423 * @param boolean If true, links will be added to keywords |
1753 * @param boolean If true, links will be added to keywords |
1754 * @since 1.0.2 |
|
1424 */ |
1755 */ |
1425 function enable_keyword_links($enable = true) { |
1756 function enable_keyword_links($enable = true) { |
1426 $this->keyword_links = ($enable) ? true : false; |
1757 $this->keyword_links = (bool) $enable; |
1758 } |
|
1759 |
|
1760 /** |
|
1761 * Setup caches needed for styling. This is automatically called in |
|
1762 * parse_code() and get_stylesheet() when appropriate. This function helps |
|
1763 * stylesheet generators as they rely on some style information being |
|
1764 * preprocessed |
|
1765 * |
|
1766 * @since 1.0.8 |
|
1767 * @access private |
|
1768 */ |
|
1769 function build_style_cache() { |
|
1770 //Build the style cache needed to highlight numbers appropriate |
|
1771 if($this->lexic_permissions['NUMBERS']) { |
|
1772 //First check what way highlighting information for numbers are given |
|
1773 if(!isset($this->language_data['NUMBERS'])) { |
|
1774 $this->language_data['NUMBERS'] = 0; |
|
1775 } |
|
1776 |
|
1777 if(is_array($this->language_data['NUMBERS'])) { |
|
1778 $this->language_data['NUMBERS_CACHE'] = $this->language_data['NUMBERS']; |
|
1779 } else { |
|
1780 $this->language_data['NUMBERS_CACHE'] = array(); |
|
1781 if(!$this->language_data['NUMBERS']) { |
|
1782 $this->language_data['NUMBERS'] = |
|
1783 GESHI_NUMBER_INT_BASIC | |
|
1784 GESHI_NUMBER_FLT_NONSCI; |
|
1785 } |
|
1786 |
|
1787 for($i = 0, $j = $this->language_data['NUMBERS']; $j > 0; ++$i, $j>>=1) { |
|
1788 //Rearrange style indices if required ... |
|
1789 if(isset($this->language_data['STYLES']['NUMBERS'][1<<$i])) { |
|
1790 $this->language_data['STYLES']['NUMBERS'][$i] = |
|
1791 $this->language_data['STYLES']['NUMBERS'][1<<$i]; |
|
1792 unset($this->language_data['STYLES']['NUMBERS'][1<<$i]); |
|
1793 } |
|
1794 |
|
1795 //Check if this bit is set for highlighting |
|
1796 if($j&1) { |
|
1797 //So this bit is set ... |
|
1798 //Check if it belongs to group 0 or the actual stylegroup |
|
1799 if(isset($this->language_data['STYLES']['NUMBERS'][$i])) { |
|
1800 $this->language_data['NUMBERS_CACHE'][$i] = 1 << $i; |
|
1801 } else { |
|
1802 if(!isset($this->language_data['NUMBERS_CACHE'][0])) { |
|
1803 $this->language_data['NUMBERS_CACHE'][0] = 0; |
|
1804 } |
|
1805 $this->language_data['NUMBERS_CACHE'][0] |= 1 << $i; |
|
1806 } |
|
1807 } |
|
1808 } |
|
1809 } |
|
1810 } |
|
1811 } |
|
1812 |
|
1813 /** |
|
1814 * Setup caches needed for parsing. This is automatically called in parse_code() when appropriate. |
|
1815 * This function makes stylesheet generators much faster as they do not need these caches. |
|
1816 * |
|
1817 * @since 1.0.8 |
|
1818 * @access private |
|
1819 */ |
|
1820 function build_parse_cache() { |
|
1821 // cache symbol regexp |
|
1822 //As this is a costy operation, we avoid doing it for multiple groups ... |
|
1823 //Instead we perform it for all symbols at once. |
|
1824 // |
|
1825 //For this to work, we need to reorganize the data arrays. |
|
1826 if ($this->lexic_permissions['SYMBOLS'] && !empty($this->language_data['SYMBOLS'])) { |
|
1827 $this->language_data['MULTIPLE_SYMBOL_GROUPS'] = count($this->language_data['STYLES']['SYMBOLS']) > 1; |
|
1828 |
|
1829 $this->language_data['SYMBOL_DATA'] = array(); |
|
1830 $symbol_preg_multi = array(); // multi char symbols |
|
1831 $symbol_preg_single = array(); // single char symbols |
|
1832 foreach ($this->language_data['SYMBOLS'] as $key => $symbols) { |
|
1833 if (is_array($symbols)) { |
|
1834 foreach ($symbols as $sym) { |
|
1835 $sym = $this->hsc($sym); |
|
1836 if (!isset($this->language_data['SYMBOL_DATA'][$sym])) { |
|
1837 $this->language_data['SYMBOL_DATA'][$sym] = $key; |
|
1838 if (isset($sym[1])) { // multiple chars |
|
1839 $symbol_preg_multi[] = preg_quote($sym, '/'); |
|
1840 } else { // single char |
|
1841 if ($sym == '-') { |
|
1842 // don't trigger range out of order error |
|
1843 $symbol_preg_single[] = '\-'; |
|
1844 } else { |
|
1845 $symbol_preg_single[] = preg_quote($sym, '/'); |
|
1846 } |
|
1847 } |
|
1848 } |
|
1849 } |
|
1850 } else { |
|
1851 $symbols = $this->hsc($symbols); |
|
1852 if (!isset($this->language_data['SYMBOL_DATA'][$symbols])) { |
|
1853 $this->language_data['SYMBOL_DATA'][$symbols] = 0; |
|
1854 if (isset($symbols[1])) { // multiple chars |
|
1855 $symbol_preg_multi[] = preg_quote($symbols, '/'); |
|
1856 } else if ($symbols == '-') { |
|
1857 // don't trigger range out of order error |
|
1858 $symbol_preg_single[] = '\-'; |
|
1859 } else { // single char |
|
1860 $symbol_preg_single[] = preg_quote($symbols, '/'); |
|
1861 } |
|
1862 } |
|
1863 } |
|
1864 } |
|
1865 |
|
1866 //Now we have an array with each possible symbol as the key and the style as the actual data. |
|
1867 //This way we can set the correct style just the moment we highlight ... |
|
1868 // |
|
1869 //Now we need to rewrite our array to get a search string that |
|
1870 $symbol_preg = array(); |
|
1871 if (!empty($symbol_preg_multi)) { |
|
1872 rsort($symbol_preg_multi); |
|
1873 $symbol_preg[] = implode('|', $symbol_preg_multi); |
|
1874 } |
|
1875 if (!empty($symbol_preg_single)) { |
|
1876 rsort($symbol_preg_single); |
|
1877 $symbol_preg[] = '[' . implode('', $symbol_preg_single) . ']'; |
|
1878 } |
|
1879 $this->language_data['SYMBOL_SEARCH'] = implode("|", $symbol_preg); |
|
1880 } |
|
1881 |
|
1882 // cache optimized regexp for keyword matching |
|
1883 // remove old cache |
|
1884 $this->language_data['CACHED_KEYWORD_LISTS'] = array(); |
|
1885 foreach (array_keys($this->language_data['KEYWORDS']) as $key) { |
|
1886 if (!isset($this->lexic_permissions['KEYWORDS'][$key]) || |
|
1887 $this->lexic_permissions['KEYWORDS'][$key]) { |
|
1888 $this->optimize_keyword_group($key); |
|
1889 } |
|
1890 } |
|
1891 |
|
1892 // brackets |
|
1893 if ($this->lexic_permissions['BRACKETS']) { |
|
1894 $this->language_data['CACHE_BRACKET_MATCH'] = array('[', ']', '(', ')', '{', '}'); |
|
1895 if (!$this->use_classes && isset($this->language_data['STYLES']['BRACKETS'][0])) { |
|
1896 $this->language_data['CACHE_BRACKET_REPLACE'] = array( |
|
1897 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">[|>', |
|
1898 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">]|>', |
|
1899 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">(|>', |
|
1900 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">)|>', |
|
1901 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">{|>', |
|
1902 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">}|>', |
|
1903 ); |
|
1904 } |
|
1905 else { |
|
1906 $this->language_data['CACHE_BRACKET_REPLACE'] = array( |
|
1907 '<| class="br0">[|>', |
|
1908 '<| class="br0">]|>', |
|
1909 '<| class="br0">(|>', |
|
1910 '<| class="br0">)|>', |
|
1911 '<| class="br0">{|>', |
|
1912 '<| class="br0">}|>', |
|
1913 ); |
|
1914 } |
|
1915 } |
|
1916 |
|
1917 //Build the parse cache needed to highlight numbers appropriate |
|
1918 if($this->lexic_permissions['NUMBERS']) { |
|
1919 //Check if the style rearrangements have been processed ... |
|
1920 //This also does some preprocessing to check which style groups are useable ... |
|
1921 if(!isset($this->language_data['NUMBERS_CACHE'])) { |
|
1922 $this->build_style_cache(); |
|
1923 } |
|
1924 |
|
1925 //Number format specification |
|
1926 //All this formats are matched case-insensitively! |
|
1927 static $numbers_format = array( |
|
1928 GESHI_NUMBER_INT_BASIC => |
|
1929 '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])([1-9]\d*?|0)(?![0-9a-z\.])', |
|
1930 GESHI_NUMBER_INT_CSTYLE => |
|
1931 '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])([1-9]\d*?|0)l(?![0-9a-z\.])', |
|
1932 GESHI_NUMBER_BIN_SUFFIX => |
|
1933 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])[01]+?b(?![0-9a-z\.])', |
|
1934 GESHI_NUMBER_BIN_PREFIX_PERCENT => |
|
1935 '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])%[01]+?(?![0-9a-z\.])', |
|
1936 GESHI_NUMBER_BIN_PREFIX_0B => |
|
1937 '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])0b[01]+?(?![0-9a-z\.])', |
|
1938 GESHI_NUMBER_OCT_PREFIX => |
|
1939 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])0[0-7]+?(?![0-9a-z\.])', |
|
1940 GESHI_NUMBER_OCT_SUFFIX => |
|
1941 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])[0-7]+?o(?![0-9a-z\.])', |
|
1942 GESHI_NUMBER_HEX_PREFIX => |
|
1943 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])0x[0-9a-f]+?(?![0-9a-z\.])', |
|
1944 GESHI_NUMBER_HEX_SUFFIX => |
|
1945 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\d[0-9a-f]*?h(?![0-9a-z\.])', |
|
1946 GESHI_NUMBER_FLT_NONSCI => |
|
1947 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\d+?\.\d+?(?![0-9a-z\.])', |
|
1948 GESHI_NUMBER_FLT_NONSCI_F => |
|
1949 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])(?:\d+?(?:\.\d*?)?|\.\d+?)f(?![0-9a-z\.])', |
|
1950 GESHI_NUMBER_FLT_SCI_SHORT => |
|
1951 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\.\d+?(?:e[+\-]?\d+?)?(?![0-9a-z\.])', |
|
1952 GESHI_NUMBER_FLT_SCI_ZERO => |
|
1953 '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])(?:\d+?(?:\.\d*?)?|\.\d+?)(?:e[+\-]?\d+?)?(?![0-9a-z\.])' |
|
1954 ); |
|
1955 |
|
1956 //At this step we have an associative array with flag groups for a |
|
1957 //specific style or an string denoting a regexp given its index. |
|
1958 $this->language_data['NUMBERS_RXCACHE'] = array(); |
|
1959 foreach($this->language_data['NUMBERS_CACHE'] as $key => $rxdata) { |
|
1960 if(is_string($rxdata)) { |
|
1961 $regexp = $rxdata; |
|
1962 } else { |
|
1963 //This is a bitfield of number flags to highlight: |
|
1964 //Build an array, implode them together and make this the actual RX |
|
1965 $rxuse = array(); |
|
1966 for($i = 1; $i <= $rxdata; $i<<=1) { |
|
1967 if($rxdata & $i) { |
|
1968 $rxuse[] = $numbers_format[$i]; |
|
1969 } |
|
1970 } |
|
1971 $regexp = implode("|", $rxuse); |
|
1972 } |
|
1973 |
|
1974 $this->language_data['NUMBERS_RXCACHE'][$key] = |
|
1975 "/(?<!<\|\/NUM!)(?<!\d\/>)($regexp)(?!\|>)/i"; |
|
1976 } |
|
1977 } |
|
1978 |
|
1979 $this->parse_cache_built = true; |
|
1427 } |
1980 } |
1428 |
1981 |
1429 /** |
1982 /** |
1430 * Returns the code in $this->source, highlighted and surrounded by the |
1983 * Returns the code in $this->source, highlighted and surrounded by the |
1431 * nessecary HTML. |
1984 * nessecary HTML. |
1440 // Start the timer |
1993 // Start the timer |
1441 $start_time = microtime(); |
1994 $start_time = microtime(); |
1442 |
1995 |
1443 // Firstly, if there is an error, we won't highlight |
1996 // Firstly, if there is an error, we won't highlight |
1444 if ($this->error) { |
1997 if ($this->error) { |
1445 $result = GeSHi::hsc($this->source); |
1998 //Escape the source for output |
1999 $result = $this->hsc($this->source); |
|
2000 |
|
2001 //This fix is related to SF#1923020, but has to be applied regardless of |
|
2002 //actually highlighting symbols. |
|
2003 $result = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $result); |
|
2004 |
|
1446 // Timing is irrelevant |
2005 // Timing is irrelevant |
1447 $this->set_time($start_time, $start_time); |
2006 $this->set_time($start_time, $start_time); |
1448 return $this->finalise($result); |
2007 $this->finalise($result); |
2008 return $result; |
|
2009 } |
|
2010 |
|
2011 // make sure the parse cache is up2date |
|
2012 if (!$this->parse_cache_built) { |
|
2013 $this->build_parse_cache(); |
|
1449 } |
2014 } |
1450 |
2015 |
1451 // Replace all newlines to a common form. |
2016 // Replace all newlines to a common form. |
1452 $code = str_replace("\r\n", "\n", $this->source); |
2017 $code = str_replace("\r\n", "\n", $this->source); |
1453 $code = str_replace("\r", "\n", $code); |
2018 $code = str_replace("\r", "\n", $code); |
2019 |
|
1454 // Add spaces for regular expression matching and line numbers |
2020 // Add spaces for regular expression matching and line numbers |
1455 $code = "\n" . $code . "\n"; |
2021 // $code = "\n" . $code . "\n"; |
1456 |
2022 |
1457 // Initialise various stuff |
2023 // Initialise various stuff |
1458 $length = strlen($code); |
2024 $length = strlen($code); |
1459 $STRING_OPEN = ''; |
|
1460 $CLOSE_STRING = false; |
|
1461 $ESCAPE_CHAR_OPEN = false; |
|
1462 $COMMENT_MATCHED = false; |
2025 $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 = ''; |
2026 $stuff_to_parse = ''; |
1470 $result = ''; |
2027 $endresult = ''; |
1471 |
2028 |
1472 // "Important" selections are handled like multiline comments |
2029 // "Important" selections are handled like multiline comments |
1473 // @todo GET RID OF THIS SHIZ |
2030 // @todo GET RID OF THIS SHIZ |
1474 if ($this->enable_important_blocks) { |
2031 if ($this->enable_important_blocks) { |
1475 $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT; |
2032 $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT; |
1476 } |
2033 } |
1477 |
2034 |
1478 if ($this->strict_mode) { |
2035 if ($this->strict_mode) { |
1479 // Break the source into bits. Each bit will be a portion of the code |
2036 // Break the source into bits. Each bit will be a portion of the code |
1480 // within script delimiters - for example, HTML between < and > |
2037 // within script delimiters - for example, HTML between < and > |
1481 $parts = array(0 => array(0 => '')); |
|
1482 $k = 0; |
2038 $k = 0; |
1483 for ($i = 0; $i < $length; $i++) { |
2039 $parts = array(); |
1484 $char = substr($code, $i, 1); |
2040 $matches = array(); |
1485 if (!$HIGHLIGHTING_ON) { |
2041 $next_match_pointer = null; |
1486 foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) { |
2042 // we use a copy to unset delimiters on demand (when they are not found) |
2043 $delim_copy = $this->language_data['SCRIPT_DELIMITERS']; |
|
2044 $i = 0; |
|
2045 while ($i < $length) { |
|
2046 $next_match_pos = $length + 1; // never true |
|
2047 foreach ($delim_copy as $dk => $delimiters) { |
|
2048 if(is_array($delimiters)) { |
|
1487 foreach ($delimiters as $open => $close) { |
2049 foreach ($delimiters as $open => $close) { |
2050 // make sure the cache is setup properly |
|
2051 if (!isset($matches[$dk][$open])) { |
|
2052 $matches[$dk][$open] = array( |
|
2053 'next_match' => -1, |
|
2054 'dk' => $dk, |
|
2055 |
|
2056 'open' => $open, // needed for grouping of adjacent code blocks (see below) |
|
2057 'open_strlen' => strlen($open), |
|
2058 |
|
2059 'close' => $close, |
|
2060 'close_strlen' => strlen($close), |
|
2061 ); |
|
2062 } |
|
1488 // Get the next little bit for this opening string |
2063 // Get the next little bit for this opening string |
1489 $check = substr($code, $i, strlen($open)); |
2064 if ($matches[$dk][$open]['next_match'] < $i) { |
1490 // If it matches... |
2065 // only find the next pos if it was not already cached |
1491 if ($check == $open) { |
2066 $open_pos = strpos($code, $open, $i); |
1492 // We start a new block with the highlightable |
2067 if ($open_pos === false) { |
1493 // code in it |
2068 // no match for this delimiter ever |
1494 $HIGHLIGHTING_ON = $open; |
2069 unset($delim_copy[$dk][$open]); |
1495 $i += strlen($open) - 1; |
2070 continue; |
1496 $char = $open; |
2071 } |
1497 $parts[++$k][0] = $char; |
2072 $matches[$dk][$open]['next_match'] = $open_pos; |
1498 |
2073 } |
1499 // No point going around again... |
2074 if ($matches[$dk][$open]['next_match'] < $next_match_pos) { |
1500 break(2); |
2075 //So we got a new match, update the close_pos |
2076 $matches[$dk][$open]['close_pos'] = |
|
2077 strpos($code, $close, $matches[$dk][$open]['next_match']+1); |
|
2078 |
|
2079 $next_match_pointer =& $matches[$dk][$open]; |
|
2080 $next_match_pos = $matches[$dk][$open]['next_match']; |
|
2081 } |
|
2082 } |
|
2083 } else { |
|
2084 //So we should match an RegExp as Strict Block ... |
|
2085 /** |
|
2086 * The value in $delimiters is expected to be an RegExp |
|
2087 * containing exactly 2 matching groups: |
|
2088 * - Group 1 is the opener |
|
2089 * - Group 2 is the closer |
|
2090 */ |
|
2091 if(!GESHI_PHP_PRE_433 && //Needs proper rewrite to work with PHP >=4.3.0; 4.3.3 is guaranteed to work. |
|
2092 preg_match($delimiters, $code, $matches_rx, PREG_OFFSET_CAPTURE, $i)) { |
|
2093 //We got a match ... |
|
2094 $matches[$dk] = array( |
|
2095 'next_match' => $matches_rx[1][1], |
|
2096 'dk' => $dk, |
|
2097 |
|
2098 'close_strlen' => strlen($matches_rx[2][0]), |
|
2099 'close_pos' => $matches_rx[2][1], |
|
2100 ); |
|
2101 } else { |
|
2102 // no match for this delimiter ever |
|
2103 unset($delim_copy[$dk]); |
|
2104 continue; |
|
2105 } |
|
2106 |
|
2107 if ($matches[$dk]['next_match'] <= $next_match_pos) { |
|
2108 $next_match_pointer =& $matches[$dk]; |
|
2109 $next_match_pos = $matches[$dk]['next_match']; |
|
2110 } |
|
2111 } |
|
2112 } |
|
2113 // non-highlightable text |
|
2114 $parts[$k] = array( |
|
2115 1 => substr($code, $i, $next_match_pos - $i) |
|
2116 ); |
|
2117 ++$k; |
|
2118 |
|
2119 if ($next_match_pos > $length) { |
|
2120 // out of bounds means no next match was found |
|
2121 break; |
|
2122 } |
|
2123 |
|
2124 // highlightable code |
|
2125 $parts[$k][0] = $next_match_pointer['dk']; |
|
2126 |
|
2127 //Only combine for non-rx script blocks |
|
2128 if(is_array($delim_copy[$next_match_pointer['dk']])) { |
|
2129 // group adjacent script blocks, e.g. <foobar><asdf> should be one block, not three! |
|
2130 $i = $next_match_pos + $next_match_pointer['open_strlen']; |
|
2131 while (true) { |
|
2132 $close_pos = strpos($code, $next_match_pointer['close'], $i); |
|
2133 if ($close_pos == false) { |
|
2134 break; |
|
2135 } |
|
2136 $i = $close_pos + $next_match_pointer['close_strlen']; |
|
2137 if ($i == $length) { |
|
2138 break; |
|
2139 } |
|
2140 if ($code[$i] == $next_match_pointer['open'][0] && ($next_match_pointer['open_strlen'] == 1 || |
|
2141 substr($code, $i, $next_match_pointer['open_strlen']) == $next_match_pointer['open'])) { |
|
2142 // merge adjacent but make sure we don't merge things like <tag><!-- comment --> |
|
2143 foreach ($matches as $submatches) { |
|
2144 foreach ($submatches as $match) { |
|
2145 if ($match['next_match'] == $i) { |
|
2146 // a different block already matches here! |
|
2147 break 3; |
|
2148 } |
|
2149 } |
|
2150 } |
|
2151 } else { |
|
2152 break; |
|
2153 } |
|
2154 } |
|
2155 } else { |
|
2156 $close_pos = $next_match_pointer['close_pos'] + $next_match_pointer['close_strlen']; |
|
2157 $i = $close_pos; |
|
2158 } |
|
2159 |
|
2160 if ($close_pos === false) { |
|
2161 // no closing delimiter found! |
|
2162 $parts[$k][1] = substr($code, $next_match_pos); |
|
2163 ++$k; |
|
2164 break; |
|
2165 } else { |
|
2166 $parts[$k][1] = substr($code, $next_match_pos, $i - $next_match_pos); |
|
2167 ++$k; |
|
2168 } |
|
2169 } |
|
2170 unset($delim_copy, $next_match_pointer, $next_match_pos, $matches); |
|
2171 $num_parts = $k; |
|
2172 |
|
2173 if ($num_parts == 1 && $this->strict_mode == GESHI_MAYBE) { |
|
2174 // when we have only one part, we don't have anything to highlight at all. |
|
2175 // if we have a "maybe" strict language, this should be handled as highlightable code |
|
2176 $parts = array( |
|
2177 0 => array( |
|
2178 0 => '', |
|
2179 1 => '' |
|
2180 ), |
|
2181 1 => array( |
|
2182 0 => null, |
|
2183 1 => $parts[0][1] |
|
2184 ) |
|
2185 ); |
|
2186 $num_parts = 2; |
|
2187 } |
|
2188 |
|
2189 } else { |
|
2190 // Not strict mode - simply dump the source into |
|
2191 // the array at index 1 (the first highlightable block) |
|
2192 $parts = array( |
|
2193 0 => array( |
|
2194 0 => '', |
|
2195 1 => '' |
|
2196 ), |
|
2197 1 => array( |
|
2198 0 => null, |
|
2199 1 => $code |
|
2200 ) |
|
2201 ); |
|
2202 $num_parts = 2; |
|
2203 } |
|
2204 |
|
2205 //Unset variables we won't need any longer |
|
2206 unset($code); |
|
2207 |
|
2208 //Preload some repeatedly used values regarding hardquotes ... |
|
2209 $hq = isset($this->language_data['HARDQUOTE']) ? $this->language_data['HARDQUOTE'][0] : false; |
|
2210 $hq_strlen = strlen($hq); |
|
2211 |
|
2212 //Preload if line numbers are to be generated afterwards |
|
2213 //Added a check if line breaks should be forced even without line numbers, fixes SF#1727398 |
|
2214 $check_linenumbers = $this->line_numbers != GESHI_NO_LINE_NUMBERS || |
|
2215 !empty($this->highlight_extra_lines) || !$this->allow_multiline_span; |
|
2216 |
|
2217 //preload the escape char for faster checking ... |
|
2218 $escaped_escape_char = $this->hsc($this->language_data['ESCAPE_CHAR']); |
|
2219 |
|
2220 // this is used for single-line comments |
|
2221 $sc_disallowed_before = ""; |
|
2222 $sc_disallowed_after = ""; |
|
2223 |
|
2224 if (isset($this->language_data['PARSER_CONTROL'])) { |
|
2225 if (isset($this->language_data['PARSER_CONTROL']['COMMENTS'])) { |
|
2226 if (isset($this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_BEFORE'])) { |
|
2227 $sc_disallowed_before = $this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_BEFORE']; |
|
2228 } |
|
2229 if (isset($this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_AFTER'])) { |
|
2230 $sc_disallowed_after = $this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_AFTER']; |
|
2231 } |
|
2232 } |
|
2233 } |
|
2234 |
|
2235 //Fix for SF#1932083: Multichar Quotemarks unsupported |
|
2236 $is_string_starter = array(); |
|
2237 if ($this->lexic_permissions['STRINGS']) { |
|
2238 foreach ($this->language_data['QUOTEMARKS'] as $quotemark) { |
|
2239 if (!isset($is_string_starter[$quotemark[0]])) { |
|
2240 $is_string_starter[$quotemark[0]] = (string)$quotemark; |
|
2241 } else if (is_string($is_string_starter[$quotemark[0]])) { |
|
2242 $is_string_starter[$quotemark[0]] = array( |
|
2243 $is_string_starter[$quotemark[0]], |
|
2244 $quotemark); |
|
2245 } else { |
|
2246 $is_string_starter[$quotemark[0]][] = $quotemark; |
|
2247 } |
|
2248 } |
|
2249 } |
|
2250 |
|
2251 // Now we go through each part. We know that even-indexed parts are |
|
2252 // code that shouldn't be highlighted, and odd-indexed parts should |
|
2253 // be highlighted |
|
2254 for ($key = 0; $key < $num_parts; ++$key) { |
|
2255 $STRICTATTRS = ''; |
|
2256 |
|
2257 // If this block should be highlighted... |
|
2258 if (!($key & 1)) { |
|
2259 // Else not a block to highlight |
|
2260 $endresult .= $this->hsc($parts[$key][1]); |
|
2261 unset($parts[$key]); |
|
2262 continue; |
|
2263 } |
|
2264 |
|
2265 $result = ''; |
|
2266 $part = $parts[$key][1]; |
|
2267 |
|
2268 $highlight_part = true; |
|
2269 if ($this->strict_mode && !is_null($parts[$key][0])) { |
|
2270 // get the class key for this block of code |
|
2271 $script_key = $parts[$key][0]; |
|
2272 $highlight_part = $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]; |
|
2273 if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' && |
|
2274 $this->lexic_permissions['SCRIPT']) { |
|
2275 // Add a span element around the source to |
|
2276 // highlight the overall source block |
|
2277 if (!$this->use_classes && |
|
2278 $this->language_data['STYLES']['SCRIPT'][$script_key] != '') { |
|
2279 $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"'; |
|
2280 } else { |
|
2281 $attributes = ' class="sc' . $script_key . '"'; |
|
2282 } |
|
2283 $result .= "<span$attributes>"; |
|
2284 $STRICTATTRS = $attributes; |
|
2285 } |
|
2286 } |
|
2287 |
|
2288 if ($highlight_part) { |
|
2289 // Now, highlight the code in this block. This code |
|
2290 // is really the engine of GeSHi (along with the method |
|
2291 // parse_non_string_part). |
|
2292 |
|
2293 // cache comment regexps incrementally |
|
2294 $next_comment_regexp_key = ''; |
|
2295 $next_comment_regexp_pos = -1; |
|
2296 $next_comment_multi_pos = -1; |
|
2297 $next_comment_single_pos = -1; |
|
2298 $comment_regexp_cache_per_key = array(); |
|
2299 $comment_multi_cache_per_key = array(); |
|
2300 $comment_single_cache_per_key = array(); |
|
2301 $next_open_comment_multi = ''; |
|
2302 $next_comment_single_key = ''; |
|
2303 $escape_regexp_cache_per_key = array(); |
|
2304 $next_escape_regexp_key = ''; |
|
2305 $next_escape_regexp_pos = -1; |
|
2306 |
|
2307 $length = strlen($part); |
|
2308 for ($i = 0; $i < $length; ++$i) { |
|
2309 // Get the next char |
|
2310 $char = $part[$i]; |
|
2311 $char_len = 1; |
|
2312 |
|
2313 // update regexp comment cache if needed |
|
2314 if (isset($this->language_data['COMMENT_REGEXP']) && $next_comment_regexp_pos < $i) { |
|
2315 $next_comment_regexp_pos = $length; |
|
2316 foreach ($this->language_data['COMMENT_REGEXP'] as $comment_key => $regexp) { |
|
2317 $match_i = false; |
|
2318 if (isset($comment_regexp_cache_per_key[$comment_key]) && |
|
2319 ($comment_regexp_cache_per_key[$comment_key]['pos'] >= $i || |
|
2320 $comment_regexp_cache_per_key[$comment_key]['pos'] === false)) { |
|
2321 // we have already matched something |
|
2322 if ($comment_regexp_cache_per_key[$comment_key]['pos'] === false) { |
|
2323 // this comment is never matched |
|
2324 continue; |
|
2325 } |
|
2326 $match_i = $comment_regexp_cache_per_key[$comment_key]['pos']; |
|
2327 } else if ( |
|
2328 //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible |
|
2329 (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $i), $match, PREG_OFFSET_CAPTURE)) || |
|
2330 (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $i)) |
|
2331 ) { |
|
2332 $match_i = $match[0][1]; |
|
2333 if (GESHI_PHP_PRE_433) { |
|
2334 $match_i += $i; |
|
2335 } |
|
2336 |
|
2337 $comment_regexp_cache_per_key[$comment_key] = array( |
|
2338 'key' => $comment_key, |
|
2339 'length' => strlen($match[0][0]), |
|
2340 'pos' => $match_i |
|
2341 ); |
|
2342 } else { |
|
2343 $comment_regexp_cache_per_key[$comment_key]['pos'] = false; |
|
2344 continue; |
|
2345 } |
|
2346 |
|
2347 if ($match_i !== false && $match_i < $next_comment_regexp_pos) { |
|
2348 $next_comment_regexp_pos = $match_i; |
|
2349 $next_comment_regexp_key = $comment_key; |
|
2350 if ($match_i === $i) { |
|
2351 break; |
|
2352 } |
|
1501 } |
2353 } |
1502 } |
2354 } |
1503 } |
2355 } |
1504 } |
2356 |
1505 else { |
2357 $string_started = false; |
1506 foreach ($this->language_data['SCRIPT_DELIMITERS'] as $key => $delimiters) { |
2358 |
1507 foreach ($delimiters as $open => $close) { |
2359 if (isset($is_string_starter[$char])) { |
1508 if ($open == $HIGHLIGHTING_ON) { |
2360 // Possibly the start of a new string ... |
1509 // Found the closing tag |
2361 |
1510 break(2); |
2362 //Check which starter it was ... |
2363 //Fix for SF#1932083: Multichar Quotemarks unsupported |
|
2364 if (is_array($is_string_starter[$char])) { |
|
2365 $char_new = ''; |
|
2366 foreach ($is_string_starter[$char] as $testchar) { |
|
2367 if ($testchar === substr($part, $i, strlen($testchar)) && |
|
2368 strlen($testchar) > strlen($char_new)) { |
|
2369 $char_new = $testchar; |
|
2370 $string_started = true; |
|
2371 } |
|
2372 } |
|
2373 if ($string_started) { |
|
2374 $char = $char_new; |
|
2375 } |
|
2376 } else { |
|
2377 $testchar = $is_string_starter[$char]; |
|
2378 if ($testchar === substr($part, $i, strlen($testchar))) { |
|
2379 $char = $testchar; |
|
2380 $string_started = true; |
|
1511 } |
2381 } |
1512 } |
2382 } |
2383 $char_len = strlen($char); |
|
1513 } |
2384 } |
1514 // We check code from our current position BACKWARDS. This is so |
2385 |
1515 // the ending string for highlighting can be included in the block |
2386 if ($string_started && $i != $next_comment_regexp_pos) { |
1516 $check = substr($code, $i - strlen($close) + 1, strlen($close)); |
2387 // Hand out the correct style information for this string |
1517 if ($check == $close) { |
2388 $string_key = array_search($char, $this->language_data['QUOTEMARKS']); |
1518 $HIGHLIGHTING_ON = ''; |
2389 if (!isset($this->language_data['STYLES']['STRINGS'][$string_key]) || |
1519 // Add the string to the rest of the string for this part |
2390 !isset($this->language_data['STYLES']['ESCAPE_CHAR'][$string_key])) { |
1520 $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; |
2391 $string_key = 0; |
1521 $parts[++$k][0] = ''; |
2392 } |
1522 $char = ''; |
2393 |
1523 } |
2394 // parse the stuff before this |
1524 } |
2395 $result .= $this->parse_non_string_part($stuff_to_parse); |
1525 $parts[$k][1] = ( isset($parts[$k][1]) ) ? $parts[$k][1] . $char : $char; |
2396 $stuff_to_parse = ''; |
1526 } |
2397 |
1527 $HIGHLIGHTING_ON = ''; |
2398 if (!$this->use_classes) { |
1528 } |
2399 $string_attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][$string_key] . '"'; |
1529 else { |
2400 } else { |
1530 // Not strict mode - simply dump the source into |
2401 $string_attributes = ' class="st'.$string_key.'"'; |
1531 // the array at index 1 (the first highlightable block) |
2402 } |
1532 $parts = array( |
2403 |
1533 1 => array( |
2404 // now handle the string |
1534 0 => '', |
2405 $string = "<span$string_attributes>" . GeSHi::hsc($char); |
1535 1 => $code |
2406 $start = $i + $char_len; |
1536 ) |
2407 $string_open = true; |
1537 ); |
2408 |
1538 } |
2409 if(empty($this->language_data['ESCAPE_REGEXP'])) { |
1539 |
2410 $next_escape_regexp_pos = $length; |
1540 // Now we go through each part. We know that even-indexed parts are |
2411 } |
1541 // code that shouldn't be highlighted, and odd-indexed parts should |
2412 |
1542 // be highlighted |
2413 do { |
1543 foreach ($parts as $key => $data) { |
2414 //Get the regular ending pos ... |
1544 $part = $data[1]; |
2415 $close_pos = strpos($part, $char, $start); |
1545 // If this block should be highlighted... |
2416 if(false === $close_pos) { |
1546 if ($key % 2) { |
2417 $close_pos = $length; |
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 } |
2418 } |
1554 } |
2419 |
1555 } |
2420 if($this->lexic_permissions['ESCAPE_CHAR']) { |
1556 |
2421 // update escape regexp cache if needed |
1557 if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' && |
2422 if (isset($this->language_data['ESCAPE_REGEXP']) && $next_escape_regexp_pos < $start) { |
1558 $this->lexic_permissions['SCRIPT']) { |
2423 $next_escape_regexp_pos = $length; |
1559 // Add a span element around the source to |
2424 foreach ($this->language_data['ESCAPE_REGEXP'] as $escape_key => $regexp) { |
1560 // highlight the overall source block |
2425 $match_i = false; |
1561 if (!$this->use_classes && |
2426 if (isset($escape_regexp_cache_per_key[$escape_key]) && |
1562 $this->language_data['STYLES']['SCRIPT'][$script_key] != '') { |
2427 ($escape_regexp_cache_per_key[$escape_key]['pos'] >= $start || |
1563 $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"'; |
2428 $escape_regexp_cache_per_key[$escape_key]['pos'] === false)) { |
1564 } |
2429 // we have already matched something |
1565 else { |
2430 if ($escape_regexp_cache_per_key[$escape_key]['pos'] === false) { |
1566 $attributes = ' class="sc' . $script_key . '"'; |
2431 // this comment is never matched |
1567 } |
2432 continue; |
1568 $result .= "<span$attributes>"; |
2433 } |
1569 $STRICTATTRS = $attributes; |
2434 $match_i = $escape_regexp_cache_per_key[$escape_key]['pos']; |
1570 } |
2435 } else if ( |
1571 } |
2436 //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible |
1572 |
2437 (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $start), $match, PREG_OFFSET_CAPTURE)) || |
1573 if (!$this->strict_mode || $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key]) { |
2438 (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $start)) |
1574 // Now, highlight the code in this block. This code |
2439 ) { |
1575 // is really the engine of GeSHi (along with the method |
2440 $match_i = $match[0][1]; |
1576 // parse_non_string_part). |
2441 if (GESHI_PHP_PRE_433) { |
1577 $length = strlen($part); |
2442 $match_i += $start; |
1578 for ($i = 0; $i < $length; $i++) { |
2443 } |
1579 // Get the next char |
2444 |
1580 $char = substr($part, $i, 1); |
2445 $escape_regexp_cache_per_key[$escape_key] = array( |
1581 $hq = isset($this->language_data['HARDQUOTE']) ? $this->language_data['HARDQUOTE'][0] : false; |
2446 'key' => $escape_key, |
1582 // Is this char the newline and line numbers being used? |
2447 'length' => strlen($match[0][0]), |
1583 if (($this->line_numbers != GESHI_NO_LINE_NUMBERS |
2448 'pos' => $match_i |
1584 || count($this->highlight_extra_lines) > 0) |
2449 ); |
1585 && $char == "\n") { |
2450 } else { |
1586 // If so, is there a string open? If there is, we should end it before |
2451 $escape_regexp_cache_per_key[$escape_key]['pos'] = false; |
2452 continue; |
|
2453 } |
|
2454 |
|
2455 if ($match_i !== false && $match_i < $next_escape_regexp_pos) { |
|
2456 $next_escape_regexp_pos = $match_i; |
|
2457 $next_escape_regexp_key = $escape_key; |
|
2458 if ($match_i === $start) { |
|
2459 break; |
|
2460 } |
|
2461 } |
|
2462 } |
|
2463 } |
|
2464 |
|
2465 //Find the next simple escape position |
|
2466 if('' != $this->language_data['ESCAPE_CHAR']) { |
|
2467 $simple_escape = strpos($part, $this->language_data['ESCAPE_CHAR'], $start); |
|
2468 if(false === $simple_escape) { |
|
2469 $simple_escape = $length; |
|
2470 } |
|
2471 } else { |
|
2472 $simple_escape = $length; |
|
2473 } |
|
2474 } else { |
|
2475 $next_escape_regexp_pos = $length; |
|
2476 $simple_escape = $length; |
|
2477 } |
|
2478 |
|
2479 if($simple_escape < $next_escape_regexp_pos && |
|
2480 $simple_escape < $length && |
|
2481 $simple_escape < $close_pos) { |
|
2482 //The nexxt escape sequence is a simple one ... |
|
2483 $es_pos = $simple_escape; |
|
2484 |
|
2485 //Add the stuff not in the string yet ... |
|
2486 $string .= $this->hsc(substr($part, $start, $es_pos - $start)); |
|
2487 |
|
2488 //Get the style for this escaped char ... |
|
2489 if (!$this->use_classes) { |
|
2490 $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"'; |
|
2491 } else { |
|
2492 $escape_char_attributes = ' class="es0"'; |
|
2493 } |
|
2494 |
|
2495 //Add the style for the escape char ... |
|
2496 $string .= "<span$escape_char_attributes>" . |
|
2497 GeSHi::hsc($this->language_data['ESCAPE_CHAR']); |
|
2498 |
|
2499 //Get the byte AFTER the ESCAPE_CHAR we just found |
|
2500 $es_char = $part[$es_pos + 1]; |
|
2501 if ($es_char == "\n") { |
|
2502 // don't put a newline around newlines |
|
2503 $string .= "</span>\n"; |
|
2504 $start = $es_pos + 2; |
|
2505 } else if (ord($es_char) >= 128) { |
|
2506 //This is an non-ASCII char (UTF8 or single byte) |
|
2507 //This code tries to work around SF#2037598 ... |
|
2508 if(function_exists('mb_substr')) { |
|
2509 $es_char_m = mb_substr(substr($part, $es_pos+1, 16), 0, 1, $this->encoding); |
|
2510 $string .= $es_char_m . '</span>'; |
|
2511 } else if (!GESHI_PHP_PRE_433 && 'utf-8' == $this->encoding) { |
|
2512 if(preg_match("/[\xC2-\xDF][\x80-\xBF]". |
|
2513 "|\xE0[\xA0-\xBF][\x80-\xBF]". |
|
2514 "|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}". |
|
2515 "|\xED[\x80-\x9F][\x80-\xBF]". |
|
2516 "|\xF0[\x90-\xBF][\x80-\xBF]{2}". |
|
2517 "|[\xF1-\xF3][\x80-\xBF]{3}". |
|
2518 "|\xF4[\x80-\x8F][\x80-\xBF]{2}/s", |
|
2519 $part, $es_char_m, null, $es_pos + 1)) { |
|
2520 $es_char_m = $es_char_m[0]; |
|
2521 } else { |
|
2522 $es_char_m = $es_char; |
|
2523 } |
|
2524 $string .= $this->hsc($es_char_m) . '</span>'; |
|
2525 } else { |
|
2526 $es_char_m = $this->hsc($es_char); |
|
2527 } |
|
2528 $start = $es_pos + strlen($es_char_m) + 1; |
|
2529 } else { |
|
2530 $string .= $this->hsc($es_char) . '</span>'; |
|
2531 $start = $es_pos + 2; |
|
2532 } |
|
2533 } else if ($next_escape_regexp_pos < $length && |
|
2534 $next_escape_regexp_pos < $close_pos) { |
|
2535 $es_pos = $next_escape_regexp_pos; |
|
2536 //Add the stuff not in the string yet ... |
|
2537 $string .= $this->hsc(substr($part, $start, $es_pos - $start)); |
|
2538 |
|
2539 //Get the key and length of this match ... |
|
2540 $escape = $escape_regexp_cache_per_key[$next_escape_regexp_key]; |
|
2541 $escape_str = substr($part, $es_pos, $escape['length']); |
|
2542 $escape_key = $escape['key']; |
|
2543 |
|
2544 //Get the style for this escaped char ... |
|
2545 if (!$this->use_classes) { |
|
2546 $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][$escape_key] . '"'; |
|
2547 } else { |
|
2548 $escape_char_attributes = ' class="es' . $escape_key . '"'; |
|
2549 } |
|
2550 |
|
2551 //Add the style for the escape char ... |
|
2552 $string .= "<span$escape_char_attributes>" . |
|
2553 $this->hsc($escape_str) . '</span>'; |
|
2554 |
|
2555 $start = $es_pos + $escape['length']; |
|
2556 } else { |
|
2557 //Copy the remainder of the string ... |
|
2558 $string .= $this->hsc(substr($part, $start, $close_pos - $start + $char_len)) . '</span>'; |
|
2559 $start = $close_pos + $char_len; |
|
2560 $string_open = false; |
|
2561 } |
|
2562 } while($string_open); |
|
2563 |
|
2564 if ($check_linenumbers) { |
|
2565 // Are line numbers used? If, we should end the string before |
|
1587 // the newline and begin it again (so when <li>s are put in the source |
2566 // the newline and begin it again (so when <li>s are put in the source |
1588 // remains XHTML compliant) |
2567 // remains XHTML compliant) |
1589 // note to self: This opens up possibility of config files specifying |
2568 // note to self: This opens up possibility of config files specifying |
1590 // that languages can/cannot have multiline strings??? |
2569 // that languages can/cannot have multiline strings??? |
1591 if ($STRING_OPEN) { |
2570 $string = str_replace("\n", "</span>\n<span$string_attributes>", $string); |
2571 } |
|
2572 |
|
2573 $result .= $string; |
|
2574 $string = ''; |
|
2575 $i = $start - 1; |
|
2576 continue; |
|
2577 } else if ($this->lexic_permissions['STRINGS'] && $hq && $hq[0] == $char && |
|
2578 substr($part, $i, $hq_strlen) == $hq) { |
|
2579 // The start of a hard quoted string |
|
2580 if (!$this->use_classes) { |
|
2581 $string_attributes = ' style="' . $this->language_data['STYLES']['STRINGS']['HARD'] . '"'; |
|
2582 $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR']['HARD'] . '"'; |
|
2583 } else { |
|
2584 $string_attributes = ' class="st_h"'; |
|
2585 $escape_char_attributes = ' class="es_h"'; |
|
2586 } |
|
2587 // parse the stuff before this |
|
2588 $result .= $this->parse_non_string_part($stuff_to_parse); |
|
2589 $stuff_to_parse = ''; |
|
2590 |
|
2591 // now handle the string |
|
2592 $string = ''; |
|
2593 |
|
2594 // look for closing quote |
|
2595 $start = $i + $hq_strlen; |
|
2596 while ($close_pos = strpos($part, $this->language_data['HARDQUOTE'][1], $start)) { |
|
2597 $start = $close_pos + 1; |
|
2598 if ($this->lexic_permissions['ESCAPE_CHAR'] && $part[$close_pos - 1] == $this->language_data['ESCAPE_CHAR']) { |
|
2599 // make sure this quote is not escaped |
|
2600 foreach ($this->language_data['HARDESCAPE'] as $hardescape) { |
|
2601 if (substr($part, $close_pos - 1, strlen($hardescape)) == $hardescape) { |
|
2602 // check wether this quote is escaped or if it is something like '\\' |
|
2603 $escape_char_pos = $close_pos - 1; |
|
2604 while ($escape_char_pos > 0 |
|
2605 && $part[$escape_char_pos - 1] == $this->language_data['ESCAPE_CHAR']) { |
|
2606 --$escape_char_pos; |
|
2607 } |
|
2608 if (($close_pos - $escape_char_pos) & 1) { |
|
2609 // uneven number of escape chars => this quote is escaped |
|
2610 continue 2; |
|
2611 } |
|
2612 } |
|
2613 } |
|
2614 } |
|
2615 |
|
2616 // found closing quote |
|
2617 break; |
|
2618 } |
|
2619 |
|
2620 //Found the closing delimiter? |
|
2621 if (!$close_pos) { |
|
2622 // span till the end of this $part when no closing delimiter is found |
|
2623 $close_pos = $length; |
|
2624 } |
|
2625 |
|
2626 //Get the actual string |
|
2627 $string = substr($part, $i, $close_pos - $i + 1); |
|
2628 $i = $close_pos; |
|
2629 |
|
2630 // handle escape chars and encode html chars |
|
2631 // (special because when we have escape chars within our string they may not be escaped) |
|
2632 if ($this->lexic_permissions['ESCAPE_CHAR'] && $this->language_data['ESCAPE_CHAR']) { |
|
2633 $start = 0; |
|
2634 $new_string = ''; |
|
2635 while ($es_pos = strpos($string, $this->language_data['ESCAPE_CHAR'], $start)) { |
|
2636 // hmtl escape stuff before |
|
2637 $new_string .= $this->hsc(substr($string, $start, $es_pos - $start)); |
|
2638 // check if this is a hard escape |
|
2639 foreach ($this->language_data['HARDESCAPE'] as $hardescape) { |
|
2640 if (substr($string, $es_pos, strlen($hardescape)) == $hardescape) { |
|
2641 // indeed, this is a hardescape |
|
2642 $new_string .= "<span$escape_char_attributes>" . |
|
2643 $this->hsc($hardescape) . '</span>'; |
|
2644 $start = $es_pos + strlen($hardescape); |
|
2645 continue 2; |
|
2646 } |
|
2647 } |
|
2648 // not a hard escape, but a normal escape |
|
2649 // they come in pairs of two |
|
2650 $c = 0; |
|
2651 while (isset($string[$es_pos + $c]) && isset($string[$es_pos + $c + 1]) |
|
2652 && $string[$es_pos + $c] == $this->language_data['ESCAPE_CHAR'] |
|
2653 && $string[$es_pos + $c + 1] == $this->language_data['ESCAPE_CHAR']) { |
|
2654 $c += 2; |
|
2655 } |
|
2656 if ($c) { |
|
2657 $new_string .= "<span$escape_char_attributes>" . |
|
2658 str_repeat($escaped_escape_char, $c) . |
|
2659 '</span>'; |
|
2660 $start = $es_pos + $c; |
|
2661 } else { |
|
2662 // this is just a single lonely escape char... |
|
2663 $new_string .= $escaped_escape_char; |
|
2664 $start = $es_pos + 1; |
|
2665 } |
|
2666 } |
|
2667 $string = $new_string . $this->hsc(substr($string, $start)); |
|
2668 } else { |
|
2669 $string = $this->hsc($string); |
|
2670 } |
|
2671 |
|
2672 if ($check_linenumbers) { |
|
2673 // Are line numbers used? If, we should end the string before |
|
2674 // the newline and begin it again (so when <li>s are put in the source |
|
2675 // remains XHTML compliant) |
|
2676 // note to self: This opens up possibility of config files specifying |
|
2677 // that languages can/cannot have multiline strings??? |
|
2678 $string = str_replace("\n", "</span>\n<span$string_attributes>", $string); |
|
2679 } |
|
2680 |
|
2681 $result .= "<span$string_attributes>" . $string . '</span>'; |
|
2682 $string = ''; |
|
2683 continue; |
|
2684 } else { |
|
2685 //Have a look for regexp comments |
|
2686 if ($i == $next_comment_regexp_pos) { |
|
2687 $COMMENT_MATCHED = true; |
|
2688 $comment = $comment_regexp_cache_per_key[$next_comment_regexp_key]; |
|
2689 $test_str = $this->hsc(substr($part, $i, $comment['length'])); |
|
2690 |
|
2691 //@todo If remove important do remove here |
|
2692 if ($this->lexic_permissions['COMMENTS']['MULTI']) { |
|
1592 if (!$this->use_classes) { |
2693 if (!$this->use_classes) { |
1593 $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; |
2694 $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment['key']] . '"'; |
2695 } else { |
|
2696 $attributes = ' class="co' . $comment['key'] . '"'; |
|
1594 } |
2697 } |
1595 else { |
2698 |
1596 $attributes = ' class="st0"'; |
2699 $test_str = "<span$attributes>" . $test_str . "</span>"; |
2700 |
|
2701 // Short-cut through all the multiline code |
|
2702 if ($check_linenumbers) { |
|
2703 // strreplace to put close span and open span around multiline newlines |
|
2704 $test_str = str_replace( |
|
2705 "\n", "</span>\n<span$attributes>", |
|
2706 str_replace("\n ", "\n ", $test_str) |
|
2707 ); |
|
1597 } |
2708 } |
1598 $char = '</span>' . $char . "<span$attributes>"; |
|
1599 } |
2709 } |
2710 |
|
2711 $i += $comment['length'] - 1; |
|
2712 |
|
2713 // parse the rest |
|
2714 $result .= $this->parse_non_string_part($stuff_to_parse); |
|
2715 $stuff_to_parse = ''; |
|
1600 } |
2716 } |
1601 else if ($char == $STRING_OPEN) { |
2717 |
1602 // A match of a string delimiter |
2718 // If we haven't matched a regexp comment, try multi-line comments |
1603 if (($this->lexic_permissions['ESCAPE_CHAR'] && $ESCAPE_CHAR_OPEN) || |
2719 if (!$COMMENT_MATCHED) { |
1604 ($this->lexic_permissions['STRINGS'] && !$ESCAPE_CHAR_OPEN)) { |
2720 // Is this a multiline comment? |
1605 $char = GeSHi::hsc($char) . '</span>'; |
2721 if (!empty($this->language_data['COMMENT_MULTI']) && $next_comment_multi_pos < $i) { |
1606 } |
2722 $next_comment_multi_pos = $length; |
1607 $escape_me = false; |
2723 foreach ($this->language_data['COMMENT_MULTI'] as $open => $close) { |
1608 if ($HARDQUOTE_OPEN) { |
2724 $match_i = false; |
1609 if ($ESCAPE_CHAR_OPEN) { |
2725 if (isset($comment_multi_cache_per_key[$open]) && |
1610 $escape_me = true; |
2726 ($comment_multi_cache_per_key[$open] >= $i || |
1611 } |
2727 $comment_multi_cache_per_key[$open] === false)) { |
1612 else { |
2728 // we have already matched something |
1613 foreach ($this->language_data['HARDESCAPE'] as $hardesc) { |
2729 if ($comment_multi_cache_per_key[$open] === false) { |
1614 if (substr($part, $i, strlen($hardesc)) == $hardesc) { |
2730 // this comment is never matched |
1615 $escape_me = true; |
2731 continue; |
2732 } |
|
2733 $match_i = $comment_multi_cache_per_key[$open]; |
|
2734 } else if (($match_i = stripos($part, $open, $i)) !== false) { |
|
2735 $comment_multi_cache_per_key[$open] = $match_i; |
|
2736 } else { |
|
2737 $comment_multi_cache_per_key[$open] = false; |
|
2738 continue; |
|
2739 } |
|
2740 if ($match_i !== false && $match_i < $next_comment_multi_pos) { |
|
2741 $next_comment_multi_pos = $match_i; |
|
2742 $next_open_comment_multi = $open; |
|
2743 if ($match_i === $i) { |
|
1616 break; |
2744 break; |
1617 } |
2745 } |
1618 } |
2746 } |
1619 } |
2747 } |
1620 } |
2748 } |
1621 |
2749 if ($i == $next_comment_multi_pos) { |
1622 if (!$ESCAPE_CHAR_OPEN) { |
2750 $open = $next_open_comment_multi; |
1623 $STRING_OPEN = ''; |
2751 $close = $this->language_data['COMMENT_MULTI'][$open]; |
1624 $CLOSE_STRING = true; |
2752 $open_strlen = strlen($open); |
2753 $close_strlen = strlen($close); |
|
2754 $COMMENT_MATCHED = true; |
|
2755 $test_str_match = $open; |
|
2756 //@todo If remove important do remove here |
|
2757 if ($this->lexic_permissions['COMMENTS']['MULTI'] || |
|
2758 $open == GESHI_START_IMPORTANT) { |
|
2759 if ($open != GESHI_START_IMPORTANT) { |
|
2760 if (!$this->use_classes) { |
|
2761 $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS']['MULTI'] . '"'; |
|
2762 } else { |
|
2763 $attributes = ' class="coMULTI"'; |
|
2764 } |
|
2765 $test_str = "<span$attributes>" . $this->hsc($open); |
|
2766 } else { |
|
2767 if (!$this->use_classes) { |
|
2768 $attributes = ' style="' . $this->important_styles . '"'; |
|
2769 } else { |
|
2770 $attributes = ' class="imp"'; |
|
2771 } |
|
2772 |
|
2773 // We don't include the start of the comment if it's an |
|
2774 // "important" part |
|
2775 $test_str = "<span$attributes>"; |
|
2776 } |
|
2777 } else { |
|
2778 $test_str = $this->hsc($open); |
|
2779 } |
|
2780 |
|
2781 $close_pos = strpos( $part, $close, $i + $open_strlen ); |
|
2782 |
|
2783 if ($close_pos === false) { |
|
2784 $close_pos = $length; |
|
2785 } |
|
2786 |
|
2787 // Short-cut through all the multiline code |
|
2788 $rest_of_comment = $this->hsc(substr($part, $i + $open_strlen, $close_pos - $i - $open_strlen + $close_strlen)); |
|
2789 if (($this->lexic_permissions['COMMENTS']['MULTI'] || |
|
2790 $test_str_match == GESHI_START_IMPORTANT) && |
|
2791 $check_linenumbers) { |
|
2792 |
|
2793 // strreplace to put close span and open span around multiline newlines |
|
2794 $test_str .= str_replace( |
|
2795 "\n", "</span>\n<span$attributes>", |
|
2796 str_replace("\n ", "\n ", $rest_of_comment) |
|
2797 ); |
|
2798 } else { |
|
2799 $test_str .= $rest_of_comment; |
|
2800 } |
|
2801 |
|
2802 if ($this->lexic_permissions['COMMENTS']['MULTI'] || |
|
2803 $test_str_match == GESHI_START_IMPORTANT) { |
|
2804 $test_str .= '</span>'; |
|
2805 } |
|
2806 |
|
2807 $i = $close_pos + $close_strlen - 1; |
|
2808 |
|
2809 // parse the rest |
|
2810 $result .= $this->parse_non_string_part($stuff_to_parse); |
|
2811 $stuff_to_parse = ''; |
|
1625 } |
2812 } |
1626 if (!$escape_me) { |
|
1627 $HARDQUOTE_OPEN = false; |
|
1628 } |
|
1629 $ESCAPE_CHAR_OPEN = false; |
|
1630 } |
2813 } |
1631 else if (in_array($char, $this->language_data['QUOTEMARKS']) && |
2814 |
1632 ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) { |
2815 // If we haven't matched a multiline comment, try single-line comments |
1633 // The start of a new string |
2816 if (!$COMMENT_MATCHED) { |
1634 $STRING_OPEN = $char; |
2817 // cache potential single line comment occurances |
1635 if (!$this->use_classes) { |
2818 if (!empty($this->language_data['COMMENT_SINGLE']) && $next_comment_single_pos < $i) { |
1636 $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; |
2819 $next_comment_single_pos = $length; |
1637 } |
2820 foreach ($this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark) { |
1638 else { |
2821 $match_i = false; |
1639 $attributes = ' class="st0"'; |
2822 if (isset($comment_single_cache_per_key[$comment_key]) && |
1640 } |
2823 ($comment_single_cache_per_key[$comment_key] >= $i || |
1641 $char = "<span$attributes>" . GeSHi::hsc($char); |
2824 $comment_single_cache_per_key[$comment_key] === false)) { |
1642 |
2825 // we have already matched something |
1643 $result .= $this->parse_non_string_part( $stuff_to_parse ); |
2826 if ($comment_single_cache_per_key[$comment_key] === false) { |
1644 $stuff_to_parse = ''; |
2827 // this comment is never matched |
1645 } |
2828 continue; |
1646 else if ($hq && substr($part, $i, strlen($hq)) == $hq && |
2829 } |
1647 ($STRING_OPEN == '') && $this->lexic_permissions['STRINGS']) { |
2830 $match_i = $comment_single_cache_per_key[$comment_key]; |
1648 // The start of a hard quoted string |
2831 } else if ( |
1649 $STRING_OPEN = $this->language_data['HARDQUOTE'][1]; |
2832 // case sensitive comments |
1650 if (!$this->use_classes) { |
2833 ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] && |
1651 $attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][0] . '"'; |
2834 ($match_i = stripos($part, $comment_mark, $i)) !== false) || |
1652 } |
2835 // non case sensitive |
1653 else { |
2836 (!$this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] && |
1654 $attributes = ' class="st0"'; |
2837 (($match_i = strpos($part, $comment_mark, $i)) !== false))) { |
1655 } |
2838 $comment_single_cache_per_key[$comment_key] = $match_i; |
1656 $char = "<span$attributes>" . $hq; |
2839 } else { |
1657 $i += strlen($hq) - 1; |
2840 $comment_single_cache_per_key[$comment_key] = false; |
1658 $HARDQUOTE_OPEN = true; |
2841 continue; |
1659 $result .= $this->parse_non_string_part($stuff_to_parse); |
2842 } |
1660 $stuff_to_parse = ''; |
2843 if ($match_i !== false && $match_i < $next_comment_single_pos) { |
1661 } |
2844 $next_comment_single_pos = $match_i; |
1662 else if ($char == $this->language_data['ESCAPE_CHAR'] && $STRING_OPEN != '') { |
2845 $next_comment_single_key = $comment_key; |
1663 // An escape character |
2846 if ($match_i === $i) { |
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; |
2847 break; |
1671 } |
2848 } |
1672 } |
2849 } |
1673 } |
2850 } |
1674 if ($ESCAPE_CHAR_OPEN && $this->lexic_permissions['ESCAPE_CHAR']) { |
2851 } |
1675 if (!$this->use_classes) { |
2852 if ($next_comment_single_pos == $i) { |
1676 $attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"'; |
2853 $comment_key = $next_comment_single_key; |
2854 $comment_mark = $this->language_data['COMMENT_SINGLE'][$comment_key]; |
|
2855 $com_len = strlen($comment_mark); |
|
2856 |
|
2857 // This check will find special variables like $# in bash |
|
2858 // or compiler directives of Delphi beginning {$ |
|
2859 if ((empty($sc_disallowed_before) || ($i == 0) || |
|
2860 (false === strpos($sc_disallowed_before, $part[$i-1]))) && |
|
2861 (empty($sc_disallowed_after) || ($length <= $i + $com_len) || |
|
2862 (false === strpos($sc_disallowed_after, $part[$i + $com_len])))) |
|
2863 { |
|
2864 // this is a valid comment |
|
2865 $COMMENT_MATCHED = true; |
|
2866 if ($this->lexic_permissions['COMMENTS'][$comment_key]) { |
|
2867 if (!$this->use_classes) { |
|
2868 $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment_key] . '"'; |
|
2869 } else { |
|
2870 $attributes = ' class="co' . $comment_key . '"'; |
|
2871 } |
|
2872 $test_str = "<span$attributes>" . $this->hsc($this->change_case($comment_mark)); |
|
2873 } else { |
|
2874 $test_str = $this->hsc($comment_mark); |
|
1677 } |
2875 } |
1678 else { |
2876 |
1679 $attributes = ' class="es0"'; |
2877 //Check if this comment is the last in the source |
2878 $close_pos = strpos($part, "\n", $i); |
|
2879 $oops = false; |
|
2880 if ($close_pos === false) { |
|
2881 $close_pos = $length; |
|
2882 $oops = true; |
|
1680 } |
2883 } |
1681 $char = "<span$attributes>" . $char; |
2884 $test_str .= $this->hsc(substr($part, $i + $com_len, $close_pos - $i - $com_len)); |
1682 if (substr($code, $i + 1, 1) == "\n") { |
2885 if ($this->lexic_permissions['COMMENTS'][$comment_key]) { |
1683 // escaping a newline, what's the point in putting the span around |
2886 $test_str .= "</span>"; |
1684 // the newline? It only causes hassles when inserting line numbers |
|
1685 $char .= '</span>'; |
|
1686 $ESCAPE_CHAR_OPEN = false; |
|
1687 } |
2887 } |
1688 } |
2888 |
1689 } |
2889 // Take into account that the comment might be the last in the source |
1690 else { |
2890 if (!$oops) { |
1691 $ESCAPE_CHAR_OPEN = false; |
2891 $test_str .= "\n"; |
1692 if ($this->lexic_permissions['ESCAPE_CHAR']) { |
2892 } |
1693 $char .= '</span>'; |
2893 |
2894 $i = $close_pos; |
|
2895 |
|
2896 // parse the rest |
|
2897 $result .= $this->parse_non_string_part($stuff_to_parse); |
|
2898 $stuff_to_parse = ''; |
|
1694 } |
2899 } |
1695 } |
2900 } |
1696 } |
2901 } |
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 } |
2902 } |
1857 // Parse the last bit |
2903 |
1858 $result .= $this->parse_non_string_part($stuff_to_parse); |
2904 // Where are we adding this char? |
1859 $stuff_to_parse = ''; |
2905 if (!$COMMENT_MATCHED) { |
1860 } |
2906 $stuff_to_parse .= $char; |
1861 else { |
2907 } else { |
1862 if ($STRICTATTRS != '') { |
2908 $result .= $test_str; |
1863 $part = str_replace("\n", "</span>\n<span$STRICTATTRS>", GeSHi::hsc($part)); |
2909 unset($test_str); |
1864 $STRICTATTRS = ''; |
2910 $COMMENT_MATCHED = false; |
1865 } |
2911 } |
1866 $result .= $part; |
2912 } |
1867 } |
2913 // Parse the last bit |
1868 // Close the <span> that surrounds the block |
2914 $result .= $this->parse_non_string_part($stuff_to_parse); |
1869 if ($this->strict_mode && $this->language_data['STYLES']['SCRIPT'][$script_key] != '' && |
2915 $stuff_to_parse = ''; |
1870 $this->lexic_permissions['SCRIPT']) { |
2916 } else { |
1871 $result .= '</span>'; |
2917 $result .= $this->hsc($part); |
1872 } |
2918 } |
1873 } |
2919 // Close the <span> that surrounds the block |
1874 else { |
2920 if ($STRICTATTRS != '') { |
1875 // Else not a block to highlight |
2921 $result = str_replace("\n", "</span>\n<span$STRICTATTRS>", $result); |
1876 $result .= GeSHi::hsc($part); |
2922 $result .= '</span>'; |
1877 } |
2923 } |
1878 } |
2924 |
1879 |
2925 $endresult .= $result; |
1880 // Parse the last stuff (redundant?) |
2926 unset($part, $parts[$key], $result); |
1881 $result .= $this->parse_non_string_part($stuff_to_parse); |
2927 } |
2928 |
|
2929 //This fix is related to SF#1923020, but has to be applied regardless of |
|
2930 //actually highlighting symbols. |
|
2931 /** NOTE: memorypeak #3 */ |
|
2932 $endresult = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $endresult); |
|
2933 |
|
2934 // // Parse the last stuff (redundant?) |
|
2935 // $result .= $this->parse_non_string_part($stuff_to_parse); |
|
1882 |
2936 |
1883 // Lop off the very first and last spaces |
2937 // Lop off the very first and last spaces |
1884 $result = substr($result, 1, -1); |
2938 // $result = substr($result, 1, -1); |
1885 |
|
1886 // Are we still in a string? |
|
1887 if ($STRING_OPEN) { |
|
1888 $result .= '</span>'; |
|
1889 } |
|
1890 |
2939 |
1891 // We're finished: stop timing |
2940 // We're finished: stop timing |
1892 $this->set_time($start_time, microtime()); |
2941 $this->set_time($start_time, microtime()); |
1893 |
2942 |
1894 return $this->finalise($result); |
2943 $this->finalise($endresult); |
2944 return $endresult; |
|
1895 } |
2945 } |
1896 |
2946 |
1897 /** |
2947 /** |
1898 * Swaps out spaces and tabs for HTML indentation. Not needed if |
2948 * Swaps out spaces and tabs for HTML indentation. Not needed if |
1899 * the code is in a pre block... |
2949 * the code is in a pre block... |
1900 * |
2950 * |
1901 * @param string The source to indent |
2951 * @param string The source to indent (reference!) |
1902 * @return string The source with HTML indenting applied |
|
1903 * @since 1.0.0 |
2952 * @since 1.0.0 |
1904 * @access private |
2953 * @access private |
1905 */ |
2954 */ |
1906 function indent($result) { |
2955 function indent(&$result) { |
1907 /// Replace tabs with the correct number of spaces |
2956 /// Replace tabs with the correct number of spaces |
1908 if (false !== strpos($result, "\t")) { |
2957 if (false !== strpos($result, "\t")) { |
1909 $lines = explode("\n", $result); |
2958 $lines = explode("\n", $result); |
1910 $tab_width = $this->get_real_tab_width(); |
2959 $result = null;//Save memory while we process the lines individually |
1911 foreach ($lines as $key => $line) { |
2960 $tab_width = $this->get_real_tab_width(); |
2961 $tab_string = ' ' . str_repeat(' ', $tab_width); |
|
2962 |
|
2963 for ($key = 0, $n = count($lines); $key < $n; $key++) { |
|
2964 $line = $lines[$key]; |
|
1912 if (false === strpos($line, "\t")) { |
2965 if (false === strpos($line, "\t")) { |
1913 $lines[$key] = $line; |
|
1914 continue; |
2966 continue; |
1915 } |
2967 } |
1916 |
2968 |
1917 $pos = 0; |
2969 $pos = 0; |
1918 $length = strlen($line); |
2970 $length = strlen($line); |
1919 $result_line = ''; |
2971 $lines[$key] = ''; // reduce memory |
1920 |
2972 |
1921 $IN_TAG = false; |
2973 $IN_TAG = false; |
1922 for ($i = 0; $i < $length; $i++) { |
2974 for ($i = 0; $i < $length; ++$i) { |
1923 $char = substr($line, $i, 1); |
2975 $char = $line[$i]; |
1924 // Simple engine to work out whether we're in a tag. |
2976 // Simple engine to work out whether we're in a tag. |
1925 // If we are we modify $pos. This is so we ignore HTML |
2977 // If we are we modify $pos. This is so we ignore HTML |
1926 // in the line and only workout the tab replacement |
2978 // in the line and only workout the tab replacement |
1927 // via the actual content of the string |
2979 // via the actual content of the string |
1928 // This test could be improved to include strings in the |
2980 // This test could be improved to include strings in the |
1929 // html so that < or > would be allowed in user's styles |
2981 // html so that < or > would be allowed in user's styles |
1930 // (e.g. quotes: '<' '>'; or similar) |
2982 // (e.g. quotes: '<' '>'; or similar) |
1931 if ($IN_TAG && '>' == $char) { |
2983 if ($IN_TAG) { |
1932 $IN_TAG = false; |
2984 if ('>' == $char) { |
1933 $result_line .= '>'; |
2985 $IN_TAG = false; |
1934 ++$pos; |
2986 } |
1935 } |
2987 $lines[$key] .= $char; |
1936 else if (!$IN_TAG && '<' == $char) { |
2988 } else if ('<' == $char) { |
1937 $IN_TAG = true; |
2989 $IN_TAG = true; |
1938 $result_line .= '<'; |
2990 $lines[$key] .= '<'; |
1939 ++$pos; |
2991 } else if ('&' == $char) { |
1940 } |
2992 $substr = substr($line, $i + 3, 5); |
1941 else if (!$IN_TAG && '&' == $char) { |
|
1942 $substr = substr($line, $i + 3, 4); |
|
1943 //$substr_5 = substr($line, 5, 1); |
|
1944 $posi = strpos($substr, ';'); |
2993 $posi = strpos($substr, ';'); |
1945 if (false !== $posi) { |
2994 if (false === $posi) { |
1946 $pos += $posi + 3; |
2995 ++$pos; |
2996 } else { |
|
2997 $pos -= $posi+2; |
|
1947 } |
2998 } |
1948 $result_line .= '&'; |
2999 $lines[$key] .= $char; |
1949 } |
3000 } else if ("\t" == $char) { |
1950 else if (!$IN_TAG && "\t" == $char) { |
|
1951 $str = ''; |
3001 $str = ''; |
1952 // OPTIMISE - move $strs out. Make an array: |
3002 // OPTIMISE - move $strs out. Make an array: |
1953 // $tabs = array( |
3003 // $tabs = array( |
1954 // 1 => ' ', |
3004 // 1 => ' ', |
1955 // 2 => ' ', |
3005 // 2 => ' ', |
1956 // 3 => ' ' etc etc |
3006 // 3 => ' ' etc etc |
1957 // to use instead of building a string every time |
3007 // to use instead of building a string every time |
1958 $strs = array(0 => ' ', 1 => ' '); |
3008 $tab_end_width = $tab_width - ($pos % $tab_width); //Moved out of the look as it doesn't change within the loop |
1959 for ($k = 0; $k < ($tab_width - (($i - $pos) % $tab_width)); $k++) $str .= $strs[$k % 2]; |
3009 if (($pos & 1) || 1 == $tab_end_width) { |
1960 $result_line .= $str; |
3010 $str .= substr($tab_string, 6, $tab_end_width); |
1961 $pos += ($i - $pos) % $tab_width + 1; |
3011 } else { |
3012 $str .= substr($tab_string, 0, $tab_end_width+5); |
|
3013 } |
|
3014 $lines[$key] .= $str; |
|
3015 $pos += $tab_end_width; |
|
1962 |
3016 |
1963 if (false === strpos($line, "\t", $i + 1)) { |
3017 if (false === strpos($line, "\t", $i + 1)) { |
1964 $result_line .= substr($line, $i + 1); |
3018 $lines[$key] .= substr($line, $i + 1); |
1965 break; |
3019 break; |
1966 } |
3020 } |
3021 } else if (0 == $pos && ' ' == $char) { |
|
3022 $lines[$key] .= ' '; |
|
3023 ++$pos; |
|
3024 } else { |
|
3025 $lines[$key] .= $char; |
|
3026 ++$pos; |
|
1967 } |
3027 } |
1968 else if ($IN_TAG) { |
3028 } |
1969 ++$pos; |
|
1970 $result_line .= $char; |
|
1971 } |
|
1972 else { |
|
1973 $result_line .= $char; |
|
1974 //++$pos; |
|
1975 } |
|
1976 } |
|
1977 $lines[$key] = $result_line; |
|
1978 } |
3029 } |
1979 $result = implode("\n", $lines); |
3030 $result = implode("\n", $lines); |
3031 unset($lines);//We don't need the lines separated beyond this --- free them! |
|
1980 } |
3032 } |
1981 // Other whitespace |
3033 // Other whitespace |
1982 // BenBE: Fix to reduce the number of replacements to be done |
3034 // BenBE: Fix to reduce the number of replacements to be done |
1983 $result = str_replace("\n ", "\n ", $result); |
3035 $result = preg_replace('/^ /m', ' ', $result); |
1984 $result = str_replace(' ', ' ', $result); |
3036 $result = str_replace(' ', ' ', $result); |
1985 |
3037 |
1986 if ($this->line_numbers == GESHI_NO_LINE_NUMBERS) { |
3038 if ($this->line_numbers == GESHI_NO_LINE_NUMBERS) { |
1987 if ($this->line_ending === null) { |
3039 if ($this->line_ending === null) { |
1988 $result = nl2br($result); |
3040 $result = nl2br($result); |
1989 } else { |
3041 } else { |
1990 $result = str_replace("\n", $this->line_ending, $result); |
3042 $result = str_replace("\n", $this->line_ending, $result); |
1991 } |
3043 } |
1992 } |
3044 } |
1993 return $result; |
|
1994 } |
3045 } |
1995 |
3046 |
1996 /** |
3047 /** |
1997 * Changes the case of a keyword for those languages where a change is asked for |
3048 * Changes the case of a keyword for those languages where a change is asked for |
1998 * |
3049 * |
2000 * @return string The keyword with its case changed |
3051 * @return string The keyword with its case changed |
2001 * @since 1.0.0 |
3052 * @since 1.0.0 |
2002 * @access private |
3053 * @access private |
2003 */ |
3054 */ |
2004 function change_case($instr) { |
3055 function change_case($instr) { |
2005 if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_UPPER) { |
3056 switch ($this->language_data['CASE_KEYWORDS']) { |
2006 return strtoupper($instr); |
3057 case GESHI_CAPS_UPPER: |
2007 } |
3058 return strtoupper($instr); |
2008 else if ($this->language_data['CASE_KEYWORDS'] == GESHI_CAPS_LOWER) { |
3059 case GESHI_CAPS_LOWER: |
2009 return strtolower($instr); |
3060 return strtolower($instr); |
2010 } |
3061 default: |
2011 return $instr; |
3062 return $instr; |
2012 } |
3063 } |
2013 |
3064 } |
2014 /** |
3065 |
2015 * Adds a url to a keyword where needed. |
3066 /** |
2016 * |
3067 * Handles replacements of keywords to include markup and links if requested |
2017 * @param string The keyword to add the URL HTML to |
3068 * |
2018 * @param int What group the keyword is from |
3069 * @param string The keyword to add the Markup to |
2019 * @param boolean Whether to get the HTML for the start or end |
3070 * @return The HTML for the match found |
2020 * @return The HTML for either the start or end of the HTML <a> tag |
3071 * @since 1.0.8 |
2021 * @since 1.0.2 |
|
2022 * @access private |
3072 * @access private |
2023 * @todo Get rid of ender |
3073 * |
2024 */ |
3074 * @todo Get rid of ender in keyword links |
2025 function add_url_to_keyword($keyword, $group, $start_or_end) { |
3075 */ |
2026 if (!$this->keyword_links) { |
3076 function handle_keyword_replace($match) { |
2027 // Keyword links have been disabled |
3077 $k = $this->_kw_replace_group; |
2028 return; |
3078 $keyword = $match[0]; |
2029 } |
3079 |
2030 |
3080 $before = ''; |
2031 if (isset($this->language_data['URLS'][$group]) && |
3081 $after = ''; |
2032 $this->language_data['URLS'][$group] != '' && |
3082 |
2033 substr($keyword, 0, 5) != '</') { |
3083 if ($this->keyword_links) { |
2034 // There is a base group for this keyword |
3084 // Keyword links have been ebabled |
2035 if ($start_or_end == 'BEGIN') { |
3085 |
2036 // HTML workaround... not good form (tm) but should work for 1.0.X |
3086 if (isset($this->language_data['URLS'][$k]) && |
2037 if ($keyword != '') { |
3087 $this->language_data['URLS'][$k] != '') { |
2038 // Old system: strtolower |
3088 // There is a base group for this keyword |
2039 //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword); |
3089 |
2040 // New system: get keyword from language file to get correct case |
3090 // Old system: strtolower |
2041 foreach ($this->language_data['KEYWORDS'][$group] as $word) { |
3091 //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword); |
2042 if (strtolower($word) == strtolower($keyword)) { |
3092 // New system: get keyword from language file to get correct case |
3093 if (!$this->language_data['CASE_SENSITIVE'][$k] && |
|
3094 strpos($this->language_data['URLS'][$k], '{FNAME}') !== false) { |
|
3095 foreach ($this->language_data['KEYWORDS'][$k] as $word) { |
|
3096 if (strcasecmp($word, $keyword) == 0) { |
|
2043 break; |
3097 break; |
2044 } |
3098 } |
2045 } |
3099 } |
2046 $word = ( substr($word, 0, 4) == '<' ) ? substr($word, 4) : $word; |
3100 } else { |
2047 $word = ( substr($word, -4) == '>' ) ? substr($word, 0, strlen($word) - 4) : $word; |
3101 $word = $keyword; |
2048 if (!$word) return ''; |
3102 } |
2049 |
3103 |
2050 return '<|UR1|"' . |
3104 $before = '<|UR1|"' . |
2051 str_replace( |
3105 str_replace( |
2052 array('{FNAME}', '.'), |
3106 array( |
2053 array(GeSHi::hsc($word), '<DOT>'), |
3107 '{FNAME}', |
2054 $this->language_data['URLS'][$group] |
3108 '{FNAMEL}', |
2055 ) . '">'; |
3109 '{FNAMEU}', |
2056 } |
3110 '.'), |
2057 return ''; |
3111 array( |
2058 // HTML fix. Again, dirty hackage... |
3112 str_replace('+', '%20', urlencode($this->hsc($word))), |
2059 } |
3113 str_replace('+', '%20', urlencode($this->hsc(strtolower($word)))), |
2060 else if (!($this->language == 'html4strict' && ('>' == $keyword || '<' == $keyword))) { |
3114 str_replace('+', '%20', urlencode($this->hsc(strtoupper($word)))), |
2061 return '</a>'; |
3115 '<DOT>'), |
2062 } |
3116 $this->language_data['URLS'][$k] |
2063 } |
3117 ) . '">'; |
3118 $after = '</a>'; |
|
3119 } |
|
3120 } |
|
3121 |
|
3122 return $before . '<|/'. $k .'/>' . $this->change_case($keyword) . '|>' . $after; |
|
3123 } |
|
3124 |
|
3125 /** |
|
3126 * handles regular expressions highlighting-definitions with callback functions |
|
3127 * |
|
3128 * @note this is a callback, don't use it directly |
|
3129 * |
|
3130 * @param array the matches array |
|
3131 * @return The highlighted string |
|
3132 * @since 1.0.8 |
|
3133 * @access private |
|
3134 */ |
|
3135 function handle_regexps_callback($matches) { |
|
3136 // before: "' style=\"' . call_user_func(\"$func\", '\\1') . '\"\\1|>'", |
|
3137 return ' style="' . call_user_func($this->language_data['STYLES']['REGEXPS'][$this->_rx_key], $matches[1]) . '"'. $matches[1] . '|>'; |
|
3138 } |
|
3139 |
|
3140 /** |
|
3141 * handles newlines in REGEXPS matches. Set the _hmr_* vars before calling this |
|
3142 * |
|
3143 * @note this is a callback, don't use it directly |
|
3144 * |
|
3145 * @param array the matches array |
|
3146 * @return string |
|
3147 * @since 1.0.8 |
|
3148 * @access private |
|
3149 */ |
|
3150 function handle_multiline_regexps($matches) { |
|
3151 $before = $this->_hmr_before; |
|
3152 $after = $this->_hmr_after; |
|
3153 if ($this->_hmr_replace) { |
|
3154 $replace = $this->_hmr_replace; |
|
3155 $search = array(); |
|
3156 |
|
3157 foreach (array_keys($matches) as $k) { |
|
3158 $search[] = '\\' . $k; |
|
3159 } |
|
3160 |
|
3161 $before = str_replace($search, $matches, $before); |
|
3162 $after = str_replace($search, $matches, $after); |
|
3163 $replace = str_replace($search, $matches, $replace); |
|
3164 } else { |
|
3165 $replace = $matches[0]; |
|
3166 } |
|
3167 return $before |
|
3168 . '<|!REG3XP' . $this->_hmr_key .'!>' |
|
3169 . str_replace("\n", "|>\n<|!REG3XP" . $this->_hmr_key . '!>', $replace) |
|
3170 . '|>' |
|
3171 . $after; |
|
2064 } |
3172 } |
2065 |
3173 |
2066 /** |
3174 /** |
2067 * Takes a string that has no strings or comments in it, and highlights |
3175 * Takes a string that has no strings or comments in it, and highlights |
2068 * stuff like keywords, numbers and methods. |
3176 * stuff like keywords, numbers and methods. |
2070 * @param string The string to parse for keyword, numbers etc. |
3178 * @param string The string to parse for keyword, numbers etc. |
2071 * @since 1.0.0 |
3179 * @since 1.0.0 |
2072 * @access private |
3180 * @access private |
2073 * @todo BUGGY! Why? Why not build string and return? |
3181 * @todo BUGGY! Why? Why not build string and return? |
2074 */ |
3182 */ |
2075 function parse_non_string_part(&$stuff_to_parse) { |
3183 function parse_non_string_part($stuff_to_parse) { |
2076 $stuff_to_parse = ' ' . GeSHi::hsc($stuff_to_parse); |
3184 $stuff_to_parse = ' ' . $this->hsc($stuff_to_parse); |
2077 $stuff_to_parse_pregquote = preg_quote($stuff_to_parse, '/'); |
3185 |
2078 $func = '$this->change_case'; |
|
2079 $func2 = '$this->add_url_to_keyword'; |
|
2080 |
|
2081 // |
|
2082 // Regular expressions |
3186 // Regular expressions |
2083 // |
|
2084 foreach ($this->language_data['REGEXPS'] as $key => $regexp) { |
3187 foreach ($this->language_data['REGEXPS'] as $key => $regexp) { |
2085 if ($this->lexic_permissions['REGEXPS'][$key]) { |
3188 if ($this->lexic_permissions['REGEXPS'][$key]) { |
2086 if (is_array($regexp)) { |
3189 if (is_array($regexp)) { |
2087 $stuff_to_parse = preg_replace( |
3190 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
2088 "/" . |
3191 // produce valid HTML when we match multiple lines |
2089 str_replace('/', '\/', $regexp[GESHI_SEARCH]) . |
3192 $this->_hmr_replace = $regexp[GESHI_REPLACE]; |
2090 "/{$regexp[GESHI_MODIFIERS]}", |
3193 $this->_hmr_before = $regexp[GESHI_BEFORE]; |
2091 "{$regexp[GESHI_BEFORE]}<|!REG3XP$key!>{$regexp[GESHI_REPLACE]}|>{$regexp[GESHI_AFTER]}", |
3194 $this->_hmr_key = $key; |
3195 $this->_hmr_after = $regexp[GESHI_AFTER]; |
|
3196 $stuff_to_parse = preg_replace_callback( |
|
3197 "/" . $regexp[GESHI_SEARCH] . "/{$regexp[GESHI_MODIFIERS]}", |
|
3198 array($this, 'handle_multiline_regexps'), |
|
3199 $stuff_to_parse); |
|
3200 $this->_hmr_replace = false; |
|
3201 $this->_hmr_before = ''; |
|
3202 $this->_hmr_after = ''; |
|
3203 } else { |
|
3204 $stuff_to_parse = preg_replace( |
|
3205 '/' . $regexp[GESHI_SEARCH] . '/' . $regexp[GESHI_MODIFIERS], |
|
3206 $regexp[GESHI_BEFORE] . '<|!REG3XP'. $key .'!>' . $regexp[GESHI_REPLACE] . '|>' . $regexp[GESHI_AFTER], |
|
3207 $stuff_to_parse); |
|
3208 } |
|
3209 } else { |
|
3210 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
3211 // produce valid HTML when we match multiple lines |
|
3212 $this->_hmr_key = $key; |
|
3213 $stuff_to_parse = preg_replace_callback( "/(" . $regexp . ")/", |
|
3214 array($this, 'handle_multiline_regexps'), $stuff_to_parse); |
|
3215 $this->_hmr_key = ''; |
|
3216 } else { |
|
3217 $stuff_to_parse = preg_replace( "/(" . $regexp . ")/", "<|!REG3XP$key!>\\1|>", $stuff_to_parse); |
|
3218 } |
|
3219 } |
|
3220 } |
|
3221 } |
|
3222 |
|
3223 // Highlight numbers. As of 1.0.8 we support diffent types of numbers |
|
3224 $numbers_found = false; |
|
3225 if ($this->lexic_permissions['NUMBERS'] && preg_match('#\d#', $stuff_to_parse )) { |
|
3226 $numbers_found = true; |
|
3227 |
|
3228 //For each of the formats ... |
|
3229 foreach($this->language_data['NUMBERS_RXCACHE'] as $id => $regexp) { |
|
3230 //Check if it should be highlighted ... |
|
3231 $stuff_to_parse = preg_replace($regexp, "<|/NUM!$id/>\\1|>", $stuff_to_parse); |
|
3232 } |
|
3233 } |
|
3234 |
|
3235 // Highlight keywords |
|
3236 $disallowed_before = "(?<![a-zA-Z0-9\$_\|\#;>|^&"; |
|
3237 $disallowed_after = "(?![a-zA-Z0-9_\|%\\-&;"; |
|
3238 if ($this->lexic_permissions['STRINGS']) { |
|
3239 $quotemarks = preg_quote(implode($this->language_data['QUOTEMARKS']), '/'); |
|
3240 $disallowed_before .= $quotemarks; |
|
3241 $disallowed_after .= $quotemarks; |
|
3242 } |
|
3243 $disallowed_before .= "])"; |
|
3244 $disallowed_after .= "])"; |
|
3245 |
|
3246 $parser_control_pergroup = false; |
|
3247 if (isset($this->language_data['PARSER_CONTROL'])) { |
|
3248 if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'])) { |
|
3249 $x = 0; // check wether per-keyword-group parser_control is enabled |
|
3250 if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) { |
|
3251 $disallowed_before = $this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE']; |
|
3252 ++$x; |
|
3253 } |
|
3254 if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) { |
|
3255 $disallowed_after = $this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER']; |
|
3256 ++$x; |
|
3257 } |
|
3258 $parser_control_pergroup = (count($this->language_data['PARSER_CONTROL']['KEYWORDS']) - $x) > 0; |
|
3259 } |
|
3260 } |
|
3261 |
|
3262 // if this is changed, don't forget to change it below |
|
3263 // if (!empty($disallowed_before)) { |
|
3264 // $disallowed_before = "(?<![$disallowed_before])"; |
|
3265 // } |
|
3266 // if (!empty($disallowed_after)) { |
|
3267 // $disallowed_after = "(?![$disallowed_after])"; |
|
3268 // } |
|
3269 |
|
3270 foreach (array_keys($this->language_data['KEYWORDS']) as $k) { |
|
3271 if (!isset($this->lexic_permissions['KEYWORDS'][$k]) || |
|
3272 $this->lexic_permissions['KEYWORDS'][$k]) { |
|
3273 |
|
3274 $case_sensitive = $this->language_data['CASE_SENSITIVE'][$k]; |
|
3275 $modifiers = $case_sensitive ? '' : 'i'; |
|
3276 |
|
3277 // NEW in 1.0.8 - per-keyword-group parser control |
|
3278 $disallowed_before_local = $disallowed_before; |
|
3279 $disallowed_after_local = $disallowed_after; |
|
3280 if ($parser_control_pergroup && isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k])) { |
|
3281 if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_BEFORE'])) { |
|
3282 $disallowed_before_local = |
|
3283 $this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_BEFORE']; |
|
3284 } |
|
3285 |
|
3286 if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_AFTER'])) { |
|
3287 $disallowed_after_local = |
|
3288 $this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_AFTER']; |
|
3289 } |
|
3290 } |
|
3291 |
|
3292 $this->_kw_replace_group = $k; |
|
3293 |
|
3294 //NEW in 1.0.8, the cached regexp list |
|
3295 // since we don't want PHP / PCRE to crash due to too large patterns we split them into smaller chunks |
|
3296 for ($set = 0, $set_length = count($this->language_data['CACHED_KEYWORD_LISTS'][$k]); $set < $set_length; ++$set) { |
|
3297 $keywordset =& $this->language_data['CACHED_KEYWORD_LISTS'][$k][$set]; |
|
3298 // Might make a more unique string for putting the number in soon |
|
3299 // Basically, we don't put the styles in yet because then the styles themselves will |
|
3300 // get highlighted if the language has a CSS keyword in it (like CSS, for example ;)) |
|
3301 $stuff_to_parse = preg_replace_callback( |
|
3302 "/$disallowed_before_local({$keywordset})(?!\<DOT\>(?:htm|php))$disallowed_after_local/$modifiers", |
|
3303 array($this, 'handle_keyword_replace'), |
|
2092 $stuff_to_parse |
3304 $stuff_to_parse |
2093 ); |
3305 ); |
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 } |
3306 } |
2154 } |
3307 } |
2155 } |
3308 } |
2156 |
3309 |
2157 // |
3310 // |
2158 // Now that's all done, replace /[number]/ with the correct styles |
3311 // Now that's all done, replace /[number]/ with the correct styles |
2159 // |
3312 // |
2160 foreach ($this->language_data['KEYWORDS'] as $k => $kws) { |
3313 foreach (array_keys($this->language_data['KEYWORDS']) as $k) { |
2161 if (!$this->use_classes) { |
3314 if (!$this->use_classes) { |
2162 $attributes = ' style="' . $this->language_data['STYLES']['KEYWORDS'][$k] . '"'; |
3315 $attributes = ' style="' . |
2163 } |
3316 (isset($this->language_data['STYLES']['KEYWORDS'][$k]) ? |
2164 else { |
3317 $this->language_data['STYLES']['KEYWORDS'][$k] : "") . '"'; |
3318 } else { |
|
2165 $attributes = ' class="kw' . $k . '"'; |
3319 $attributes = ' class="kw' . $k . '"'; |
2166 } |
3320 } |
2167 $stuff_to_parse = str_replace("/$k/", $attributes, $stuff_to_parse); |
3321 $stuff_to_parse = str_replace("<|/$k/>", "<|$attributes>", $stuff_to_parse); |
2168 } |
3322 } |
2169 |
3323 |
2170 // Put number styles in |
3324 if ($numbers_found) { |
2171 if (!$this->use_classes && $this->lexic_permissions['NUMBERS']) { |
3325 // Put number styles in |
2172 $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][0] . '"'; |
3326 foreach($this->language_data['NUMBERS_RXCACHE'] as $id => $regexp) { |
2173 } |
3327 //Commented out for now, as this needs some review ... |
2174 else { |
3328 // if ($numbers_permissions & $id) { |
2175 $attributes = ' class="nu0"'; |
3329 //Get the appropriate style ... |
2176 } |
3330 //Checking for unset styles is done by the style cache builder ... |
2177 $stuff_to_parse = str_replace('/NUM!/', $attributes, $stuff_to_parse); |
3331 if (!$this->use_classes) { |
2178 |
3332 $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][$id] . '"'; |
2179 // |
3333 } else { |
3334 $attributes = ' class="nu'.$id.'"'; |
|
3335 } |
|
3336 |
|
3337 //Set in the correct styles ... |
|
3338 $stuff_to_parse = str_replace("/NUM!$id/", $attributes, $stuff_to_parse); |
|
3339 // } |
|
3340 } |
|
3341 } |
|
3342 |
|
2180 // Highlight methods and fields in objects |
3343 // Highlight methods and fields in objects |
2181 // |
|
2182 if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) { |
3344 if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) { |
3345 $oolang_spaces = "[\s]*"; |
|
3346 $oolang_before = ""; |
|
3347 $oolang_after = "[a-zA-Z][a-zA-Z0-9_]*"; |
|
3348 if (isset($this->language_data['PARSER_CONTROL'])) { |
|
3349 if (isset($this->language_data['PARSER_CONTROL']['OOLANG'])) { |
|
3350 if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_BEFORE'])) { |
|
3351 $oolang_before = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_BEFORE']; |
|
3352 } |
|
3353 if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_AFTER'])) { |
|
3354 $oolang_after = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_AFTER']; |
|
3355 } |
|
3356 if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_SPACES'])) { |
|
3357 $oolang_spaces = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_SPACES']; |
|
3358 } |
|
3359 } |
|
3360 } |
|
3361 |
|
2183 foreach ($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) { |
3362 foreach ($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) { |
2184 if (false !== stristr($stuff_to_parse, $splitter)) { |
3363 if (false !== strpos($stuff_to_parse, $splitter)) { |
2185 if (!$this->use_classes) { |
3364 if (!$this->use_classes) { |
2186 $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][$key] . '"'; |
3365 $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][$key] . '"'; |
2187 } |
3366 } else { |
2188 else { |
|
2189 $attributes = ' class="me' . $key . '"'; |
3367 $attributes = ' class="me' . $key . '"'; |
2190 } |
3368 } |
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); |
3369 $stuff_to_parse = preg_replace("/($oolang_before)(" . preg_quote($this->language_data['OBJECT_SPLITTERS'][$key], '/') . ")($oolang_spaces)($oolang_after)/", "\\1\\2\\3<|$attributes>\\4|>", $stuff_to_parse); |
2192 } |
3370 } |
2193 } |
3371 } |
2194 } |
3372 } |
2195 |
3373 |
2196 // |
3374 // |
2198 // You try it, and see what happens ;) |
3376 // You try it, and see what happens ;) |
2199 // TODO: Fix lexic permissions not converting entities if shouldn't |
3377 // TODO: Fix lexic permissions not converting entities if shouldn't |
2200 // be highlighting regardless |
3378 // be highlighting regardless |
2201 // |
3379 // |
2202 if ($this->lexic_permissions['BRACKETS']) { |
3380 if ($this->lexic_permissions['BRACKETS']) { |
2203 $code_entities_match = array('[', ']', '(', ')', '{', '}'); |
3381 $stuff_to_parse = str_replace( $this->language_data['CACHE_BRACKET_MATCH'], |
2204 if (!$this->use_classes) { |
3382 $this->language_data['CACHE_BRACKET_REPLACE'], $stuff_to_parse ); |
2205 $code_entities_replace = array( |
3383 } |
2206 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">[|>', |
3384 |
2207 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">]|>', |
3385 |
2208 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">(|>', |
3386 //FIX for symbol highlighting ... |
2209 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">)|>', |
3387 if ($this->lexic_permissions['SYMBOLS'] && !empty($this->language_data['SYMBOLS'])) { |
2210 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">{|>', |
3388 //Get all matches and throw away those witin a block that is already highlighted... (i.e. matched by a regexp) |
2211 '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">}|>', |
3389 $n_symbols = preg_match_all("/<\|(?:<DOT>|[^>])+>(?:(?!\|>).*?)\|>|<\/a>|(?:" . $this->language_data['SYMBOL_SEARCH'] . ")+/", $stuff_to_parse, $pot_symbols, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); |
2212 ); |
3390 $global_offset = 0; |
2213 } |
3391 for ($s_id = 0; $s_id < $n_symbols; ++$s_id) { |
2214 else { |
3392 $symbol_match = $pot_symbols[$s_id][0][0]; |
2215 $code_entities_replace = array( |
3393 if (strpos($symbol_match, '<') !== false || strpos($symbol_match, '>') !== false) { |
2216 '<| class="br0">[|>', |
3394 // already highlighted blocks _must_ include either < or > |
2217 '<| class="br0">]|>', |
3395 // so if this conditional applies, we have to skip this match |
2218 '<| class="br0">(|>', |
3396 // BenBE: UNLESS the block contains <SEMI> or <PIPE> |
2219 '<| class="br0">)|>', |
3397 if(strpos($symbol_match, '<SEMI>') === false && |
2220 '<| class="br0">{|>', |
3398 strpos($symbol_match, '<PIPE>') === false) { |
2221 '<| class="br0">}|>', |
3399 continue; |
2222 ); |
3400 } |
2223 } |
3401 } |
2224 $stuff_to_parse = str_replace( $code_entities_match, $code_entities_replace, $stuff_to_parse ); |
3402 |
2225 } |
3403 // if we reach this point, we have a valid match which needs to be highlighted |
2226 |
3404 |
2227 // |
3405 $symbol_length = strlen($symbol_match); |
3406 $symbol_offset = $pot_symbols[$s_id][0][1]; |
|
3407 unset($pot_symbols[$s_id]); |
|
3408 $symbol_end = $symbol_length + $symbol_offset; |
|
3409 $symbol_hl = ""; |
|
3410 |
|
3411 // if we have multiple styles, we have to handle them properly |
|
3412 if ($this->language_data['MULTIPLE_SYMBOL_GROUPS']) { |
|
3413 $old_sym = -1; |
|
3414 // Split the current stuff to replace into its atomic symbols ... |
|
3415 preg_match_all("/" . $this->language_data['SYMBOL_SEARCH'] . "/", $symbol_match, $sym_match_syms, PREG_PATTERN_ORDER); |
|
3416 foreach ($sym_match_syms[0] as $sym_ms) { |
|
3417 //Check if consequtive symbols belong to the same group to save output ... |
|
3418 if (isset($this->language_data['SYMBOL_DATA'][$sym_ms]) |
|
3419 && ($this->language_data['SYMBOL_DATA'][$sym_ms] != $old_sym)) { |
|
3420 if (-1 != $old_sym) { |
|
3421 $symbol_hl .= "|>"; |
|
3422 } |
|
3423 $old_sym = $this->language_data['SYMBOL_DATA'][$sym_ms]; |
|
3424 if (!$this->use_classes) { |
|
3425 $symbol_hl .= '<| style="' . $this->language_data['STYLES']['SYMBOLS'][$old_sym] . '">'; |
|
3426 } else { |
|
3427 $symbol_hl .= '<| class="sy' . $old_sym . '">'; |
|
3428 } |
|
3429 } |
|
3430 $symbol_hl .= $sym_ms; |
|
3431 } |
|
3432 unset($sym_match_syms); |
|
3433 |
|
3434 //Close remaining tags and insert the replacement at the right position ... |
|
3435 //Take caution if symbol_hl is empty to avoid doubled closing spans. |
|
3436 if (-1 != $old_sym) { |
|
3437 $symbol_hl .= "|>"; |
|
3438 } |
|
3439 } else { |
|
3440 if (!$this->use_classes) { |
|
3441 $symbol_hl = '<| style="' . $this->language_data['STYLES']['SYMBOLS'][0] . '">'; |
|
3442 } else { |
|
3443 $symbol_hl = '<| class="sy0">'; |
|
3444 } |
|
3445 $symbol_hl .= $symbol_match . '|>'; |
|
3446 } |
|
3447 |
|
3448 $stuff_to_parse = substr_replace($stuff_to_parse, $symbol_hl, $symbol_offset + $global_offset, $symbol_length); |
|
3449 |
|
3450 // since we replace old text with something of different size, |
|
3451 // we'll have to keep track of the differences |
|
3452 $global_offset += strlen($symbol_hl) - $symbol_length; |
|
3453 } |
|
3454 } |
|
3455 //FIX for symbol highlighting ... |
|
3456 |
|
2228 // Add class/style for regexps |
3457 // Add class/style for regexps |
2229 // |
3458 foreach (array_keys($this->language_data['REGEXPS']) as $key) { |
2230 foreach ($this->language_data['REGEXPS'] as $key => $regexp) { |
|
2231 if ($this->lexic_permissions['REGEXPS'][$key]) { |
3459 if ($this->lexic_permissions['REGEXPS'][$key]) { |
2232 if (!$this->use_classes) { |
3460 if (is_callable($this->language_data['STYLES']['REGEXPS'][$key])) { |
2233 $attributes = ' style="' . $this->language_data['STYLES']['REGEXPS'][$key] . '"'; |
3461 $this->_rx_key = $key; |
2234 } |
3462 $stuff_to_parse = preg_replace_callback("/!REG3XP$key!(.*)\|>/U", |
2235 else { |
3463 array($this, 'handle_regexps_callback'), |
2236 if(is_array($this->language_data['REGEXPS'][$key]) && |
3464 $stuff_to_parse); |
3465 } else { |
|
3466 if (!$this->use_classes) { |
|
3467 $attributes = ' style="' . $this->language_data['STYLES']['REGEXPS'][$key] . '"'; |
|
3468 } else { |
|
3469 if (is_array($this->language_data['REGEXPS'][$key]) && |
|
2237 array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$key])) { |
3470 array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$key])) { |
2238 $attributes = ' class="' |
3471 $attributes = ' class="' . |
2239 . $this->language_data['REGEXPS'][$key][GESHI_CLASS] . '"'; |
3472 $this->language_data['REGEXPS'][$key][GESHI_CLASS] . '"'; |
3473 } else { |
|
3474 $attributes = ' class="re' . $key . '"'; |
|
3475 } |
|
2240 } |
3476 } |
2241 else { |
3477 $stuff_to_parse = str_replace("!REG3XP$key!", "$attributes", $stuff_to_parse); |
2242 $attributes = ' class="re' . $key . '"'; |
3478 } |
2243 } |
|
2244 } |
|
2245 $stuff_to_parse = str_replace("!REG3XP$key!", "$attributes", $stuff_to_parse); |
|
2246 } |
3479 } |
2247 } |
3480 } |
2248 |
3481 |
2249 // Replace <DOT> with . for urls |
3482 // Replace <DOT> with . for urls |
2250 $stuff_to_parse = str_replace('<DOT>', '.', $stuff_to_parse); |
3483 $stuff_to_parse = str_replace('<DOT>', '.', $stuff_to_parse); |
2251 // Replace <|UR1| with <a href= for urls also |
3484 // Replace <|UR1| with <a href= for urls also |
2252 if (isset($this->link_styles[GESHI_LINK])) { |
3485 if (isset($this->link_styles[GESHI_LINK])) { |
2253 if ($this->use_classes) { |
3486 if ($this->use_classes) { |
2254 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse); |
3487 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse); |
2255 } |
3488 } else { |
2256 else { |
|
2257 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' style="' . $this->link_styles[GESHI_LINK] . '" href=', $stuff_to_parse); |
3489 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' style="' . $this->link_styles[GESHI_LINK] . '" href=', $stuff_to_parse); |
2258 } |
3490 } |
2259 } |
3491 } else { |
2260 else { |
|
2261 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse); |
3492 $stuff_to_parse = str_replace('<|UR1|', '<a' . $this->link_target . ' href=', $stuff_to_parse); |
2262 } |
3493 } |
2263 |
3494 |
2264 // |
3495 // |
2265 // NOW we add the span thingy ;) |
3496 // NOW we add the span thingy ;) |
2266 // |
3497 // |
2267 |
3498 |
2268 $stuff_to_parse = str_replace('<|', '<span', $stuff_to_parse); |
3499 $stuff_to_parse = str_replace('<|', '<span', $stuff_to_parse); |
2269 $stuff_to_parse = str_replace ( '|>', '</span>', $stuff_to_parse ); |
3500 $stuff_to_parse = str_replace ( '|>', '</span>', $stuff_to_parse ); |
2270 |
|
2271 return substr($stuff_to_parse, 1); |
3501 return substr($stuff_to_parse, 1); |
2272 } |
3502 } |
2273 |
3503 |
2274 /** |
3504 /** |
2275 * Sets the time taken to parse the code |
3505 * Sets the time taken to parse the code |
2294 function get_time() { |
3524 function get_time() { |
2295 return $this->time; |
3525 return $this->time; |
2296 } |
3526 } |
2297 |
3527 |
2298 /** |
3528 /** |
3529 * Merges arrays recursively, overwriting values of the first array with values of later arrays |
|
3530 * |
|
3531 * @since 1.0.8 |
|
3532 * @access private |
|
3533 */ |
|
3534 function merge_arrays() { |
|
3535 $arrays = func_get_args(); |
|
3536 $narrays = count($arrays); |
|
3537 |
|
3538 // check arguments |
|
3539 // comment out if more performance is necessary (in this case the foreach loop will trigger a warning if the argument is not an array) |
|
3540 for ($i = 0; $i < $narrays; $i ++) { |
|
3541 if (!is_array($arrays[$i])) { |
|
3542 // also array_merge_recursive returns nothing in this case |
|
3543 trigger_error('Argument #' . ($i+1) . ' is not an array - trying to merge array with scalar! Returning false!', E_USER_WARNING); |
|
3544 return false; |
|
3545 } |
|
3546 } |
|
3547 |
|
3548 // the first array is in the output set in every case |
|
3549 $ret = $arrays[0]; |
|
3550 |
|
3551 // merege $ret with the remaining arrays |
|
3552 for ($i = 1; $i < $narrays; $i ++) { |
|
3553 foreach ($arrays[$i] as $key => $value) { |
|
3554 if (is_array($value) && isset($ret[$key])) { |
|
3555 // if $ret[$key] is not an array you try to merge an scalar value with an array - the result is not defined (incompatible arrays) |
|
3556 // in this case the call will trigger an E_USER_WARNING and the $ret[$key] will be false. |
|
3557 $ret[$key] = $this->merge_arrays($ret[$key], $value); |
|
3558 } else { |
|
3559 $ret[$key] = $value; |
|
3560 } |
|
3561 } |
|
3562 } |
|
3563 |
|
3564 return $ret; |
|
3565 } |
|
3566 |
|
3567 /** |
|
2299 * Gets language information and stores it for later use |
3568 * Gets language information and stores it for later use |
2300 * |
3569 * |
3570 * @param string The filename of the language file you want to load |
|
3571 * @since 1.0.0 |
|
2301 * @access private |
3572 * @access private |
2302 * @todo Needs to load keys for lexic permissions for keywords, regexps etc |
3573 * @todo Needs to load keys for lexic permissions for keywords, regexps etc |
2303 */ |
3574 */ |
2304 function load_language($file_name) { |
3575 function load_language($file_name) { |
3576 if ($file_name == $this->loaded_language) { |
|
3577 // this file is already loaded! |
|
3578 return; |
|
3579 } |
|
3580 |
|
3581 //Prepare some stuff before actually loading the language file |
|
3582 $this->loaded_language = $file_name; |
|
3583 $this->parse_cache_built = false; |
|
2305 $this->enable_highlighting(); |
3584 $this->enable_highlighting(); |
2306 $language_data = array(); |
3585 $language_data = array(); |
3586 |
|
3587 //Load the language file |
|
2307 require $file_name; |
3588 require $file_name; |
3589 |
|
2308 // Perhaps some checking might be added here later to check that |
3590 // Perhaps some checking might be added here later to check that |
2309 // $language data is a valid thing but maybe not |
3591 // $language data is a valid thing but maybe not |
2310 $this->language_data = $language_data; |
3592 $this->language_data = $language_data; |
3593 |
|
2311 // Set strict mode if should be set |
3594 // Set strict mode if should be set |
2312 if ($this->language_data['STRICT_MODE_APPLIES'] == GESHI_ALWAYS) { |
3595 $this->strict_mode = $this->language_data['STRICT_MODE_APPLIES']; |
2313 $this->strict_mode = true; |
3596 |
2314 } |
|
2315 // Set permissions for all lexics to true |
3597 // Set permissions for all lexics to true |
2316 // so they'll be highlighted by default |
3598 // so they'll be highlighted by default |
2317 foreach ($this->language_data['KEYWORDS'] as $key => $words) { |
3599 foreach (array_keys($this->language_data['KEYWORDS']) as $key) { |
2318 $this->lexic_permissions['KEYWORDS'][$key] = true; |
3600 if (!empty($this->language_data['KEYWORDS'][$key])) { |
2319 } |
3601 $this->lexic_permissions['KEYWORDS'][$key] = true; |
2320 foreach ($this->language_data['COMMENT_SINGLE'] as $key => $comment) { |
3602 } else { |
3603 $this->lexic_permissions['KEYWORDS'][$key] = false; |
|
3604 } |
|
3605 } |
|
3606 |
|
3607 foreach (array_keys($this->language_data['COMMENT_SINGLE']) as $key) { |
|
2321 $this->lexic_permissions['COMMENTS'][$key] = true; |
3608 $this->lexic_permissions['COMMENTS'][$key] = true; |
2322 } |
3609 } |
2323 foreach ($this->language_data['REGEXPS'] as $key => $regexp) { |
3610 foreach (array_keys($this->language_data['REGEXPS']) as $key) { |
2324 $this->lexic_permissions['REGEXPS'][$key] = true; |
3611 $this->lexic_permissions['REGEXPS'][$key] = true; |
2325 } |
3612 } |
2326 // Set default class for CSS |
3613 |
2327 $this->overall_class = $this->language; |
3614 // for BenBE and future code reviews: |
3615 // we can use empty here since we only check for existance and emptiness of an array |
|
3616 // if it is not an array at all but rather false or null this will work as intended as well |
|
3617 // even if $this->language_data['PARSER_CONTROL'] is undefined this won't trigger a notice |
|
3618 if (!empty($this->language_data['PARSER_CONTROL']['ENABLE_FLAGS'])) { |
|
3619 foreach ($this->language_data['PARSER_CONTROL']['ENABLE_FLAGS'] as $flag => $value) { |
|
3620 // it's either true or false and maybe is true as well |
|
3621 $perm = $value !== GESHI_NEVER; |
|
3622 if ($flag == 'ALL') { |
|
3623 $this->enable_highlighting($perm); |
|
3624 continue; |
|
3625 } |
|
3626 if (!isset($this->lexic_permissions[$flag])) { |
|
3627 // unknown lexic permission |
|
3628 continue; |
|
3629 } |
|
3630 if (is_array($this->lexic_permissions[$flag])) { |
|
3631 foreach ($this->lexic_permissions[$flag] as $key => $val) { |
|
3632 $this->lexic_permissions[$flag][$key] = $perm; |
|
3633 } |
|
3634 } else { |
|
3635 $this->lexic_permissions[$flag] = $perm; |
|
3636 } |
|
3637 } |
|
3638 unset($this->language_data['PARSER_CONTROL']['ENABLE_FLAGS']); |
|
3639 } |
|
3640 |
|
3641 //NEW in 1.0.8: Allow styles to be loaded from a separate file to override defaults |
|
3642 $style_filename = substr($file_name, 0, -4) . '.style.php'; |
|
3643 if (is_readable($style_filename)) { |
|
3644 //Clear any style_data that could have been set before ... |
|
3645 if (isset($style_data)) { |
|
3646 unset($style_data); |
|
3647 } |
|
3648 |
|
3649 //Read the Style Information from the style file |
|
3650 include $style_filename; |
|
3651 |
|
3652 //Apply the new styles to our current language styles |
|
3653 if (isset($style_data) && is_array($style_data)) { |
|
3654 $this->language_data['STYLES'] = |
|
3655 $this->merge_arrays($this->language_data['STYLES'], $style_data); |
|
3656 } |
|
3657 } |
|
2328 } |
3658 } |
2329 |
3659 |
2330 /** |
3660 /** |
2331 * Takes the parsed code and various options, and creates the HTML |
3661 * Takes the parsed code and various options, and creates the HTML |
2332 * surrounding it to make it look nice. |
3662 * surrounding it to make it look nice. |
2333 * |
3663 * |
2334 * @param string The code already parsed |
3664 * @param string The code already parsed (reference!) |
2335 * @return string The code nicely finalised |
|
2336 * @since 1.0.0 |
3665 * @since 1.0.0 |
2337 * @access private |
3666 * @access private |
2338 */ |
3667 */ |
2339 function finalise($parsed_code) { |
3668 function finalise(&$parsed_code) { |
2340 // Remove end parts of important declarations |
3669 // Remove end parts of important declarations |
2341 // This is BUGGY!! My fault for bad code: fix coming in 1.2 |
3670 // This is BUGGY!! My fault for bad code: fix coming in 1.2 |
2342 // @todo Remove this crap |
3671 // @todo Remove this crap |
2343 if ($this->enable_important_blocks && |
3672 if ($this->enable_important_blocks && |
2344 (strstr($parsed_code, GeSHi::hsc(GESHI_START_IMPORTANT)) === false)) { |
3673 (strpos($parsed_code, $this->hsc(GESHI_START_IMPORTANT)) === false)) { |
2345 $parsed_code = str_replace(GeSHi::hsc(GESHI_END_IMPORTANT), '', $parsed_code); |
3674 $parsed_code = str_replace($this->hsc(GESHI_END_IMPORTANT), '', $parsed_code); |
2346 } |
3675 } |
2347 |
3676 |
2348 // Add HTML whitespace stuff if we're using the <div> header |
3677 // Add HTML whitespace stuff if we're using the <div> header |
2349 if ($this->header_type != GESHI_HEADER_PRE) { |
3678 if ($this->header_type != GESHI_HEADER_PRE && $this->header_type != GESHI_HEADER_PRE_VALID) { |
2350 $parsed_code = $this->indent($parsed_code); |
3679 $this->indent($parsed_code); |
2351 } |
3680 } |
2352 |
3681 |
2353 // purge some unnecessary stuff |
3682 // purge some unnecessary stuff |
3683 /** NOTE: memorypeak #1 */ |
|
2354 $parsed_code = preg_replace('#<span[^>]+>(\s*)</span>#', '\\1', $parsed_code); |
3684 $parsed_code = preg_replace('#<span[^>]+>(\s*)</span>#', '\\1', $parsed_code); |
2355 $parsed_code = preg_replace('#<div[^>]+>(\s*)</div>#', '\\1', $parsed_code); |
|
2356 |
3685 |
2357 // If we are using IDs for line numbers, there needs to be an overall |
3686 // If we are using IDs for line numbers, there needs to be an overall |
2358 // ID set to prevent collisions. |
3687 // ID set to prevent collisions. |
2359 if ($this->add_ids && !$this->overall_id) { |
3688 if ($this->add_ids && !$this->overall_id) { |
2360 $this->overall_id = 'geshi-' . substr(md5(microtime()), 0, 4); |
3689 $this->overall_id = 'geshi-' . substr(md5(microtime()), 0, 4); |
2361 } |
3690 } |
2362 |
3691 |
3692 // Get code into lines |
|
3693 /** NOTE: memorypeak #2 */ |
|
3694 $code = explode("\n", $parsed_code); |
|
3695 $parsed_code = $this->header(); |
|
3696 |
|
2363 // If we're using line numbers, we insert <li>s and appropriate |
3697 // If we're using line numbers, we insert <li>s and appropriate |
2364 // markup to style them (otherwise we don't need to do anything) |
3698 // markup to style them (otherwise we don't need to do anything) |
2365 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
3699 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->header_type != GESHI_HEADER_PRE_TABLE) { |
2366 // If we're using the <pre> header, we shouldn't add newlines because |
3700 // If we're using the <pre> header, we shouldn't add newlines because |
2367 // the <pre> will line-break them (and the <li>s already do this for us) |
3701 // the <pre> will line-break them (and the <li>s already do this for us) |
2368 $ls = ($this->header_type != GESHI_HEADER_PRE) ? "\n" : ''; |
3702 $ls = ($this->header_type != GESHI_HEADER_PRE && $this->header_type != GESHI_HEADER_PRE_VALID) ? "\n" : ''; |
2369 // Get code into lines |
3703 |
2370 $code = explode("\n", $parsed_code); |
|
2371 // Set vars to defaults for following loop |
3704 // Set vars to defaults for following loop |
2372 $parsed_code = ''; |
|
2373 $i = 0; |
3705 $i = 0; |
2374 $attrs = array(); |
|
2375 |
3706 |
2376 // Foreach line... |
3707 // Foreach line... |
2377 foreach ($code as $line) { |
3708 for ($i = 0, $n = count($code); $i < $n;) { |
3709 //Reset the attributes for a new line ... |
|
3710 $attrs = array(); |
|
3711 |
|
2378 // Make lines have at least one space in them if they're empty |
3712 // Make lines have at least one space in them if they're empty |
2379 // BenBE: Checking emptiness using trim instead of relying on blanks |
3713 // BenBE: Checking emptiness using trim instead of relying on blanks |
2380 if ('' == trim($line)) { |
3714 if ('' == trim($code[$i])) { |
2381 $line = ' '; |
3715 $code[$i] = ' '; |
2382 } |
3716 } |
3717 |
|
2383 // If this is a "special line"... |
3718 // If this is a "special line"... |
2384 if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && |
3719 if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && |
2385 $i % $this->line_nth_row == ($this->line_nth_row - 1)) { |
3720 $i % $this->line_nth_row == ($this->line_nth_row - 1)) { |
2386 // Set the attributes to style the line |
3721 // Set the attributes to style the line |
2387 if ($this->use_classes) { |
3722 if ($this->use_classes) { |
2388 //$attr = ' class="li2"'; |
3723 //$attr = ' class="li2"'; |
2389 $attrs['class'][] = 'li2'; |
3724 $attrs['class'][] = 'li2'; |
2390 $def_attr = ' class="de2"'; |
3725 $def_attr = ' class="de2"'; |
2391 } |
3726 } else { |
2392 else { |
|
2393 //$attr = ' style="' . $this->line_style2 . '"'; |
3727 //$attr = ' style="' . $this->line_style2 . '"'; |
2394 $attrs['style'][] = $this->line_style2; |
3728 $attrs['style'][] = $this->line_style2; |
2395 // This style "covers up" the special styles set for special lines |
3729 // This style "covers up" the special styles set for special lines |
2396 // so that styles applied to special lines don't apply to the actual |
3730 // so that styles applied to special lines don't apply to the actual |
2397 // code on that line |
3731 // code on that line |
2398 $def_attr = ' style="' . $this->code_style . '"'; |
3732 $def_attr = ' style="' . $this->code_style . '"'; |
2399 } |
3733 } |
2400 // Span or div? |
3734 } else { |
2401 $start = "<div$def_attr>"; |
|
2402 $end = '</div>'; |
|
2403 } |
|
2404 else { |
|
2405 if ($this->use_classes) { |
3735 if ($this->use_classes) { |
2406 //$attr = ' class="li1"'; |
3736 //$attr = ' class="li1"'; |
2407 $attrs['class'][] = 'li1'; |
3737 $attrs['class'][] = 'li1'; |
2408 $def_attr = ' class="de1"'; |
3738 $def_attr = ' class="de1"'; |
2409 } |
3739 } else { |
2410 else { |
|
2411 //$attr = ' style="' . $this->line_style1 . '"'; |
3740 //$attr = ' style="' . $this->line_style1 . '"'; |
2412 $attrs['style'][] = $this->line_style1; |
3741 $attrs['style'][] = $this->line_style1; |
2413 $def_attr = ' style="' . $this->code_style . '"'; |
3742 $def_attr = ' style="' . $this->code_style . '"'; |
2414 } |
3743 } |
3744 } |
|
3745 |
|
3746 //Check which type of tag to insert for this line |
|
3747 if ($this->header_type == GESHI_HEADER_PRE_VALID) { |
|
3748 $start = "<pre$def_attr>"; |
|
3749 $end = '</pre>'; |
|
3750 } else { |
|
3751 // Span or div? |
|
2415 $start = "<div$def_attr>"; |
3752 $start = "<div$def_attr>"; |
2416 $end = '</div>'; |
3753 $end = '</div>'; |
2417 } |
3754 } |
2418 |
3755 |
2419 ++$i; |
3756 ++$i; |
3757 |
|
2420 // Are we supposed to use ids? If so, add them |
3758 // Are we supposed to use ids? If so, add them |
2421 if ($this->add_ids) { |
3759 if ($this->add_ids) { |
2422 $attrs['id'][] = "$this->overall_id-$i"; |
3760 $attrs['id'][] = "$this->overall_id-$i"; |
2423 } |
3761 } |
2424 if ($this->use_classes && in_array($i, $this->highlight_extra_lines)) { |
3762 |
2425 $attrs['class'][] = 'ln-xtra'; |
3763 //Is this some line with extra styles??? |
2426 } |
3764 if (in_array($i, $this->highlight_extra_lines)) { |
2427 if (!$this->use_classes && in_array($i, $this->highlight_extra_lines)) { |
3765 if ($this->use_classes) { |
2428 $attrs['style'][] = $this->highlight_extra_lines_style; |
3766 if (isset($this->highlight_extra_lines_styles[$i])) { |
3767 $attrs['class'][] = "lx$i"; |
|
3768 } else { |
|
3769 $attrs['class'][] = "ln-xtra"; |
|
3770 } |
|
3771 } else { |
|
3772 array_push($attrs['style'], $this->get_line_style($i)); |
|
3773 } |
|
2429 } |
3774 } |
2430 |
3775 |
2431 // Add in the line surrounded by appropriate list HTML |
3776 // Add in the line surrounded by appropriate list HTML |
2432 $attr_string = ' '; |
3777 $attr_string = ''; |
2433 foreach ($attrs as $key => $attr) { |
3778 foreach ($attrs as $key => $attr) { |
2434 $attr_string .= $key . '="' . implode(' ', $attr) . '" '; |
3779 $attr_string .= ' ' . $key . '="' . implode(' ', $attr) . '"'; |
2435 } |
3780 } |
2436 $attr_string = substr($attr_string, 0, -1); |
3781 |
2437 $parsed_code .= "<li$attr_string>$start$line$end</li>$ls"; |
3782 $parsed_code .= "<li$attr_string>$start{$code[$i-1]}$end</li>$ls"; |
2438 $attrs = array(); |
3783 unset($code[$i - 1]); |
2439 } |
3784 } |
2440 } |
3785 } else { |
2441 else { |
3786 $n = count($code); |
3787 if ($this->use_classes) { |
|
3788 $attributes = ' class="de1"'; |
|
3789 } else { |
|
3790 $attributes = ' style="'. $this->code_style .'"'; |
|
3791 } |
|
3792 if ($this->header_type == GESHI_HEADER_PRE_VALID) { |
|
3793 $parsed_code .= '<pre'. $attributes .'>'; |
|
3794 } elseif ($this->header_type == GESHI_HEADER_PRE_TABLE) { |
|
3795 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
3796 if ($this->use_classes) { |
|
3797 $attrs = ' class="ln"'; |
|
3798 } else { |
|
3799 $attrs = ' style="'. $this->table_linenumber_style .'"'; |
|
3800 } |
|
3801 $parsed_code .= '<td'.$attrs.'><pre'.$attributes.'>'; |
|
3802 // get linenumbers |
|
3803 // we don't merge it with the for below, since it should be better for |
|
3804 // memory consumption this way |
|
3805 // @todo: but... actually it would still be somewhat nice to merge the two loops |
|
3806 // the mem peaks are at different positions |
|
3807 for ($i = 0; $i < $n; ++$i) { |
|
3808 $close = 0; |
|
3809 // fancy lines |
|
3810 if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && |
|
3811 $i % $this->line_nth_row == ($this->line_nth_row - 1)) { |
|
3812 // Set the attributes to style the line |
|
3813 if ($this->use_classes) { |
|
3814 $parsed_code .= '<span class="xtra li2"><span class="de2">'; |
|
3815 } else { |
|
3816 // This style "covers up" the special styles set for special lines |
|
3817 // so that styles applied to special lines don't apply to the actual |
|
3818 // code on that line |
|
3819 $parsed_code .= '<span style="display:block;' . $this->line_style2 . '">' |
|
3820 .'<span style="' . $this->code_style .'">'; |
|
3821 } |
|
3822 $close += 2; |
|
3823 } |
|
3824 //Is this some line with extra styles??? |
|
3825 if (in_array($i + 1, $this->highlight_extra_lines)) { |
|
3826 if ($this->use_classes) { |
|
3827 if (isset($this->highlight_extra_lines_styles[$i])) { |
|
3828 $parsed_code .= "<span class=\"xtra lx$i\">"; |
|
3829 } else { |
|
3830 $parsed_code .= "<span class=\"xtra ln-xtra\">"; |
|
3831 } |
|
3832 } else { |
|
3833 $parsed_code .= "<span style=\"display:block;" . $this->get_line_style($i) . "\">"; |
|
3834 } |
|
3835 ++$close; |
|
3836 } |
|
3837 $parsed_code .= $this->line_numbers_start + $i; |
|
3838 if ($close) { |
|
3839 $parsed_code .= str_repeat('</span>', $close); |
|
3840 } else if ($i != $n) { |
|
3841 $parsed_code .= "\n"; |
|
3842 } |
|
3843 } |
|
3844 $parsed_code .= '</pre></td><td'.$attributes.'>'; |
|
3845 } |
|
3846 $parsed_code .= '<pre'. $attributes .'>'; |
|
3847 } |
|
2442 // No line numbers, but still need to handle highlighting lines extra. |
3848 // No line numbers, but still need to handle highlighting lines extra. |
2443 // Have to use divs so the full width of the code is highlighted |
3849 // Have to use divs so the full width of the code is highlighted |
2444 $code = explode("\n", $parsed_code); |
3850 $close = 0; |
2445 $parsed_code = ''; |
3851 for ($i = 0; $i < $n; ++$i) { |
2446 $i = 0; |
|
2447 foreach ($code as $line) { |
|
2448 // Make lines have at least one space in them if they're empty |
3852 // Make lines have at least one space in them if they're empty |
2449 // BenBE: Checking emptiness using trim instead of relying on blanks |
3853 // BenBE: Checking emptiness using trim instead of relying on blanks |
2450 if ('' == trim($line)) { |
3854 if ('' == trim($code[$i])) { |
2451 $line = ' '; |
3855 $code[$i] = ' '; |
2452 } |
3856 } |
2453 if (in_array(++$i, $this->highlight_extra_lines)) { |
3857 // fancy lines |
3858 if ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && |
|
3859 $i % $this->line_nth_row == ($this->line_nth_row - 1)) { |
|
3860 // Set the attributes to style the line |
|
2454 if ($this->use_classes) { |
3861 if ($this->use_classes) { |
2455 $parsed_code .= '<div class="ln-xtra">'; |
3862 $parsed_code .= '<span class="xtra li2"><span class="de2">'; |
3863 } else { |
|
3864 // This style "covers up" the special styles set for special lines |
|
3865 // so that styles applied to special lines don't apply to the actual |
|
3866 // code on that line |
|
3867 $parsed_code .= '<span style="display:block;' . $this->line_style2 . '">' |
|
3868 .'<span style="' . $this->code_style .'">'; |
|
2456 } |
3869 } |
2457 else { |
3870 $close += 2; |
2458 $parsed_code .= "<div style=\"{$this->highlight_extra_lines_style}\">"; |
3871 } |
3872 //Is this some line with extra styles??? |
|
3873 if (in_array($i + 1, $this->highlight_extra_lines)) { |
|
3874 if ($this->use_classes) { |
|
3875 if (isset($this->highlight_extra_lines_styles[$i])) { |
|
3876 $parsed_code .= "<span class=\"xtra lx$i\">"; |
|
3877 } else { |
|
3878 $parsed_code .= "<span class=\"xtra ln-xtra\">"; |
|
3879 } |
|
3880 } else { |
|
3881 $parsed_code .= "<span style=\"display:block;" . $this->get_line_style($i) . "\">"; |
|
2459 } |
3882 } |
2460 // Remove \n because it stuffs up <pre> header |
3883 ++$close; |
2461 $parsed_code .= $line . "</div>"; |
3884 } |
2462 } |
3885 |
2463 else { |
3886 $parsed_code .= $code[$i]; |
2464 $parsed_code .= $line . "\n"; |
3887 |
2465 } |
3888 if ($close) { |
2466 } |
3889 $parsed_code .= str_repeat('</span>', $close); |
2467 } |
3890 $close = 0; |
2468 |
3891 } |
2469 if ($this->header_type == GESHI_HEADER_PRE) { |
3892 elseif ($i + 1 < $n) { |
2470 // enforce line numbers when using pre |
3893 $parsed_code .= "\n"; |
2471 $parsed_code = str_replace('<li></li>', '<li> </li>', $parsed_code); |
3894 } |
2472 } |
3895 unset($code[$i]); |
2473 |
3896 } |
2474 return $this->header() . chop($parsed_code) . $this->footer(); |
3897 |
3898 if ($this->header_type == GESHI_HEADER_PRE_VALID || $this->header_type == GESHI_HEADER_PRE_TABLE) { |
|
3899 $parsed_code .= '</pre>'; |
|
3900 } |
|
3901 if ($this->header_type == GESHI_HEADER_PRE_TABLE && $this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
3902 $parsed_code .= '</td>'; |
|
3903 } |
|
3904 } |
|
3905 |
|
3906 $parsed_code .= $this->footer(); |
|
2475 } |
3907 } |
2476 |
3908 |
2477 /** |
3909 /** |
2478 * Creates the header for the code block (with correct attributes) |
3910 * Creates the header for the code block (with correct attributes) |
2479 * |
3911 * |
2481 * @since 1.0.0 |
3913 * @since 1.0.0 |
2482 * @access private |
3914 * @access private |
2483 */ |
3915 */ |
2484 function header() { |
3916 function header() { |
2485 // Get attributes needed |
3917 // Get attributes needed |
2486 $attributes = $this->get_attributes(); |
3918 /** |
3919 * @todo Document behaviour change - class is outputted regardless of whether |
|
3920 * we're using classes or not. Same with style |
|
3921 */ |
|
3922 $attributes = ' class="' . $this->language; |
|
3923 if ($this->overall_class != '') { |
|
3924 $attributes .= " ".$this->overall_class; |
|
3925 } |
|
3926 $attributes .= '"'; |
|
3927 |
|
3928 if ($this->overall_id != '') { |
|
3929 $attributes .= " id=\"{$this->overall_id}\""; |
|
3930 } |
|
3931 if ($this->overall_style != '') { |
|
3932 $attributes .= ' style="' . $this->overall_style . '"'; |
|
3933 } |
|
2487 |
3934 |
2488 $ol_attributes = ''; |
3935 $ol_attributes = ''; |
2489 |
3936 |
2490 if ($this->line_numbers_start != 1) { |
3937 if ($this->line_numbers_start != 1) { |
2491 $ol_attributes .= ' start="' . $this->line_numbers_start . '"'; |
3938 $ol_attributes .= ' start="' . $this->line_numbers_start . '"'; |
2492 } |
3939 } |
2493 |
3940 |
2494 // Get the header HTML |
3941 // Get the header HTML |
2495 $header = $this->format_header_content(); |
3942 $header = $this->header_content; |
3943 if ($header) { |
|
3944 if ($this->header_type == GESHI_HEADER_PRE || $this->header_type == GESHI_HEADER_PRE_VALID) { |
|
3945 $header = str_replace("\n", '', $header); |
|
3946 } |
|
3947 $header = $this->replace_keywords($header); |
|
3948 |
|
3949 if ($this->use_classes) { |
|
3950 $attr = ' class="head"'; |
|
3951 } else { |
|
3952 $attr = " style=\"{$this->header_content_style}\""; |
|
3953 } |
|
3954 if ($this->header_type == GESHI_HEADER_PRE_TABLE && $this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
3955 $header = "<thead><tr><td colspan=\"2\" $attr>$header</td></tr></thead>"; |
|
3956 } else { |
|
3957 $header = "<div$attr>$header</div>"; |
|
3958 } |
|
3959 } |
|
2496 |
3960 |
2497 if (GESHI_HEADER_NONE == $this->header_type) { |
3961 if (GESHI_HEADER_NONE == $this->header_type) { |
2498 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
3962 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
2499 return "$header<ol$ol_attributes>"; |
3963 return "$header<ol$attributes$ol_attributes>"; |
2500 } |
3964 } |
2501 return $header . |
3965 return $header . ($this->force_code_block ? '<div>' : ''); |
2502 ($this->force_code_block ? '<div>' : ''); |
|
2503 } |
3966 } |
2504 |
3967 |
2505 // Work out what to return and do it |
3968 // Work out what to return and do it |
2506 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
3969 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
2507 if ($this->header_type == GESHI_HEADER_PRE) { |
3970 if ($this->header_type == GESHI_HEADER_PRE) { |
2508 return "<pre$attributes>$header<ol$ol_attributes>"; |
3971 return "<pre$attributes>$header<ol$ol_attributes>"; |
2509 } |
3972 } else if ($this->header_type == GESHI_HEADER_DIV || |
2510 else if ($this->header_type == GESHI_HEADER_DIV) { |
3973 $this->header_type == GESHI_HEADER_PRE_VALID) { |
2511 return "<div$attributes>$header<ol$ol_attributes>"; |
3974 return "<div$attributes>$header<ol$ol_attributes>"; |
2512 } |
3975 } else if ($this->header_type == GESHI_HEADER_PRE_TABLE) { |
2513 } |
3976 return "<table$attributes>$header<tbody><tr class=\"li1\">"; |
2514 else { |
3977 } |
3978 } else { |
|
2515 if ($this->header_type == GESHI_HEADER_PRE) { |
3979 if ($this->header_type == GESHI_HEADER_PRE) { |
2516 return "<pre$attributes>$header" . |
3980 return "<pre$attributes>$header" . |
2517 ($this->force_code_block ? '<div>' : ''); |
3981 ($this->force_code_block ? '<div>' : ''); |
2518 } |
3982 } else { |
2519 else if ($this->header_type == GESHI_HEADER_DIV) { |
|
2520 return "<div$attributes>$header" . |
3983 return "<div$attributes>$header" . |
2521 ($this->force_code_block ? '<div>' : ''); |
3984 ($this->force_code_block ? '<div>' : ''); |
2522 } |
3985 } |
2523 } |
|
2524 } |
|
2525 |
|
2526 /** |
|
2527 * Returns the header content, formatted for output |
|
2528 * |
|
2529 * @return string The header content, formatted for output |
|
2530 * @since 1.0.2 |
|
2531 * @access private |
|
2532 */ |
|
2533 function format_header_content() { |
|
2534 $header = $this->header_content; |
|
2535 if ($header) { |
|
2536 if ($this->header_type == GESHI_HEADER_PRE) { |
|
2537 $header = str_replace("\n", '', $header); |
|
2538 } |
|
2539 $header = $this->replace_keywords($header); |
|
2540 |
|
2541 if ($this->use_classes) { |
|
2542 $attr = ' class="head"'; |
|
2543 } |
|
2544 else { |
|
2545 $attr = " style=\"{$this->header_content_style}\""; |
|
2546 } |
|
2547 return "<div$attr>$header</div>"; |
|
2548 } |
3986 } |
2549 } |
3987 } |
2550 |
3988 |
2551 /** |
3989 /** |
2552 * Returns the footer for the code block. |
3990 * Returns the footer for the code block. |
2554 * @return string The footer for the code block |
3992 * @return string The footer for the code block |
2555 * @since 1.0.0 |
3993 * @since 1.0.0 |
2556 * @access private |
3994 * @access private |
2557 */ |
3995 */ |
2558 function footer() { |
3996 function footer() { |
2559 $footer_content = $this->format_footer_content(); |
|
2560 |
|
2561 if (GESHI_HEADER_NONE == $this->header_type) { |
|
2562 return ($this->line_numbers != GESHI_NO_LINE_NUMBERS) ? '</ol>' . $footer_content |
|
2563 : $footer_content; |
|
2564 } |
|
2565 |
|
2566 if ($this->header_type == GESHI_HEADER_DIV) { |
|
2567 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
2568 return "</ol>$footer_content</div>"; |
|
2569 } |
|
2570 return ($this->force_code_block ? '</div>' : '') . |
|
2571 "$footer_content</div>"; |
|
2572 } |
|
2573 else { |
|
2574 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
2575 return "</ol>$footer_content</pre>"; |
|
2576 } |
|
2577 return ($this->force_code_block ? '</div>' : '') . |
|
2578 "$footer_content</pre>"; |
|
2579 } |
|
2580 } |
|
2581 |
|
2582 /** |
|
2583 * Returns the footer content, formatted for output |
|
2584 * |
|
2585 * @return string The footer content, formatted for output |
|
2586 * @since 1.0.2 |
|
2587 * @access private |
|
2588 */ |
|
2589 function format_footer_content() { |
|
2590 $footer = $this->footer_content; |
3997 $footer = $this->footer_content; |
2591 if ($footer) { |
3998 if ($footer) { |
2592 if ($this->header_type == GESHI_HEADER_PRE) { |
3999 if ($this->header_type == GESHI_HEADER_PRE) { |
2593 $footer = str_replace("\n", '', $footer);; |
4000 $footer = str_replace("\n", '', $footer);; |
2594 } |
4001 } |
2595 $footer = $this->replace_keywords($footer); |
4002 $footer = $this->replace_keywords($footer); |
2596 |
4003 |
2597 if ($this->use_classes) { |
4004 if ($this->use_classes) { |
2598 $attr = ' class="foot"'; |
4005 $attr = ' class="foot"'; |
2599 } |
4006 } else { |
2600 else { |
|
2601 $attr = " style=\"{$this->footer_content_style}\""; |
4007 $attr = " style=\"{$this->footer_content_style}\""; |
2602 } |
4008 } |
2603 return "<div$attr>$footer</div>"; |
4009 if ($this->header_type == GESHI_HEADER_PRE_TABLE && $this->linenumbers != GESHI_NO_LINE_NUMBERS) { |
4010 $footer = "<tfoot><tr><td colspan=\"2\">$footer</td></tr></tfoot>"; |
|
4011 } else { |
|
4012 $footer = "<div$attr>$footer</div>"; |
|
4013 } |
|
4014 } |
|
4015 |
|
4016 if (GESHI_HEADER_NONE == $this->header_type) { |
|
4017 return ($this->line_numbers != GESHI_NO_LINE_NUMBERS) ? '</ol>' . $footer : $footer; |
|
4018 } |
|
4019 |
|
4020 if ($this->header_type == GESHI_HEADER_DIV || $this->header_type == GESHI_HEADER_PRE_VALID) { |
|
4021 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
4022 return "</ol>$footer</div>"; |
|
4023 } |
|
4024 return ($this->force_code_block ? '</div>' : '') . |
|
4025 "$footer</div>"; |
|
4026 } |
|
4027 elseif ($this->header_type == GESHI_HEADER_PRE_TABLE) { |
|
4028 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
4029 return "</tr></tbody>$footer</table>"; |
|
4030 } |
|
4031 return ($this->force_code_block ? '</div>' : '') . |
|
4032 "$footer</div>"; |
|
4033 } |
|
4034 else { |
|
4035 if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) { |
|
4036 return "</ol>$footer</pre>"; |
|
4037 } |
|
4038 return ($this->force_code_block ? '</div>' : '') . |
|
4039 "$footer</pre>"; |
|
2604 } |
4040 } |
2605 } |
4041 } |
2606 |
4042 |
2607 /** |
4043 /** |
2608 * Replaces certain keywords in the header and footer with |
4044 * Replaces certain keywords in the header and footer with |
2616 function replace_keywords($instr) { |
4052 function replace_keywords($instr) { |
2617 $keywords = $replacements = array(); |
4053 $keywords = $replacements = array(); |
2618 |
4054 |
2619 $keywords[] = '<TIME>'; |
4055 $keywords[] = '<TIME>'; |
2620 $keywords[] = '{TIME}'; |
4056 $keywords[] = '{TIME}'; |
2621 $replacements[] = $replacements[] = number_format($this->get_time(), 3); |
4057 $replacements[] = $replacements[] = number_format($time = $this->get_time(), 3); |
2622 |
4058 |
2623 $keywords[] = '<LANGUAGE>'; |
4059 $keywords[] = '<LANGUAGE>'; |
2624 $keywords[] = '{LANGUAGE}'; |
4060 $keywords[] = '{LANGUAGE}'; |
2625 $replacements[] = $replacements[] = $this->language; |
4061 $replacements[] = $replacements[] = $this->language_data['LANG_NAME']; |
2626 |
4062 |
2627 $keywords[] = '<VERSION>'; |
4063 $keywords[] = '<VERSION>'; |
2628 $keywords[] = '{VERSION}'; |
4064 $keywords[] = '{VERSION}'; |
2629 $replacements[] = $replacements[] = GESHI_VERSION; |
4065 $replacements[] = $replacements[] = GESHI_VERSION; |
2630 |
4066 |
4067 $keywords[] = '<SPEED>'; |
|
4068 $keywords[] = '{SPEED}'; |
|
4069 if ($time <= 0) { |
|
4070 $speed = 'N/A'; |
|
4071 } else { |
|
4072 $speed = strlen($this->source) / $time; |
|
4073 if ($speed >= 1024) { |
|
4074 $speed = sprintf("%.2f KB/s", $speed / 1024.0); |
|
4075 } else { |
|
4076 $speed = sprintf("%.0f B/s", $speed); |
|
4077 } |
|
4078 } |
|
4079 $replacements[] = $replacements[] = $speed; |
|
4080 |
|
2631 return str_replace($keywords, $replacements, $instr); |
4081 return str_replace($keywords, $replacements, $instr); |
2632 } |
|
2633 |
|
2634 /** |
|
2635 * Gets the CSS attributes for this code |
|
2636 * |
|
2637 * @return The CSS attributes for this code |
|
2638 * @since 1.0.0 |
|
2639 * @access private |
|
2640 * @todo Document behaviour change - class is outputted regardless of whether we're using classes or not. |
|
2641 * Same with style |
|
2642 */ |
|
2643 function get_attributes() { |
|
2644 $attributes = ''; |
|
2645 |
|
2646 if ($this->overall_class != '') { |
|
2647 $attributes .= " class=\"{$this->overall_class}\""; |
|
2648 } |
|
2649 if ($this->overall_id != '') { |
|
2650 $attributes .= " id=\"{$this->overall_id}\""; |
|
2651 } |
|
2652 if ($this->overall_style != '') { |
|
2653 $attributes .= ' style="' . $this->overall_style . '"'; |
|
2654 } |
|
2655 return $attributes; |
|
2656 } |
4082 } |
2657 |
4083 |
2658 /** |
4084 /** |
2659 * Secure replacement for PHP built-in function htmlspecialchars(). |
4085 * Secure replacement for PHP built-in function htmlspecialchars(). |
2660 * |
4086 * |
2697 * @license http://www.gnu.org/copyleft/lgpl.html |
4123 * @license http://www.gnu.org/copyleft/lgpl.html |
2698 * GNU Lesser General Public License |
4124 * GNU Lesser General Public License |
2699 * @copyright Copyright 2007, {@link http://wikkawiki.org/CreditsPage |
4125 * @copyright Copyright 2007, {@link http://wikkawiki.org/CreditsPage |
2700 * Wikka Development Team} |
4126 * Wikka Development Team} |
2701 * |
4127 * |
2702 * @access public |
4128 * @access private |
2703 * @param string $string string to be converted |
4129 * @param string $string string to be converted |
2704 * @param integer $quote_style |
4130 * @param integer $quote_style |
2705 * - ENT_COMPAT: escapes &, <, > and double quote (default) |
4131 * - ENT_COMPAT: escapes &, <, > and double quote (default) |
2706 * - ENT_NOQUOTES: escapes only &, < and > |
4132 * - ENT_NOQUOTES: escapes only &, < and > |
2707 * - ENT_QUOTES: escapes &, <, >, double and single quotes |
4133 * - ENT_QUOTES: escapes &, <, >, double and single quotes |
2708 * @return string converted string |
4134 * @return string converted string |
2709 */ |
4135 * @since 1.0.7.18 |
2710 function hsc($string, $quote_style=ENT_COMPAT) { |
4136 */ |
4137 function hsc($string, $quote_style = ENT_COMPAT) { |
|
2711 // init |
4138 // init |
2712 $aTransSpecchar = array( |
4139 static $aTransSpecchar = array( |
2713 '&' => '&', |
4140 '&' => '&', |
2714 '"' => '"', |
4141 '"' => '"', |
2715 '<' => '<', |
4142 '<' => '<', |
2716 '>' => '>' |
4143 '>' => '>', |
4144 |
|
4145 //This fix is related to SF#1923020, but has to be applied |
|
4146 //regardless of actually highlighting symbols. |
|
4147 |
|
4148 //Circumvent a bug with symbol highlighting |
|
4149 //This is required as ; would produce undesirable side-effects if it |
|
4150 //was not to be processed as an entity. |
|
4151 ';' => '<SEMI>', // Force ; to be processed as entity |
|
4152 '|' => '<PIPE>' // Force | to be processed as entity |
|
2717 ); // ENT_COMPAT set |
4153 ); // ENT_COMPAT set |
2718 |
4154 |
2719 if (ENT_NOQUOTES == $quote_style) // don't convert double quotes |
4155 switch ($quote_style) { |
2720 { |
4156 case ENT_NOQUOTES: // don't convert double quotes |
2721 unset($aTransSpecchar['"']); |
4157 unset($aTransSpecchar['"']); |
2722 } |
4158 break; |
2723 elseif (ENT_QUOTES == $quote_style) // convert single quotes as well |
4159 case ENT_QUOTES: // convert single quotes as well |
2724 { |
4160 $aTransSpecchar["'"] = '''; // (apos) htmlspecialchars() uses ''' |
2725 $aTransSpecchar["'"] = '''; // (apos) htmlspecialchars() uses ''' |
4161 break; |
2726 } |
4162 } |
2727 |
4163 |
2728 // return translated string |
4164 // return translated string |
2729 return strtr($string,$aTransSpecchar); |
4165 return strtr($string, $aTransSpecchar); |
2730 } |
4166 } |
2731 |
4167 |
2732 /** |
4168 /** |
2733 * Returns a stylesheet for the highlighted code. If $economy mode |
4169 * Returns a stylesheet for the highlighted code. If $economy mode |
2734 * is true, we only return the stylesheet declarations that matter for |
4170 * is true, we only return the stylesheet declarations that matter for |
2743 // won't have populated the language data file, so we can't |
4179 // won't have populated the language data file, so we can't |
2744 // risk getting a stylesheet... |
4180 // risk getting a stylesheet... |
2745 if ($this->error) { |
4181 if ($this->error) { |
2746 return ''; |
4182 return ''; |
2747 } |
4183 } |
4184 |
|
4185 //Check if the style rearrangements have been processed ... |
|
4186 //This also does some preprocessing to check which style groups are useable ... |
|
4187 if(!isset($this->language_data['NUMBERS_CACHE'])) { |
|
4188 $this->build_style_cache(); |
|
4189 } |
|
4190 |
|
2748 // First, work out what the selector should be. If there's an ID, |
4191 // First, work out what the selector should be. If there's an ID, |
2749 // that should be used, the same for a class. Otherwise, a selector |
4192 // that should be used, the same for a class. Otherwise, a selector |
2750 // of '' means that these styles will be applied anywhere |
4193 // of '' means that these styles will be applied anywhere |
2751 $selector = ($this->overall_id != '') ? "#{$this->overall_id} " : ''; |
4194 if ($this->overall_id) { |
2752 $selector = ($selector == '' && $this->overall_class != '') ? ".{$this->overall_class} " : $selector; |
4195 $selector = '#' . $this->overall_id; |
4196 } else { |
|
4197 $selector = '.' . $this->language; |
|
4198 if ($this->overall_class) { |
|
4199 $selector .= '.' . $this->overall_class; |
|
4200 } |
|
4201 } |
|
4202 $selector .= ' '; |
|
2753 |
4203 |
2754 // Header of the stylesheet |
4204 // Header of the stylesheet |
2755 if (!$economy_mode) { |
4205 if (!$economy_mode) { |
2756 $stylesheet = "/**\n * GeSHi Dynamically Generated Stylesheet\n * --------------------------------------\n * Dynamically generated stylesheet for {$this->language}\n * CSS class: {$this->overall_class}, CSS id: {$this->overall_id}\n * GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter)\n */\n"; |
4206 $stylesheet = "/**\n". |
2757 } else { |
4207 " * GeSHi Dynamically Generated Stylesheet\n". |
2758 $stylesheet = '/* GeSHi (C) 2004 - 2007 Nigel McNie (http://qbnz.com/highlighter) */' . "\n"; |
4208 " * --------------------------------------\n". |
4209 " * Dynamically generated stylesheet for {$this->language}\n". |
|
4210 " * CSS class: {$this->overall_class}, CSS id: {$this->overall_id}\n". |
|
4211 " * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann\n" . |
|
4212 " * (http://qbnz.com/highlighter/ and http://geshi.org/)\n". |
|
4213 " * --------------------------------------\n". |
|
4214 " */\n"; |
|
4215 } else { |
|
4216 $stylesheet = "/**\n". |
|
4217 " * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann\n" . |
|
4218 " * (http://qbnz.com/highlighter/ and http://geshi.org/)\n". |
|
4219 " */\n"; |
|
2759 } |
4220 } |
2760 |
4221 |
2761 // Set the <ol> to have no effect at all if there are line numbers |
4222 // Set the <ol> to have no effect at all if there are line numbers |
2762 // (<ol>s have margins that should be destroyed so all layout is |
4223 // (<ol>s have margins that should be destroyed so all layout is |
2763 // controlled by the set_overall_style method, which works on the |
4224 // controlled by the set_overall_style method, which works on the |
2766 //$stylesheet .= "$selector, {$selector}ol, {$selector}ol li {margin: 0;}\n"; |
4227 //$stylesheet .= "$selector, {$selector}ol, {$selector}ol li {margin: 0;}\n"; |
2767 $stylesheet .= "$selector.de1, $selector.de2 {{$this->code_style}}\n"; |
4228 $stylesheet .= "$selector.de1, $selector.de2 {{$this->code_style}}\n"; |
2768 } |
4229 } |
2769 |
4230 |
2770 // Add overall styles |
4231 // Add overall styles |
2771 if (!$economy_mode || $this->overall_style != '') { |
4232 // note: neglect economy_mode, empty styles are meaningless |
4233 if ($this->overall_style != '') { |
|
2772 $stylesheet .= "$selector {{$this->overall_style}}\n"; |
4234 $stylesheet .= "$selector {{$this->overall_style}}\n"; |
2773 } |
4235 } |
2774 |
4236 |
2775 // Add styles for links |
4237 // Add styles for links |
4238 // note: economy mode does not make _any_ sense here |
|
4239 // either the style is empty and thus no selector is needed |
|
4240 // or the appropriate key is given. |
|
2776 foreach ($this->link_styles as $key => $style) { |
4241 foreach ($this->link_styles as $key => $style) { |
2777 if (!$economy_mode || $key == GESHI_LINK && $style != '') { |
4242 if ($style != '') { |
2778 $stylesheet .= "{$selector}a:link {{$style}}\n"; |
4243 switch ($key) { |
2779 } |
4244 case GESHI_LINK: |
2780 if (!$economy_mode || $key == GESHI_HOVER && $style != '') { |
4245 $stylesheet .= "{$selector}a:link {{$style}}\n"; |
2781 $stylesheet .= "{$selector}a:hover {{$style}}\n"; |
4246 break; |
2782 } |
4247 case GESHI_HOVER: |
2783 if (!$economy_mode || $key == GESHI_ACTIVE && $style != '') { |
4248 $stylesheet .= "{$selector}a:hover {{$style}}\n"; |
2784 $stylesheet .= "{$selector}a:active {{$style}}\n"; |
4249 break; |
2785 } |
4250 case GESHI_ACTIVE: |
2786 if (!$economy_mode || $key == GESHI_VISITED && $style != '') { |
4251 $stylesheet .= "{$selector}a:active {{$style}}\n"; |
2787 $stylesheet .= "{$selector}a:visited {{$style}}\n"; |
4252 break; |
4253 case GESHI_VISITED: |
|
4254 $stylesheet .= "{$selector}a:visited {{$style}}\n"; |
|
4255 break; |
|
4256 } |
|
2788 } |
4257 } |
2789 } |
4258 } |
2790 |
4259 |
2791 // Header and footer |
4260 // Header and footer |
2792 if (!$economy_mode || $this->header_content_style != '') { |
4261 // note: neglect economy_mode, empty styles are meaningless |
4262 if ($this->header_content_style != '') { |
|
2793 $stylesheet .= "$selector.head {{$this->header_content_style}}\n"; |
4263 $stylesheet .= "$selector.head {{$this->header_content_style}}\n"; |
2794 } |
4264 } |
2795 if (!$economy_mode || $this->footer_content_style != '') { |
4265 if ($this->footer_content_style != '') { |
2796 $stylesheet .= "$selector.foot {{$this->footer_content_style}}\n"; |
4266 $stylesheet .= "$selector.foot {{$this->footer_content_style}}\n"; |
2797 } |
4267 } |
2798 |
4268 |
2799 // Styles for important stuff |
4269 // Styles for important stuff |
2800 if (!$economy_mode || $this->important_styles != '') { |
4270 // note: neglect economy_mode, empty styles are meaningless |
4271 if ($this->important_styles != '') { |
|
2801 $stylesheet .= "$selector.imp {{$this->important_styles}}\n"; |
4272 $stylesheet .= "$selector.imp {{$this->important_styles}}\n"; |
2802 } |
4273 } |
2803 |
4274 |
2804 // Styles for lines being highlighted extra |
|
2805 if (!$economy_mode || count($this->highlight_extra_lines)) { |
|
2806 $stylesheet .= "$selector.ln-xtra {{$this->highlight_extra_lines_style}}\n"; |
|
2807 } |
|
2808 |
|
2809 // Simple line number styles |
4275 // Simple line number styles |
2810 if (!$economy_mode || ($this->line_numbers != GESHI_NO_LINE_NUMBERS && $this->line_style1 != '')) { |
4276 if ((!$economy_mode || $this->line_numbers != GESHI_NO_LINE_NUMBERS) && $this->line_style1 != '') { |
2811 $stylesheet .= "{$selector}li {{$this->line_style1}}\n"; |
4277 $stylesheet .= "{$selector}li, {$selector}.li1 {{$this->line_style1}}\n"; |
2812 } |
4278 } |
2813 |
4279 if ((!$economy_mode || $this->line_numbers != GESHI_NO_LINE_NUMBERS) && $this->table_linenumber_style != '') { |
4280 $stylesheet .= "{$selector}.ln {{$this->table_linenumber_style}}\n"; |
|
4281 } |
|
2814 // If there is a style set for fancy line numbers, echo it out |
4282 // If there is a style set for fancy line numbers, echo it out |
2815 if (!$economy_mode || ($this->line_numbers == GESHI_FANCY_LINE_NUMBERS && $this->line_style2 != '')) { |
4283 if ((!$economy_mode || $this->line_numbers == GESHI_FANCY_LINE_NUMBERS) && $this->line_style2 != '') { |
2816 $stylesheet .= "{$selector}li.li2 {{$this->line_style2}}\n"; |
4284 $stylesheet .= "{$selector}.li2 {{$this->line_style2}}\n"; |
2817 } |
4285 } |
2818 |
4286 |
4287 // note: empty styles are meaningless |
|
2819 foreach ($this->language_data['STYLES']['KEYWORDS'] as $group => $styles) { |
4288 foreach ($this->language_data['STYLES']['KEYWORDS'] as $group => $styles) { |
2820 if (!$economy_mode || !($economy_mode && (!$this->lexic_permissions['KEYWORDS'][$group] || $styles == ''))) { |
4289 if ($styles != '' && (!$economy_mode || |
4290 (isset($this->lexic_permissions['KEYWORDS'][$group]) && |
|
4291 $this->lexic_permissions['KEYWORDS'][$group]))) { |
|
2821 $stylesheet .= "$selector.kw$group {{$styles}}\n"; |
4292 $stylesheet .= "$selector.kw$group {{$styles}}\n"; |
2822 } |
4293 } |
2823 } |
4294 } |
2824 foreach ($this->language_data['STYLES']['COMMENTS'] as $group => $styles) { |
4295 foreach ($this->language_data['STYLES']['COMMENTS'] as $group => $styles) { |
2825 if (!$economy_mode || !($economy_mode && $styles == '') && |
4296 if ($styles != '' && (!$economy_mode || |
2826 !($economy_mode && !$this->lexic_permissions['COMMENTS'][$group])) { |
4297 (isset($this->lexic_permissions['COMMENTS'][$group]) && |
4298 $this->lexic_permissions['COMMENTS'][$group]) || |
|
4299 (!empty($this->language_data['COMMENT_REGEXP']) && |
|
4300 !empty($this->language_data['COMMENT_REGEXP'][$group])))) { |
|
2827 $stylesheet .= "$selector.co$group {{$styles}}\n"; |
4301 $stylesheet .= "$selector.co$group {{$styles}}\n"; |
2828 } |
4302 } |
2829 } |
4303 } |
2830 foreach ($this->language_data['STYLES']['ESCAPE_CHAR'] as $group => $styles) { |
4304 foreach ($this->language_data['STYLES']['ESCAPE_CHAR'] as $group => $styles) { |
2831 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4305 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['ESCAPE_CHAR'])) { |
2832 !$this->lexic_permissions['ESCAPE_CHAR'])) { |
4306 // NEW: since 1.0.8 we have to handle hardescapes |
4307 if ($group === 'HARD') { |
|
4308 $group = '_h'; |
|
4309 } |
|
2833 $stylesheet .= "$selector.es$group {{$styles}}\n"; |
4310 $stylesheet .= "$selector.es$group {{$styles}}\n"; |
2834 } |
4311 } |
2835 } |
4312 } |
4313 foreach ($this->language_data['STYLES']['BRACKETS'] as $group => $styles) { |
|
4314 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['BRACKETS'])) { |
|
4315 $stylesheet .= "$selector.br$group {{$styles}}\n"; |
|
4316 } |
|
4317 } |
|
2836 foreach ($this->language_data['STYLES']['SYMBOLS'] as $group => $styles) { |
4318 foreach ($this->language_data['STYLES']['SYMBOLS'] as $group => $styles) { |
2837 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4319 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['SYMBOLS'])) { |
2838 !$this->lexic_permissions['BRACKETS'])) { |
4320 $stylesheet .= "$selector.sy$group {{$styles}}\n"; |
2839 $stylesheet .= "$selector.br$group {{$styles}}\n"; |
|
2840 } |
4321 } |
2841 } |
4322 } |
2842 foreach ($this->language_data['STYLES']['STRINGS'] as $group => $styles) { |
4323 foreach ($this->language_data['STYLES']['STRINGS'] as $group => $styles) { |
2843 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4324 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['STRINGS'])) { |
2844 !$this->lexic_permissions['STRINGS'])) { |
4325 // NEW: since 1.0.8 we have to handle hardquotes |
4326 if ($group === 'HARD') { |
|
4327 $group = '_h'; |
|
4328 } |
|
2845 $stylesheet .= "$selector.st$group {{$styles}}\n"; |
4329 $stylesheet .= "$selector.st$group {{$styles}}\n"; |
2846 } |
4330 } |
2847 } |
4331 } |
2848 foreach ($this->language_data['STYLES']['NUMBERS'] as $group => $styles) { |
4332 foreach ($this->language_data['STYLES']['NUMBERS'] as $group => $styles) { |
2849 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4333 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['NUMBERS'])) { |
2850 !$this->lexic_permissions['NUMBERS'])) { |
|
2851 $stylesheet .= "$selector.nu$group {{$styles}}\n"; |
4334 $stylesheet .= "$selector.nu$group {{$styles}}\n"; |
2852 } |
4335 } |
2853 } |
4336 } |
2854 foreach ($this->language_data['STYLES']['METHODS'] as $group => $styles) { |
4337 foreach ($this->language_data['STYLES']['METHODS'] as $group => $styles) { |
2855 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4338 if ($styles != '' && (!$economy_mode || $this->lexic_permissions['METHODS'])) { |
2856 !$this->lexic_permissions['METHODS'])) { |
|
2857 $stylesheet .= "$selector.me$group {{$styles}}\n"; |
4339 $stylesheet .= "$selector.me$group {{$styles}}\n"; |
2858 } |
4340 } |
2859 } |
4341 } |
4342 // note: neglect economy_mode, empty styles are meaningless |
|
2860 foreach ($this->language_data['STYLES']['SCRIPT'] as $group => $styles) { |
4343 foreach ($this->language_data['STYLES']['SCRIPT'] as $group => $styles) { |
2861 if (!$economy_mode || !($economy_mode && $styles == '')) { |
4344 if ($styles != '') { |
2862 $stylesheet .= "$selector.sc$group {{$styles}}\n"; |
4345 $stylesheet .= "$selector.sc$group {{$styles}}\n"; |
2863 } |
4346 } |
2864 } |
4347 } |
2865 foreach ($this->language_data['STYLES']['REGEXPS'] as $group => $styles) { |
4348 foreach ($this->language_data['STYLES']['REGEXPS'] as $group => $styles) { |
2866 if (!$economy_mode || !($economy_mode && $styles == '') && !($economy_mode && |
4349 if ($styles != '' && (!$economy_mode || |
2867 !$this->lexic_permissions['REGEXPS'][$group])) { |
4350 (isset($this->lexic_permissions['REGEXPS'][$group]) && |
4351 $this->lexic_permissions['REGEXPS'][$group]))) { |
|
2868 if (is_array($this->language_data['REGEXPS'][$group]) && |
4352 if (is_array($this->language_data['REGEXPS'][$group]) && |
2869 array_key_exists(GESHI_CLASS, |
4353 array_key_exists(GESHI_CLASS, $this->language_data['REGEXPS'][$group])) { |
2870 $this->language_data['REGEXPS'][$group])) { |
|
2871 $stylesheet .= "$selector."; |
4354 $stylesheet .= "$selector."; |
2872 $stylesheet .= $this->language_data['REGEXPS'][$group][GESHI_CLASS]; |
4355 $stylesheet .= $this->language_data['REGEXPS'][$group][GESHI_CLASS]; |
2873 $stylesheet .= " {{$styles}}\n"; |
4356 $stylesheet .= " {{$styles}}\n"; |
2874 } |
4357 } else { |
2875 else { |
|
2876 $stylesheet .= "$selector.re$group {{$styles}}\n"; |
4358 $stylesheet .= "$selector.re$group {{$styles}}\n"; |
2877 } |
4359 } |
2878 } |
4360 } |
2879 } |
4361 } |
4362 // Styles for lines being highlighted extra |
|
4363 if (!$economy_mode || (count($this->highlight_extra_lines)!=count($this->highlight_extra_lines_styles))) { |
|
4364 $stylesheet .= "{$selector}.ln-xtra, {$selector}li.ln-xtra, {$selector}div.ln-xtra {{$this->highlight_extra_lines_style}}\n"; |
|
4365 } |
|
4366 $stylesheet .= "{$selector}span.xtra { display:block; }\n"; |
|
4367 foreach ($this->highlight_extra_lines_styles as $lineid => $linestyle) { |
|
4368 $stylesheet .= "{$selector}.lx$lineid, {$selector}li.lx$lineid, {$selector}div.lx$lineid {{$linestyle}}\n"; |
|
4369 } |
|
2880 |
4370 |
2881 return $stylesheet; |
4371 return $stylesheet; |
2882 } |
4372 } |
2883 |
4373 |
4374 /** |
|
4375 * Get's the style that is used for the specified line |
|
4376 * |
|
4377 * @param int The line number information is requested for |
|
4378 * @access private |
|
4379 * @since 1.0.7.21 |
|
4380 */ |
|
4381 function get_line_style($line) { |
|
4382 //$style = null; |
|
4383 $style = null; |
|
4384 if (isset($this->highlight_extra_lines_styles[$line])) { |
|
4385 $style = $this->highlight_extra_lines_styles[$line]; |
|
4386 } else { // if no "extra" style assigned |
|
4387 $style = $this->highlight_extra_lines_style; |
|
4388 } |
|
4389 |
|
4390 return $style; |
|
4391 } |
|
4392 |
|
4393 /** |
|
4394 * this functions creates an optimized regular expression list |
|
4395 * of an array of strings. |
|
4396 * |
|
4397 * Example: |
|
4398 * <code>$list = array('faa', 'foo', 'foobar'); |
|
4399 * => string 'f(aa|oo(bar)?)'</code> |
|
4400 * |
|
4401 * @param $list array of (unquoted) strings |
|
4402 * @param $regexp_delimiter your regular expression delimiter, @see preg_quote() |
|
4403 * @return string for regular expression |
|
4404 * @author Milian Wolff <mail@milianw.de> |
|
4405 * @since 1.0.8 |
|
4406 * @access private |
|
4407 */ |
|
4408 function optimize_regexp_list($list, $regexp_delimiter = '/') { |
|
4409 $regex_chars = array('.', '\\', '+', '*', '?', '[', '^', ']', '$', |
|
4410 '(', ')', '{', '}', '=', '!', '<', '>', '|', ':', $regexp_delimiter); |
|
4411 sort($list); |
|
4412 $regexp_list = array(''); |
|
4413 $num_subpatterns = 0; |
|
4414 $list_key = 0; |
|
4415 |
|
4416 // the tokens which we will use to generate the regexp list |
|
4417 $tokens = array(); |
|
4418 $prev_keys = array(); |
|
4419 // go through all entries of the list and generate the token list |
|
4420 $cur_len = 0; |
|
4421 for ($i = 0, $i_max = count($list); $i < $i_max; ++$i) { |
|
4422 if ($cur_len > GESHI_MAX_PCRE_LENGTH) { |
|
4423 // seems like the length of this pcre is growing exorbitantly |
|
4424 $regexp_list[++$list_key] = $this->_optimize_regexp_list_tokens_to_string($tokens); |
|
4425 $num_subpatterns = substr_count($regexp_list[$list_key], '(?:'); |
|
4426 $tokens = array(); |
|
4427 $cur_len = 0; |
|
4428 } |
|
4429 $level = 0; |
|
4430 $entry = preg_quote((string) $list[$i], $regexp_delimiter); |
|
4431 $pointer = &$tokens; |
|
4432 // properly assign the new entry to the correct position in the token array |
|
4433 // possibly generate smaller common denominator keys |
|
4434 while (true) { |
|
4435 // get the common denominator |
|
4436 if (isset($prev_keys[$level])) { |
|
4437 if ($prev_keys[$level] == $entry) { |
|
4438 // this is a duplicate entry, skip it |
|
4439 continue 2; |
|
4440 } |
|
4441 $char = 0; |
|
4442 while (isset($entry[$char]) && isset($prev_keys[$level][$char]) |
|
4443 && $entry[$char] == $prev_keys[$level][$char]) { |
|
4444 ++$char; |
|
4445 } |
|
4446 if ($char > 0) { |
|
4447 // this entry has at least some chars in common with the current key |
|
4448 if ($char == strlen($prev_keys[$level])) { |
|
4449 // current key is totally matched, i.e. this entry has just some bits appended |
|
4450 $pointer = &$pointer[$prev_keys[$level]]; |
|
4451 } else { |
|
4452 // only part of the keys match |
|
4453 $new_key_part1 = substr($prev_keys[$level], 0, $char); |
|
4454 $new_key_part2 = substr($prev_keys[$level], $char); |
|
4455 |
|
4456 if (in_array($new_key_part1[0], $regex_chars) |
|
4457 || in_array($new_key_part2[0], $regex_chars)) { |
|
4458 // this is bad, a regex char as first character |
|
4459 $pointer[$entry] = array('' => true); |
|
4460 array_splice($prev_keys, $level, count($prev_keys), $entry); |
|
4461 $cur_len += strlen($entry); |
|
4462 continue; |
|
4463 } else { |
|
4464 // relocate previous tokens |
|
4465 $pointer[$new_key_part1] = array($new_key_part2 => $pointer[$prev_keys[$level]]); |
|
4466 unset($pointer[$prev_keys[$level]]); |
|
4467 $pointer = &$pointer[$new_key_part1]; |
|
4468 // recreate key index |
|
4469 array_splice($prev_keys, $level, count($prev_keys), array($new_key_part1, $new_key_part2)); |
|
4470 $cur_len += strlen($new_key_part2); |
|
4471 } |
|
4472 } |
|
4473 ++$level; |
|
4474 $entry = substr($entry, $char); |
|
4475 continue; |
|
4476 } |
|
4477 // else: fall trough, i.e. no common denominator was found |
|
4478 } |
|
4479 if ($level == 0 && !empty($tokens)) { |
|
4480 // we can dump current tokens into the string and throw them away afterwards |
|
4481 $new_entry = $this->_optimize_regexp_list_tokens_to_string($tokens); |
|
4482 $new_subpatterns = substr_count($new_entry, '(?:'); |
|
4483 if (GESHI_MAX_PCRE_SUBPATTERNS && $num_subpatterns + $new_subpatterns > GESHI_MAX_PCRE_SUBPATTERNS) { |
|
4484 $regexp_list[++$list_key] = $new_entry; |
|
4485 $num_subpatterns = $new_subpatterns; |
|
4486 } else { |
|
4487 if (!empty($regexp_list[$list_key])) { |
|
4488 $new_entry = '|' . $new_entry; |
|
4489 } |
|
4490 $regexp_list[$list_key] .= $new_entry; |
|
4491 $num_subpatterns += $new_subpatterns; |
|
4492 } |
|
4493 $tokens = array(); |
|
4494 $cur_len = 0; |
|
4495 } |
|
4496 // no further common denominator found |
|
4497 $pointer[$entry] = array('' => true); |
|
4498 array_splice($prev_keys, $level, count($prev_keys), $entry); |
|
4499 |
|
4500 $cur_len += strlen($entry); |
|
4501 break; |
|
4502 } |
|
4503 unset($list[$i]); |
|
4504 } |
|
4505 // make sure the last tokens get converted as well |
|
4506 $new_entry = $this->_optimize_regexp_list_tokens_to_string($tokens); |
|
4507 if (GESHI_MAX_PCRE_SUBPATTERNS && $num_subpatterns + substr_count($new_entry, '(?:') > GESHI_MAX_PCRE_SUBPATTERNS) { |
|
4508 $regexp_list[++$list_key] = $new_entry; |
|
4509 } else { |
|
4510 if (!empty($regexp_list[$list_key])) { |
|
4511 $new_entry = '|' . $new_entry; |
|
4512 } |
|
4513 $regexp_list[$list_key] .= $new_entry; |
|
4514 } |
|
4515 return $regexp_list; |
|
4516 } |
|
4517 /** |
|
4518 * this function creates the appropriate regexp string of an token array |
|
4519 * you should not call this function directly, @see $this->optimize_regexp_list(). |
|
4520 * |
|
4521 * @param &$tokens array of tokens |
|
4522 * @param $recursed bool to know wether we recursed or not |
|
4523 * @return string |
|
4524 * @author Milian Wolff <mail@milianw.de> |
|
4525 * @since 1.0.8 |
|
4526 * @access private |
|
4527 */ |
|
4528 function _optimize_regexp_list_tokens_to_string(&$tokens, $recursed = false) { |
|
4529 $list = ''; |
|
4530 foreach ($tokens as $token => $sub_tokens) { |
|
4531 $list .= $token; |
|
4532 $close_entry = isset($sub_tokens['']); |
|
4533 unset($sub_tokens['']); |
|
4534 if (!empty($sub_tokens)) { |
|
4535 $list .= '(?:' . $this->_optimize_regexp_list_tokens_to_string($sub_tokens, true) . ')'; |
|
4536 if ($close_entry) { |
|
4537 // make sub_tokens optional |
|
4538 $list .= '?'; |
|
4539 } |
|
4540 } |
|
4541 $list .= '|'; |
|
4542 } |
|
4543 if (!$recursed) { |
|
4544 // do some optimizations |
|
4545 // common trailing strings |
|
4546 // BUGGY! |
|
4547 //$list = preg_replace_callback('#(?<=^|\:|\|)\w+?(\w+)(?:\|.+\1)+(?=\|)#', create_function( |
|
4548 // '$matches', 'return "(?:" . preg_replace("#" . preg_quote($matches[1], "#") . "(?=\||$)#", "", $matches[0]) . ")" . $matches[1];'), $list); |
|
4549 // (?:p)? => p? |
|
4550 $list = preg_replace('#\(\?\:(.)\)\?#', '\1?', $list); |
|
4551 // (?:a|b|c|d|...)? => [abcd...]? |
|
4552 // TODO: a|bb|c => [ac]|bb |
|
4553 static $callback_2; |
|
4554 if (!isset($callback_2)) { |
|
4555 $callback_2 = create_function('$matches', 'return "[" . str_replace("|", "", $matches[1]) . "]";'); |
|
4556 } |
|
4557 $list = preg_replace_callback('#\(\?\:((?:.\|)+.)\)#', $callback_2, $list); |
|
4558 } |
|
4559 // return $list without trailing pipe |
|
4560 return substr($list, 0, -1); |
|
4561 } |
|
2884 } // End Class GeSHi |
4562 } // End Class GeSHi |
2885 |
4563 |
2886 |
4564 |
2887 if (!function_exists('geshi_highlight')) { |
4565 if (!function_exists('geshi_highlight')) { |
2888 /** |
4566 /** |
2897 * @since 1.0.2 |
4575 * @since 1.0.2 |
2898 */ |
4576 */ |
2899 function geshi_highlight($string, $language, $path = null, $return = false) { |
4577 function geshi_highlight($string, $language, $path = null, $return = false) { |
2900 $geshi = new GeSHi($string, $language, $path); |
4578 $geshi = new GeSHi($string, $language, $path); |
2901 $geshi->set_header_type(GESHI_HEADER_NONE); |
4579 $geshi->set_header_type(GESHI_HEADER_NONE); |
4580 |
|
2902 if ($return) { |
4581 if ($return) { |
2903 return '<code>' . $geshi->parse_code() . '</code>'; |
4582 return '<code>' . $geshi->parse_code() . '</code>'; |
2904 } |
4583 } |
4584 |
|
2905 echo '<code>' . $geshi->parse_code() . '</code>'; |
4585 echo '<code>' . $geshi->parse_code() . '</code>'; |
4586 |
|
2906 if ($geshi->error()) { |
4587 if ($geshi->error()) { |
2907 return false; |
4588 return false; |
2908 } |
4589 } |
2909 return true; |
4590 return true; |
2910 } |
4591 } |