[FEATURE] Enable Syntax Highlighting 81/15881/1
authorBastian Bringenberg <mail@bastian-bringenberg.de>
Mon, 22 Oct 2012 12:44:48 +0000 (14:44 +0200)
committerBastian Bringenberg <mail@bastian-bringenberg.de>
Mon, 22 Oct 2012 12:44:48 +0000 (14:44 +0200)
Including GeSHi and rendering =).

Change-Id: I631b95fcdc6b60a03f61d065b0db1faa76eeb1ba
Fixes: #41505 @1h00

232 files changed:
Classes/Controller/PageController.php
Classes/Helper/RenderHelper.php
Classes/Helper/contrib/aliased.php [new file with mode: 0644]
Classes/Helper/contrib/cssgen.php [new file with mode: 0644]
Classes/Helper/contrib/cssgen2.php [new file with mode: 0644]
Classes/Helper/contrib/example.php [new file with mode: 0644]
Classes/Helper/contrib/langcheck.php [new file with mode: 0644]
Classes/Helper/contrib/langwiz.php [new file with mode: 0644]
Classes/Helper/geshi.php [new file with mode: 0644]
Classes/Helper/geshi/4cs.php [new file with mode: 0644]
Classes/Helper/geshi/6502acme.php [new file with mode: 0644]
Classes/Helper/geshi/6502kickass.php [new file with mode: 0644]
Classes/Helper/geshi/6502tasm.php [new file with mode: 0644]
Classes/Helper/geshi/68000devpac.php [new file with mode: 0644]
Classes/Helper/geshi/abap.php [new file with mode: 0644]
Classes/Helper/geshi/actionscript.php [new file with mode: 0644]
Classes/Helper/geshi/actionscript3.php [new file with mode: 0644]
Classes/Helper/geshi/ada.php [new file with mode: 0644]
Classes/Helper/geshi/algol68.php [new file with mode: 0644]
Classes/Helper/geshi/apache.php [new file with mode: 0644]
Classes/Helper/geshi/applescript.php [new file with mode: 0644]
Classes/Helper/geshi/apt_sources.php [new file with mode: 0644]
Classes/Helper/geshi/arm.php [new file with mode: 0644]
Classes/Helper/geshi/asm.php [new file with mode: 0644]
Classes/Helper/geshi/asp.php [new file with mode: 0644]
Classes/Helper/geshi/asymptote.php [new file with mode: 0644]
Classes/Helper/geshi/autoconf.php [new file with mode: 0644]
Classes/Helper/geshi/autohotkey.php [new file with mode: 0644]
Classes/Helper/geshi/autoit.php [new file with mode: 0644]
Classes/Helper/geshi/avisynth.php [new file with mode: 0644]
Classes/Helper/geshi/awk.php [new file with mode: 0644]
Classes/Helper/geshi/bascomavr.php [new file with mode: 0644]
Classes/Helper/geshi/bash.php [new file with mode: 0644]
Classes/Helper/geshi/basic4gl.php [new file with mode: 0644]
Classes/Helper/geshi/bf.php [new file with mode: 0644]
Classes/Helper/geshi/bibtex.php [new file with mode: 0644]
Classes/Helper/geshi/blitzbasic.php [new file with mode: 0644]
Classes/Helper/geshi/bnf.php [new file with mode: 0644]
Classes/Helper/geshi/boo.php [new file with mode: 0644]
Classes/Helper/geshi/c.php [new file with mode: 0644]
Classes/Helper/geshi/c_loadrunner.php [new file with mode: 0644]
Classes/Helper/geshi/c_mac.php [new file with mode: 0644]
Classes/Helper/geshi/caddcl.php [new file with mode: 0644]
Classes/Helper/geshi/cadlisp.php [new file with mode: 0644]
Classes/Helper/geshi/cfdg.php [new file with mode: 0644]
Classes/Helper/geshi/cfm.php [new file with mode: 0644]
Classes/Helper/geshi/chaiscript.php [new file with mode: 0644]
Classes/Helper/geshi/cil.php [new file with mode: 0644]
Classes/Helper/geshi/clojure.php [new file with mode: 0644]
Classes/Helper/geshi/cmake.php [new file with mode: 0644]
Classes/Helper/geshi/cobol.php [new file with mode: 0644]
Classes/Helper/geshi/coffeescript.php [new file with mode: 0644]
Classes/Helper/geshi/cpp-qt.php [new file with mode: 0644]
Classes/Helper/geshi/cpp.php [new file with mode: 0644]
Classes/Helper/geshi/csharp.php [new file with mode: 0644]
Classes/Helper/geshi/css.php [new file with mode: 0644]
Classes/Helper/geshi/cuesheet.php [new file with mode: 0644]
Classes/Helper/geshi/d.php [new file with mode: 0644]
Classes/Helper/geshi/dcl.php [new file with mode: 0644]
Classes/Helper/geshi/dcpu16.php [new file with mode: 0644]
Classes/Helper/geshi/dcs.php [new file with mode: 0644]
Classes/Helper/geshi/delphi.php [new file with mode: 0644]
Classes/Helper/geshi/diff.php [new file with mode: 0644]
Classes/Helper/geshi/div.php [new file with mode: 0644]
Classes/Helper/geshi/dos.php [new file with mode: 0644]
Classes/Helper/geshi/dot.php [new file with mode: 0644]
Classes/Helper/geshi/e.php [new file with mode: 0644]
Classes/Helper/geshi/ecmascript.php [new file with mode: 0644]
Classes/Helper/geshi/eiffel.php [new file with mode: 0644]
Classes/Helper/geshi/email.php [new file with mode: 0644]
Classes/Helper/geshi/epc.php [new file with mode: 0644]
Classes/Helper/geshi/erlang.php [new file with mode: 0644]
Classes/Helper/geshi/euphoria.php [new file with mode: 0644]
Classes/Helper/geshi/f1.php [new file with mode: 0644]
Classes/Helper/geshi/falcon.php [new file with mode: 0644]
Classes/Helper/geshi/fo.php [new file with mode: 0644]
Classes/Helper/geshi/fortran.php [new file with mode: 0644]
Classes/Helper/geshi/freebasic.php [new file with mode: 0644]
Classes/Helper/geshi/freeswitch.php [new file with mode: 0644]
Classes/Helper/geshi/fsharp.php [new file with mode: 0644]
Classes/Helper/geshi/gambas.php [new file with mode: 0644]
Classes/Helper/geshi/gdb.php [new file with mode: 0644]
Classes/Helper/geshi/genero.php [new file with mode: 0644]
Classes/Helper/geshi/genie.php [new file with mode: 0644]
Classes/Helper/geshi/gettext.php [new file with mode: 0644]
Classes/Helper/geshi/glsl.php [new file with mode: 0644]
Classes/Helper/geshi/gml.php [new file with mode: 0644]
Classes/Helper/geshi/gnuplot.php [new file with mode: 0644]
Classes/Helper/geshi/go.php [new file with mode: 0644]
Classes/Helper/geshi/groovy.php [new file with mode: 0644]
Classes/Helper/geshi/gwbasic.php [new file with mode: 0644]
Classes/Helper/geshi/haskell.php [new file with mode: 0644]
Classes/Helper/geshi/haxe.php [new file with mode: 0644]
Classes/Helper/geshi/hicest.php [new file with mode: 0644]
Classes/Helper/geshi/hq9plus.php [new file with mode: 0644]
Classes/Helper/geshi/html4strict.php [new file with mode: 0644]
Classes/Helper/geshi/html5.php [new file with mode: 0644]
Classes/Helper/geshi/icon.php [new file with mode: 0644]
Classes/Helper/geshi/idl.php [new file with mode: 0644]
Classes/Helper/geshi/ini.php [new file with mode: 0644]
Classes/Helper/geshi/inno.php [new file with mode: 0644]
Classes/Helper/geshi/intercal.php [new file with mode: 0644]
Classes/Helper/geshi/io.php [new file with mode: 0644]
Classes/Helper/geshi/j.php [new file with mode: 0644]
Classes/Helper/geshi/java.php [new file with mode: 0644]
Classes/Helper/geshi/java5.php [new file with mode: 0644]
Classes/Helper/geshi/javascript.php [new file with mode: 0644]
Classes/Helper/geshi/jquery.php [new file with mode: 0644]
Classes/Helper/geshi/kixtart.php [new file with mode: 0644]
Classes/Helper/geshi/klonec.php [new file with mode: 0644]
Classes/Helper/geshi/klonecpp.php [new file with mode: 0644]
Classes/Helper/geshi/latex.php [new file with mode: 0644]
Classes/Helper/geshi/lb.php [new file with mode: 0644]
Classes/Helper/geshi/ldif.php [new file with mode: 0644]
Classes/Helper/geshi/lisp.php [new file with mode: 0644]
Classes/Helper/geshi/llvm.php [new file with mode: 0644]
Classes/Helper/geshi/locobasic.php [new file with mode: 0644]
Classes/Helper/geshi/logtalk.php [new file with mode: 0644]
Classes/Helper/geshi/lolcode.php [new file with mode: 0644]
Classes/Helper/geshi/lotusformulas.php [new file with mode: 0644]
Classes/Helper/geshi/lotusscript.php [new file with mode: 0644]
Classes/Helper/geshi/lscript.php [new file with mode: 0644]
Classes/Helper/geshi/lsl2.php [new file with mode: 0644]
Classes/Helper/geshi/lua.php [new file with mode: 0644]
Classes/Helper/geshi/m68k.php [new file with mode: 0644]
Classes/Helper/geshi/magiksf.php [new file with mode: 0644]
Classes/Helper/geshi/make.php [new file with mode: 0644]
Classes/Helper/geshi/mapbasic.php [new file with mode: 0644]
Classes/Helper/geshi/matlab.php [new file with mode: 0644]
Classes/Helper/geshi/mirc.php [new file with mode: 0644]
Classes/Helper/geshi/mmix.php [new file with mode: 0644]
Classes/Helper/geshi/modula2.php [new file with mode: 0644]
Classes/Helper/geshi/modula3.php [new file with mode: 0644]
Classes/Helper/geshi/mpasm.php [new file with mode: 0644]
Classes/Helper/geshi/mxml.php [new file with mode: 0644]
Classes/Helper/geshi/mysql.php [new file with mode: 0644]
Classes/Helper/geshi/nagios.php [new file with mode: 0644]
Classes/Helper/geshi/netrexx.php [new file with mode: 0644]
Classes/Helper/geshi/newlisp.php [new file with mode: 0644]
Classes/Helper/geshi/nsis.php [new file with mode: 0644]
Classes/Helper/geshi/oberon2.php [new file with mode: 0644]
Classes/Helper/geshi/objc.php [new file with mode: 0644]
Classes/Helper/geshi/objeck.php [new file with mode: 0644]
Classes/Helper/geshi/ocaml-brief.php [new file with mode: 0644]
Classes/Helper/geshi/ocaml.php [new file with mode: 0644]
Classes/Helper/geshi/octave.php [new file with mode: 0644]
Classes/Helper/geshi/oobas.php [new file with mode: 0644]
Classes/Helper/geshi/oorexx.php [new file with mode: 0644]
Classes/Helper/geshi/oracle11.php [new file with mode: 0644]
Classes/Helper/geshi/oracle8.php [new file with mode: 0644]
Classes/Helper/geshi/oxygene.php [new file with mode: 0644]
Classes/Helper/geshi/oz.php [new file with mode: 0644]
Classes/Helper/geshi/parasail.php [new file with mode: 0644]
Classes/Helper/geshi/parigp.php [new file with mode: 0644]
Classes/Helper/geshi/pascal.php [new file with mode: 0644]
Classes/Helper/geshi/pcre.php [new file with mode: 0644]
Classes/Helper/geshi/per.php [new file with mode: 0644]
Classes/Helper/geshi/perl.php [new file with mode: 0644]
Classes/Helper/geshi/perl6.php [new file with mode: 0644]
Classes/Helper/geshi/pf.php [new file with mode: 0644]
Classes/Helper/geshi/php-brief.php [new file with mode: 0644]
Classes/Helper/geshi/php.php [new file with mode: 0644]
Classes/Helper/geshi/pic16.php [new file with mode: 0644]
Classes/Helper/geshi/pike.php [new file with mode: 0644]
Classes/Helper/geshi/pixelbender.php [new file with mode: 0644]
Classes/Helper/geshi/pli.php [new file with mode: 0644]
Classes/Helper/geshi/plsql.php [new file with mode: 0644]
Classes/Helper/geshi/postgresql.php [new file with mode: 0644]
Classes/Helper/geshi/povray.php [new file with mode: 0644]
Classes/Helper/geshi/powerbuilder.php [new file with mode: 0644]
Classes/Helper/geshi/powershell.php [new file with mode: 0644]
Classes/Helper/geshi/proftpd.php [new file with mode: 0644]
Classes/Helper/geshi/progress.php [new file with mode: 0644]
Classes/Helper/geshi/prolog.php [new file with mode: 0644]
Classes/Helper/geshi/properties.php [new file with mode: 0644]
Classes/Helper/geshi/providex.php [new file with mode: 0644]
Classes/Helper/geshi/purebasic.php [new file with mode: 0644]
Classes/Helper/geshi/pycon.php [new file with mode: 0644]
Classes/Helper/geshi/pys60.php [new file with mode: 0644]
Classes/Helper/geshi/python.php [new file with mode: 0644]
Classes/Helper/geshi/q.php [new file with mode: 0644]
Classes/Helper/geshi/qbasic.php [new file with mode: 0644]
Classes/Helper/geshi/rails.php [new file with mode: 0644]
Classes/Helper/geshi/rebol.php [new file with mode: 0644]
Classes/Helper/geshi/reg.php [new file with mode: 0644]
Classes/Helper/geshi/rexx.php [new file with mode: 0644]
Classes/Helper/geshi/robots.php [new file with mode: 0644]
Classes/Helper/geshi/rpmspec.php [new file with mode: 0644]
Classes/Helper/geshi/rsplus.php [new file with mode: 0644]
Classes/Helper/geshi/ruby.php [new file with mode: 0644]
Classes/Helper/geshi/sas.php [new file with mode: 0644]
Classes/Helper/geshi/scala.php [new file with mode: 0644]
Classes/Helper/geshi/scheme.php [new file with mode: 0644]
Classes/Helper/geshi/scilab.php [new file with mode: 0644]
Classes/Helper/geshi/sdlbasic.php [new file with mode: 0644]
Classes/Helper/geshi/smalltalk.php [new file with mode: 0644]
Classes/Helper/geshi/smarty.php [new file with mode: 0644]
Classes/Helper/geshi/spark.php [new file with mode: 0644]
Classes/Helper/geshi/sparql.php [new file with mode: 0644]
Classes/Helper/geshi/sql.php [new file with mode: 0644]
Classes/Helper/geshi/stonescript.php [new file with mode: 0644]
Classes/Helper/geshi/systemverilog.php [new file with mode: 0644]
Classes/Helper/geshi/tcl.php [new file with mode: 0644]
Classes/Helper/geshi/teraterm.php [new file with mode: 0644]
Classes/Helper/geshi/text.php [new file with mode: 0644]
Classes/Helper/geshi/thinbasic.php [new file with mode: 0644]
Classes/Helper/geshi/tsql.php [new file with mode: 0644]
Classes/Helper/geshi/typoscript.php [new file with mode: 0644]
Classes/Helper/geshi/unicon.php [new file with mode: 0644]
Classes/Helper/geshi/upc.php [new file with mode: 0644]
Classes/Helper/geshi/urbi.php [new file with mode: 0644]
Classes/Helper/geshi/uscript.php [new file with mode: 0644]
Classes/Helper/geshi/vala.php [new file with mode: 0644]
Classes/Helper/geshi/vb.php [new file with mode: 0644]
Classes/Helper/geshi/vbnet.php [new file with mode: 0644]
Classes/Helper/geshi/vedit.php [new file with mode: 0644]
Classes/Helper/geshi/verilog.php [new file with mode: 0644]
Classes/Helper/geshi/vhdl.php [new file with mode: 0644]
Classes/Helper/geshi/vim.php [new file with mode: 0644]
Classes/Helper/geshi/visualfoxpro.php [new file with mode: 0644]
Classes/Helper/geshi/visualprolog.php [new file with mode: 0644]
Classes/Helper/geshi/whitespace.php [new file with mode: 0644]
Classes/Helper/geshi/whois.php [new file with mode: 0644]
Classes/Helper/geshi/winbatch.php [new file with mode: 0644]
Classes/Helper/geshi/xbasic.php [new file with mode: 0644]
Classes/Helper/geshi/xml.php [new file with mode: 0644]
Classes/Helper/geshi/xorg_conf.php [new file with mode: 0644]
Classes/Helper/geshi/xpp.php [new file with mode: 0644]
Classes/Helper/geshi/yaml.php [new file with mode: 0644]
Classes/Helper/geshi/z80.php [new file with mode: 0644]
Classes/Helper/geshi/zxbasic.php [new file with mode: 0644]
Configuration/FlexForms/flexform_typo3wiki.xml

index 585e6c3..7444ebf 100755 (executable)
@@ -57,7 +57,7 @@ class Tx_Typo3wiki_Controller_PageController extends Tx_Extbase_MVC_Controller_A
         * @return void
         */
        public function indexAction() {
-               $this->redirect('show', NULL, NULL, array('page' => $this->settings['indexPageTitle']));
+               $this->forward('show', NULL, NULL, array('page' => $this->settings['indexPageTitle']));
        }
 
        /**
@@ -72,7 +72,7 @@ class Tx_Typo3wiki_Controller_PageController extends Tx_Extbase_MVC_Controller_A
                if($page === NULL ) $page = $this->pageRepository->findOneByPageTitle($this->request->getArgument('page'));
                if($page === NULL || $page->getMainRevision() === NULL){
                        if($page === NULL ){
-                               $target = $this->request->getArgument('page');
+                               $this->request->getArgument('page');
                        }else{
                                $target = $page->getPageTitle();
                        }
index 2ec211a..f127762 100644 (file)
@@ -32,6 +32,7 @@
         */
        // @todo order Methods
        require(__DIR__ . '/markdown.php');
+       require(__DIR__ . '/geshi.php');
        class Tx_Typo3wiki_Helper_RenderHelper extends MarkdownExtra_Parser {
 
                /**
                        $text = $this->_renderInternalLinks($text);
                        $text = $this->_renderHeadlineLinks($text);
                        $text = $this->_renderContentList($text);
+                       $text = $this->_renderCodeHighlighting($text);
                        return $text;
                }
 
                                $catPage = $this->createPageIfNotExists($cat);
                                if(!$catPage->getCategoryPages()->contains($this->relatedPage)){
                                        $catPage->addCategoryPage($this->relatedPage);
-                                       $catPage->setIsCategory(true);
+                                       $catPage->setIsCategory(TRUE);
                                }
                                $this->helper[$cat] = TRUE;
                        }
                }
 
                /**
+                * Method for SyntaxHighlighting
+                *
+                * @param string $text
+                * @return string
+                */
+               private function _renderCodeHighlighting($text){
+                       $text = preg_replace_callback('/<p>LanG:\s*(.*?)<\/p>.*?<pre>.?<code>(.*?)<\/code>.?<\/pre>/ism', array( $this, '_renderCodeHighlighting_Helper'), $text);
+                       return $text;
+               }
+
+               /**
+                * Helper Method for Syntax Highlighting
+                *
+                * @param string $text
+                * @return string
+                */
+               private function _renderCodeHighlighting_Helper($text){
+                       $geshi = new GeSHi(htmlspecialchars_decode($text[2]), $text[1]);
+                       $geshi->enable_classes();
+                       $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS);
+                       return '<style type="text/css" scoped="scoped">'.$geshi->get_stylesheet().'</style><pre>'.$geshi->parse_code().'</pre>';
+               }
+
+               /**
                 * Create Stand Alone Viewhelper
                 *
                 * @param string $templateName
diff --git a/Classes/Helper/contrib/aliased.php b/Classes/Helper/contrib/aliased.php
new file mode 100644 (file)
index 0000000..cee3128
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+/**
+ * Another GeSHi example script
+ *
+ * Configure your Apache server with 'AcceptPathInfo true' and something like
+ * 'Alias /viewmysource /var/www/geshi/contrib/aliased.php'. Don't forget
+ * to protect this alias as necessary.
+ *
+ * Usage - visit /viewmysource/file.name.ext to see that file with syntax
+ * highlighting, where "viewmysource" is the name of the alias you set up.
+ * You can use this without an alias too, just by visiting
+ * aliased.php/file.name.ext.
+ *
+ * @author  Ross Golder <ross@golder.org>
+ * @version $Id: aliased.php 2533 2012-08-15 18:49:04Z benbe $
+ */
+
+// Your config here
+define("SOURCE_ROOT", "/var/www/your/source/root/");
+
+// Assume you've put geshi in the include_path already
+require_once("geshi.php");
+
+// Get path info
+$path = SOURCE_ROOT.$_SERVER['PATH_INFO'];
+
+// Check for dickheads trying to use '../' to get to sensitive areas
+$base_path_len = strlen(SOURCE_ROOT);
+$real_path = realpath($path);
+if(strncmp($real_path, SOURCE_ROOT, $base_path_len)) {
+    exit("Access outside acceptable path.");
+}
+
+// Check file exists
+if(!file_exists($path)) {
+    exit("File not found ($path).");
+}
+
+// Prepare GeSHi instance
+$geshi = new GeSHi();
+$geshi->set_language('text');
+$geshi->load_from_file($path);
+$geshi->set_header_type(GESHI_HEADER_PRE);
+$geshi->enable_classes();
+$geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 10);
+$geshi->set_overall_style('color: #000066; border: 1px solid #d0d0d0; background-color: #f0f0f0;', true);
+$geshi->set_line_style('font: normal normal 95% \'Courier New\', Courier, monospace; color: #003030;', 'font-weight: bold; color: #006060;', true);
+$geshi->set_code_style('color: #000020;', 'color: #000020;');
+$geshi->set_link_styles(GESHI_LINK, 'color: #000060;');
+$geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;');
+$geshi->set_header_content('Source code viewer - ' . $path . ' - ' . $geshi->get_language_name());
+$geshi->set_header_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;');
+$geshi->set_footer_content('Parsed in <TIME> seconds,  using GeSHi <VERSION>');
+$geshi->set_footer_content_style('font-family: Verdana, Arial, sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-top: 1px solid #d0d0d0; padding: 2px;');
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>Source code viewer - <?php echo $path; ?> - <?php $geshi->get_language_name(); ?></title>
+    <style type="text/css">
+    <!--
+    <?php
+        // Output the stylesheet. Note it doesn't output the <style> tag
+    echo $geshi->get_stylesheet();
+    ?>
+    html {
+        background-color: #f0f0f0;
+    }
+    body {
+        font-family: Verdana, Arial, sans-serif;
+        margin: 10px;
+        border: 2px solid #e0e0e0;
+        background-color: #fcfcfc;
+        padding: 5px;
+    }
+    h2 {
+        margin: .1em 0 .2em .5em;
+        border-bottom: 1px solid #b0b0b0;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 150%;
+    }
+    h3 {
+        margin: .1em 0 .2em .5em;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 120%;
+    }
+    #footer {
+        text-align: center;
+        font-size: 80%;
+        color: #a9a9a9;
+    }
+    #footer a {
+        color: #9999ff;
+    }
+    textarea {
+        border: 1px solid #b0b0b0;
+        font-size: 90%;
+        color: #333;
+        margin-left: 20px;
+    }
+    select, input {
+        margin-left: 20px;
+    }
+    p {
+        font-size: 90%;
+        margin-left: .5em;
+    }
+    -->
+    </style>
+</head>
+<body>
+<?php
+// The fun part :)
+echo $geshi->parse_code();
+?>
+<hr/>
+</body>
+</html>
diff --git a/Classes/Helper/contrib/cssgen.php b/Classes/Helper/contrib/cssgen.php
new file mode 100644 (file)
index 0000000..cc71b58
--- /dev/null
@@ -0,0 +1,466 @@
+<?php
+/*************************************************************************************
+ * cssgen.php
+ * ----------
+ * Author: Nigel McNie (nigel@geshi.org)
+ * Copyright: (c) 2004 Nigel McNie
+ * Release Version: 1.0.8.6
+ * Date Started: 2004/05/20
+ *
+ * Application to generate custom CSS files for GeSHi (based on an idea by Andreas
+ * Gohr)
+ *
+ *************************************************************************************
+ *
+ *     This file is part of GeSHi.
+ *
+ *   GeSHi is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   GeSHi is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with GeSHi; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ************************************************************************************/
+
+set_magic_quotes_runtime(0);
+//
+// Functions
+//
+
+function make_header ( $title )
+{
+    echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>GeSHi CSS Generator :: ' . $title . ' </title>
+    <style type="text/css" media="screen">
+    <!--
+        html {
+            font-family: Verdana, Arial, sans-serif;
+            font-size: 80%;
+            background-color: #d0d0d0;
+        }
+        body {
+            margin: 10px;
+            padding: 5px;
+            border: 1px solid #f0f0f0;
+            background-color: #f6f6f6;
+        }
+        h1 {
+            border-bottom: 2px solid #e0e0e0;
+            font-weight: normal;
+            font-size: 150%;
+            color: #c0c0c0;
+        }
+        input, textarea {
+            border: 1px solid #d0d0d0;
+        }
+        th {
+            text-align: right;
+            font-weight: normal;
+        }
+        pre {
+            font-size: 110%;
+            color: #202020;
+        }
+        #footer {
+            color: #b0b0b0;
+            text-align: center;
+            font-size: 90%;
+            margin: 0 auto;
+            border-top: 1px solid #e0e0e0;
+        }
+        #footer a {
+            color: #c0c0c0;
+        }
+    -->
+    </style>
+    <script type="text/javascript">
+    function select (state)
+    {
+        var cboxes = document.getElementsByTagName(\'input\');
+        for (var i = 0; i < cboxes.length; i++) {
+            if (cboxes[i].type == "checkbox") {
+                if (state == "true") {
+                    cboxes[i].checked = true;
+                } elseif (state == "false") {
+                    cboxes[i].checked = false;
+                } elseif (state == "invert") {
+                    cboxes[i].checked = !cboxes[i].checked;
+                }
+            }
+        }
+    }
+    </script>
+</head>
+<body>
+<h1>' . $title . '</h1>
+';
+}
+
+function make_footer ()
+{
+    echo '<div id="footer"><a href="http://qbnz.com/highlighter/">GeSHi</a> &copy; Nigel McNie, 2004, released under the GPL</div></body>
+</html>';
+}
+
+
+function get_var ( $var_name )
+{
+    if ( isset($_GET[$var_name]) )
+    {
+        return str_replace("\'", "'", $_GET[$var_name]);
+    }
+    elseif ( isset($_POST[$var_name]) )
+    {
+        return str_replace("\'", "'", $_POST[$var_name]);
+    }
+    return null;
+}
+
+
+
+//
+// Unset everything
+//
+foreach ( $_REQUEST as $var )
+{
+    unset($$var);
+}
+foreach ( array(
+    '_POST' => 'HTTP_POST_VARS',
+    '_GET' => 'HTTP_GET_VARS',
+    '_COOKIE' => 'HTTP_COOKIE_VARS',
+    '_SERVER' => 'HTTP_SERVER_VARS',
+    '_ENV' => 'HTTP_ENV_VARS',
+    '_FILES' => 'HTTP_POST_FILES')  as $array => $other )
+{
+    if ( !isset($$array) )
+    {
+        $$array = $$other;
+    }
+    unset($$other);
+}
+
+
+// Get what step we're up to
+$step = get_var('step');
+
+if ( !$step || $step == 1 )
+{
+    $errors = 0;
+    make_header('Step 1');
+    echo "Welcome to the GeSHi CSS generator.<br /><pre>Searching for GeSHi...          ";
+
+    // Find GeSHi
+    $geshi_path = get_var('geshi-path');
+    $geshi_lang_path = get_var('geshi-lang-path');
+
+    if(strstr($geshi_path, '..')) {
+        unset($geshi_path);
+    }
+    if(strstr($geshi_lang_path, '..')) {
+        unset($geshi_lang_path);
+    }
+
+    if ( !$geshi_path )
+    {
+        $geshi_path = '../geshi.php';
+    }
+    if ( !$geshi_lang_path )
+    {
+        $geshi_lang_path = '../geshi/';
+    }
+
+    if ( is_file($geshi_path) && is_readable($geshi_path) )
+    {
+        // Get file contents and see if GeSHi is in here
+        $file = @file($geshi_path);
+        $contents = '';
+        foreach ( $file as $line )
+        {
+            $contents .= $line;
+        }
+        if ( strpos($contents, '<?php
+/**
+ * GeSHi - Generic Syntax Highlighter') !== false )
+         {
+            echo '<span style="color: green;">Found at ' . realpath($geshi_path) . '</span>';
+        }
+        else
+        {
+            ++$errors;
+            $no_geshi_dot_php_error = true;
+            echo '<span style="color: red;">Not found</span>';
+        }
+    }
+    else
+    {
+        ++$errors;
+        $no_geshi_dot_php_error = true;
+        echo '<span style="color: red;">Not found</span>';
+    }
+
+    // Find language files
+    echo "\nSearching for language files... ";
+    if ( is_readable($geshi_lang_path . 'css-gen.cfg') )
+    {
+
+        echo '<span style="color: green;">Found at ' . realpath($geshi_lang_path) . '</span>';
+    }
+    else
+    {
+        ++$errors;
+        $no_lang_dir_error = true;
+        echo '<span style="color: red;">Not found</span>';
+    }
+    echo "</pre>\n";
+
+    if ( $errors > 0 )
+    {
+        // We're gonna have to ask for the paths...
+        echo 'Unfortunately CSSGen could not detect the following paths. Please input them and press &quot;submit&quot; to try again.';
+        echo "
+<form action=\"cssgen.php\" method=\"post\">";
+        if ( $no_geshi_dot_php_error )
+        {
+            echo "
+<br />geshi.php: <input type=\"text\" name=\"geshi-path\" value=\"" . realpath('../geshi.php') . "\" size=\"50\" />";
+        }
+        else
+        {
+            echo '<input type="hidden" name="geshi-path" value="' . htmlspecialchars($geshi_path) . '" />';
+        }
+        if ( $no_lang_dir_error )
+        {
+            echo "
+<br />language files directory: <input type=\"text\" name=\"geshi-lang-path\" value=\"" . realpath('../geshi/') . "/\" size=\"50\" /> (should have a trailing slash)";
+        }
+        else
+        {
+            echo '<input type="hidden" name="geshi-lang-path" value="' . $geshi_lang_path . '" />';
+        }
+
+        echo "
+<br /><input type=\"submit\" value=\"Search\" /></form>";
+    }
+    else
+    {
+        // no errors - echo continue form
+        echo 'Everything seems to be detected successfully. Use the button to continue.
+<br /><br /><form action="cssgen.php?step=2" method="post">
+<input type="hidden" name="geshi-path" value="' . realpath($geshi_path) . '" /><input type="hidden" name="geshi-lang-path" value="' . realpath($geshi_lang_path) . '" />
+<input type="submit" value="Step 2" />';
+    }
+
+    make_footer();
+}
+// Step 2
+elseif ( $step == 2 )
+{
+    make_header('Step 2');
+
+    $geshi_path = get_var('geshi-path');
+    $geshi_lang_path = get_var('geshi-lang-path');
+
+    $dh = opendir($geshi_lang_path);
+    $lang_files = array();
+    $file = readdir($dh);
+    while ( $file !== false )
+    {
+        if ( $file == '.' || $file == '..' || $file == 'CVS' || $file == 'css-gen.cfg' )
+        {
+            $file = readdir($dh);
+            continue;
+        }
+        if(!strstr(file_get_contents($dh . DIRECTORY_SEPARATOR . $file), '$language_data')) {
+            $file = readdir($dh);
+            continue;
+        }
+        $lang_files[] = $file;
+        $file = readdir($dh);
+    }
+    closedir($dh);
+    sort($lang_files);
+
+    // Now installed languages are in $lang_files
+
+    echo '<form action="cssgen.php?step=3" method="post" id="step2">
+What languages are you wanting to make this stylesheet for?<br /><br />
+Detected languages:<br />';
+
+    foreach ( $lang_files as $lang )
+    {
+        $lang = substr($lang, 0, strpos($lang, '.'));
+        if ($lang) {
+            echo "<input type=\"checkbox\" name=\"langs[$lang]\" checked=\"checked\" />&nbsp;$lang<br />\n";
+        }
+    }
+
+    echo "Select: <a href=\"javascript:select('true')\">All</a>, <a href=\"javascript:select('false')\">None</a>, <a href=\"javascript:select('invert')\">Invert</a><br />\n";
+
+    echo 'If you\'d like any other languages not detected here to be supported, please enter
+them here, one per line:<br /><textarea rows="4" cols="20" name="extra-langs"></textarea><br />
+';
+
+    echo '<br />Styles:
+<table>
+    <tr><th>Style for the overall code block:</th><td><input type="text" name="overall" value="border: 1px dotted #a0a0a0; font-family: \'Courier New\', Courier, monospace; background-color: #f0f0f0; color: #0000bb;" /></td></tr>
+    <tr><th>Default Styles</th><td><input type="text" name="default-styles" value="font-weight:normal;background:transparent;color:#000; padding-left: 5px;" /></td></tr>
+    <tr><th>Keywords I (if, do, while etc)</th><td><input type="text" name="keywords-1" value="color: #a1a100;" /></td></tr>
+    <tr><th>Keywords II (null, true, false etc)</th><td><input type="text" name="keywords-2" value="color: #000; font-weight: bold;" /></td></tr>
+    <tr><th>Inbuilt Functions (echo, print etc)</th><td><input type="text" name="keywords-3" value="color: #000066;" /></td></tr>
+    <tr><th>Data Types (int, boolean etc)</th><td><input type="text" name="keywords-4" value="color: #f63333;" /></td></tr>
+
+    <tr><th>Comments (//, <!--  --> etc)</th><td><input type="text" name="comments" value="color: #808080;" /></td></tr>
+    <tr><th>Escaped Characters (\n, \t etc)</th><td><input type="text" name="escaped-chars" value="color: #000033; font-weight: bold;" /></td></tr>
+    <tr><th>Brackets ( ([{}]) etc)</th><td><input type="text" name="brackets" value="color: #66cc66;" /></td></tr>
+    <tr><th>Strings ("foo" etc)</th><td><input type="text" name="strings" value="color: #ff0000;" /></td></tr>
+    <tr><th>Numbers (1, -54, 2.5 etc)</th><td><input type="text" name="numbers" value="color: #ff33ff;" /></td></tr>
+    <tr><th>Methods (Foo.bar() etc)</th><td><input type="text" name="methods" value="color: #006600;" /></td></tr>
+</table>';
+
+    echo '<input type="hidden" name="geshi-path" value="' . realpath($geshi_path) . '" /><input type="hidden" name="geshi-lang-path" value="' . realpath($geshi_lang_path) . '" />
+<input type="submit" value="Step 3" /></form>';
+
+    make_footer();
+}
+// Step 3
+elseif ( $step == 3 )
+{
+    make_header('Step 3');
+    echo '<p>Here is your completed stylesheet. Note that it may not be perfect - no regular expression styles are included for one thing,
+you\'ll have to add those yourself (php and xml are just two languages that use them), and line numbers are not included, however
+it includes most of the basic information.</p>';
+
+    // Make the stylesheet
+    $part_selector_1 = '';
+    $part_selector_2 = '';
+    $part_selector_3 = '';
+
+    $langs = get_var('langs');
+    $extra_langs = trim(get_var('extra-langs'));
+    if ( $extra_langs != '' )
+    {
+        $l = explode("\r\n", $extra_langs);
+        foreach ( $l as $lng )
+        {
+            $langs[$lng] = true;
+        }
+    }
+
+
+    foreach ( $langs as $lang => $dummy )
+    {
+        $part_selector_1 .= ".$lang {PART}, ";
+        $part_selector_2 .= ".$lang {PART1}, .$lang {PART2}, ";
+        $part_selector_3 .= ".$lang {PART1}, .$lang {PART2}, .$lang {PART3}, ";
+    }
+    $part_selector_1 = substr($part_selector_1, 0, -2);
+    $part_selector_2 = substr($part_selector_2, 0, -2);
+    $part_selector_3 = substr($part_selector_3, 0, -2);
+
+
+    $default_styles = get_var('default-styles');
+    $ol_selector = str_replace('{PART}', 'ol', $part_selector_1);
+    $overall_styles = get_var('overall');
+    $overall_selector = str_replace('{PART}', '', $part_selector_1);
+
+    $stylesheet = "/* GeSHi (c) Nigel McNie 2004 (http://qbnz.com/highlighter) */";
+
+    if ( $overall != '' )
+    {
+        $stylesheet .= "\n$overall_selector {{$overall_styles}}";
+    }
+    if ( $default_styles != '' )
+    {
+        $default_selector = str_replace(array('{PART1}', '{PART2}'), array('.de1', '.de2'), $part_selector_2);
+        $stylesheet .= "\n$default_selector {{$default_styles}}";
+    }
+
+    // Do keywords
+    $keywords_1 = get_var('keywords-1');
+    $keyword_selector_1 = str_replace('{PART}', '.kw1', $part_selector_1);
+    if ( $keywords_1 != '' )
+    {
+        $stylesheet .= "\n$keyword_selector_1 {{$keywords_1}}";
+    }
+
+    $keywords_2 = get_var('keywords-2');
+    $keyword_selector_2 = str_replace('{PART}', '.kw2', $part_selector_1);
+    if ( $keywords_2 != '' )
+    {
+        $stylesheet .= "\n$keyword_selector_2 {{$keywords_2}}";
+    }
+
+    $keywords_3 = get_var('keywords-3');
+    $keyword_selector_3 = str_replace('{PART}', '.kw3', $part_selector_1);
+    if ( $keywords_3 != '' )
+    {
+        $stylesheet .= "\n$keyword_selector_3 {{$keywords_3}}";
+    }
+
+    $keywords_4 = get_var('keywords-4');
+    $keyword_selector_4 = str_replace('{PART}', '.kw4', $part_selector_1);
+    if ( $keywords_4 != '' )
+    {
+        $stylesheet .= "\n$keyword_selector_4 {{$keywords_4}}";
+    }
+
+    // Do other lexics
+    $comments = get_var('comments');
+    $comment_selector = str_replace(array('{PART1}', '{PART2}', '{PART3}'), array('.co1', '.co2', '.coMULTI'), $part_selector_3);
+    if ( $comments != '' )
+    {
+        $stylesheet .= "\n$comment_selector {{$comments}}";
+    }
+
+    $esc = get_var('escaped-chars');
+    $esc_selector = str_replace('{PART}', '.es0', $part_selector_1);
+    if ( $esc != '' )
+    {
+        $stylesheet .= "\n$esc_selector {{$esc}}";
+    }
+
+    $brackets = get_var('brackets');
+    $brk_selector = str_replace('{PART}', '.br0', $part_selector_1);
+    if ( $brackets != '' )
+    {
+        $stylesheet .= "\n$brk_selector {{$brackets}}";
+    }
+
+    $strings = get_var('strings');
+    $string_selector = str_replace('{PART}', '.st0', $part_selector_1);
+    if ( $strings != '' )
+    {
+        $stylesheet .= "\n$string_selector {{$strings}}";
+    }
+
+    $numbers = get_var('numbers');
+    $num_selector = str_replace('{PART}', '.nu0', $part_selector_1);
+    if ( $numbers != '' )
+    {
+        $stylesheet .= "\n$num_selector {{$numbers}}";
+    }
+
+    $methods = get_var('methods');
+    $method_selector = str_replace('{PART}', '.me0', $part_selector_1);
+    if ( $methods != '' )
+    {
+        $stylesheet .= "\n$method_selector {{$methods}}";
+    }
+
+    echo "<pre>$stylesheet</pre>";
+
+    make_footer();
+}
+
+?>
diff --git a/Classes/Helper/contrib/cssgen2.php b/Classes/Helper/contrib/cssgen2.php
new file mode 100644 (file)
index 0000000..cc3c39c
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ *  A simple script which outputs the CSS classes for all languages
+ *  supported by GeSHi. You can access it directly to download
+ *  the CSS file. On *NIX you can also do a simple `php cssgen.php > geshi.css`.
+ *
+ *   This file is part of GeSHi.
+ *
+ *  GeSHi is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GeSHi is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GeSHi; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @package    geshi
+ * @subpackage contrib
+ * @author     revulo <revulon@gmail.com>
+ * @copyright  2008 revulo
+ * @license    http://gnu.org/copyleft/gpl.html GNU GPL
+ *
+ */
+
+require dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'geshi.php';
+$geshi = new GeSHi;
+
+$languages = array();
+if ($handle = opendir($geshi->language_path)) {
+    while (($file = readdir($handle)) !== false) {
+        $pos = strpos($file, '.');
+        if ($pos > 0 && substr($file, $pos) == '.php') {
+            $languages[] = substr($file, 0, $pos);
+        }
+    }
+    closedir($handle);
+}
+sort($languages);
+
+header('Content-Type: application/octet-stream');
+header('Content-Disposition: attachment; filename="geshi.css"');
+
+echo "/**\n".
+     " * GeSHi (C) 2004 - 2007 Nigel McNie, 2007 - 2008 Benny Baumann\n" .
+     " * (http://qbnz.com/highlighter/ and http://geshi.org/)\n".
+     " */\n";
+
+foreach ($languages as $language) {
+    $geshi->set_language($language);
+    // note: the false argument is required for stylesheet generators, see API documentation
+    $css = $geshi->get_stylesheet(false);
+    echo preg_replace('/^\/\*\*.*?\*\//s', '', $css);
+}
diff --git a/Classes/Helper/contrib/example.php b/Classes/Helper/contrib/example.php
new file mode 100644 (file)
index 0000000..e07399e
--- /dev/null
@@ -0,0 +1,217 @@
+<?php
+/**
+ * GeSHi example script
+ *
+ * Just point your browser at this script (with geshi.php in the parent directory,
+ * and the language files in subdirectory "../geshi/")
+ *
+ * @author  Nigel McNie
+ * @version $Id: example.php 2510 2012-06-27 15:57:55Z reedy_boy $
+ */
+header('Content-Type: text/html; charset=utf-8');
+
+error_reporting(E_ALL);
+
+// Rudimentary checking of where GeSHi is. In a default install it will be in ../, but
+// it could be in the current directory if the include_path is set. There's nowhere else
+// we can reasonably guess.
+if (is_readable('../geshi.php')) {
+    $path = '../';
+} elseif (is_readable('geshi.php')) {
+    $path = './';
+} else {
+    die('Could not find geshi.php - make sure it is in your include path!');
+}
+require $path . 'geshi.php';
+
+$fill_source = false;
+if (isset($_POST['submit'])) {
+    if (get_magic_quotes_gpc()) {
+        $_POST['source'] = stripslashes($_POST['source']);
+    }
+    if (!strlen(trim($_POST['source']))) {
+        $_POST['language'] = preg_replace('#[^a-zA-Z0-9\-_]#', '', $_POST['language']);
+        $_POST['source'] = implode('', @file($path . 'geshi/' . $_POST['language'] . '.php'));
+        $_POST['language'] = 'php';
+    } else {
+        $fill_source = true;
+    }
+
+    // Here's a free demo of how GeSHi works.
+
+    // First the initialisation: source code to highlight and the language to use. Make sure
+    // you sanitise correctly if you use $_POST of course - this very script has had a security
+    // advisory against it in the past because of this. Please try not to use this script on a
+    // live site.
+    $geshi = new GeSHi($_POST['source'], $_POST['language']);
+
+    // Use the PRE_VALID header. This means less output source since we don't have to output &nbsp;
+    // everywhere. Of course it also means you can't set the tab width.
+    // HEADER_PRE_VALID puts the <pre> tag inside the list items (<li>) thus producing valid HTML markup.
+    // HEADER_PRE puts the <pre> tag around the list (<ol>) which is invalid in HTML 4 and XHTML 1
+    // HEADER_DIV puts a <div> tag arount the list (valid!) but needs to replace whitespaces with &nbsp
+    //            thus producing much larger overhead. You can set the tab width though.
+    $geshi->set_header_type(GESHI_HEADER_PRE_VALID);
+
+    // Enable CSS classes. You can use get_stylesheet() to output a stylesheet for your code. Using
+    // CSS classes results in much less output source.
+    $geshi->enable_classes();
+
+    // Enable line numbers. We want fancy line numbers, and we want every 5th line number to be fancy
+    $geshi->enable_line_numbers(GESHI_FANCY_LINE_NUMBERS, 5);
+
+    // Set the style for the PRE around the code. The line numbers are contained within this box (not
+    // XHTML compliant btw, but if you are liberally minded about these things then you'll appreciate
+    // the reduced source output).
+    $geshi->set_overall_style('font: normal normal 90% monospace; color: #000066; border: 1px solid #d0d0d0; background-color: #f0f0f0;', false);
+
+    // Set the style for line numbers. In order to get style for line numbers working, the <li> element
+    // is being styled. This means that the code on the line will also be styled, and most of the time
+    // you don't want this. So the set_code_style reverts styles for the line (by using a <div> on the line).
+    // So the source output looks like this:
+    //
+    // <pre style="[set_overall_style styles]"><ol>
+    // <li style="[set_line_style styles]"><div style="[set_code_style styles]>...</div></li>
+    // ...
+    // </ol></pre>
+    $geshi->set_line_style('color: #003030;', 'font-weight: bold; color: #006060;', true);
+    $geshi->set_code_style('color: #000020;', true);
+
+    // Styles for hyperlinks in the code. GESHI_LINK for default styles, GESHI_HOVER for hover style etc...
+    // note that classes must be enabled for this to work.
+    $geshi->set_link_styles(GESHI_LINK, 'color: #000060;');
+    $geshi->set_link_styles(GESHI_HOVER, 'background-color: #f0f000;');
+
+    // Use the header/footer functionality. This puts a div with content within the PRE element, so it is
+    // affected by the styles set by set_overall_style. So if the PRE has a border then the header/footer will
+    // appear inside it.
+    $geshi->set_header_content('<SPEED> <TIME> GeSHi &copy; 2004-2007, Nigel McNie, 2007-2008 Benny Baumann. View source of example.php for example of using GeSHi');
+    $geshi->set_header_content_style('font-family: sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-bottom: 1px solid #d0d0d0; padding: 2px;');
+
+    // You can use <TIME> and <VERSION> as placeholders
+    $geshi->set_footer_content('Parsed in <TIME> seconds at <SPEED>, using GeSHi <VERSION>');
+    $geshi->set_footer_content_style('font-family: sans-serif; color: #808080; font-size: 70%; font-weight: bold; background-color: #f0f0ff; border-top: 1px solid #d0d0d0; padding: 2px;');
+} else {
+    // make sure we don't preselect any language
+    $_POST['language'] = null;
+}
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>GeSHi examples</title>
+    <style type="text/css">
+    <!--
+    <?php
+    if (isset($_POST['submit'])) {
+        // Output the stylesheet. Note it doesn't output the <style> tag
+        echo $geshi->get_stylesheet(true);
+    }
+    ?>
+    html {
+        background-color: #f0f0f0;
+    }
+    body {
+        font-family: Verdana, Arial, sans-serif;
+        margin: 10px;
+        border: 2px solid #e0e0e0;
+        background-color: #fcfcfc;
+        padding: 5px;
+    }
+    h2 {
+        margin: .1em 0 .2em .5em;
+        border-bottom: 1px solid #b0b0b0;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 150%;
+    }
+    h3 {
+        margin: .1em 0 .2em .5em;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 120%;
+    }
+    #footer {
+        text-align: center;
+        font-size: 80%;
+        color: #a9a9a9;
+    }
+    #footer a {
+        color: #9999ff;
+    }
+    textarea {
+        border: 1px solid #b0b0b0;
+        font-size: 90%;
+        color: #333;
+        margin-left: 20px;
+    }
+    select, input {
+        margin-left: 20px;
+    }
+    p {
+        font-size: 90%;
+        margin-left: .5em;
+    }
+    -->
+    </style>
+</head>
+<body>
+<h2>GeSHi Example Script</h2>
+<p>To use this script, make sure that <strong>geshi.php</strong> is in the parent directory or in your
+include_path, and that the language files are in a subdirectory of GeSHi's directory called <strong>geshi/</strong>.</p>
+<p>Enter your source and a language to highlight the source in and submit, or just choose a language to
+have that language file highlighted in PHP.</p>
+<?php
+if (isset($_POST['submit'])) {
+    // The fun part :)
+    echo $geshi->parse_code();
+    echo '<hr />';
+}
+?>
+<form action="<?php echo basename($_SERVER['PHP_SELF']); ?>" method="post">
+<h3>Source to highlight</h3>
+<p>
+<textarea rows="10" cols="60" name="source" id="source"><?php echo $fill_source ? htmlspecialchars($_POST['source']) : '' ?></textarea>
+</p>
+<h3>Choose a language</h3>
+<p>
+<select name="language" id="language">
+<?php
+if (!($dir = @opendir(dirname(__FILE__) . '/geshi'))) {
+    if (!($dir = @opendir(dirname(__FILE__) . '/../geshi'))) {
+        echo '<option>No languages available!</option>';
+    }
+}
+$languages = array();
+while ($file = readdir($dir)) {
+    if ( $file[0] == '.' || strpos($file, '.', 1) === false) {
+        continue;
+    }
+    $lang = substr($file, 0,  strpos($file, '.'));
+    $languages[] = $lang;
+}
+closedir($dir);
+sort($languages);
+foreach ($languages as $lang) {
+    if (isset($_POST['language']) && $_POST['language'] == $lang) {
+        $selected = 'selected="selected"';
+    } else {
+        $selected = '';
+    }
+    echo '<option value="' . $lang . '" '. $selected .'>' . $lang . "</option>\n";
+}
+
+?>
+</select>
+</p>
+<p>
+<input type="submit" name="submit" value="Highlight Source" />
+<input type="submit" name="clear" onclick="document.getElementById('source').value='';document.getElementById('language').value='';return false" value="clear" />
+</p>
+</form>
+<div id="footer">GeSHi &copy; Nigel McNie, 2004, released under the GNU GPL<br />
+For a better demonstration, check out the <a href="http://qbnz.com/highlighter/demo.php">online demo</a>
+</div>
+</body>
+</html>
diff --git a/Classes/Helper/contrib/langcheck.php b/Classes/Helper/contrib/langcheck.php
new file mode 100644 (file)
index 0000000..fa8288b
--- /dev/null
@@ -0,0 +1,769 @@
+<?php
+/**
+ * GeSHi language file validation script
+ *
+ * Just point your browser at this script (with geshi.php in the parent directory)
+ * and the language files in subdirectory "../geshi/" are being validated
+ *
+ * CLI mode is supported
+ *
+ * @author  Benny Baumann
+ * @version $Id: langcheck.php 2510 2012-06-27 15:57:55Z reedy_boy $
+ */
+header('Content-Type: text/html; charset=utf-8');
+
+set_time_limit(0);
+error_reporting(E_ALL);
+$time_start = explode(' ', microtime());
+
+function colorize($level, $string) {
+    static $colors, $end;
+    if ( !isset($colors) ) {
+      if ( PHP_SAPI != 'cli' ) {
+          $end = '</span>';
+          $colors = array(
+              TYPE_NOTICE => '<span style="color:#080;font-weight:bold;">',
+              TYPE_WARNING => '<span style="color:#CC0; font-weight: bold;">',
+              TYPE_ERROR => '<span style="color:#F00; font-weight: bold;">',
+              TYPE_OK => '<span style="color: #080; font-weight: bold;">'
+          );
+      } else {
+          $end = chr(27).'[0m';
+          $colors = array(
+              TYPE_NOTICE => chr(27).'[1m',
+              TYPE_WARNING => chr(27).'[1;33m',
+              TYPE_ERROR => chr(27).'[1;31m',
+              TYPE_OK => chr(27).'[1;32m'
+          );
+      }
+    }
+
+    if ( !isset($colors[$level]) ) {
+        trigger_error("no colors for level $level", E_USER_ERROR);
+    }
+
+    return $colors[$level].$string.$end;
+}
+
+define ('TYPE_NOTICE', 0);
+define ('TYPE_WARNING', 1);
+define ('TYPE_ERROR', 2);
+define ('TYPE_OK', 3);
+
+$error_abort = false;
+$error_cache = array();
+function output_error_cache(){
+    global $error_cache, $error_abort;
+
+    if(count($error_cache)) {
+        echo colorize(TYPE_ERROR, "Failed");
+        if ( PHP_SAPI == 'cli' ) {
+            echo "\n\n";
+        } else {
+            echo "<br /><ol>\n";
+        }
+        foreach($error_cache as $error_msg) {
+            if ( PHP_SAPI == 'cli' ) {
+              echo "\n";
+            } else {
+              echo "<li>";
+            }
+            switch($error_msg['t']) {
+                case TYPE_NOTICE:
+                    $msg = 'NOTICE';
+                    break;
+                case TYPE_WARNING:
+                    $msg = 'WARNING';
+                    break;
+                case TYPE_ERROR:
+                    $msg = 'ERROR';
+                    break;
+            }
+            echo colorize($error_msg['t'], $msg);
+            if ( PHP_SAPI == 'cli' ) {
+                echo "\t" . $error_msg['m'];
+            } else {
+                echo " " . $error_msg['m'] . "</li>";
+            }
+        }
+        if ( PHP_SAPI == 'cli' ) {
+            echo "\n";
+        } else {
+            echo "</ol>\n";
+        }
+    } else {
+        echo colorize(TYPE_OK, "OK");
+        if ( PHP_SAPI == 'cli' ) {
+            echo "\n";
+        } else {
+            echo "\n<br />";
+        }
+    }
+    echo "\n";
+
+    $error_cache = array();
+}
+
+function report_error($type, $message) {
+    global $error_cache, $error_abort;
+
+    $error_cache[] = array('t' => $type, 'm' => $message);
+    if(TYPE_ERROR == $type) {
+        $error_abort = true;
+    }
+}
+
+function dupfind_strtolower(&$value){
+    $value = strtolower($value);
+}
+
+if ( PHP_SAPI != 'cli' ) { ?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>GeSHi Language File Validation Script</title>
+    <style type="text/css">
+    <!--
+    html {
+        background-color: #f0f0f0;
+    }
+    body {
+        font-family: Verdana, Arial, sans-serif;
+        margin: 10px;
+        border: 2px solid #e0e0e0;
+        background-color: #fcfcfc;
+        padding: 5px;
+        font-size: 10pt;
+    }
+    h2 {
+        margin: .1em 0 .2em .5em;
+        border-bottom: 1px solid #b0b0b0;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 150%;
+    }
+    h3 {
+        margin: .1em 0 .2em .5em;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 120%;
+    }
+    #footer {
+        text-align: center;
+        font-size: 80%;
+        color: #a9a9a9;
+    }
+    #footer a {
+        color: #9999ff;
+    }
+    textarea {
+        border: 1px solid #b0b0b0;
+        font-size: 90%;
+        color: #333;
+        margin-left: 20px;
+    }
+    select, input {
+        margin-left: 20px;
+    }
+    p {
+        font-size: 90%;
+        margin-left: .5em;
+    }
+    -->
+    </style>
+</head>
+<body>
+<h2>GeSHi Language File Validation Script</h2>
+<p>To use this script, make sure that <strong>geshi.php</strong> is in the
+parent directory or in your include_path, and that the language files are in a
+subdirectory of GeSHi's directory called <strong>geshi/</strong>.</p>
+<p>Everything else will be done by this script automatically. After the script
+finished you should see messages of what could cause trouble with GeSHi or where
+your language files can be improved. Please be patient, as this might take some time.</p>
+
+<ol>
+<li>Checking where to find GeSHi installation ...<?php
+} else { ?>
+<?php echo colorize(TYPE_NOTICE, "#### GeSHi Language File Validation Script ####") ?>
+
+
+To use this script, make sure that <?php echo colorize(TYPE_NOTICE, "geshi.php"); ?> is in the
+parent directory or in your include_path, and that the language files are in a
+subdirectory of GeSHi's directory called <?php echo colorize(TYPE_NOTICE, "geshi/"); ?>.
+
+Everything else will be done by this script automatically. After the script
+finished you should see messages of what could cause trouble with GeSHi or where
+your language files can be improved. Please be patient, as this might take some time.
+
+
+Checking where to find GeSHi installation ...<?php echo "\t";
+}
+
+// Rudimentary checking of where GeSHi is. In a default install it will be in ../, but
+// it could be in the current directory if the include_path is set. There's nowhere else
+// we can reasonably guess.
+if (is_readable('../geshi.php')) {
+    $path = '../';
+} elseif (is_readable('geshi.php')) {
+    $path = './';
+} else {
+    report_error(TYPE_ERROR, 'Could not find geshi.php - make sure it is in your include path!');
+}
+
+if(!$error_abort) {
+    require $path . 'geshi.php';
+
+    if(!class_exists('GeSHi')) {
+        report_error(TYPE_ERROR, 'The GeSHi class was not found, although it seemed we loaded the correct file!');
+    }
+}
+
+if(!$error_abort) {
+    if(!defined('GESHI_LANG_ROOT')) {
+        report_error(TYPE_ERROR, 'There\'s no information present on where to find the language files!');
+    } elseif(!is_dir(GESHI_LANG_ROOT)) {
+        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" given, does not ressemble a directory!');
+    } elseif(!is_readable(GESHI_LANG_ROOT)) {
+        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" is not readable to this script!');
+    }
+}
+
+output_error_cache();
+
+if(!$error_abort) {
+    if ( PHP_SAPI == 'cli' ) {
+        echo "Listing available language files ...\t\t";
+    } else {
+        echo "</li>\n<li>Listing available language files ... ";
+    }
+
+    if (!($dir = @opendir(GESHI_LANG_ROOT))) {
+        report_error(TYPE_ERROR, 'Error requesting listing for available language files!');
+    }
+
+    $languages = array();
+
+    if(!$error_abort) {
+        while ($file = readdir($dir)) {
+            if (!$file || $file[0] == '.' || strpos($file, '.php') === false) {
+                continue;
+            }
+            $lang = substr($file, 0,  strpos($file, '.'));
+            if(4 != strlen($file) - strlen($lang)) {
+                continue;
+            }
+            $languages[] = $lang;
+        }
+        closedir($dir);
+    }
+
+    $languages = array_unique($languages);
+    sort($languages);
+
+    if(!count($languages)) {
+        report_error(TYPE_WARNING, 'Unable to locate any usable language files in "'.GESHI_LANG_ROOT.'"!');
+    }
+
+    output_error_cache();
+}
+
+if ( PHP_SAPI == 'cli' ) {
+    if (isset($_SERVER['argv'][1]) && in_array($_SERVER['argv'][1], $languages)) {
+        $languages = array($_SERVER['argv'][1]);
+    }
+} else {
+    if (isset($_REQUEST['show']) && in_array($_REQUEST['show'], $languages)) {
+        $languages = array($_REQUEST['show']);
+    }
+}
+
+if(!$error_abort) {
+    foreach ($languages as $lang) {
+
+        if ( PHP_SAPI == 'cli' ) {
+            echo "Validating language file for '$lang' ...\t\t";
+        } else {
+            echo "</li>\n<li>Validating language file for '$lang' ... ";
+        }
+
+        $langfile = GESHI_LANG_ROOT . $lang . '.php';
+
+        $language_data = array();
+
+        if(!is_file($langfile)) {
+            report_error(TYPE_ERROR, 'The path "' .$langfile. '" does not ressemble a regular file!');
+        } elseif(!is_readable($langfile)) {
+            report_error(TYPE_ERROR, 'Cannot read file "' .$langfile. '"!');
+        } else {
+            $langfile_content = file_get_contents($langfile);
+            if(preg_match("/\?>(?:\r?\n|\r(?!\n)){2,}\Z/", $langfile_content)) {
+                report_error(TYPE_ERROR, 'Language file contains trailing empty lines at EOF!');
+            }
+            if(!preg_match("/\?>(?:\r?\n|\r(?!\n))?\Z/", $langfile_content)) {
+                report_error(TYPE_ERROR, 'Language file contains no PHP end marker at EOF!');
+            }
+            if(preg_match("/\t/", $langfile_content)) {
+                report_error(TYPE_NOTICE, 'Language file contains unescaped tabulator chars (probably for indentation)!');
+            }
+            if(preg_match('/^(?:    )*(?!    )(?! \*) /m', $langfile_content)) {
+                report_error(TYPE_NOTICE, 'Language file contains irregular indentation (other than 4 spaces per indentation level)!');
+            }
+
+            if(!preg_match("/\/\*\*((?!\*\/).)*?Author:((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not contain a specification of an author!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?Copyright:((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not contain a specification of the copyright!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?Release Version:((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not contain a specification of the release version!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?Date Started:((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not contain a specification of the date it was started!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?This file is part of GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not state that it belongs to GeSHi!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?language file for GeSHi\.((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not state that it is a language file for GeSHi!');
+            }
+            if(!preg_match("/\/\*\*((?!\*\/).)*?GNU General Public License((?!\*\/).)*?\*\//s", $langfile_content)) {
+                report_error(TYPE_WARNING, 'Language file does not state that it is provided under the terms of the GNU GPL!');
+            }
+
+            unset($langfile_content);
+
+            include $langfile;
+
+            if(!isset($language_data)) {
+                report_error(TYPE_ERROR, 'Language file does not contain a $language_data structure to check!');
+            } elseif (!is_array($language_data)) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data structure which is not an array!');
+            }
+        }
+
+        if(!$error_abort) {
+            if(!isset($language_data['LANG_NAME'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'LANG_NAME\'] specification!');
+            } elseif (!is_string($language_data['LANG_NAME'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'LANG_NAME\'] specification which is not a string!');
+            }
+
+            if(!isset($language_data['COMMENT_SINGLE'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_SIGNLE\'] structure to check!');
+            } elseif (!is_array($language_data['COMMENT_SINGLE'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_SINGLE\'] structure which is not an array!');
+            }
+
+            if(!isset($language_data['COMMENT_MULTI'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'COMMENT_MULTI\'] structure to check!');
+            } elseif (!is_array($language_data['COMMENT_MULTI'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_MULTI\'] structure which is not an array!');
+            }
+
+            if(isset($language_data['COMMENT_REGEXP'])) {
+                if (!is_array($language_data['COMMENT_REGEXP'])) {
+                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'COMMENT_REGEXP\'] structure which is not an array!');
+                }
+            }
+
+            if(!isset($language_data['QUOTEMARKS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'QUOTEMARKS\'] structure to check!');
+            } elseif (!is_array($language_data['QUOTEMARKS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'QUOTEMARKS\'] structure which is not an array!');
+            }
+
+            if(isset($language_data['HARDQUOTE'])) {
+                if (!is_array($language_data['HARDQUOTE'])) {
+                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HARDQUOTE\'] structure which is not an array!');
+                }
+            }
+
+            if(!isset($language_data['ESCAPE_CHAR'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'ESCAPE_CHAR\'] specification to check!');
+            } elseif (!is_string($language_data['ESCAPE_CHAR'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification which is not a string!');
+            } elseif (1 < strlen($language_data['ESCAPE_CHAR'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'ESCAPE_CHAR\'] specification is not empty or exactly one char!');
+            }
+
+            if(!isset($language_data['CASE_KEYWORDS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_KEYWORDS\'] specification!');
+            } elseif (!is_int($language_data['CASE_KEYWORDS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is not an integer!');
+            } elseif (GESHI_CAPS_NO_CHANGE != $language_data['CASE_KEYWORDS'] &&
+                GESHI_CAPS_LOWER != $language_data['CASE_KEYWORDS'] &&
+                GESHI_CAPS_UPPER != $language_data['CASE_KEYWORDS']) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_KEYWORDS\'] specification which is neither of GESHI_CAPS_NO_CHANGE, GESHI_CAPS_LOWER nor GESHI_CAPS_UPPER!');
+            }
+
+            if(!isset($language_data['KEYWORDS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'KEYWORDS\'] structure to check!');
+            } elseif (!is_array($language_data['KEYWORDS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'KEYWORDS\'] structure which is not an array!');
+            } else {
+                foreach($language_data['KEYWORDS'] as $kw_key => $kw_value) {
+                    if(!is_integer($kw_key)) {
+                        report_error(TYPE_WARNING, "Language file contains an key '$kw_key' in \$language_data['KEYWORDS'] that is not integer!");
+                    } elseif (!is_array($kw_value)) {
+                        report_error(TYPE_ERROR, "Language file contains a \$language_data['KEYWORDS']['$kw_value'] structure which is not an array!");
+                    }
+                }
+            }
+
+            if(!isset($language_data['SYMBOLS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SYMBOLS\'] structure to check!');
+            } elseif (!is_array($language_data['SYMBOLS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SYMBOLS\'] structure which is not an array!');
+            }
+
+            if(!isset($language_data['CASE_SENSITIVE'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'CASE_SENSITIVE\'] structure to check!');
+            } elseif (!is_array($language_data['CASE_SENSITIVE'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'CASE_SENSITIVE\'] structure which is not an array!');
+            } else {
+                foreach($language_data['CASE_SENSITIVE'] as $cs_key => $cs_value) {
+                    if(!is_integer($cs_key)) {
+                        report_error(TYPE_WARNING, "Language file contains an key '$cs_key' in \$language_data['CASE_SENSITIVE'] that is not integer!");
+                    } elseif (!is_bool($cs_value)) {
+                        report_error(TYPE_ERROR, "Language file contains a Case Sensitivity specification for \$language_data['CASE_SENSITIVE']['$cs_value'] which is not a boolean!");
+                    }
+                }
+            }
+
+            if(!isset($language_data['URLS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'URLS\'] structure to check!');
+            } elseif (!is_array($language_data['URLS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'URLS\'] structure which is not an array!');
+            } else {
+                foreach($language_data['URLS'] as $url_key => $url_value) {
+                    if(!is_integer($url_key)) {
+                        report_error(TYPE_WARNING, "Language file contains an key '$url_key' in \$language_data['URLS'] that is not integer!");
+                    } elseif (!is_string($url_value)) {
+                        report_error(TYPE_ERROR, "Language file contains a Documentation URL specification for \$language_data['URLS']['$url_value'] which is not a string!");
+                    } elseif (preg_match('#&([^;]*(=|$))#U', $url_value)) {
+                        report_error(TYPE_ERROR, "Language file contains unescaped ampersands (&amp;) in \$language_data['URLS']!");
+                    }
+                }
+            }
+
+            if(!isset($language_data['OOLANG'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OOLANG\'] specification!');
+            } elseif (!is_int($language_data['OOLANG']) && !is_bool($language_data['OOLANG'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither boolean nor integer!');
+            } elseif (false !== $language_data['OOLANG'] &&
+                true !== $language_data['OOLANG'] &&
+                2 !== $language_data['OOLANG']) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OOLANG\'] specification which is neither of false, true or 2!');
+            }
+
+            if(!isset($language_data['OBJECT_SPLITTERS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'OBJECT_SPLITTERS\'] structure to check!');
+            } elseif (!is_array($language_data['OBJECT_SPLITTERS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'OBJECT_SPLITTERS\'] structure which is not an array!');
+            }
+
+            if(!isset($language_data['REGEXPS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'REGEXPS\'] structure to check!');
+            } elseif (!is_array($language_data['REGEXPS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'REGEXPS\'] structure which is not an array!');
+            }
+
+            if(!isset($language_data['STRICT_MODE_APPLIES'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STRICT_MODE_APPLIES\'] specification!');
+            } elseif (!is_int($language_data['STRICT_MODE_APPLIES'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is not an integer!');
+            } elseif (GESHI_MAYBE != $language_data['STRICT_MODE_APPLIES'] &&
+                GESHI_ALWAYS != $language_data['STRICT_MODE_APPLIES'] &&
+                GESHI_NEVER != $language_data['STRICT_MODE_APPLIES']) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STRICT_MODE_APPLIES\'] specification which is neither of GESHI_MAYBE, GESHI_ALWAYS nor GESHI_NEVER!');
+            }
+
+            if(!isset($language_data['SCRIPT_DELIMITERS'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'SCRIPT_DELIMITERS\'] structure to check!');
+            } elseif (!is_array($language_data['SCRIPT_DELIMITERS'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'SCRIPT_DELIMITERS\'] structure which is not an array!');
+            }
+
+            if(!isset($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure to check!');
+            } elseif (!is_array($language_data['HIGHLIGHT_STRICT_BLOCK'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'HIGHLIGHT_STRICT_BLOCK\'] structure which is not an array!');
+            }
+
+            if(isset($language_data['TAB_WIDTH'])) {
+                if (!is_int($language_data['TAB_WIDTH'])) {
+                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is not an integer!');
+                } elseif (1 > $language_data['TAB_WIDTH']) {
+                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'TAB_WIDTH\'] specification which is less than 1!');
+                }
+            }
+
+            if(isset($language_data['PARSER_CONTROL'])) {
+                if (!is_array($language_data['PARSER_CONTROL'])) {
+                    report_error(TYPE_ERROR, 'Language file contains a $language_data[\'PARSER_CONTROL\'] structure which is not an array!');
+                }
+            }
+
+            if(!isset($language_data['STYLES'])) {
+                report_error(TYPE_ERROR, 'Language file contains no $language_data[\'STYLES\'] structure to check!');
+            } elseif (!is_array($language_data['STYLES'])) {
+                report_error(TYPE_ERROR, 'Language file contains a $language_data[\'STYLES\'] structure which is not an array!');
+            } else {
+                $style_arrays = array('KEYWORDS', 'COMMENTS', 'ESCAPE_CHAR',
+                    'BRACKETS', 'STRINGS', 'NUMBERS', 'METHODS', 'SYMBOLS',
+                    'REGEXPS', 'SCRIPT');
+                foreach($style_arrays as $style_kind) {
+                    if(!isset($language_data['STYLES'][$style_kind])) {
+                        report_error(TYPE_ERROR, "Language file contains no \$language_data['STYLES']['$style_kind'] structure to check!");
+                    } elseif (!is_array($language_data['STYLES'][$style_kind])) {
+                        report_error(TYPE_ERROR, "Language file contains a \$language_data['STYLES\']['$style_kind'] structure which is not an array!");
+                    } else {
+                        foreach($language_data['STYLES'][$style_kind] as $sk_key => $sk_value) {
+                            if(!is_int($sk_key) && ('COMMENTS' != $style_kind && 'MULTI' != $sk_key)
+                                && !(('STRINGS' == $style_kind || 'ESCAPE_CHAR' == $style_kind) && 'HARD' == $sk_key)) {
+                                report_error(TYPE_WARNING, "Language file contains an key '$sk_key' in \$language_data['STYLES']['$style_kind'] that is not integer!");
+                            } elseif (!is_string($sk_value)) {
+                                report_error(TYPE_WARNING, "Language file contains a CSS specification for \$language_data['STYLES']['$style_kind'][$key] which is not a string!");
+                            }
+                        }
+                    }
+                }
+
+                unset($style_arrays);
+            }
+        }
+
+        if(!$error_abort) {
+            //Initial sanity checks survived? --> Let's dig deeper!
+            foreach($language_data['KEYWORDS'] as $key => $keywords) {
+                if(!isset($language_data['CASE_SENSITIVE'][$key])) {
+                    report_error(TYPE_ERROR, "Language file contains no \$language_data['CASE_SENSITIVE'] specification for keyword group $key!");
+                }
+                if(!isset($language_data['URLS'][$key])) {
+                    report_error(TYPE_ERROR, "Language file contains no \$language_data['URLS'] specification for keyword group $key!");
+                }
+                if(empty($keywords)) {
+                    report_error(TYPE_WARNING, "Language file contains an empty keyword list in \$language_data['KEYWORDS'] for group $key!");
+                }
+                foreach($keywords as $id => $kw) {
+                    if(!is_string($kw)) {
+                        report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['KEYWORDS'][$key][$id]!");
+                    } elseif (!strlen($kw)) {
+                        report_error(TYPE_ERROR, "Language file contains an empty string entry at \$language_data['KEYWORDS'][$key][$id]!");
+                    } elseif (preg_match('/^([\(\)\{\}\[\]\^=.,:;\-+\*\/%\$\"\'\?]|&[\w#]\w*;)+$/i', $kw)) {
+                        report_error(TYPE_NOTICE, "Language file contains an keyword ('$kw') at \$language_data['KEYWORDS'][$key][$id] which seems to be better suited for the symbols section!");
+                    }
+                }
+                if(isset($language_data['CASE_SENSITIVE'][$key]) && !$language_data['CASE_SENSITIVE'][$key]) {
+                    array_walk($keywords, 'dupfind_strtolower');
+                }
+                if(count($keywords) != count(array_unique($keywords))) {
+                    $kw_diffs = array_count_values($keywords);
+                    foreach($kw_diffs as $kw => $kw_count) {
+                        if($kw_count > 1) {
+                            report_error(TYPE_WARNING, "Language file contains per-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key]!");
+                        }
+                    }
+                }
+            }
+
+            $disallowed_before = "(?<![a-zA-Z0-9\$_\|\#;>|^&";
+            $disallowed_after = "(?![a-zA-Z0-9_\|%\\-&;";
+
+            foreach($language_data['KEYWORDS'] as $key => $keywords) {
+                foreach($language_data['KEYWORDS'] as $key2 => $keywords2) {
+                    if($key2 <= $key) {
+                        continue;
+                    }
+                    $kw_diffs = array_intersect($keywords, $keywords2);
+                    foreach($kw_diffs as $kw) {
+                        if(isset($language_data['PARSER_CONTROL']['KEYWORDS'])) {
+                            //Check the precondition\post-cindition for the involved keyword groups
+                            $g1_pre = $disallowed_before;
+                            $g2_pre = $disallowed_before;
+                            $g1_post = $disallowed_after;
+                            $g2_post = $disallowed_after;
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) {
+                                $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
+                                $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
+                            }
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) {
+                                $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
+                                $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
+                            }
+
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'])) {
+                                $g1_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_BEFORE'];
+                            }
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'])) {
+                                $g1_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key]['DISALLOWED_AFTER'];
+                            }
+
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'])) {
+                                $g2_pre = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_BEFORE'];
+                            }
+                            if(isset($language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'])) {
+                                $g2_post = $language_data['PARSER_CONTROL']['KEYWORDS'][$key2]['DISALLOWED_AFTER'];
+                            }
+
+                            if($g1_pre != $g2_pre || $g1_post != $g2_post) {
+                                continue;
+                            }
+                        }
+                        report_error(TYPE_WARNING, "Language file contains cross-group duplicate keyword '$kw' in \$language_data['KEYWORDS'][$key] and \$language_data['KEYWORDS'][$key2]!");
+                    }
+                }
+            }
+            foreach($language_data['CASE_SENSITIVE'] as $key => $keywords) {
+                if(!isset($language_data['KEYWORDS'][$key]) && $key != GESHI_COMMENTS) {
+                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['CASE_SENSITIVE'] specification for non-existing keyword group $key!");
+                }
+            }
+            foreach($language_data['URLS'] as $key => $keywords) {
+                if(!isset($language_data['KEYWORDS'][$key])) {
+                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['URLS'] specification for non-existing keyword group $key!");
+                }
+            }
+            foreach($language_data['STYLES']['KEYWORDS'] as $key => $keywords) {
+                if(!isset($language_data['KEYWORDS'][$key])) {
+                    report_error(TYPE_WARNING, "Language file contains an superfluous \$language_data['STYLES']['KEYWORDS'] specification for non-existing keyword group $key!");
+                }
+            }
+
+            foreach($language_data['COMMENT_SINGLE'] as $ck => $cv) {
+                if(!is_int($ck)) {
+                    report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_SINGLE'] that is not integer!");
+                }
+                if(!is_string($cv)) {
+                    report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_SINGLE'][$ck]!");
+                }
+                if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
+                    report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
+                }
+            }
+            if(isset($language_data['COMMENT_REGEXP'])) {
+                foreach($language_data['COMMENT_REGEXP'] as $ck => $cv) {
+                    if(!is_int($ck)) {
+                        report_error(TYPE_WARNING, "Language file contains an key '$ck' in \$language_data['COMMENT_REGEXP'] that is not integer!");
+                    }
+                    if(!is_string($cv)) {
+                        report_error(TYPE_WARNING, "Language file contains an non-string entry at \$language_data['COMMENT_REGEXP'][$ck]!");
+                    }
+                    if(!isset($language_data['STYLES']['COMMENTS'][$ck])) {
+                        report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['COMMENTS'] specification for comment group $ck!");
+                    }
+                }
+            }
+            foreach($language_data['STYLES']['COMMENTS'] as $ck => $cv) {
+                if($ck != 'MULTI' && !isset($language_data['COMMENT_SINGLE'][$ck]) &&
+                    !isset($language_data['COMMENT_REGEXP'][$ck])) {
+                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['COMMENTS'] specification for Single Line or Regular-Expression Comment key $ck!");
+                }
+            }
+            if (isset($language_data['STYLES']['STRINGS']['HARD'])) {
+                if (empty($language_data['HARDQUOTE'])) {
+                    report_error(TYPE_NOTICE, "Language file contains superfluous \$language_data['STYLES']['STRINGS'] specification for key 'HARD', but no 'HARDQUOTE's are defined!");
+                }
+                unset($language_data['STYLES']['STRINGS']['HARD']);
+            }
+            foreach($language_data['STYLES']['STRINGS'] as $sk => $sv) {
+                if($sk && !isset($language_data['QUOTEMARKS'][$sk])) {
+                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['STRINGS'] specification for non-existing quotemark key $sk!");
+                }
+            }
+
+            foreach($language_data['REGEXPS'] as $rk => $rv) {
+                if(!is_int($rk)) {
+                    report_error(TYPE_WARNING, "Language file contains an key '$rk' in \$language_data['REGEXPS'] that is not integer!");
+                }
+                if(is_string($rv)) {
+                    //Check for unmasked / in regular expressions ...
+                    if(empty($rv)) {
+                        report_error(TYPE_WARNING, "Language file contains an empty regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } else {
+                        if(preg_match("/(?<!\\\\)\//s", $rv)) {
+                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
+                        } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv)) {
+                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
+                        }
+                    }
+                } elseif(is_array($rv)) {
+                    if(!isset($rv[GESHI_SEARCH])) {
+                        report_error(TYPE_ERROR, "Language file contains no GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } elseif(!is_string($rv[GESHI_SEARCH])) {
+                        report_error(TYPE_ERROR, "Language file contains a GESHI_SEARCH entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
+                    } else {
+                        if(preg_match("/(?<!\\\\)\//s", $rv[GESHI_SEARCH])) {
+                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unmasked / character at \$language_data['REGEXPS'][$rk]!");
+                        } elseif (preg_match("/(?<!<)(\\\\\\\\)*\\\\\|(?!>)/s", $rv[GESHI_SEARCH])) {
+                            report_error(TYPE_WARNING, "Language file contains a regular expression with an unescaped match for a pipe character '|' which needs escaping as '&lt;PIPE&gt;' instead at \$language_data['REGEXPS'][$rk]!");
+                        }
+                    }
+                    if(!isset($rv[GESHI_REPLACE])) {
+                        report_error(TYPE_WARNING, "Language file contains no GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } elseif(!is_string($rv[GESHI_REPLACE])) {
+                        report_error(TYPE_ERROR, "Language file contains a GESHI_REPLACE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
+                    }
+                    if(!isset($rv[GESHI_MODIFIERS])) {
+                        report_error(TYPE_WARNING, "Language file contains no GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } elseif(!is_string($rv[GESHI_MODIFIERS])) {
+                        report_error(TYPE_ERROR, "Language file contains a GESHI_MODIFIERS entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
+                    }
+                    if(!isset($rv[GESHI_BEFORE])) {
+                        report_error(TYPE_WARNING, "Language file contains no GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } elseif(!is_string($rv[GESHI_BEFORE])) {
+                        report_error(TYPE_ERROR, "Language file contains a GESHI_BEFORE entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
+                    }
+                    if(!isset($rv[GESHI_AFTER])) {
+                        report_error(TYPE_WARNING, "Language file contains no GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk]!");
+                    } elseif(!is_string($rv[GESHI_AFTER])) {
+                        report_error(TYPE_ERROR, "Language file contains a GESHI_AFTER entry in extended regular expression at \$language_data['REGEXPS'][$rk] which is not a string!");
+                    }
+                } else {
+                    report_error(TYPE_WARNING, "Language file contains an non-string and non-array entry at \$language_data['REGEXPS'][$rk]!");
+                }
+                if(!isset($language_data['STYLES']['REGEXPS'][$rk])) {
+                    report_error(TYPE_WARNING, "Language file contains no \$language_data['STYLES']['REGEXPS'] specification for regexp group $rk!");
+                }
+            }
+            foreach($language_data['STYLES']['REGEXPS'] as $rk => $rv) {
+                if(!isset($language_data['REGEXPS'][$rk])) {
+                    report_error(TYPE_NOTICE, "Language file contains an superfluous \$language_data['STYLES']['REGEXPS'] specification for regexp key $rk!");
+                }
+            }
+
+
+        }
+
+        output_error_cache();
+
+        flush();
+
+        if($error_abort) {
+            break;
+        }
+    }
+}
+
+$time_end = explode(' ', microtime());
+$time_diff = $time_end[0] + $time_end[1] - $time_start[0] - $time_start[1];
+
+if ( PHP_SAPI != 'cli' ) {
+?></li>
+</ol>
+
+<p>Validation process completed in <? printf("%.2f", $time_diff); ?> seconds.</p>
+
+<div id="footer">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2008 Benny Baumann, released under the GNU GPL</div>
+</body>
+</html>
+
+<?php } else { ?>
+
+Validation process completed in <? printf("%.2f", $time_diff); ?> seconds.
+
+GeSHi &copy; 2004-2007 Nigel McNie, 2007-2012 Benny Baumann, released under the GNU GPL
+
+<?php } ?>
\ No newline at end of file
diff --git a/Classes/Helper/contrib/langwiz.php b/Classes/Helper/contrib/langwiz.php
new file mode 100644 (file)
index 0000000..32d025a
--- /dev/null
@@ -0,0 +1,1158 @@
+<?php
+/**
+ * GeSHi example script
+ *
+ * Just point your browser at this script (with geshi.php in the parent directory,
+ * and the language files in subdirectory "../geshi/")
+ *
+ *This script
+ *
+ * @author  Nigel McNie, Benny Baumann (BenBE@geshi.org), Andreas 'Segaja' Schleifer (webmaster at segaja dot de)
+ * @version $Id: langwiz.php 2510 2012-06-27 15:57:55Z reedy_boy $
+ */
+header('Content-Type: text/html; charset=utf-8');
+
+set_time_limit(0);
+error_reporting(E_ALL);
+$time_start = explode(' ', microtime());
+
+//Handle crappy PHP magic:
+if (get_magic_quotes_gpc()) {
+    function stripslashes_deep($value) {
+        $value = is_array($value) ?
+                    array_map('stripslashes_deep', $value) :
+                    stripslashes($value);
+
+        return $value;
+    }
+
+    $_POST = array_map('stripslashes_deep', $_POST);
+    $_GET = array_map('stripslashes_deep', $_GET);
+    $_COOKIE = array_map('stripslashes_deep', $_COOKIE);
+    $_REQUEST = array_map('stripslashes_deep', $_REQUEST);
+}
+
+function htmlspecialchars_deep($value) {
+    return is_array($value) ? array_map('htmlspecialchars_deep', $value) : htmlspecialchars($value);
+}
+
+define ('TYPE_NOTICE', 0);
+define ('TYPE_WARNING', 1);
+define ('TYPE_ERROR', 2);
+
+$error_abort = false;
+$error_cache = array();
+function output_error_cache(){
+    global $error_cache, $error_abort;
+
+    if(count($error_cache)) {
+        echo "<span style=\"color: #F00; font-weight: bold;\">Failed</span><br />";
+        echo "<ol>\n";
+        foreach($error_cache as $error_msg) {
+            echo "<li>";
+            switch($error_msg['t']) {
+                case TYPE_NOTICE:
+                    echo "<span style=\"color: #080; font-weight: bold;\">NOTICE:</span>";
+                    break;
+                case TYPE_WARNING:
+                    echo "<span style=\"color: #CC0; font-weight: bold;\">WARNING:</span>";
+                    break;
+                case TYPE_ERROR:
+                    echo "<span style=\"color: #F00; font-weight: bold;\">ERROR:</span>";
+                    break;
+            }
+            echo " " . $error_msg['m'] . "</li>";
+        }
+        echo "</ol>\n";
+    } else {
+        echo "<span style=\"color: #080; font-weight: bold;\">OK</span><br />";
+    }
+    echo "\n";
+
+    $error_cache = array();
+}
+
+function report_error($type, $message) {
+    global $error_cache, $error_abort;
+
+    $error_cache[] = array('t' => $type, 'm' => $message);
+    if(TYPE_ERROR == $type) {
+        $error_abort = true;
+    }
+}
+
+?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>GeSHi Language File Generator Script</title>
+    <style type="text/css">
+    <!--
+    html {
+        background-color: #f0f0f0;
+    }
+    body {
+        font-family: Verdana, Arial, sans-serif;
+        margin: 10px;
+        border: 2px solid #e0e0e0;
+        background-color: #fcfcfc;
+        padding: 5px;
+        font-size: 10pt;
+    }
+    h2 {
+        margin: .1em 0 .2em .5em;
+        border-bottom: 1px solid #b0b0b0;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 150%;
+    }
+    h3 {
+        margin: .1em 0 .2em .5em;
+        color: #b0b0b0;
+        font-weight: normal;
+        font-size: 120%;
+    }
+    #footer {
+        text-align: center;
+        font-size: 80%;
+        color: #a9a9a9;
+    }
+    #footer a {
+        color: #9999ff;
+    }
+    textarea {
+        border: 1px solid #b0b0b0;
+        font-size: 90%;
+        color: #333;
+        margin-left: 20px;
+    }
+    select, input {
+        margin-left: 2px;
+        border: 1px solid #808080;
+    }
+    p {
+        font-size: 90%;
+        margin-left: .5em;
+    }
+    fieldset {
+        border: 1px dotted gray;
+        background-color: #f0f0f0;
+        margin-bottom: .5em;
+    }
+    legend {
+        font-weight: bold;
+        background-color: #f9f9f9;
+        border: 1px solid #a0a0a0;
+        border-width: 1px 2px 2px 1px;
+    }
+    fieldset table > tbody > tr > td {
+        width: 20%;
+    }
+    fieldset table > tbody > tr > td+td {
+        width: 80%;
+    }
+
+    fieldset table > tbody > tr > td+td > input {
+        width: 98%;
+    }
+    -->
+    </style>
+</head>
+<body>
+<h2>GeSHi Language File Generator Script</h2>
+<p>To use this script, make sure that <strong>geshi.php</strong> is in the
+parent directory or in your include_path, and that the language files are in a
+subdirectory of GeSHi's directory called <strong>geshi/</strong>.</p>
+<p>If not already done, select a language file below that will be used as
+base for the language file to generate or create a blank one. Following this
+you can do whatever you like to edit your language file. But note that not all
+features are made available through this script.</p>
+
+<p>Checking GeSHi installation ... <?php
+// Rudimentary checking of where GeSHi is. In a default install it will be in ../, but
+// it could be in the current directory if the include_path is set. There's nowhere else
+// we can reasonably guess.
+if (is_readable('../geshi.php')) {
+    $path = '../';
+} elseif (is_readable('geshi.php')) {
+    $path = './';
+} else {
+    report_error(TYPE_ERROR, 'Could not find geshi.php - make sure it is in your include path!');
+}
+
+if(!$error_abort) {
+    require $path . 'geshi.php';
+
+    if(!class_exists('GeSHi')) {
+        report_error(TYPE_ERROR, 'The GeSHi class was not found, although it seemed we loaded the correct file!');
+    }
+}
+
+if(!$error_abort) {
+    if(!defined('GESHI_LANG_ROOT')) {
+        report_error(TYPE_ERROR, 'There\'s no information present on where to find the language files!');
+    } elseif(!is_dir(GESHI_LANG_ROOT)) {
+        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" given, does not ressemble a directory!');
+    } elseif(!is_readable(GESHI_LANG_ROOT)) {
+        report_error(TYPE_ERROR, 'The path "'.GESHI_LANG_ROOT.'" is not readable to this script!');
+    }
+}
+
+if(!$error_abort) {
+    if (!($dir = @opendir(GESHI_LANG_ROOT))) {
+        report_error(TYPE_ERROR, 'Error requesting listing for available language files!');
+    }
+
+    $languages = array();
+
+    if(!$error_abort) {
+        while ($file = readdir($dir)) {
+            if (!$file || $file[0] == '.' || strpos($file, '.') === false) {
+                continue;
+            }
+            $lang = substr($file, 0,  strpos($file, '.'));
+            $languages[] = $lang;
+        }
+        closedir($dir);
+    }
+
+    $languages = array_unique($languages);
+    sort($languages);
+
+    if(!count($languages)) {
+        report_error(TYPE_WARNING, 'Unable to locate any usable language files in "'.GESHI_LANG_ROOT.'"!');
+    }
+}
+
+output_error_cache();
+
+// --- empty variables for values of $_POST - begin ---
+$post_var_names = array('li', 'ai', 'ld');
+
+$li = array(
+    'file' => 'example',
+    'name' => 'Example'
+    );
+
+$ai = array(
+    'name' => 'Benny Baumann',
+    'email' => 'BenBE@geshi.org',
+    'web' => 'http://qbnz.com/highlighter/'
+    );
+
+$ld = array(
+    'cmt' => array(
+        'sl' => array(
+            1 => array(
+                'start' => '//',
+                'style' => 'font-style: italic; color: #666666;'
+                ),
+            2 => array(
+                'start' => '#',
+                'style' => 'font-style: italic; color: #666666;'
+                )
+            ),
+        'ml' => array(
+            1 => array(
+                'start' => '/*',
+                'end' => '*/',
+                'style' => 'font-style: italic; color: #666666;'
+                ),
+            2 => array(
+                'start' => '/**',
+                'end' => '*/',
+                'style' => 'font-style: italic; color: #006600;'
+                )
+            ),
+        'rxc' => array(
+            1 => array(
+                'rx' => '/Hello RegExp/',
+                'style' => 'font-style: italic; color: #666666;'
+                )
+            )
+        ),
+    'str' => array(
+        'qm' => array(
+            1 => array(
+                'delim' => "'",
+                'style' => 'color: #0000FF;'
+                ),
+            2 => array(
+              'delim' => "&quot;",
+                'style' => 'color: #0000FF;'
+                )
+            ),
+        'ec' => array(
+            'char' => '\\',
+            'style' => 'font-weight: bold; color: #000080;'
+            ),
+        'erx' => array(
+            1 => array(
+                'rx' => '/\{\\\\$\w+\}/',
+                'style' => 'font-weight: bold; color: #008080;'
+                ),
+            2 => array(
+                'rx'=> '/\{\\\\$\w+\}/',
+                'style' => 'font-weight: bold; color: #008080;'
+                )
+            )
+        ),
+    'kw_case' => 'GESHI_CAPS_NO_CHANGE',
+    'kw' => array(
+        1 => array(
+            'list' => '',
+            'case' => '0',
+            'style' => 'color: #0000FF; font-weight: bold;',
+            'docs' => ''
+            )
+        ),
+    'sy' => array(
+        0 => array(
+            'list' => '',
+            'style' => 'color: #0000FF; font-weight: bold;'
+            )
+        )
+    );
+
+$kw_case_sel = array(
+    'GESHI_CAPS_NO_CHANGE' => '',
+    'GESHI_CAPS_UPPER' => '',
+    'GESHI_CAPS_LOWER' => ''
+    );
+
+$kw_cases_sel = array(
+    1 => array(
+        0 => '',
+        1 => ''
+        )
+    );
+// --- empty variables for values of $_POST - end ---
+
+echo "<pre>";
+//var_dump($languages);
+
+foreach($post_var_names as $varName) { // export wanted variables of $_POST array...
+    if(array_key_exists($varName, $_POST)) {
+      $$varName = htmlspecialchars_deep($_POST[$varName]);
+    }
+}
+
+// determine the selected kw_case...
+$kw_case_sel[$ld['kw_case']] = ' selected="selected"';
+
+// determine the selected kw_cases...
+for($i = 1; $i <= count($kw_cases_sel); $i += 1) {
+    $kw_cases_sel[$i][(int) $ld['kw'][$i]['case']] = ' selected="selected"';
+}
+
+$lang = validate_lang();
+var_dump($lang);
+echo "</pre>";
+
+?>
+
+<form action="?action=test" method="post">
+    <fieldset>
+        <legend>Generic Information</legend>
+
+        <table width="100%">
+            <tr>
+                <td>
+                    <label for="li[file]">Language File ID:</label>
+                </td>
+                <td>
+                    <input type="text" name="li[file]" id="li[file]" value="<?=$li['file']; ?>" />
+                </td>
+            </tr>
+
+            <tr>
+                <td>
+                    <label for="li[name]">Language Name:</label>
+                </td>
+                <td>
+                    <input type="text" name="li[name]" id="li[name]" value="<?=$li['name']; ?>" />
+                </td>
+            </tr>
+
+        </table>
+    </fieldset>
+
+    <fieldset>
+        <legend>Author</legend>
+
+        <table width="100%">
+            <tr>
+                <td>
+                    <label for="ai[name]">Full Name:</label>
+                </td>
+                <td>
+                    <input type="text" name="ai[name]" id="ai[name]" value="<?=$ai['name']; ?>" />
+                </td>
+            </tr>
+
+            <tr>
+                <td>
+                    <label for="ai[email]">eMail address:</label>
+                </td>
+                <td>
+                    <input type="text" name="ai[email]" id="ai[email]" value="<?=$ai['email']; ?>" />
+                </td>
+            </tr>
+
+            <tr>
+                <td>
+                    <label for="ai[web]">Homepage:</label>
+                </td>
+                <td>
+                    <input type="text" name="ai[web]" id="ai[web]" value="<?=$ai['web']; ?>" />
+                </td>
+            </tr>
+        </table>
+    </fieldset>
+
+    <fieldset>
+        <legend>Comments</legend>
+
+        <fieldset>
+            <legend>Single Line</legend>
+
+            <fieldset>
+                <legend>Comment Group 1</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][sl][1][start]">Comment Start:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][sl][1][start]" id="ld[cmt][sl][1][start]" value="<?=$ld['cmt']['sl'][1]['start']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][sl][1][style]">Comment Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][sl][1][style]" id="ld[cmt][sl][1][style]" value="<?=$ld['cmt']['sl'][1]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+
+            <fieldset>
+                <legend>Comment Group 2</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][sl][2][start]">Comment Start:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][sl][2][start]" id="ld[cmt][sl][2][start]" value="<?=$ld['cmt']['sl'][2]['start']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][sl][2][style]">Comment Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][sl][2][style]" id="ld[cmt][sl][2][style]" value="<?=$ld['cmt']['sl'][2]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+        </fieldset>
+
+        <fieldset>
+            <legend>Multiple Lines</legend>
+
+            <fieldset>
+                <legend>Comment Group 1</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][1][start]">Comment Start:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][1][start]" id="ld[cmt][ml][1][start]" value="<?=$ld['cmt']['ml'][1]['start']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][1][end]">Comment End:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][1][end]" id="ld[cmt][ml][1][end]" value="<?=$ld['cmt']['ml'][1]['end']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][1][style]">Comment Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][1][style]" id="ld[cmt][ml][1][style]" value="<?=$ld['cmt']['ml'][1]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+
+            <fieldset>
+                <legend>Comment Group 2</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][2][start]">Comment Start:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][2][start]" id="ld[cmt][ml][2][start]" value="<?=$ld['cmt']['ml'][2]['start']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][2][end]">Comment End:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][2][end]" id="ld[cmt][ml][2][end]" value="<?=$ld['cmt']['ml'][2]['end']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][ml][2][style]">Comment Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][ml][2][style]" id="ld[cmt][ml][2][style]" value="<?=$ld['cmt']['ml'][2]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+        </fieldset>
+
+        <fieldset>
+            <legend>Regular Expressions</legend>
+
+            <fieldset>
+                <legend>Comment Group 1</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][rxc][1][rx]">Comment RX:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][rxc][1][rx]" id="ld[cmt][rxc][1][rx]" value="<?=$ld['cmt']['rxc'][1]['rx']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[cmt][rxc][1][style]">Comment Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[cmt][rxc][1][style]" id="ld[cmt][rxc][1][style]" value="<?=$ld['cmt']['rxc'][1]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+        </fieldset>
+    </fieldset>
+
+    <fieldset>
+        <legend>Strings</legend>
+
+        <fieldset>
+            <legend>String \ Quotes (delimiters, parsed)</legend>
+
+            <fieldset>
+                <legend>Quotemark Group 1</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[str][qm][1][delim]">String Delimiter:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][qm][1][delim]" id="ld[str][qm][1][delim]" value="<?=$ld['str']['qm'][1]['delim']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[str][qm][1][style]">String Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][qm][1][style]" id="ld[str][qm][1][style]" value="<?=$ld['str']['qm'][1]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+            <fieldset>
+                <legend>Quotemark Group 2</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[str][qm][1][delim]">String Delimiter:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][qm][2][delim]" id="ld[str][qm][2][delim]" value="<?=$ld['str']['qm'][2]['delim']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[str][qm][1][style]">String Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][qm][2][style]" id="ld[str][qm][2][style]" value="<?=$ld['str']['qm'][2]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+
+
+        </fieldset>
+
+        <fieldset>
+            <legend>Escape Sequences</legend>
+
+            <fieldset>
+                <legend>Generic Escape Char</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[str][ec][char]">Escape Char:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][ec][char]" id="ld[str][ec][char]" value="<?=$ld['str']['ec']['char']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[str][ec][style]">Escape Char Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][ec][style]" id="ld[str][ec][style]" value="<?=$ld['str']['ec']['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+
+            <fieldset>
+                <legend>Escape Regexp Group 1</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[str][erx][1][rx]">Escape Regexp:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][erx][1][rx]" id="ld[str][erx][1][rx]" value="<?=$ld['str']['erx'][1]['rx']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[str][erx][1][style]">Escape Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][erx][1][style]" id="ld[str][erx][1][style]" value="<?=$ld['str']['erx'][1]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+
+            <fieldset>
+                <legend>Escape Regexp Group 2</legend>
+
+                <table width="100%">
+                    <tr>
+                        <td>
+                            <label for="ld[str][erx][2][rx]">Escape Regexp:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][erx][2][rx]" id="ld[str][erx][2][rx]" value="<?=$ld['str']['erx'][2]['rx']; ?>" />
+                        </td>
+                    </tr>
+
+                    <tr>
+                        <td>
+                            <label for="ld[str][erx][2][style]">Escape Style:</label>
+                        </td>
+                        <td>
+                            <input type="text" name="ld[str][erx][2][style]" id="ld[str][erx][2][style]" value="<?=$ld['str']['erx'][2]['style']; ?>" />
+                        </td>
+                    </tr>
+                </table>
+            </fieldset>
+        </fieldset>
+    </fieldset>
+
+    <fieldset>
+        <legend>Keywords</legend>
+
+        <fieldset>
+            <legend>Case of Keywords</legend>
+
+            <table width="100%">
+                <tr>
+                    <td>
+                        <label for="ld[kw_case]">Handling of keywords case:</label>
+                    </td>
+                    <td>
+                        <select name=ld[kw_case]" id="ld[kw_case]">
+                            <option value="GESHI_CAPS_NO_CHANGE"<?=$kw_case_sel['GESHI_CAPS_NO_CHANGE']; ?>>Don’t change the case of any keyword</option>
+                            <option value="GESHI_CAPS_UPPER"<?=$kw_case_sel['GESHI_CAPS_UPPER']; ?>>Convert the case of all keywords to upper case</option>
+                            <option value="GESHI_CAPS_LOWER"<?=$kw_case_sel['GESHI_CAPS_LOWER']; ?>>Convert the case of all keywords to lower case</option>
+                        </select>
+                    </td>
+                </tr>
+            </table>
+        </fieldset>
+
+        <fieldset>
+            <legend>Keyword Group 1</legend>
+
+            <table width="100%">
+                <tr>
+                    <td>
+                        <label for="ld[kw][1][list]">Keyword List:</label>
+                    </td>
+                    <td>
+                      <textarea name="ld[kw][1][list]" id="ld[kw][1][list]" rows="10" cols="80"><?=$ld['kw'][1]['list']; ?></textarea>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td>
+                        <label for="ld[kw][1][case]">Case Sensitive:</label>
+                    </td>
+                    <td>
+                        <select name="ld[kw][1][case]" id="ld[kw][1][case]">
+                            <option value="0"<?=$kw_cases_sel[1][0]; ?>>No</option>
+                            <option value="1"<?=$kw_cases_sel[1][1]; ?>>Yes</option>
+                        </select>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td>
+                        <label for="ld[kw][1][style]">Keyword Style:</label>
+                    </td>
+                    <td>
+                        <input type="text" name="ld[kw][1][style]" id="ld[kw][1][style]" value="<?=$ld['kw'][1]['style']; ?>" />
+                    </td>
+                </tr>
+
+                <tr>
+                    <td>
+                        <label for="ld[kw][1][docs]">Documentation URL:</label>
+                    </td>
+                    <td>
+                        <input type="text" name="ld[kw][1][docs]" id="ld[kw][1][docs]" value="<?=$ld['kw'][1]['docs']; ?>" />
+                    </td>
+                </tr>
+            </table>
+        </fieldset>
+
+    </fieldset>
+
+
+    <fieldset>
+        <legend>Symbols</legend>
+
+        <fieldset>
+            <legend>Symbols Group 1</legend>
+
+            <table width="100%">
+                <tr>
+                    <td>
+                        <label for="ld[sy][0][list]">Symbols List:</label>
+                    </td>
+                    <td>
+                      <textarea name="ld[sy][0][list]" id="ld[sy][0][list]" rows="10" cols="80"><?=$ld['sy'][0]['list']; ?></textarea>
+                    </td>
+                </tr>
+
+                <tr>
+                    <td>
+                        <label for="ld[sy][0][style]">Symbols Style:</label>
+                    </td>
+                    <td>
+                        <input type="text" name="ld[sy][0][style]" id="ld[sy][0][style]" value="<?=$ld['sy'][0]['style']; ?>" />
+                    </td>
+                </tr>
+            </table>
+        </fieldset>
+
+    </fieldset>
+
+
+    <div id="langfile">
+        <fieldset>
+        <legend>Language File Source</legend>
+<?
+$G = new GeSHi('', 'php');
+$langfile_source = gen_langfile($lang);
+$G->set_source($langfile_source);
+echo $G->parse_code();
+unset($G);
+?>
+        </fieldset>
+    </div>
+
+    <input type="submit" name="btn" value="Send!" />
+</form>
+
+<p>Operation completed in <?
+$time_end = explode(' ', microtime());
+$time_diff = $time_end[0] + $time_end[1] - $time_start[0] - $time_start[1];
+
+echo sprintf("%.2f", $time_diff);
+?> seconds.</p>
+
+<div id="footer">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2009 Benny Baumann, released under the GNU GPL</div>
+</body>
+</html>
+<?
+
+function str_to_phpstring($str, $doublequote = false){
+    if($doublequote) {
+        return '"' . strtr($str,
+            array(
+                "\"" => "\\\"",
+                "\\" => "\\\\",
+                "\0" => "\\0",
+                "\n" => "\\n",
+                "\r" => "\\r",
+                "\t" => "\\t",
+                "\$" => "\\\$"
+                )
+            ) . '"';
+    } else {
+        return "'" . strtr($str,
+            array(
+                "'" => "\\'",
+                "\\" => "\\\\"
+                )
+            ) . "'";
+    }
+}
+
+function validate_lang(){
+    $ai = array(
+        'name' => 'Benny Baumann',
+        'email' => 'BenBE@geshi.org',
+        'web' => 'http://qbnz.com/highlighter/'
+        );
+
+    $li = array(
+        'file' => 'example',
+        'desc' => 'Example'
+        );
+
+    if(isset($_POST['ld'])) {
+        $ld = $_POST['ld'];
+    } else {
+        $ld = array(
+            'cmt' => array(
+                'sl' => array(
+                    1 => array(
+                        'start' => '//',
+                        'style' => 'test'
+                        )
+                    ),
+                'ml' => array(
+                    1 => array(
+                        'start' => '/*',
+                        'end' => '*/',
+                        'style' => 'font-style: italic; color: #666666;'
+                        )
+                    ),
+                'rxc' => array(
+                    1 => array(
+                        'rx' => '/Hello/',
+                        'style' => 'color: #00000'
+                        )
+                    )
+                ),
+            'str' => array(
+                'qm' => array(),
+                'ec' => array(
+                    'char' => ''
+                    ),
+                'erx' => array()
+                ),
+            'kw' => array(),
+            'kw_case' => 'GESHI_CAPS_NO_CHANGE',
+            'sy' => array()
+            );
+        }
+
+    return array('ai' => $ai, 'li' => $li, 'ld' => $ld);
+}
+
+function gen_langfile($lang){
+    $langfile = $lang['li']['file'];
+    $langdesc = $lang['li']['desc'];
+
+    $langauthor_name = $lang['ai']['name'];
+    $langauthor_email = $lang['ai']['email'];
+    $langauthor_web = $lang['ai']['web'];
+
+    $langversion = GESHI_VERSION;
+
+    $langdate = date('Y/m/d');
+    $langyear = date('Y');
+
+    $i = '    ';
+    $i = array('', $i, $i.$i, $i.$i.$i);
+
+    $src = <<<GESHI_LANGFILE_HEAD
+<?php
+/*************************************************************************************
+ * {$langfile}.php
+ * --------
+ * Author: {$langauthor_name} ({$langauthor_email})
+ * Copyright: (c) {$langyear} {$langauthor_name} ({$langauthor_web})
+ * Release Version: {$langversion}
+ * Date Started: {$langdate}
+ *
+ * {$langdesc} language file for GeSHi.
+ *
+ * CHANGES
+ * -------
+ * {$langdate} ({$langversion})
+ *  -  First Release
+ *
+ * TODO (updated {$langdate})
+ * -------------------------
+ * * Complete language file
+ *
+ *************************************************************************************
+ *
+ *     This file is part of GeSHi.
+ *
+ *   GeSHi is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   GeSHi is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with GeSHi; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ ************************************************************************************/
+
+\$language_data = array(
+
+GESHI_LANGFILE_HEAD;
+
+    //Language Name
+    $src .= $i[1] . "'LANG_NAME' => ".str_to_phpstring($langdesc).",\n";
+
+    //Comments
+    $src .= $i[1] . "'COMMENT_SINGLE' => array(\n";
+    foreach($lang['ld']['cmt']['sl'] as $idx_cmt_sl => $tmp_cmt_sl) {
+        $src .= $i[2] . ((int)$idx_cmt_sl). " => ". str_to_phpstring($tmp_cmt_sl['start']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'COMMENT_MULTI' => array(\n";
+    foreach($lang['ld']['cmt']['ml'] as $tmp_cmt_ml) {
+        $src .= $i[2] . str_to_phpstring($tmp_cmt_ml['start']). " => ". str_to_phpstring($tmp_cmt_ml['end']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'COMMENT_REGEXP' => array(\n";
+    foreach($lang['ld']['cmt']['rxc'] as $idx_cmt_rxc => $tmp_cmt_rxc) {
+        $src .= $i[2] . ((int)$idx_cmt_rxc). " => ". str_to_phpstring($tmp_cmt_rxc['rx']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+
+    //Case Keywords
+    $src .= $i[1] . "'CASE_KEYWORDS' => " . $lang['ld']['kw_case'] . ",\n";
+
+    //Quotes \ Strings
+    $src .= $i[1] . "'QUOTEMARKS' => array(\n";
+    foreach($lang['ld']['str']['qm'] as $idx_str_qm => $tmp_str_qm) {
+        $src .= $i[2] . ((int)$idx_str_qm). " => ". str_to_phpstring($tmp_str_qm['delim']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'ESCAPE_CHAR' => " . str_to_phpstring($lang['ld']['str']['ec']['char']) . ",\n";
+    $src .= $i[1] . "'ESCAPE_REGEXP' => array(\n";
+    foreach($lang['ld']['str']['erx'] as $idx_str_erx => $tmp_str_erx) {
+        $src .= $i[2] . ((int)$idx_str_erx). " => ". str_to_phpstring($tmp_str_erx['rx']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+
+    //HardQuotes
+    $src .= $i[1] . "'HARDQUOTE' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'HARDESCAPE' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'HARDCHAR' => '',\n";
+
+    //Numbers
+    $src .= $i[1] . "'NUMBERS' =>\n";
+    $src .= $i[2] . "GESHI_NUMBER_INT_BASIC | GESHI_NUMBER_OCT_PREFIX | GESHI_NUMBER_HEX_PREFIX |\n";
+    $src .= $i[2] . "GESHI_NUMBER_FLT_SCI_ZERO,\n";
+
+    //Keywords
+    $src .= $i[1] . "'KEYWRODS' => array(\n";
+    foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
+        $src .= $i[2] . ((int)$idx_kw) . " => array(\n";
+        if(!is_array($tmp_kw['list'])) {
+            $tmp_kw['list'] = explode("\n", $tmp_kw['list']);
+        }
+        $tmp_kw['list'] = array_map('trim', $tmp_kw['list']);
+        sort($tmp_kw['list']);
+        $kw_esc = array_map('str_to_phpstring', $tmp_kw['list']);
+        $kw_nl = true;
+        $kw_pos = 0;
+        foreach($kw_esc as $kw_data) {
+            if((strlen($kw_data) + $kw_pos > 79) && $kw_pos > strlen($i[3])) {
+                $src .= "\n";
+                $kw_nl = true;
+                $kw_pos = 0;
+            }
+            if($kw_nl) {
+                $src .= $i[3];
+                $kw_pos += strlen($i[3]);
+                $kw_nl = false;
+            }
+            $src .= $kw_data . ', ';
+            $kw_pos += strlen($kw_data) + 2;
+        }
+        $src .= "\n";
+        $src .= $i[3] . "),\n";
+    }
+    $src .= $i[2] . "),\n";
+
+    //Case Sensitivity
+    $src .= $i[1] . "'CASE_SENSITIVE' => array(\n";
+    foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
+        $src .= $i[2] . ((int)$idx_kw) . " => " . ($tmp_kw['case'] ? 'true' : 'false') . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+
+    //Symbols
+    $src .= $i[1] . "'SYMBOLS' => array(\n";
+    foreach($lang['ld']['sy'] as $idx_kw => $tmp_kw) {
+        $src .= $i[2] . ((int)$idx_kw) . " => array(\n";
+        $tmp_kw['list'] = (array)$tmp_kw['list'];
+        sort($tmp_kw['list']);
+        $kw_esc = array_map('str_to_phpstring', $tmp_kw['list']);
+        $kw_nl = true;
+        $kw_pos = strlen($i[3]);
+        foreach($kw_esc as $kw_data) {
+            if((strlen($kw_data) + $kw_pos > 79) && $kw_pos > strlen($i[3])) {
+                $src .= "\n";
+                $kw_nl = true;
+                $kw_pos = 0;
+            }
+            if($kw_nl) {
+                $src .= $i[3];
+                $kw_pos += strlen($i[3]);
+                $kw_nl = false;
+            }
+            $src .= $kw_data . ', ';
+            $kw_pos += strlen($kw_data) + 2;
+        }
+        $src .= "\n";
+        $src .= $i[3] . "),\n";
+    }
+    $src .= $i[2] . "),\n";
+
+    //Styles \ CSS
+    $src .= $i[1] . "'STYLES' => array(\n";
+    $src .= $i[2] . "'KEYWRODS' => array(\n";
+    foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
+        $src .= $i[3] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['style']) . ",\n";
+    }
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'COMMENTS' => array(\n";
+    foreach($lang['ld']['cmt']['sl'] as $idx_cmt_sl => $tmp_cmt_sl) {
+        $src .= $i[3] . ((int)$idx_cmt_sl) . " => " . str_to_phpstring($tmp_cmt_sl['style']) . ",\n";
+    }
+    foreach($lang['ld']['cmt']['rxc'] as $idx_cmt_rxc => $tmp_cmt_rxc) {
+        $src .= $i[3] . ((int)$idx_cmt_rxc) . " => " . str_to_phpstring($tmp_cmt_rxc['style']) . ",\n";
+    }
+    $src .= $i[3] . "'MULTI' => " . str_to_phpstring($lang['ld']['cmt']['ml'][1]['style']) . "\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'ESCAPE_CHAR' => array(\n";
+    foreach($lang['ld']['str']['erx'] as $idx_str_erx => $tmp_str_erx) {
+        $src .= $i[3] . ((int)$idx_str_erx). " => ". str_to_phpstring($tmp_str_erx['style']) . ",\n";
+    }
+    //    'HARD' => 'color: #000099; font-weight: bold;'
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'BRACKETS' => array(\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'STRINGS' => array(\n";
+    foreach($lang['ld']['str']['qm'] as $idx_str_qm => $tmp_str_qm) {
+        $src .= $i[3] . ((int)$idx_str_qm). " => ". str_to_phpstring($tmp_str_qm['style']) . ",\n";
+    }
+    //    'HARD' => 'color: #0000ff;'
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'NUMBERS' => array(\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'METHODS' => array(\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'SYMBOLS' => array(\n";
+    foreach($lang['ld']['sy'] as $idx_kw => $tmp_kw) {
+        $src .= $i[3] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['style']) . ",\n";
+    }
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'REGEXPS' => array(\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "'SCRIPT' => array(\n";
+    $src .= $i[3] . "),\n";
+    $src .= $i[2] . "),\n";
+
+    //Keyword Documentation
+    $src .= $i[1] . "'URLS' => array(\n";
+    foreach($lang['ld']['kw'] as $idx_kw => $tmp_kw) {
+        $src .= $i[2] . ((int)$idx_kw) . " => " . str_to_phpstring($tmp_kw['docs']) . ",\n";
+    }
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'OOLANG' => false,\n";
+    $src .= $i[1] . "'OBJECT_SPLITTERS' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'REGEXPS' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'STRICT_MODE_APPLIES' => GESHI_MAYBE,\n";
+    $src .= $i[1] . "'SCRIPT_DELIMITERS' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'HIGHLIGHT_STRICT_BLOCK' => array(\n";
+    $src .= $i[2] . "),\n";
+    $src .= $i[1] . "'TAB_WIDTH' => 4,\n";
+
+    $src .= <<<GESHI_LANGFILE_FOOTER
+);
+
+?>
+GESHI_LANGFILE_FOOTER;
+
+    //Reduce source ...
+    $src = preg_replace('/array\(\s*\)/s', 'array()', $src);
+    $src = preg_replace('/\,(\s*\))/s', '\1', $src);
+    $src = preg_replace('/\s+$/m', '', $src);
+
+    return $src;
+}
+
+// vim: shiftwidth=4 softtabstop=4
+?>
diff --git a/Classes/Helper/geshi.php b/Classes/Helper/geshi.php
new file mode 100644 (file)
index 0000000..c6ff9ef
--- /dev/null
@@ -0,0 +1,4775 @@
+<?php
+/**
+ * GeSHi - Generic Syntax Highlighter
+ *
+ * The GeSHi class for Generic Syntax Highlighting. Please refer to the
+ * documentation at http://qbnz.com/highlighter/documentation.php for more
+ * information about how to use this class.
+ *
+ * For changes, release notes, TODOs etc, see the relevant files in the docs/
+ * directory.
+ *
+ *   This file is part of GeSHi.
+ *
+ *  GeSHi is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GeSHi is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GeSHi; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * @package    geshi
+ * @subpackage core
+ * @author     Nigel McNie <nigel@geshi.org>, Benny Baumann <BenBE@omorphia.de>
+ * @copyright  (C) 2004 - 2007 Nigel McNie, (C) 2007 - 2008 Benny Baumann
+ * @license    http://gnu.org/copyleft/gpl.html GNU GPL
+ *
+ */
+
+//
+// GeSHi Constants
+// You should use these constant names in your programs instead of
+// their values - you never know when a value may change in a future
+// version
+//
+
+/** The version of this GeSHi file */
+define('GESHI_VERSION', '1.0.8.11');
+
+// Define the root directory for the GeSHi code tree
+if (!defined('GESHI_ROOT')) {
+    /** The root directory for GeSHi */
+    define('GESHI_ROOT', dirname(__FILE__) . DIRECTORY_SEPARATOR);
+}
+/** The language file directory for GeSHi
+    @access private */
+define('GESHI_LANG_ROOT', GESHI_ROOT . 'geshi' . DIRECTORY_SEPARATOR);
+
+// Define if GeSHi should be paranoid about security
+if (!defined('GESHI_SECURITY_PARANOID')) {
+    /** Tells GeSHi to be paranoid about security settings */
+    define('GESHI_SECURITY_PARANOID', false);
+}
+
+// Line numbers - use with enable_line_numbers()
+/** Use no line numbers when building the result */
+define('GESHI_NO_LINE_NUMBERS', 0);
+/** Use normal line numbers when building the result */
+define('GESHI_NORMAL_LINE_NUMBERS', 1);
+/** Use fancy line numbers when building the result */
+define('GESHI_FANCY_LINE_NUMBERS', 2);
+
+// Container HTML type
+/** Use nothing to surround the source */
+define('GESHI_HEADER_NONE', 0);
+/** Use a "div" to surround the source */
+define('GESHI_HEADER_DIV', 1);
+/** Use a "pre" to surround the source */
+define('GESHI_HEADER_PRE', 2);
+/** Use a pre to wrap lines when line numbers are enabled or to wrap the whole code. */
+define('GESHI_HEADER_PRE_VALID', 3);
+/**
+ * Use a "table" to surround the source:
+ *
+ *  <table>
+ *    <thead><tr><td colspan="2">$header</td></tr></thead>
+ *    <tbody><tr><td><pre>$linenumbers</pre></td><td><pre>$code></pre></td></tr></tbody>
+ *    <tfooter><tr><td colspan="2">$footer</td></tr></tfoot>
+ *  </table>
+ *
+ * this is essentially only a workaround for Firefox, see sf#1651996 or take a look at
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=365805
+ * @note when linenumbers are disabled this is essentially the same as GESHI_HEADER_PRE
+ */
+define('GESHI_HEADER_PRE_TABLE', 4);
+
+// Capatalisation constants
+/** Lowercase keywords found */
+define('GESHI_CAPS_NO_CHANGE', 0);
+/** Uppercase keywords found */
+define('GESHI_CAPS_UPPER', 1);
+/** Leave keywords found as the case that they are */
+define('GESHI_CAPS_LOWER', 2);
+
+// Link style constants
+/** Links in the source in the :link state */
+define('GESHI_LINK', 0);
+/** Links in the source in the :hover state */
+define('GESHI_HOVER', 1);
+/** Links in the source in the :active state */
+define('GESHI_ACTIVE', 2);
+/** Links in the source in the :visited state */
+define('GESHI_VISITED', 3);
+
+// Important string starter/finisher
+// Note that if you change these, they should be as-is: i.e., don't
+// write them as if they had been run through htmlentities()
+/** The starter for important parts of the source */
+define('GESHI_START_IMPORTANT', '<BEGIN GeSHi>');
+/** The ender for important parts of the source */
+define('GESHI_END_IMPORTANT', '<END GeSHi>');
+
+/**#@+
+ *  @access private
+ */
+// When strict mode applies for a language
+/** Strict mode never applies (this is the most common) */
+define('GESHI_NEVER', 0);
+/** Strict mode *might* apply, and can be enabled or
+    disabled by {@link GeSHi->enable_strict_mode()} */
+define('GESHI_MAYBE', 1);
+/** Strict mode always applies */
+define('GESHI_ALWAYS', 2);
+
+// Advanced regexp handling constants, used in language files
+/** The key of the regex array defining what to search for */
+define('GESHI_SEARCH', 0);
+/** The key of the regex array defining what bracket group in a
+    matched search to use as a replacement */
+define('GESHI_REPLACE', 1);
+/** The key of the regex array defining any modifiers to the regular expression */
+define('GESHI_MODIFIERS', 2);
+/** The key of the regex array defining what bracket group in a
+    matched search to put before the replacement */
+define('GESHI_BEFORE', 3);
+/** The key of the regex array defining what bracket group in a
+    matched search to put after the replacement */
+define('GESHI_AFTER', 4);
+/** The key of the regex array defining a custom keyword to use
+    for this regexp's html tag class */
+define('GESHI_CLASS', 5);
+
+/** Used in language files to mark comments */
+define('GESHI_COMMENTS', 0);
+
+/** Used to work around missing PHP features **/
+define('GESHI_PHP_PRE_433', !(version_compare(PHP_VERSION, '4.3.3') === 1));
+
+/** make sure we can call stripos **/
+if (!function_exists('stripos')) {
+    // the offset param of preg_match is not supported below PHP 4.3.3
+    if (GESHI_PHP_PRE_433) {
+        /**
+         * @ignore
+         */
+        function stripos($haystack, $needle, $offset = null) {
+            if (!is_null($offset)) {
+                $haystack = substr($haystack, $offset);
+            }
+            if (preg_match('/'. preg_quote($needle, '/') . '/', $haystack, $match, PREG_OFFSET_CAPTURE)) {
+                return $match[0][1];
+            }
+            return false;
+        }
+    }
+    else {
+        /**
+         * @ignore
+         */
+        function stripos($haystack, $needle, $offset = null) {
+            if (preg_match('/'. preg_quote($needle, '/') . '/', $haystack, $match, PREG_OFFSET_CAPTURE, $offset)) {
+                return $match[0][1];
+            }
+            return false;
+        }
+    }
+}
+
+/** some old PHP / PCRE subpatterns only support up to xxx subpatterns in
+    regular expressions. Set this to false if your PCRE lib is up to date
+    @see GeSHi->optimize_regexp_list()
+    **/
+define('GESHI_MAX_PCRE_SUBPATTERNS', 500);
+/** it's also important not to generate too long regular expressions
+    be generous here... but keep in mind, that when reaching this limit we
+    still have to close open patterns. 12k should do just fine on a 16k limit.
+    @see GeSHi->optimize_regexp_list()
+    **/
+define('GESHI_MAX_PCRE_LENGTH', 12288);
+
+//Number format specification
+/** Basic number format for integers */
+define('GESHI_NUMBER_INT_BASIC', 1);        //Default integers \d+
+/** Enhanced number format for integers like seen in C */
+define('GESHI_NUMBER_INT_CSTYLE', 2);       //Default C-Style \d+[lL]?
+/** Number format to highlight binary numbers with a suffix "b" */
+define('GESHI_NUMBER_BIN_SUFFIX', 16);           //[01]+[bB]
+/** Number format to highlight binary numbers with a prefix % */
+define('GESHI_NUMBER_BIN_PREFIX_PERCENT', 32);   //%[01]+
+/** Number format to highlight binary numbers with a prefix 0b (C) */
+define('GESHI_NUMBER_BIN_PREFIX_0B', 64);        //0b[01]+
+/** Number format to highlight octal numbers with a leading zero */
+define('GESHI_NUMBER_OCT_PREFIX', 256);           //0[0-7]+
+/** Number format to highlight octal numbers with a prefix 0o (logtalk) */
+define('GESHI_NUMBER_OCT_PREFIX_0O', 512);           //0[0-7]+
+/** Number format to highlight octal numbers with a leading @ (Used in HiSofts Devpac series). */
+define('GESHI_NUMBER_OCT_PREFIX_AT', 1024);           //@[0-7]+
+/** Number format to highlight octal numbers with a suffix of o */
+define('GESHI_NUMBER_OCT_SUFFIX', 2048);           //[0-7]+[oO]
+/** Number format to highlight hex numbers with a prefix 0x */
+define('GESHI_NUMBER_HEX_PREFIX', 4096);           //0x[0-9a-fA-F]+
+/** Number format to highlight hex numbers with a prefix $ */
+define('GESHI_NUMBER_HEX_PREFIX_DOLLAR', 8192);           //$[0-9a-fA-F]+
+/** Number format to highlight hex numbers with a suffix of h */
+define('GESHI_NUMBER_HEX_SUFFIX', 16384);           //[0-9][0-9a-fA-F]*h
+/** Number format to highlight floating-point numbers without support for scientific notation */
+define('GESHI_NUMBER_FLT_NONSCI', 65536);          //\d+\.\d+
+/** Number format to highlight floating-point numbers without support for scientific notation */
+define('GESHI_NUMBER_FLT_NONSCI_F', 131072);       //\d+(\.\d+)?f
+/** Number format to highlight floating-point numbers with support for scientific notation (E) and optional leading zero */
+define('GESHI_NUMBER_FLT_SCI_SHORT', 262144);      //\.\d+e\d+
+/** Number format to highlight floating-point numbers with support for scientific notation (E) and required leading digit */
+define('GESHI_NUMBER_FLT_SCI_ZERO', 524288);       //\d+(\.\d+)?e\d+
+//Custom formats are passed by RX array
+
+// Error detection - use these to analyse faults
+/** No sourcecode to highlight was specified
+ * @deprecated
+ */
+define('GESHI_ERROR_NO_INPUT', 1);
+/** The language specified does not exist */
+define('GESHI_ERROR_NO_SUCH_LANG', 2);
+/** GeSHi could not open a file for reading (generally a language file) */
+define('GESHI_ERROR_FILE_NOT_READABLE', 3);
+/** The header type passed to {@link GeSHi->set_header_type()} was invalid */
+define('GESHI_ERROR_INVALID_HEADER_TYPE', 4);
+/** The line number type passed to {@link GeSHi->enable_line_numbers()} was invalid */
+define('GESHI_ERROR_INVALID_LINE_NUMBER_TYPE', 5);
+/**#@-*/
+
+
+/**
+ * The GeSHi Class.
+ *
+ * Please refer to the documentation for GeSHi 1.0.X that is available
+ * at http://qbnz.com/highlighter/documentation.php for more information
+ * about how to use this class.
+ *
+ * @package   geshi
+ * @author    Nigel McNie <nigel@geshi.org>, Benny Baumann <BenBE@omorphia.de>
+ * @copyright (C) 2004 - 2007 Nigel McNie, (C) 2007 - 2008 Benny Baumann
+ */
+class GeSHi {
+    /**#@+
+     * @access private
+     */
+    /**
+     * The source code to highlight
+     * @var string
+     */
+    var $source = '';
+
+    /**
+     * The language to use when highlighting
+     * @var string
+     */
+    var $language = '';
+
+    /**
+     * The data for the language used
+     * @var array
+     */
+    var $language_data = array();
+
+    /**
+     * The path to the language files
+     * @var string
+     */
+    var $language_path = GESHI_LANG_ROOT;
+
+    /**
+     * The error message associated with an error
+     * @var string
+     * @todo check err reporting works
+     */
+    var $error = false;
+
+    /**
+     * Possible error messages
+     * @var array
+     */
+    var $error_messages = array(
+        GESHI_ERROR_NO_SUCH_LANG => 'GeSHi could not find the language {LANGUAGE} (using path {PATH})',
+        GESHI_ERROR_FILE_NOT_READABLE => 'The file specified for load_from_file was not readable',
+        GESHI_ERROR_INVALID_HEADER_TYPE => 'The header type specified is invalid',
+        GESHI_ERROR_INVALID_LINE_NUMBER_TYPE => 'The line number type specified is invalid'
+    );
+
+    /**
+     * Whether highlighting is strict or not
+     * @var boolean
+     */
+    var $strict_mode = false;
+
+    /**
+     * Whether to use CSS classes in output
+     * @var boolean
+     */
+    var $use_classes = false;
+
+    /**
+     * The type of header to use. Can be one of the following
+     * values:
+     *
+     * - GESHI_HEADER_PRE: Source is outputted in a "pre" HTML element.
+     * - GESHI_HEADER_DIV: Source is outputted in a "div" HTML element.
+     * - GESHI_HEADER_NONE: No header is outputted.
+     *
+     * @var int
+     */
+    var $header_type = GESHI_HEADER_PRE;
+
+    /**
+     * Array of permissions for which lexics should be highlighted
+     * @var array
+     */
+    var $lexic_permissions = array(
+        'KEYWORDS' =>    array(),
+        'COMMENTS' =>    array('MULTI' => true),
+        'REGEXPS' =>     array(),
+        'ESCAPE_CHAR' => true,
+        'BRACKETS' =>    true,
+        'SYMBOLS' =>     false,
+        'STRINGS' =>     true,
+        'NUMBERS' =>     true,
+        'METHODS' =>     true,
+        'SCRIPT' =>      true
+    );
+
+    /**
+     * The time it took to parse the code
+     * @var double
+     */
+    var $time = 0;
+
+    /**
+     * The content of the header block
+     * @var string
+     */
+    var $header_content = '';
+
+    /**
+     * The content of the footer block
+     * @var string
+     */
+    var $footer_content = '';
+
+    /**
+     * The style of the header block
+     * @var string
+     */
+    var $header_content_style = '';
+
+    /**
+     * The style of the footer block
+     * @var string
+     */
+    var $footer_content_style = '';
+
+    /**
+     * Tells if a block around the highlighted source should be forced
+     * if not using line numbering
+     * @var boolean
+     */
+    var $force_code_block = false;
+
+    /**
+     * The styles for hyperlinks in the code
+     * @var array
+     */
+    var $link_styles = array();
+
+    /**
+     * Whether important blocks should be recognised or not
+     * @var boolean
+     * @deprecated
+     * @todo REMOVE THIS FUNCTIONALITY!
+     */
+    var $enable_important_blocks = false;
+
+    /**
+     * Styles for important parts of the code
+     * @var string
+     * @deprecated
+     * @todo As above - rethink the whole idea of important blocks as it is buggy and
+     * will be hard to implement in 1.2
+     */
+    var $important_styles = 'font-weight: bold; color: red;'; // Styles for important parts of the code
+
+    /**
+     * Whether CSS IDs should be added to the code
+     * @var boolean
+     */
+    var $add_ids = false;
+
+    /**
+     * Lines that should be highlighted extra
+     * @var array
+     */
+    var $highlight_extra_lines = array();
+
+    /**
+     * Styles of lines that should be highlighted extra
+     * @var array
+     */
+    var $highlight_extra_lines_styles = array();
+
+    /**
+     * Styles of extra-highlighted lines
+     * @var string
+     */
+    var $highlight_extra_lines_style = 'background-color: #ffc;';
+
+    /**
+     * The line ending
+     * If null, nl2br() will be used on the result string.
+     * Otherwise, all instances of \n will be replaced with $line_ending
+     * @var string
+     */
+    var $line_ending = null;
+
+    /**
+     * Number at which line numbers should start at
+     * @var int
+     */
+    var $line_numbers_start = 1;
+
+    /**
+     * The overall style for this code block
+     * @var string
+     */
+    var $overall_style = 'font-family:monospace;';
+
+    /**
+     *  The style for the actual code
+     * @var string
+     */
+    var $code_style = 'font: normal normal 1em/1.2em monospace; margin:0; padding:0; background:none; vertical-align:top;';
+
+    /**
+     * The overall class for this code block
+     * @var string
+     */
+    var $overall_class = '';
+
+    /**
+     * The overall ID for this code block
+     * @var string
+     */
+    var $overall_id = '';
+
+    /**
+     * Line number styles
+     * @var string
+     */
+    var $line_style1 = 'font-weight: normal; vertical-align:top;';
+
+    /**
+     * Line number styles for fancy lines
+     * @var string
+     */
+    var $line_style2 = 'font-weight: bold; vertical-align:top;';
+
+    /**
+     * Style for line numbers when GESHI_HEADER_PRE_TABLE is chosen
+     * @var string
+     */
+    var $table_linenumber_style = 'width:1px;text-align:right;margin:0;padding:0 2px;vertical-align:top;';
+
+    /**
+     * Flag for how line numbers are displayed
+     * @var boolean
+     */
+    var $line_numbers = GESHI_NO_LINE_NUMBERS;
+
+    /**
+     * Flag to decide if multi line spans are allowed. Set it to false to make sure
+     * each tag is closed before and reopened after each linefeed.
+     * @var boolean
+     */
+    var $allow_multiline_span = true;
+
+    /**
+     * The "nth" value for fancy line highlighting
+     * @var int
+     */
+    var $line_nth_row = 0;
+
+    /**
+     * The size of tab stops
+     * @var int
+     */
+    var $tab_width = 8;
+
+    /**
+     * Should we use language-defined tab stop widths?
+     * @var int
+     */
+    var $use_language_tab_width = false;
+
+    /**
+     * Default target for keyword links
+     * @var string
+     */
+    var $link_target = '';
+
+    /**
+     * The encoding to use for entity encoding
+     * NOTE: Used with Escape Char Sequences to fix UTF-8 handling (cf. SF#2037598)
+     * @var string
+     */
+    var $encoding = 'utf-8';
+
+    /**
+     * Should keywords be linked?
+     * @var boolean
+     */
+    var $keyword_links = true;
+
+    /**
+     * Currently loaded language file
+     * @var string
+     * @since 1.0.7.22
+     */
+    var $loaded_language = '';
+
+    /**
+     * Wether the caches needed for parsing are built or not
+     *
+     * @var bool
+     * @since 1.0.8
+     */
+    var $parse_cache_built = false;
+
+    /**
+     * Work around for Suhosin Patch with disabled /e modifier
+     *
+     * Note from suhosins author in config file:
+     * <blockquote>
+     *   The /e modifier inside <code>preg_replace()</code> allows code execution.
+     *   Often it is the cause for remote code execution exploits. It is wise to
+     *   deactivate this feature and test where in the application it is used.
+     *   The developer using the /e modifier should be made aware that he should
+     *   use <code>preg_replace_callback()</code> instead
+     * </blockquote>
+     *
+     * @var array
+     * @since 1.0.8
+     */
+    var $_kw_replace_group = 0;
+    var $_rx_key = 0;
+
+    /**
+     * some "callback parameters" for handle_multiline_regexps
+     *
+     * @since 1.0.8
+     * @access private
+     * @var string
+     */
+    var $_hmr_before = '';
+    var $_hmr_replace = '';
+    var $_hmr_after = '';
+    var $_hmr_key = 0;
+
+    /**#@-*/
+
+    /**
+     * Creates a new GeSHi object, with source and language
+     *
+     * @param string The source code to highlight
+     * @param string The language to highlight the source with
+     * @param string The path to the language file directory. <b>This
+     *               is deprecated!</b> I've backported the auto path
+     *               detection from the 1.1.X dev branch, so now it
+     *               should be automatically set correctly. If you have
+     *               renamed the language directory however, you will
+     *               still need to set the path using this parameter or
+     *               {@link GeSHi->set_language_path()}
+     * @since 1.0.0
+     */
+    function GeSHi($source = '', $language = '', $path = '') {
+        if (!empty($source)) {
+            $this->set_source($source);
+        }
+        if (!empty($language)) {
+            $this->set_language($language);
+        }
+        $this->set_language_path($path);
+    }
+
+    /**
+     * Returns the version of GeSHi
+     *
+     * @return string
+     * @since 1 0.8.11
+     */
+    function get_version()
+    {
+        return GESHI_VERSION;
+    }
+
+    /**
+     * Returns an error message associated with the last GeSHi operation,
+     * or false if no error has occured
+     *
+     * @return string|false An error message if there has been an error, else false
+     * @since  1.0.0
+     */
+    function error() {
+        if ($this->error) {
+            //Put some template variables for debugging here ...
+            $debug_tpl_vars = array(
+                '{LANGUAGE}' => $this->language,
+                '{PATH}' => $this->language_path
+            );
+            $msg = str_replace(
+                array_keys($debug_tpl_vars),
+                array_values($debug_tpl_vars),
+                $this->error_messages[$this->error]);
+
+            return "<br /><strong>GeSHi Error:</strong> $msg (code {$this->error})<br />";
+        }
+        return false;
+    }
+
+    /**
+     * Gets a human-readable language name (thanks to Simon Patterson
+     * for the idea :))
+     *
+     * @return string The name for the current language
+     * @since  1.0.2
+     */
+    function get_language_name() {
+        if (GESHI_ERROR_NO_SUCH_LANG == $this->error) {
+            return $this->language_data['LANG_NAME'] . ' (Unknown Language)';
+        }
+        return $this->language_data['LANG_NAME'];
+    }
+
+    /**
+     * Sets the source code for this object
+     *
+     * @param string The source code to highlight
+     * @since 1.0.0
+     */
+    function set_source($source) {
+        $this->source = $source;
+        $this->highlight_extra_lines = array();
+    }
+
+    /**
+     * Sets the language for this object
+     *
+     * @note since 1.0.8 this function won't reset language-settings by default anymore!
+     *       if you need this set $force_reset = true
+     *
+     * @param string The name of the language to use
+     * @since 1.0.0
+     */
+    function set_language($language, $force_reset = false) {
+        if ($force_reset) {
+            $this->loaded_language = false;
+        }
+
+        //Clean up the language name to prevent malicious code injection
+        $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language);
+
+        $language = strtolower($language);
+
+        //Retreive the full filename
+        $file_name = $this->language_path . $language . '.php';
+        if ($file_name == $this->loaded_language) {
+            // this language is already loaded!
+            return;
+        }
+
+        $this->language = $language;
+
+        $this->error = false;
+        $this->strict_mode = GESHI_NEVER;
+
+        //Check if we can read the desired file
+        if (!is_readable($file_name)) {
+            $this->error = GESHI_ERROR_NO_SUCH_LANG;
+            return;
+        }
+
+        // Load the language for parsing
+        $this->load_language($file_name);
+    }
+
+    /**
+     * Sets the path to the directory containing the language files. Note
+     * that this path is relative to the directory of the script that included
+     * geshi.php, NOT geshi.php itself.
+     *
+     * @param string The path to the language directory
+     * @since 1.0.0
+     * @deprecated The path to the language files should now be automatically
+     *             detected, so this method should no longer be needed. The
+     *             1.1.X branch handles manual setting of the path differently
+     *             so this method will disappear in 1.2.0.
+     */
+    function set_language_path($path) {
+        if(strpos($path,':')) {
+            //Security Fix to prevent external directories using fopen wrappers.
+            if(DIRECTORY_SEPARATOR == "\\") {
+                if(!preg_match('#^[a-zA-Z]:#', $path) || false !== strpos($path, ':', 2)) {
+                    return;
+                }
+            } else {
+                return;
+            }
+        }
+        if(preg_match('#[^/a-zA-Z0-9_\.\-\\\s:]#', $path)) {
+            //Security Fix to prevent external directories using fopen wrappers.
+            return;
+        }
+        if(GESHI_SECURITY_PARANOID && false !== strpos($path, '/.')) {
+            //Security Fix to prevent external directories using fopen wrappers.
+            return;
+        }
+        if(GESHI_SECURITY_PARANOID && false !== strpos($path, '..')) {
+            //Security Fix to prevent external directories using fopen wrappers.
+            return;
+        }
+        if ($path) {
+            $this->language_path = ('/' == $path[strlen($path) - 1]) ? $path : $path . '/';
+            $this->set_language($this->language); // otherwise set_language_path has no effect
+        }
+    }
+
+    /**
+     * Get supported langs or an associative array lang=>full_name.
+     * @param boolean $longnames
+     * @return array
+     */
+    function get_supported_languages($full_names=false)
+    {
+        // return array
+        $back = array();
+
+        // we walk the lang root
+        $dir = dir($this->language_path);
+
+        // foreach entry
+        while (false !== ($entry = $dir->read()))
+        {
+            $full_path = $this->language_path.$entry;
+
+            // Skip all dirs
+            if (is_dir($full_path)) {
+                continue;
+            }
+
+            // we only want lang.php files
+            if (!preg_match('/^([^.]+)\.php$/', $entry, $matches)) {
+                continue;
+            }
+
+            // Raw lang name is here
+            $langname = $matches[1];
+
+            // We want the fullname too?
+            if ($full_names === true)
+            {
+                if (false !== ($fullname = $this->get_language_fullname($langname)))
+                {
+                    $back[$langname] = $fullname; // we go associative
+                }
+            }
+            else
+            {
+                // just store raw langname
+                $back[] = $langname;
+            }
+        }
+
+        $dir->close();
+
+        return $back;
+    }
+
+    /**
+     * Get full_name for a lang or false.
+     * @param string $language short langname (html4strict for example)
+     * @return mixed
+     */
+    function get_language_fullname($language)
+    {
+        //Clean up the language name to prevent malicious code injection
+        $language = preg_replace('#[^a-zA-Z0-9\-_]#', '', $language);
+
+        $language = strtolower($language);
+
+        // get fullpath-filename for a langname
+        $fullpath = $this->language_path.$language.'.php';
+
+        // we need to get contents :S
+        if (false === ($data = file_get_contents($fullpath))) {
+            $this->error = sprintf('Geshi::get_lang_fullname() Unknown Language: %s', $language);
+            return false;
+        }
+
+        // match the langname
+        if (!preg_match('/\'LANG_NAME\'\s*=>\s*\'((?:[^\']|\\\')+?)\'/', $data, $matches)) {
+            $this->error = sprintf('Geshi::get_lang_fullname(%s): Regex can not detect language', $language);
+            return false;
+        }
+
+        // return fullname for langname
+        return stripcslashes($matches[1]);
+    }
+
+    /**
+     * Sets the type of header to be used.
+     *
+     * If GESHI_HEADER_DIV is used, the code is surrounded in a "div".This
+     * means more source code but more control over tab width and line-wrapping.
+     * GESHI_HEADER_PRE means that a "pre" is used - less source, but less
+     * control. Default is GESHI_HEADER_PRE.
+     *
+     * From 1.0.7.2, you can use GESHI_HEADER_NONE to specify that no header code
+     * should be outputted.
+     *
+     * @param int The type of header to be used
+     * @since 1.0.0
+     */
+    function set_header_type($type) {
+        //Check if we got a valid header type
+        if (!in_array($type, array(GESHI_HEADER_NONE, GESHI_HEADER_DIV,
+            GESHI_HEADER_PRE, GESHI_HEADER_PRE_VALID, GESHI_HEADER_PRE_TABLE))) {
+            $this->error = GESHI_ERROR_INVALID_HEADER_TYPE;
+            return;
+        }
+
+        //Set that new header type
+        $this->header_type = $type;
+    }
+
+    /**
+     * Sets the styles for the code that will be outputted
+     * when this object is parsed. The style should be a
+     * string of valid stylesheet declarations
+     *
+     * @param string  The overall style for the outputted code block
+     * @param boolean Whether to merge the styles with the current styles or not
+     * @since 1.0.0
+     */
+    function set_overall_style($style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->overall_style = $style;
+        } else {
+            $this->overall_style .= $style;
+        }
+    }
+
+    /**
+     * Sets the overall classname for this block of code. This
+     * class can then be used in a stylesheet to style this object's
+     * output
+     *
+     * @param string The class name to use for this block of code
+     * @since 1.0.0
+     */
+    function set_overall_class($class) {
+        $this->overall_class = $class;
+    }
+
+    /**
+     * Sets the overall id for this block of code. This id can then
+     * be used in a stylesheet to style this object's output
+     *
+     * @param string The ID to use for this block of code
+     * @since 1.0.0
+     */
+    function set_overall_id($id) {
+        $this->overall_id = $id;
+    }
+
+    /**
+     * Sets whether CSS classes should be used to highlight the source. Default
+     * is off, calling this method with no arguments will turn it on
+     *
+     * @param boolean Whether to turn classes on or not
+     * @since 1.0.0
+     */
+    function enable_classes($flag = true) {
+        $this->use_classes = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the style for the actual code. This should be a string
+     * containing valid stylesheet declarations. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * Note: Use this method to override any style changes you made to
+     * the line numbers if you are using line numbers, else the line of
+     * code will have the same style as the line number! Consult the
+     * GeSHi documentation for more information about this.
+     *
+     * @param string  The style to use for actual code
+     * @param boolean Whether to merge the current styles with the new styles
+     * @since 1.0.2
+     */
+    function set_code_style($style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->code_style = $style;
+        } else {
+            $this->code_style .= $style;
+        }
+    }
+
+    /**
+     * Sets the styles for the line numbers.
+     *
+     * @param string The style for the line numbers that are "normal"
+     * @param string|boolean If a string, this is the style of the line
+     *        numbers that are "fancy", otherwise if boolean then this
+     *        defines whether the normal styles should be merged with the
+     *        new normal styles or not
+     * @param boolean If set, is the flag for whether to merge the "fancy"
+     *        styles with the current styles or not
+     * @since 1.0.2
+     */
+    function set_line_style($style1, $style2 = '', $preserve_defaults = false) {
+        //Check if we got 2 or three parameters
+        if (is_bool($style2)) {
+            $preserve_defaults = $style2;
+            $style2 = '';
+        }
+
+        //Actually set the new styles
+        if (!$preserve_defaults) {
+            $this->line_style1 = $style1;
+            $this->line_style2 = $style2;
+        } else {
+            $this->line_style1 .= $style1;
+            $this->line_style2 .= $style2;
+        }
+    }
+
+    /**
+     * Sets whether line numbers should be displayed.
+     *
+     * Valid values for the first parameter are:
+     *
+     *  - GESHI_NO_LINE_NUMBERS: Line numbers will not be displayed
+     *  - GESHI_NORMAL_LINE_NUMBERS: Line numbers will be displayed
+     *  - GESHI_FANCY_LINE_NUMBERS: Fancy line numbers will be displayed
+     *
+     * For fancy line numbers, the second parameter is used to signal which lines
+     * are to be fancy. For example, if the value of this parameter is 5 then every
+     * 5th line will be fancy.
+     *
+     * @param int How line numbers should be displayed
+     * @param int Defines which lines are fancy
+     * @since 1.0.0
+     */
+    function enable_line_numbers($flag, $nth_row = 5) {
+        if (GESHI_NO_LINE_NUMBERS != $flag && GESHI_NORMAL_LINE_NUMBERS != $flag
+            && GESHI_FANCY_LINE_NUMBERS != $flag) {
+            $this->error = GESHI_ERROR_INVALID_LINE_NUMBER_TYPE;
+        }
+        $this->line_numbers = $flag;
+        $this->line_nth_row = $nth_row;
+    }
+
+    /**
+     * Sets wether spans and other HTML markup generated by GeSHi can
+     * span over multiple lines or not. Defaults to true to reduce overhead.
+     * Set it to false if you want to manipulate the output or manually display
+     * the code in an ordered list.
+     *
+     * @param boolean Wether multiline spans are allowed or not
+     * @since 1.0.7.22
+     */
+    function enable_multiline_span($flag) {
+        $this->allow_multiline_span = (bool) $flag;
+    }
+
+    /**
+     * Get current setting for multiline spans, see GeSHi->enable_multiline_span().
+     *
+     * @see enable_multiline_span
+     * @return bool
+     */
+    function get_multiline_span() {
+        return $this->allow_multiline_span;
+    }
+
+    /**
+     * Sets the style for a keyword group. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param int     The key of the keyword group to change the styles of
+     * @param string  The style to make the keywords
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     */
+    function set_keyword_group_style($key, $style, $preserve_defaults = false) {
+        //Set the style for this keyword group
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['KEYWORDS'][$key] = $style;
+        } else {
+            $this->language_data['STYLES']['KEYWORDS'][$key] .= $style;
+        }
+
+        //Update the lexic permissions
+        if (!isset($this->lexic_permissions['KEYWORDS'][$key])) {
+            $this->lexic_permissions['KEYWORDS'][$key] = true;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for a keyword group
+     *
+     * @param int     The key of the keyword group to turn on or off
+     * @param boolean Whether to turn highlighting for that group on or off
+     * @since 1.0.0
+     */
+    function set_keyword_group_highlighting($key, $flag = true) {
+        $this->lexic_permissions['KEYWORDS'][$key] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for comment groups.  If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param int     The key of the comment group to change the styles of
+     * @param string  The style to make the comments
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     */
+    function set_comments_style($key, $style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['COMMENTS'][$key] = $style;
+        } else {
+            $this->language_data['STYLES']['COMMENTS'][$key] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for comment groups
+     *
+     * @param int     The key of the comment group to turn on or off
+     * @param boolean Whether to turn highlighting for that group on or off
+     * @since 1.0.0
+     */
+    function set_comments_highlighting($key, $flag = true) {
+        $this->lexic_permissions['COMMENTS'][$key] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for escaped characters. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the escape characters
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     */
+    function set_escape_characters_style($style, $preserve_defaults = false, $group = 0) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['ESCAPE_CHAR'][$group] = $style;
+        } else {
+            $this->language_data['STYLES']['ESCAPE_CHAR'][$group] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for escaped characters
+     *
+     * @param boolean Whether to turn highlighting for escape characters on or off
+     * @since 1.0.0
+     */
+    function set_escape_characters_highlighting($flag = true) {
+        $this->lexic_permissions['ESCAPE_CHAR'] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for brackets. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * This method is DEPRECATED: use set_symbols_style instead.
+     * This method will be removed in 1.2.X
+     *
+     * @param string  The style to make the brackets
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     * @deprecated In favour of set_symbols_style
+     */
+    function set_brackets_style($style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['BRACKETS'][0] = $style;
+        } else {
+            $this->language_data['STYLES']['BRACKETS'][0] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for brackets
+     *
+     * This method is DEPRECATED: use set_symbols_highlighting instead.
+     * This method will be remove in 1.2.X
+     *
+     * @param boolean Whether to turn highlighting for brackets on or off
+     * @since 1.0.0
+     * @deprecated In favour of set_symbols_highlighting
+     */
+    function set_brackets_highlighting($flag) {
+        $this->lexic_permissions['BRACKETS'] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for symbols. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the symbols
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @param int     Tells the group of symbols for which style should be set.
+     * @since 1.0.1
+     */
+    function set_symbols_style($style, $preserve_defaults = false, $group = 0) {
+        // Update the style of symbols
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['SYMBOLS'][$group] = $style;
+        } else {
+            $this->language_data['STYLES']['SYMBOLS'][$group] .= $style;
+        }
+
+        // For backward compatibility
+        if (0 == $group) {
+            $this->set_brackets_style ($style, $preserve_defaults);
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for symbols
+     *
+     * @param boolean Whether to turn highlighting for symbols on or off
+     * @since 1.0.0
+     */
+    function set_symbols_highlighting($flag) {
+        // Update lexic permissions for this symbol group
+        $this->lexic_permissions['SYMBOLS'] = ($flag) ? true : false;
+
+        // For backward compatibility
+        $this->set_brackets_highlighting ($flag);
+    }
+
+    /**
+     * Sets the styles for strings. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the escape characters
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @param int     Tells the group of strings for which style should be set.
+     * @since 1.0.0
+     */
+    function set_strings_style($style, $preserve_defaults = false, $group = 0) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['STRINGS'][$group] = $style;
+        } else {
+            $this->language_data['STYLES']['STRINGS'][$group] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for strings
+     *
+     * @param boolean Whether to turn highlighting for strings on or off
+     * @since 1.0.0
+     */
+    function set_strings_highlighting($flag) {
+        $this->lexic_permissions['STRINGS'] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for strict code blocks. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the script blocks
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @param int     Tells the group of script blocks for which style should be set.
+     * @since 1.0.8.4
+     */
+    function set_script_style($style, $preserve_defaults = false, $group = 0) {
+        // Update the style of symbols
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['SCRIPT'][$group] = $style;
+        } else {
+            $this->language_data['STYLES']['SCRIPT'][$group] .= $style;
+        }
+    }
+
+    /**
+     * Sets the styles for numbers. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the numbers
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @param int     Tells the group of numbers for which style should be set.
+     * @since 1.0.0
+     */
+    function set_numbers_style($style, $preserve_defaults = false, $group = 0) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['NUMBERS'][$group] = $style;
+        } else {
+            $this->language_data['STYLES']['NUMBERS'][$group] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for numbers
+     *
+     * @param boolean Whether to turn highlighting for numbers on or off
+     * @since 1.0.0
+     */
+    function set_numbers_highlighting($flag) {
+        $this->lexic_permissions['NUMBERS'] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for methods. $key is a number that references the
+     * appropriate "object splitter" - see the language file for the language
+     * you are highlighting to get this number. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param int     The key of the object splitter to change the styles of
+     * @param string  The style to make the methods
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     */
+    function set_methods_style($key, $style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['METHODS'][$key] = $style;
+        } else {
+            $this->language_data['STYLES']['METHODS'][$key] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for methods
+     *
+     * @param boolean Whether to turn highlighting for methods on or off
+     * @since 1.0.0
+     */
+    function set_methods_highlighting($flag) {
+        $this->lexic_permissions['METHODS'] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets the styles for regexps. If $preserve_defaults is
+     * true, then styles are merged with the default styles, with the
+     * user defined styles having priority
+     *
+     * @param string  The style to make the regular expression matches
+     * @param boolean Whether to merge the new styles with the old or just
+     *                to overwrite them
+     * @since 1.0.0
+     */
+    function set_regexps_style($key, $style, $preserve_defaults = false) {
+        if (!$preserve_defaults) {
+            $this->language_data['STYLES']['REGEXPS'][$key] = $style;
+        } else {
+            $this->language_data['STYLES']['REGEXPS'][$key] .= $style;
+        }
+    }
+
+    /**
+     * Turns highlighting on/off for regexps
+     *
+     * @param int     The key of the regular expression group to turn on or off
+     * @param boolean Whether to turn highlighting for the regular expression group on or off
+     * @since 1.0.0
+     */
+    function set_regexps_highlighting($key, $flag) {
+        $this->lexic_permissions['REGEXPS'][$key] = ($flag) ? true : false;
+    }
+
+    /**
+     * Sets whether a set of keywords are checked for in a case sensitive manner
+     *
+     * @param int The key of the keyword group to change the case sensitivity of
+     * @param boolean Whether to check in a case sensitive manner or not
+     * @since 1.0.0
+     */
+    function set_case_sensitivity($key, $case) {
+        $this->language_data['CASE_SENSITIVE'][$key] = ($case) ? true : false;
+    }
+
+    /**
+     * Sets the case that keywords should use when found. Use the constants:
+     *
+     *  - GESHI_CAPS_NO_CHANGE: leave keywords as-is
+     *  - GESHI_CAPS_UPPER: convert all keywords to uppercase where found
+     *  - GESHI_CAPS_LOWER: convert all keywords to lowercase where found
+     *
+     * @param int A constant specifying what to do with matched keywords
+     * @since 1.0.1
+     */
+    function set_case_keywords($case) {
+        if (in_array($case, array(
+            GESHI_CAPS_NO_CHANGE, GESHI_CAPS_UPPER, GESHI_CAPS_LOWER))) {
+            $this->language_data['CASE_KEYWORDS'] = $case;
+        }
+    }
+
+    /**
+     * Sets how many spaces a tab is substituted for
+     *
+     * Widths below zero are ignored
+     *
+     * @param int The tab width
+     * @since 1.0.0
+     */
+    function set_tab_width($width) {
+        $this->tab_width = intval($width);
+
+        //Check if it fit's the constraints:
+        if ($this->tab_width < 1) {
+            //Return it to the default
+            $this->tab_width = 8;
+        }
+    }
+
+    /**
+     * Sets whether or not to use tab-stop width specifed by language
+     *
+     * @param boolean Whether to use language-specific tab-stop widths
+     * @since 1.0.7.20
+     */
+    function set_use_language_tab_width($use) {
+        $this->use_language_tab_width = (bool) $use;
+    }
+
+    /**
+     * Returns the tab width to use, based on the current language and user
+     * preference
+     *
+     * @return int Tab width
+     * @since 1.0.7.20
+     */
+    function get_real_tab_width() {
+        if (!$this->use_language_tab_width ||
+            !isset($this->language_data['TAB_WIDTH'])) {
+            return $this->tab_width;
+        } else {
+            return $this->language_data['TAB_WIDTH'];
+        }
+    }
+
+    /**
+     * Enables/disables strict highlighting. Default is off, calling this
+     * method without parameters will turn it on. See documentation
+     * for more details on strict mode and where to use it.
+     *
+     * @param boolean Whether to enable strict mode or not
+     * @since 1.0.0
+     */
+    function enable_strict_mode($mode = true) {
+        if (GESHI_MAYBE == $this->language_data['STRICT_MODE_APPLIES']) {
+            $this->strict_mode = ($mode) ? GESHI_ALWAYS : GESHI_NEVER;
+        }
+    }
+
+    /**
+     * Disables all highlighting
+     *
+     * @since 1.0.0
+     * @todo  Rewrite with array traversal
+     * @deprecated In favour of enable_highlighting
+     */
+    function disable_highlighting() {
+        $this->enable_highlighting(false);
+    }
+
+    /**
+     * Enables all highlighting
+     *
+     * The optional flag parameter was added in version 1.0.7.21 and can be used
+     * to enable (true) or disable (false) all highlighting.
+     *
+     * @since 1.0.0
+     * @param boolean A flag specifying whether to enable or disable all highlighting
+     * @todo  Rewrite with array traversal
+     */
+    function enable_highlighting($flag = true) {
+        $flag = $flag ? true : false;
+        foreach ($this->lexic_permissions as $key => $value) {
+            if (is_array($value)) {
+                foreach ($value as $k => $v) {
+                    $this->lexic_permissions[$key][$k] = $flag;
+                }
+            } else {
+                $this->lexic_permissions[$key] = $flag;
+            }
+        }
+
+        // Context blocks
+        $this->enable_important_blocks = $flag;
+    }
+
+    /**
+     * Given a file extension, this method returns either a valid geshi language
+     * name, or the empty string if it couldn't be found
+     *
+     * @param string The extension to get a language name for
+     * @param array  A lookup array to use instead of the default one
+     * @since 1.0.5
+     * @todo Re-think about how this method works (maybe make it private and/or make it
+     *       a extension->lang lookup?)
+     * @todo static?
+     */
+    function get_language_name_from_extension( $extension, $lookup = array() ) {
+        $extension = strtolower($extension);
+
+        if ( !is_array($lookup) || empty($lookup)) {
+            $lookup = array(
+                '6502acme' => array( 'a', 's', 'asm', 'inc' ),
+                '6502tasm' => array( 'a', 's', 'asm', 'inc' ),
+                '6502kickass' => array( 'a', 's', 'asm', 'inc' ),
+                '68000devpac' => array( 'a', 's', 'asm', 'inc' ),
+                'abap' => array('abap'),
+                'actionscript' => array('as'),
+                'ada' => array('a', 'ada', 'adb', 'ads'),
+                'apache' => array('conf'),
+                'asm' => array('ash', 'asm', 'inc'),
+                'asp' => array('asp'),
+                'bash' => array('sh'),
+                'bf' => array('bf'),
+                'c' => array('c', 'h'),
+                'c_mac' => array('c', 'h'),
+                'caddcl' => array(),
+                'cadlisp' => array(),
+                'cdfg' => array('cdfg'),
+                'cobol' => array('cbl'),
+                'cpp' => array('cpp', 'hpp', 'C', 'H', 'CPP', 'HPP'),
+                'csharp' => array('cs'),
+                'css' => array('css'),
+                'd' => array('d'),
+                'delphi' => array('dpk', 'dpr', 'pp', 'pas'),
+                'diff' => array('diff', 'patch'),
+                'dos' => array('bat', 'cmd'),
+                'gdb' => array('kcrash', 'crash', 'bt'),
+                'gettext' => array('po', 'pot'),
+                'gml' => array('gml'),
+                'gnuplot' => array('plt'),
+                'groovy' => array('groovy'),
+                'haskell' => array('hs'),
+                'haxe' => array('hx'),
+                'html4strict' => array('html', 'htm'),
+                'ini' => array('ini', 'desktop'),
+                'java' => array('java'),
+                'javascript' => array('js'),
+                'klonec' => array('kl1'),
+                'klonecpp' => array('klx'),
+                'latex' => array('tex'),
+                'lisp' => array('lisp'),
+                'lua' => array('lua'),
+                'matlab' => array('m'),
+                'mpasm' => array(),
+                'mysql' => array('sql'),
+                'nsis' => array(),
+                'objc' => array(),
+                'oobas' => array(),
+                'oracle8' => array(),
+                'oracle10' => array(),
+                'pascal' => array('pas'),
+                'perl' => array('pl', 'pm'),
+                'php' => array('php', 'php5', 'phtml', 'phps'),
+                'povray' => array('pov'),
+                'providex' => array('pvc', 'pvx'),
+                'prolog' => array('pl'),
+                'python' => array('py'),
+                'qbasic' => array('bi'),
+                'reg' => array('reg'),
+                'ruby' => array('rb'),
+                'sas' => array('sas'),
+                'scala' => array('scala'),
+                'scheme' => array('scm'),
+                'scilab' => array('sci'),
+                'smalltalk' => array('st'),
+                'smarty' => array(),
+                'tcl' => array('tcl'),
+                'text' => array('txt'),
+                'vb' => array('bas'),
+                'vbnet' => array(),
+                'visualfoxpro' => array(),
+                'whitespace' => array('ws'),
+                'xml' => array('xml', 'svg', 'xrc'),
+                'z80' => array('z80', 'asm', 'inc')
+            );
+        }
+
+        foreach ($lookup as $lang => $extensions) {
+            if (in_array($extension, $extensions)) {
+                return $lang;
+            }
+        }
+
+        return 'text';
+    }
+
+    /**
+     * Given a file name, this method loads its contents in, and attempts
+     * to set the language automatically. An optional lookup table can be
+     * passed for looking up the language name. If not specified a default
+     * table is used
+     *
+     * The language table is in the form
+     * <pre>array(
+     *   'lang_name' => array('extension', 'extension', ...),
+     *   'lang_name' ...
+     * );</pre>
+     *
+     * @param string The filename to load the source from
+     * @param array  A lookup array to use instead of the default one
+     * @todo Complete rethink of this and above method
+     * @since 1.0.5
+     */
+    function load_from_file($file_name, $lookup = array()) {
+        if (is_readable($file_name)) {
+            $this->set_source(file_get_contents($file_name));
+            $this->set_language($this->get_language_name_from_extension(substr(strrchr($file_name, '.'), 1), $lookup));
+        } else {
+            $this->error = GESHI_ERROR_FILE_NOT_READABLE;
+        }
+    }
+
+    /**
+     * Adds a keyword to a keyword group for highlighting
+     *
+     * @param int    The key of the keyword group to add the keyword to
+     * @param string The word to add to the keyword group
+     * @since 1.0.0
+     */
+    function add_keyword($key, $word) {
+        if (!is_array($this->language_data['KEYWORDS'][$key])) {
+            $this->language_data['KEYWORDS'][$key] = array();
+        }
+        if (!in_array($word, $this->language_data['KEYWORDS'][$key])) {
+            $this->language_data['KEYWORDS'][$key][] = $word;
+
+            //NEW in 1.0.8 don't recompile the whole optimized regexp, simply append it
+            if ($this->parse_cache_built) {
+                $subkey = count($this->language_data['CACHED_KEYWORD_LISTS'][$key]) - 1;
+                $this->language_data['CACHED_KEYWORD_LISTS'][$key][$subkey] .= '|' . preg_quote($word, '/');
+            }
+        }
+    }
+
+    /**
+     * Removes a keyword from a keyword group
+     *
+     * @param int    The key of the keyword group to remove the keyword from
+     * @param string The word to remove from the keyword group
+     * @param bool   Wether to automatically recompile the optimized regexp list or not.
+     *               Note: if you set this to false and @see GeSHi->parse_code() was already called once,
+     *               for the current language, you have to manually call @see GeSHi->optimize_keyword_group()
+     *               or the removed keyword will stay in cache and still be highlighted! On the other hand
+     *               it might be too expensive to recompile the regexp list for every removal if you want to
+     *               remove a lot of keywords.
+     * @since 1.0.0
+     */
+    function remove_keyword($key, $word, $recompile = true) {
+        $key_to_remove = array_search($word, $this->language_data['KEYWORDS'][$key]);
+        if ($key_to_remove !== false) {
+            unset($this->language_data['KEYWORDS'][$key][$key_to_remove]);
+
+            //NEW in 1.0.8, optionally recompile keyword group
+            if ($recompile && $this->parse_cache_built) {
+                $this->optimize_keyword_group($key);
+            }
+        }
+    }
+
+    /**
+     * Creates a new keyword group
+     *
+     * @param int    The key of the keyword group to create
+     * @param string The styles for the keyword group
+     * @param boolean Whether the keyword group is case sensitive ornot
+     * @param array  The words to use for the keyword group
+     * @since 1.0.0
+     */
+    function add_keyword_group($key, $styles, $case_sensitive = true, $words = array()) {
+        $words = (array) $words;
+        if  (empty($words)) {
+            // empty word lists mess up highlighting
+            return false;
+        }
+
+        //Add the new keyword group internally
+        $this->language_data['KEYWORDS'][$key] = $words;
+        $this->lexic_permissions['KEYWORDS'][$key] = true;
+        $this->language_data['CASE_SENSITIVE'][$key] = $case_sensitive;
+        $this->language_data['STYLES']['KEYWORDS'][$key] = $styles;
+
+        //NEW in 1.0.8, cache keyword regexp
+        if ($this->parse_cache_built) {
+            $this->optimize_keyword_group($key);
+        }
+    }
+
+    /**
+     * Removes a keyword group
+     *
+     * @param int    The key of the keyword group to remove
+     * @since 1.0.0
+     */
+    function remove_keyword_group ($key) {
+        //Remove the keyword group internally
+        unset($this->language_data['KEYWORDS'][$key]);
+        unset($this->lexic_permissions['KEYWORDS'][$key]);
+        unset($this->language_data['CASE_SENSITIVE'][$key]);
+        unset($this->language_data['STYLES']['KEYWORDS'][$key]);
+
+        //NEW in 1.0.8
+        unset($this->language_data['CACHED_KEYWORD_LISTS'][$key]);
+    }
+
+    /**
+     * compile optimized regexp list for keyword group
+     *
+     * @param int   The key of the keyword group to compile & optimize
+     * @since 1.0.8
+     */
+    function optimize_keyword_group($key) {
+        $this->language_data['CACHED_KEYWORD_LISTS'][$key] =
+            $this->optimize_regexp_list($this->language_data['KEYWORDS'][$key]);
+        $space_as_whitespace = false;
+        if(isset($this->language_data['PARSER_CONTROL'])) {
+            if(isset($this->language_data['PARSER_CONTROL']['KEYWORDS'])) {
+                if(isset($this->language_data['PARSER_CONTROL']['KEYWORDS']['SPACE_AS_WHITESPACE'])) {
+                    $space_as_whitespace = $this->language_data['PARSER_CONTROL']['KEYWORDS']['SPACE_AS_WHITESPACE'];
+                }
+                if(isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$key]['SPACE_AS_WHITESPACE'])) {
+                    if(isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$key]['SPACE_AS_WHITESPACE'])) {
+                        $space_as_whitespace = $this->language_data['PARSER_CONTROL']['KEYWORDS'][$key]['SPACE_AS_WHITESPACE'];
+                    }
+                }
+            }
+        }
+        if($space_as_whitespace) {
+            foreach($this->language_data['CACHED_KEYWORD_LISTS'][$key] as $rxk => $rxv) {
+                $this->language_data['CACHED_KEYWORD_LISTS'][$key][$rxk] =
+                    str_replace(" ", "\\s+", $rxv);
+            }
+        }
+    }
+
+    /**
+     * Sets the content of the header block
+     *
+     * @param string The content of the header block
+     * @since 1.0.2
+     */
+    function set_header_content($content) {
+        $this->header_content = $content;
+    }
+
+    /**
+     * Sets the content of the footer block
+     *
+     * @param string The content of the footer block
+     * @since 1.0.2
+     */
+    function set_footer_content($content) {
+        $this->footer_content = $content;
+    }
+
+    /**
+     * Sets the style for the header content
+     *
+     * @param string The style for the header content
+     * @since 1.0.2
+     */
+    function set_header_content_style($style) {
+        $this->header_content_style = $style;
+    }
+
+    /**
+     * Sets the style for the footer content
+     *
+     * @param string The style for the footer content
+     * @since 1.0.2
+     */
+    function set_footer_content_style($style) {
+        $this->footer_content_style = $style;
+    }
+
+    /**
+     * Sets whether to force a surrounding block around
+     * the highlighted code or not
+     *
+     * @param boolean Tells whether to enable or disable this feature
+     * @since 1.0.7.20
+     */
+    function enable_inner_code_block($flag) {
+        $this->force_code_block = (bool)$flag;
+    }
+
+    /**
+     * Sets the base URL to be used for keywords
+     *
+     * @param int The key of the keyword group to set the URL for
+     * @param string The URL to set for the group. If {FNAME} is in
+     *               the url somewhere, it is replaced by the keyword
+     *               that the URL is being made for
+     * @since 1.0.2
+     */
+    function set_url_for_keyword_group($group, $url) {
+        $this->language_data['URLS'][$group] = $url;
+    }
+
+    /**
+     * Sets styles for links in code
+     *
+     * @param int A constant that specifies what state the style is being
+     *            set for - e.g. :hover or :visited
+     * @param string The styles to use for that state
+     * @since 1.0.2
+     */
+    function set_link_styles($type, $styles) {
+        $this->link_styles[$type] = $styles;
+    }
+
+    /**
+     * Sets the target for links in code
+     *
+     * @param string The target for links in the code, e.g. _blank
+     * @since 1.0.3
+     */
+    function set_link_target($target) {
+        if (!$target) {
+            $this->link_target = '';
+        } else {
+            $this->link_target = ' target="' . $target . '"';
+        }
+    }
+
+    /**
+     * Sets styles for important parts of the code
+     *
+     * @param string The styles to use on important parts of the code
+     * @since 1.0.2
+     */
+    function set_important_styles($styles) {
+        $this->important_styles = $styles;
+    }
+
+    /**
+     * Sets whether context-important blocks are highlighted
+     *
+     * @param boolean Tells whether to enable or disable highlighting of important blocks
+     * @todo REMOVE THIS SHIZ FROM GESHI!
+     * @deprecated
+     * @since 1.0.2
+     */
+    function enable_important_blocks($flag) {
+        $this->enable_important_blocks = ( $flag ) ? true : false;
+    }
+
+    /**
+     * Whether CSS IDs should be added to each line
+     *
+     * @param boolean If true, IDs will be added to each line.
+     * @since 1.0.2
+     */
+    function enable_ids($flag = true) {
+        $this->add_ids = ($flag) ? true : false;
+    }
+
+    /**
+     * Specifies which lines to highlight extra
+     *
+     * The extra style parameter was added in 1.0.7.21.
+     *
+     * @param mixed An array of line numbers to highlight, or just a line
+     *              number on its own.
+     * @param string A string specifying the style to use for this line.
+     *              If null is specified, the default style is used.
+     *              If false is specified, the line will be removed from
+     *              special highlighting
+     * @since 1.0.2
+     * @todo  Some data replication here that could be cut down on
+     */
+    function highlight_lines_extra($lines, $style = null) {
+        if (is_array($lines)) {
+            //Split up the job using single lines at a time
+            foreach ($lines as $line) {
+                $this->highlight_lines_extra($line, $style);
+            }
+        } else {
+            //Mark the line as being highlighted specially
+            $lines = intval($lines);
+            $this->highlight_extra_lines[$lines] = $lines;
+
+            //Decide on which style to use
+            if ($style === null) { //Check if we should use default style
+                unset($this->highlight_extra_lines_styles[$lines]);
+            } elseif ($style === false) { //Check if to remove this line
+                unset($this->highlight_extra_lines[$lines]);
+                unset($this->highlight_extra_lines_styles[$lines]);
+            } else {
+                $this->highlight_extra_lines_styles[$lines] = $style;
+            }
+        }
+    }
+
+    /**
+     * Sets the style for extra-highlighted lines
+     *
+     * @param string The style for extra-highlighted lines
+     * @since 1.0.2
+     */
+    function set_highlight_lines_extra_style($styles) {
+        $this->highlight_extra_lines_style = $styles;
+    }
+
+    /**
+     * Sets the line-ending
+     *
+     * @param string The new line-ending
+     * @since 1.0.2
+     */
+    function set_line_ending($line_ending) {
+        $this->line_ending = (string)$line_ending;
+    }
+
+    /**
+     * Sets what number line numbers should start at. Should
+     * be a positive integer, and will be converted to one.
+     *
+     * <b>Warning:</b> Using this method will add the "start"
+     * attribute to the &lt;ol&gt; that is used for line numbering.
+     * This is <b>not</b> valid XHTML strict, so if that's what you
+     * care about then don't use this method. Firefox is getting
+     * support for the CSS method of doing this in 1.1 and Opera
+     * has support for the CSS method, but (of course) IE doesn't
+     * so it's not worth doing it the CSS way yet.
+     *
+     * @param int The number to start line numbers at
+     * @since 1.0.2
+     */
+    function start_line_numbers_at($number) {
+        $this->line_numbers_start = abs(intval($number));
+    }
+
+    /**
+     * Sets the encoding used for htmlspecialchars(), for international
+     * support.
+     *
+     * NOTE: This is not needed for now because htmlspecialchars() is not
+     * being used (it has a security hole in PHP4 that has not been patched).
+     * Maybe in a future version it may make a return for speed reasons, but
+     * I doubt it.
+     *
+     * @param string The encoding to use for the source
+     * @since 1.0.3
+     */
+    function set_encoding($encoding) {
+        if ($encoding) {
+          $this->encoding = strtolower($encoding);
+        }
+    }
+
+    /**
+     * Turns linking of keywords on or off.
+     *
+     * @param boolean If true, links will be added to keywords
+     * @since 1.0.2
+     */
+    function enable_keyword_links($enable = true) {
+        $this->keyword_links = (bool) $enable;
+    }
+
+    /**
+     * Setup caches needed for styling. This is automatically called in
+     * parse_code() and get_stylesheet() when appropriate. This function helps
+     * stylesheet generators as they rely on some style information being
+     * preprocessed
+     *
+     * @since 1.0.8
+     * @access private
+     */
+    function build_style_cache() {
+        //Build the style cache needed to highlight numbers appropriate
+        if($this->lexic_permissions['NUMBERS']) {
+            //First check what way highlighting information for numbers are given
+            if(!isset($this->language_data['NUMBERS'])) {
+                $this->language_data['NUMBERS'] = 0;
+            }
+
+            if(is_array($this->language_data['NUMBERS'])) {
+                $this->language_data['NUMBERS_CACHE'] = $this->language_data['NUMBERS'];
+            } else {
+                $this->language_data['NUMBERS_CACHE'] = array();
+                if(!$this->language_data['NUMBERS']) {
+                    $this->language_data['NUMBERS'] =
+                        GESHI_NUMBER_INT_BASIC |
+                        GESHI_NUMBER_FLT_NONSCI;
+                }
+
+                for($i = 0, $j = $this->language_data['NUMBERS']; $j > 0; ++$i, $j>>=1) {
+                    //Rearrange style indices if required ...
+                    if(isset($this->language_data['STYLES']['NUMBERS'][1<<$i])) {
+                        $this->language_data['STYLES']['NUMBERS'][$i] =
+                            $this->language_data['STYLES']['NUMBERS'][1<<$i];
+                        unset($this->language_data['STYLES']['NUMBERS'][1<<$i]);
+                    }
+
+                    //Check if this bit is set for highlighting
+                    if($j&1) {
+                        //So this bit is set ...
+                        //Check if it belongs to group 0 or the actual stylegroup
+                        if(isset($this->language_data['STYLES']['NUMBERS'][$i])) {
+                            $this->language_data['NUMBERS_CACHE'][$i] = 1 << $i;
+                        } else {
+                            if(!isset($this->language_data['NUMBERS_CACHE'][0])) {
+                                $this->language_data['NUMBERS_CACHE'][0] = 0;
+                            }
+                            $this->language_data['NUMBERS_CACHE'][0] |= 1 << $i;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Setup caches needed for parsing. This is automatically called in parse_code() when appropriate.
+     * This function makes stylesheet generators much faster as they do not need these caches.
+     *
+     * @since 1.0.8
+     * @access private
+     */
+    function build_parse_cache() {
+        // cache symbol regexp
+        //As this is a costy operation, we avoid doing it for multiple groups ...
+        //Instead we perform it for all symbols at once.
+        //
+        //For this to work, we need to reorganize the data arrays.
+        if ($this->lexic_permissions['SYMBOLS'] && !empty($this->language_data['SYMBOLS'])) {
+            $this->language_data['MULTIPLE_SYMBOL_GROUPS'] = count($this->language_data['STYLES']['SYMBOLS']) > 1;
+
+            $this->language_data['SYMBOL_DATA'] = array();
+            $symbol_preg_multi = array(); // multi char symbols
+            $symbol_preg_single = array(); // single char symbols
+            foreach ($this->language_data['SYMBOLS'] as $key => $symbols) {
+                if (is_array($symbols)) {
+                    foreach ($symbols as $sym) {
+                        $sym = $this->hsc($sym);
+                        if (!isset($this->language_data['SYMBOL_DATA'][$sym])) {
+                            $this->language_data['SYMBOL_DATA'][$sym] = $key;
+                            if (isset($sym[1])) { // multiple chars
+                                $symbol_preg_multi[] = preg_quote($sym, '/');
+                            } else { // single char
+                                if ($sym == '-') {
+                                    // don't trigger range out of order error
+                                    $symbol_preg_single[] = '\-';
+                                } else {
+                                    $symbol_preg_single[] = preg_quote($sym, '/');
+                                }
+                            }
+                        }
+                    }
+                } else {
+                    $symbols = $this->hsc($symbols);
+                    if (!isset($this->language_data['SYMBOL_DATA'][$symbols])) {
+                        $this->language_data['SYMBOL_DATA'][$symbols] = 0;
+                        if (isset($symbols[1])) { // multiple chars
+                            $symbol_preg_multi[] = preg_quote($symbols, '/');
+                        } elseif ($symbols == '-') {
+                            // don't trigger range out of order error
+                            $symbol_preg_single[] = '\-';
+                        } else { // single char
+                            $symbol_preg_single[] = preg_quote($symbols, '/');
+                        }
+                    }
+                }
+            }
+
+            //Now we have an array with each possible symbol as the key and the style as the actual data.
+            //This way we can set the correct style just the moment we highlight ...
+            //
+            //Now we need to rewrite our array to get a search string that
+            $symbol_preg = array();
+            if (!empty($symbol_preg_multi)) {
+                rsort($symbol_preg_multi);
+                $symbol_preg[] = implode('|', $symbol_preg_multi);
+            }
+            if (!empty($symbol_preg_single)) {
+                rsort($symbol_preg_single);
+                $symbol_preg[] = '[' . implode('', $symbol_preg_single) . ']';
+            }
+            $this->language_data['SYMBOL_SEARCH'] = implode("|", $symbol_preg);
+        }
+
+        // cache optimized regexp for keyword matching
+        // remove old cache
+        $this->language_data['CACHED_KEYWORD_LISTS'] = array();
+        foreach (array_keys($this->language_data['KEYWORDS']) as $key) {
+            if (!isset($this->lexic_permissions['KEYWORDS'][$key]) ||
+                    $this->lexic_permissions['KEYWORDS'][$key]) {
+                $this->optimize_keyword_group($key);
+            }
+        }
+
+        // brackets
+        if ($this->lexic_permissions['BRACKETS']) {
+            $this->language_data['CACHE_BRACKET_MATCH'] = array('[', ']', '(', ')', '{', '}');
+            if (!$this->use_classes && isset($this->language_data['STYLES']['BRACKETS'][0])) {
+                $this->language_data['CACHE_BRACKET_REPLACE'] = array(
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#91;|>',
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#93;|>',
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#40;|>',
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#41;|>',
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#123;|>',
+                    '<| style="' . $this->language_data['STYLES']['BRACKETS'][0] . '">&#125;|>',
+                );
+            }
+            else {
+                $this->language_data['CACHE_BRACKET_REPLACE'] = array(
+                    '<| class="br0">&#91;|>',
+                    '<| class="br0">&#93;|>',
+                    '<| class="br0">&#40;|>',
+                    '<| class="br0">&#41;|>',
+                    '<| class="br0">&#123;|>',
+                    '<| class="br0">&#125;|>',
+                );
+            }
+        }
+
+        //Build the parse cache needed to highlight numbers appropriate
+        if($this->lexic_permissions['NUMBERS']) {
+            //Check if the style rearrangements have been processed ...
+            //This also does some preprocessing to check which style groups are useable ...
+            if(!isset($this->language_data['NUMBERS_CACHE'])) {
+                $this->build_style_cache();
+            }
+
+            //Number format specification
+            //All this formats are matched case-insensitively!
+            static $numbers_format = array(
+                GESHI_NUMBER_INT_BASIC =>
+                    '(?:(?<![0-9a-z_\.%$@])|(?<=\.\.))(?<![\d\.]e[+\-])([1-9]\d*?|0)(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_INT_CSTYLE =>
+                    '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])([1-9]\d*?|0)l(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_BIN_SUFFIX =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])[01]+?[bB](?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_BIN_PREFIX_PERCENT =>
+                    '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])%[01]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_BIN_PREFIX_0B =>
+                    '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])0b[01]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_OCT_PREFIX =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])0[0-7]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_OCT_PREFIX_0O =>
+                    '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])0o[0-7]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_OCT_PREFIX_AT =>
+                    '(?<![0-9a-z_\.%])(?<![\d\.]e[+\-])\@[0-7]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_OCT_SUFFIX =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])[0-7]+?o(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_HEX_PREFIX =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])0x[0-9a-fA-F]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_HEX_PREFIX_DOLLAR =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\$[0-9a-fA-F]+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_HEX_SUFFIX =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\d[0-9a-fA-F]*?[hH](?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_FLT_NONSCI =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\d+?\.\d+?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_FLT_NONSCI_F =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])(?:\d+?(?:\.\d*?)?|\.\d+?)f(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_FLT_SCI_SHORT =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])\.\d+?(?:e[+\-]?\d+?)?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)',
+                GESHI_NUMBER_FLT_SCI_ZERO =>
+                    '(?<![0-9a-z_\.])(?<![\d\.]e[+\-])(?:\d+?(?:\.\d*?)?|\.\d+?)(?:e[+\-]?\d+?)?(?![0-9a-z]|\.(?:[eE][+\-]?)?\d)'
+                );
+
+            //At this step we have an associative array with flag groups for a
+            //specific style or an string denoting a regexp given its index.
+            $this->language_data['NUMBERS_RXCACHE'] = array();
+            foreach($this->language_data['NUMBERS_CACHE'] as $key => $rxdata) {
+                if(is_string($rxdata)) {
+                    $regexp = $rxdata;
+                } else {
+                    //This is a bitfield of number flags to highlight:
+                    //Build an array, implode them together and make this the actual RX
+                    $rxuse = array();
+                    for($i = 1; $i <= $rxdata; $i<<=1) {
+                        if($rxdata & $i) {
+                            $rxuse[] = $numbers_format[$i];
+                        }
+                    }
+                    $regexp = implode("|", $rxuse);
+                }
+
+                $this->language_data['NUMBERS_RXCACHE'][$key] =
+                    "/(?<!<\|\/)(?<!<\|!REG3XP)(?<!<\|\/NUM!)(?<!\d\/>)($regexp)(?!(?:<DOT>|(?>[^\<]))+>)(?![^<]*>)(?!\|>)(?!\/>)/i"; //
+            }
+
+            if(!isset($this->language_data['PARSER_CONTROL']['NUMBERS']['PRECHECK_RX'])) {
+                $this->language_data['PARSER_CONTROL']['NUMBERS']['PRECHECK_RX'] = '#\d#';
+            }
+        }
+
+        $this->parse_cache_built = true;
+    }
+
+    /**
+     * Returns the code in $this->source, highlighted and surrounded by the
+     * nessecary HTML.
+     *
+     * This should only be called ONCE, cos it's SLOW! If you want to highlight
+     * the same source multiple times, you're better off doing a whole lot of
+     * str_replaces to replace the &lt;span&gt;s
+     *
+     * @since 1.0.0
+     */
+    function parse_code () {
+        // Start the timer
+        $start_time = microtime();
+
+        // Replace all newlines to a common form.
+        $code = str_replace("\r\n", "\n", $this->source);
+        $code = str_replace("\r", "\n", $code);
+
+        // Firstly, if there is an error, we won't highlight
+        if ($this->error) {
+            //Escape the source for output
+            $result = $this->hsc($this->source);
+
+            //This fix is related to SF#1923020, but has to be applied regardless of
+            //actually highlighting symbols.
+            $result = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $result);
+
+            // Timing is irrelevant
+            $this->set_time($start_time, $start_time);
+            $this->finalise($result);
+            return $result;
+        }
+
+        // make sure the parse cache is up2date
+        if (!$this->parse_cache_built) {
+            $this->build_parse_cache();
+        }
+
+        // Initialise various stuff
+        $length           = strlen($code);
+        $COMMENT_MATCHED  = false;
+        $stuff_to_parse   = '';
+        $endresult        = '';
+
+        // "Important" selections are handled like multiline comments
+        // @todo GET RID OF THIS SHIZ
+        if ($this->enable_important_blocks) {
+            $this->language_data['COMMENT_MULTI'][GESHI_START_IMPORTANT] = GESHI_END_IMPORTANT;
+        }
+
+        if ($this->strict_mode) {
+            // Break the source into bits. Each bit will be a portion of the code
+            // within script delimiters - for example, HTML between < and >
+            $k = 0;
+            $parts = array();
+            $matches = array();
+            $next_match_pointer = null;
+            // we use a copy to unset delimiters on demand (when they are not found)
+            $delim_copy = $this->language_data['SCRIPT_DELIMITERS'];
+            $i = 0;
+            while ($i < $length) {
+                $next_match_pos = $length + 1; // never true
+                foreach ($delim_copy as $dk => $delimiters) {
+                    if(is_array($delimiters)) {
+                        foreach ($delimiters as $open => $close) {
+                            // make sure the cache is setup properly
+                            if (!isset($matches[$dk][$open])) {
+                                $matches[$dk][$open] = array(
+                                    'next_match' => -1,
+                                    'dk' => $dk,
+
+                                    'open' => $open, // needed for grouping of adjacent code blocks (see below)
+                                    'open_strlen' => strlen($open),
+
+                                    'close' => $close,
+                                    'close_strlen' => strlen($close),
+                                );
+                            }
+                            // Get the next little bit for this opening string
+                            if ($matches[$dk][$open]['next_match'] < $i) {
+                                // only find the next pos if it was not already cached
+                                $open_pos = strpos($code, $open, $i);
+                                if ($open_pos === false) {
+                                    // no match for this delimiter ever
+                                    unset($delim_copy[$dk][$open]);
+                                    continue;
+                                }
+                                $matches[$dk][$open]['next_match'] = $open_pos;
+                            }
+                            if ($matches[$dk][$open]['next_match'] < $next_match_pos) {
+                                //So we got a new match, update the close_pos
+                                $matches[$dk][$open]['close_pos'] =
+                                    strpos($code, $close, $matches[$dk][$open]['next_match']+1);
+
+                                $next_match_pointer =& $matches[$dk][$open];
+                                $next_match_pos = $matches[$dk][$open]['next_match'];
+                            }
+                        }
+                    } else {
+                        //So we should match an RegExp as Strict Block ...
+                        /**
+                         * The value in $delimiters is expected to be an RegExp
+                         * containing exactly 2 matching groups:
+                         *  - Group 1 is the opener
+                         *  - Group 2 is the closer
+                         */
+                        if(!GESHI_PHP_PRE_433 && //Needs proper rewrite to work with PHP >=4.3.0; 4.3.3 is guaranteed to work.
+                            preg_match($delimiters, $code, $matches_rx, PREG_OFFSET_CAPTURE, $i)) {
+                            //We got a match ...
+                            if(isset($matches_rx['start']) && isset($matches_rx['end']))
+                            {
+                                $matches[$dk] = array(
+                                    'next_match' => $matches_rx['start'][1],
+                                    'dk' => $dk,
+
+                                    'close_strlen' => strlen($matches_rx['end'][0]),
+                                    'close_pos' => $matches_rx['end'][1],
+                                    );
+                            } else {
+                                $matches[$dk] = array(
+                                    'next_match' => $matches_rx[1][1],
+                                    'dk' => $dk,
+
+                                    'close_strlen' => strlen($matches_rx[2][0]),
+                                    'close_pos' => $matches_rx[2][1],
+                                    );
+                            }
+                        } else {
+                            // no match for this delimiter ever
+                            unset($delim_copy[$dk]);
+                            continue;
+                        }
+
+                        if ($matches[$dk]['next_match'] <= $next_match_pos) {
+                            $next_match_pointer =& $matches[$dk];
+                            $next_match_pos = $matches[$dk]['next_match'];
+                        }
+                    }
+                }
+
+                // non-highlightable text
+                $parts[$k] = array(
+                    1 => substr($code, $i, $next_match_pos - $i)
+                );
+                ++$k;
+
+                if ($next_match_pos > $length) {
+                    // out of bounds means no next match was found
+                    break;
+                }
+
+                // highlightable code
+                $parts[$k][0] = $next_match_pointer['dk'];
+
+                //Only combine for non-rx script blocks
+                if(is_array($delim_copy[$next_match_pointer['dk']])) {
+                    // group adjacent script blocks, e.g. <foobar><asdf> should be one block, not three!
+                    $i = $next_match_pos + $next_match_pointer['open_strlen'];
+                    while (true) {
+                        $close_pos = strpos($code, $next_match_pointer['close'], $i);
+                        if ($close_pos == false) {
+                            break;
+                        }
+                        $i = $close_pos + $next_match_pointer['close_strlen'];
+                        if ($i == $length) {
+                            break;
+                        }
+                        if ($code[$i] == $next_match_pointer['open'][0] && ($next_match_pointer['open_strlen'] == 1 ||
+                            substr($code, $i, $next_match_pointer['open_strlen']) == $next_match_pointer['open'])) {
+                            // merge adjacent but make sure we don't merge things like <tag><!-- comment -->
+                            foreach ($matches as $submatches) {
+                                foreach ($submatches as $match) {
+                                    if ($match['next_match'] == $i) {
+                                        // a different block already matches here!
+                                        break 3;
+                                    }
+                                }
+                            }
+                        } else {
+                            break;
+                        }
+                    }
+                } else {
+                    $close_pos = $next_match_pointer['close_pos'] + $next_match_pointer['close_strlen'];
+                    $i = $close_pos;
+                }
+
+                if ($close_pos === false) {
+                    // no closing delimiter found!
+                    $parts[$k][1] = substr($code, $next_match_pos);
+                    ++$k;
+                    break;
+                } else {
+                    $parts[$k][1] = substr($code, $next_match_pos, $i - $next_match_pos);
+                    ++$k;
+                }
+            }
+            unset($delim_copy, $next_match_pointer, $next_match_pos, $matches);
+            $num_parts = $k;
+
+            if ($num_parts == 1 && $this->strict_mode == GESHI_MAYBE) {
+                // when we have only one part, we don't have anything to highlight at all.
+                // if we have a "maybe" strict language, this should be handled as highlightable code
+                $parts = array(
+                    0 => array(
+                        0 => '',
+                        1 => ''
+                    ),
+                    1 => array(
+                        0 => null,
+                        1 => $parts[0][1]
+                    )
+                );
+                $num_parts = 2;
+            }
+
+        } else {
+            // Not strict mode - simply dump the source into
+            // the array at index 1 (the first highlightable block)
+            $parts = array(
+                0 => array(
+                    0 => '',
+                    1 => ''
+                ),
+                1 => array(
+                    0 => null,
+                    1 => $code
+                )
+            );
+            $num_parts = 2;
+        }
+
+        //Unset variables we won't need any longer
+        unset($code);
+
+        //Preload some repeatedly used values regarding hardquotes ...
+        $hq = isset($this->language_data['HARDQUOTE']) ? $this->language_data['HARDQUOTE'][0] : false;
+        $hq_strlen = strlen($hq);
+
+        //Preload if line numbers are to be generated afterwards
+        //Added a check if line breaks should be forced even without line numbers, fixes SF#1727398
+        $check_linenumbers = $this->line_numbers != GESHI_NO_LINE_NUMBERS ||
+            !empty($this->highlight_extra_lines) || !$this->allow_multiline_span;
+
+        //preload the escape char for faster checking ...
+        $escaped_escape_char = $this->hsc($this->language_data['ESCAPE_CHAR']);
+
+        // this is used for single-line comments
+        $sc_disallowed_before = "";
+        $sc_disallowed_after = "";
+
+        if (isset($this->language_data['PARSER_CONTROL'])) {
+            if (isset($this->language_data['PARSER_CONTROL']['COMMENTS'])) {
+                if (isset($this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_BEFORE'])) {
+                    $sc_disallowed_before = $this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_BEFORE'];
+                }
+                if (isset($this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_AFTER'])) {
+                    $sc_disallowed_after = $this->language_data['PARSER_CONTROL']['COMMENTS']['DISALLOWED_AFTER'];
+                }
+            }
+        }
+
+        //Fix for SF#1932083: Multichar Quotemarks unsupported
+        $is_string_starter = array();
+        if ($this->lexic_permissions['STRINGS']) {
+            foreach ($this->language_data['QUOTEMARKS'] as $quotemark) {
+                if (!isset($is_string_starter[$quotemark[0]])) {
+                    $is_string_starter[$quotemark[0]] = (string)$quotemark;
+                } elseif (is_string($is_string_starter[$quotemark[0]])) {
+                    $is_string_starter[$quotemark[0]] = array(
+                        $is_string_starter[$quotemark[0]],
+                        $quotemark);
+                } else {
+                    $is_string_starter[$quotemark[0]][] = $quotemark;
+                }
+            }
+        }
+
+        // Now we go through each part. We know that even-indexed parts are
+        // code that shouldn't be highlighted, and odd-indexed parts should
+        // be highlighted
+        for ($key = 0; $key < $num_parts; ++$key) {
+            $STRICTATTRS = '';
+
+            // If this block should be highlighted...
+            if (!($key & 1)) {
+                // Else not a block to highlight
+                $endresult .= $this->hsc($parts[$key][1]);
+                unset($parts[$key]);
+                continue;
+            }
+
+            $result = '';
+            $part = $parts[$key][1];
+
+            $highlight_part = true;
+            if ($this->strict_mode && !is_null($parts[$key][0])) {
+                // get the class key for this block of code
+                $script_key = $parts[$key][0];
+                $highlight_part = $this->language_data['HIGHLIGHT_STRICT_BLOCK'][$script_key];
+                if ($this->language_data['STYLES']['SCRIPT'][$script_key] != '' &&
+                    $this->lexic_permissions['SCRIPT']) {
+                    // Add a span element around the source to
+                    // highlight the overall source block
+                    if (!$this->use_classes &&
+                        $this->language_data['STYLES']['SCRIPT'][$script_key] != '') {
+                        $attributes = ' style="' . $this->language_data['STYLES']['SCRIPT'][$script_key] . '"';
+                    } else {
+                        $attributes = ' class="sc' . $script_key . '"';
+                    }
+                    $result .= "<span$attributes>";
+                    $STRICTATTRS = $attributes;
+                }
+            }
+
+            if ($highlight_part) {
+                // Now, highlight the code in this block. This code
+                // is really the engine of GeSHi (along with the method
+                // parse_non_string_part).
+
+                // cache comment regexps incrementally
+                $next_comment_regexp_key = '';
+                $next_comment_regexp_pos = -1;
+                $next_comment_multi_pos = -1;
+                $next_comment_single_pos = -1;
+                $comment_regexp_cache_per_key = array();
+                $comment_multi_cache_per_key = array();
+                $comment_single_cache_per_key = array();
+                $next_open_comment_multi = '';
+                $next_comment_single_key = '';
+                $escape_regexp_cache_per_key = array();
+                $next_escape_regexp_key = '';
+                $next_escape_regexp_pos = -1;
+
+                $length = strlen($part);
+                for ($i = 0; $i < $length; ++$i) {
+                    // Get the next char
+                    $char = $part[$i];
+                    $char_len = 1;
+
+                    // update regexp comment cache if needed
+                    if (isset($this->language_data['COMMENT_REGEXP']) && $next_comment_regexp_pos < $i) {
+                        $next_comment_regexp_pos = $length;
+                        foreach ($this->language_data['COMMENT_REGEXP'] as $comment_key => $regexp) {
+                            $match_i = false;
+                            if (isset($comment_regexp_cache_per_key[$comment_key]) &&
+                                ($comment_regexp_cache_per_key[$comment_key]['pos'] >= $i ||
+                                 $comment_regexp_cache_per_key[$comment_key]['pos'] === false)) {
+                                // we have already matched something
+                                if ($comment_regexp_cache_per_key[$comment_key]['pos'] === false) {
+                                    // this comment is never matched
+                                    continue;
+                                }
+                                $match_i = $comment_regexp_cache_per_key[$comment_key]['pos'];
+                            } elseif (
+                                //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible
+                                (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $i), $match, PREG_OFFSET_CAPTURE)) ||
+                                (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $i))
+                                ) {
+                                $match_i = $match[0][1];
+                                if (GESHI_PHP_PRE_433) {
+                                    $match_i += $i;
+                                }
+
+                                $comment_regexp_cache_per_key[$comment_key] = array(
+                                    'key' => $comment_key,
+                                    'length' => strlen($match[0][0]),
+                                    'pos' => $match_i
+                                );
+                            } else {
+                                $comment_regexp_cache_per_key[$comment_key]['pos'] = false;
+                                continue;
+                            }
+
+                            if ($match_i !== false && $match_i < $next_comment_regexp_pos) {
+                                $next_comment_regexp_pos = $match_i;
+                                $next_comment_regexp_key = $comment_key;
+                                if ($match_i === $i) {
+                                    break;
+                                }
+                            }
+                        }
+                    }
+
+                    $string_started = false;
+
+                    if (isset($is_string_starter[$char])) {
+                        // Possibly the start of a new string ...
+
+                        //Check which starter it was ...
+                        //Fix for SF#1932083: Multichar Quotemarks unsupported
+                        if (is_array($is_string_starter[$char])) {
+                            $char_new = '';
+                            foreach ($is_string_starter[$char] as $testchar) {
+                                if ($testchar === substr($part, $i, strlen($testchar)) &&
+                                    strlen($testchar) > strlen($char_new)) {
+                                    $char_new = $testchar;
+                                    $string_started = true;
+                                }
+                            }
+                            if ($string_started) {
+                                $char = $char_new;
+                            }
+                        } else {
+                            $testchar = $is_string_starter[$char];
+                            if ($testchar === substr($part, $i, strlen($testchar))) {
+                                $char = $testchar;
+                                $string_started = true;
+                            }
+                        }
+                        $char_len = strlen($char);
+                    }
+
+                    if ($string_started && ($i != $next_comment_regexp_pos)) {
+                        // Hand out the correct style information for this string
+                        $string_key = array_search($char, $this->language_data['QUOTEMARKS']);
+                        if (!isset($this->language_data['STYLES']['STRINGS'][$string_key]) ||
+                            !isset($this->language_data['STYLES']['ESCAPE_CHAR'][$string_key])) {
+                            $string_key = 0;
+                        }
+
+                        // parse the stuff before this
+                        $result .= $this->parse_non_string_part($stuff_to_parse);
+                        $stuff_to_parse = '';
+
+                        if (!$this->use_classes) {
+                            $string_attributes = ' style="' . $this->language_data['STYLES']['STRINGS'][$string_key] . '"';
+                        } else {
+                            $string_attributes = ' class="st'.$string_key.'"';
+                        }
+
+                        // now handle the string
+                        $string = "<span$string_attributes>" . GeSHi::hsc($char);
+                        $start = $i + $char_len;
+                        $string_open = true;
+
+                        if(empty($this->language_data['ESCAPE_REGEXP'])) {
+                            $next_escape_regexp_pos = $length;
+                        }
+
+                        do {
+                            //Get the regular ending pos ...
+                            $close_pos = strpos($part, $char, $start);
+                            if(false === $close_pos) {
+                                $close_pos = $length;
+                            }
+
+                            if($this->lexic_permissions['ESCAPE_CHAR']) {
+                                // update escape regexp cache if needed
+                                if (isset($this->language_data['ESCAPE_REGEXP']) && $next_escape_regexp_pos < $start) {
+                                    $next_escape_regexp_pos = $length;
+                                    foreach ($this->language_data['ESCAPE_REGEXP'] as $escape_key => $regexp) {
+                                        $match_i = false;
+                                        if (isset($escape_regexp_cache_per_key[$escape_key]) &&
+                                            ($escape_regexp_cache_per_key[$escape_key]['pos'] >= $start ||
+                                             $escape_regexp_cache_per_key[$escape_key]['pos'] === false)) {
+                                            // we have already matched something
+                                            if ($escape_regexp_cache_per_key[$escape_key]['pos'] === false) {
+                                                // this comment is never matched
+                                                continue;
+                                            }
+                                            $match_i = $escape_regexp_cache_per_key[$escape_key]['pos'];
+                                        } elseif (
+                                            //This is to allow use of the offset parameter in preg_match and stay as compatible with older PHP versions as possible
+                                            (GESHI_PHP_PRE_433 && preg_match($regexp, substr($part, $start), $match, PREG_OFFSET_CAPTURE)) ||
+                                            (!GESHI_PHP_PRE_433 && preg_match($regexp, $part, $match, PREG_OFFSET_CAPTURE, $start))
+                                            ) {
+                                            $match_i = $match[0][1];
+                                            if (GESHI_PHP_PRE_433) {
+                                                $match_i += $start;
+                                            }
+
+                                            $escape_regexp_cache_per_key[$escape_key] = array(
+                                                'key' => $escape_key,
+                                                'length' => strlen($match[0][0]),
+                                                'pos' => $match_i
+                                            );
+                                        } else {
+                                            $escape_regexp_cache_per_key[$escape_key]['pos'] = false;
+                                            continue;
+                                        }
+
+                                        if ($match_i !== false && $match_i < $next_escape_regexp_pos) {
+                                            $next_escape_regexp_pos = $match_i;
+                                            $next_escape_regexp_key = $escape_key;
+                                            if ($match_i === $start) {
+                                                break;
+                                            }
+                                        }
+                                    }
+                                }
+
+                                //Find the next simple escape position
+                                if('' != $this->language_data['ESCAPE_CHAR']) {
+                                    $simple_escape = strpos($part, $this->language_data['ESCAPE_CHAR'], $start);
+                                    if(false === $simple_escape) {
+                                        $simple_escape = $length;
+                                    }
+                                } else {
+                                    $simple_escape = $length;
+                                }
+                            } else {
+                                $next_escape_regexp_pos = $length;
+                                $simple_escape = $length;
+                            }
+
+                            if($simple_escape < $next_escape_regexp_pos &&
+                                $simple_escape < $length &&
+                                $simple_escape < $close_pos) {
+                                //The nexxt escape sequence is a simple one ...
+                                $es_pos = $simple_escape;
+
+                                //Add the stuff not in the string yet ...
+                                $string .= $this->hsc(substr($part, $start, $es_pos - $start));
+
+                                //Get the style for this escaped char ...
+                                if (!$this->use_classes) {
+                                    $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][0] . '"';
+                                } else {
+                                    $escape_char_attributes = ' class="es0"';
+                                }
+
+                                //Add the style for the escape char ...
+                                $string .= "<span$escape_char_attributes>" .
+                                    GeSHi::hsc($this->language_data['ESCAPE_CHAR']);
+
+                                //Get the byte AFTER the ESCAPE_CHAR we just found
+                                $es_char = $part[$es_pos + 1];
+                                if ($es_char == "\n") {
+                                    // don't put a newline around newlines
+                                    $string .= "</span>\n";
+                                    $start = $es_pos + 2;
+                                } elseif (ord($es_char) >= 128) {
+                                    //This is an non-ASCII char (UTF8 or single byte)
+                                    //This code tries to work around SF#2037598 ...
+                                    if(function_exists('mb_substr')) {
+                                        $es_char_m = mb_substr(substr($part, $es_pos+1, 16), 0, 1, $this->encoding);
+                                        $string .= $es_char_m . '</span>';
+                                    } elseif (!GESHI_PHP_PRE_433 && 'utf-8' == $this->encoding) {
+                                        if(preg_match("/[\xC2-\xDF][\x80-\xBF]".
+                                            "|\xE0[\xA0-\xBF][\x80-\xBF]".
+                                            "|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}".
+                                            "|\xED[\x80-\x9F][\x80-\xBF]".
+                                            "|\xF0[\x90-\xBF][\x80-\xBF]{2}".
+                                            "|[\xF1-\xF3][\x80-\xBF]{3}".
+                                            "|\xF4[\x80-\x8F][\x80-\xBF]{2}/s",
+                                            $part, $es_char_m, null, $es_pos + 1)) {
+                                            $es_char_m = $es_char_m[0];
+                                        } else {
+                                            $es_char_m = $es_char;
+                                        }
+                                        $string .= $this->hsc($es_char_m) . '</span>';
+                                    } else {
+                                        $es_char_m = $this->hsc($es_char);
+                                    }
+                                    $start = $es_pos + strlen($es_char_m) + 1;
+                                } else {
+                                    $string .= $this->hsc($es_char) . '</span>';
+                                    $start = $es_pos + 2;
+                                }
+                            } elseif ($next_escape_regexp_pos < $length &&
+                                $next_escape_regexp_pos < $close_pos) {
+                                $es_pos = $next_escape_regexp_pos;
+                                //Add the stuff not in the string yet ...
+                                $string .= $this->hsc(substr($part, $start, $es_pos - $start));
+
+                                //Get the key and length of this match ...
+                                $escape = $escape_regexp_cache_per_key[$next_escape_regexp_key];
+                                $escape_str = substr($part, $es_pos, $escape['length']);
+                                $escape_key = $escape['key'];
+
+                                //Get the style for this escaped char ...
+                                if (!$this->use_classes) {
+                                    $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR'][$escape_key] . '"';
+                                } else {
+                                    $escape_char_attributes = ' class="es' . $escape_key . '"';
+                                }
+
+                                //Add the style for the escape char ...
+                                $string .= "<span$escape_char_attributes>" .
+                                    $this->hsc($escape_str) . '</span>';
+
+                                $start = $es_pos + $escape['length'];
+                            } else {
+                                //Copy the remainder of the string ...
+                                $string .= $this->hsc(substr($part, $start, $close_pos - $start + $char_len)) . '</span>';
+                                $start = $close_pos + $char_len;
+                                $string_open = false;
+                            }
+                        } while($string_open);
+
+                        if ($check_linenumbers) {
+                            // Are line numbers used? If, we should end the string before
+                            // the newline and begin it again (so when <li>s are put in the source
+                            // remains XHTML compliant)
+                            // note to self: This opens up possibility of config files specifying
+                            // that languages can/cannot have multiline strings???
+                            $string = str_replace("\n", "</span>\n<span$string_attributes>", $string);
+                        }
+
+                        $result .= $string;
+                        $string = '';
+                        $i = $start - 1;
+                        continue;
+                    } elseif ($this->lexic_permissions['STRINGS'] && $hq && $hq[0] == $char &&
+                        substr($part, $i, $hq_strlen) == $hq && ($i != $next_comment_regexp_pos)) {
+                        // The start of a hard quoted string
+                        if (!$this->use_classes) {
+                            $string_attributes = ' style="' . $this->language_data['STYLES']['STRINGS']['HARD'] . '"';
+                            $escape_char_attributes = ' style="' . $this->language_data['STYLES']['ESCAPE_CHAR']['HARD'] . '"';
+                        } else {
+                            $string_attributes = ' class="st_h"';
+                            $escape_char_attributes = ' class="es_h"';
+                        }
+                        // parse the stuff before this
+                        $result .= $this->parse_non_string_part($stuff_to_parse);
+                        $stuff_to_parse = '';
+
+                        // now handle the string
+                        $string = '';
+
+                        // look for closing quote
+                        $start = $i + $hq_strlen;
+                        while ($close_pos = strpos($part, $this->language_data['HARDQUOTE'][1], $start)) {
+                            $start = $close_pos + 1;
+                            if ($this->lexic_permissions['ESCAPE_CHAR'] && $part[$close_pos - 1] == $this->language_data['HARDCHAR'] &&
+                                (($i + $hq_strlen) != ($close_pos))) { //Support empty string for HQ escapes if Starter = Escape
+                                // make sure this quote is not escaped
+                                foreach ($this->language_data['HARDESCAPE'] as $hardescape) {
+                                    if (substr($part, $close_pos - 1, strlen($hardescape)) == $hardescape) {
+                                        // check wether this quote is escaped or if it is something like '\\'
+                                        $escape_char_pos = $close_pos - 1;
+                                        while ($escape_char_pos > 0
+                                                && $part[$escape_char_pos - 1] == $this->language_data['HARDCHAR']) {
+                                            --$escape_char_pos;
+                                        }
+                                        if (($close_pos - $escape_char_pos) & 1) {
+                                            // uneven number of escape chars => this quote is escaped
+                                            continue 2;
+                                        }
+                                    }
+                                }
+                            }
+
+                            // found closing quote
+                            break;
+                        }
+
+                        //Found the closing delimiter?
+                        if (!$close_pos) {
+                            // span till the end of this $part when no closing delimiter is found
+                            $close_pos = $length;
+                        }
+
+                        //Get the actual string
+                        $string = substr($part, $i, $close_pos - $i + 1);
+                        $i = $close_pos;
+
+                        // handle escape chars and encode html chars
+                        // (special because when we have escape chars within our string they may not be escaped)
+                        if ($this->lexic_permissions['ESCAPE_CHAR'] && $this->language_data['ESCAPE_CHAR']) {
+                            $start = 0;
+                            $new_string = '';
+                            while ($es_pos = strpos($string, $this->language_data['ESCAPE_CHAR'], $start)) {
+                                // hmtl escape stuff before
+                                $new_string .= $this->hsc(substr($string, $start, $es_pos - $start));
+                                // check if this is a hard escape
+                                foreach ($this->language_data['HARDESCAPE'] as $hardescape) {
+                                    if (substr($string, $es_pos, strlen($hardescape)) == $hardescape) {
+                                        // indeed, this is a hardescape
+                                        $new_string .= "<span$escape_char_attributes>" .
+                                            $this->hsc($hardescape) . '</span>';
+                                        $start = $es_pos + strlen($hardescape);
+                                        continue 2;
+                                    }
+                                }
+                                // not a hard escape, but a normal escape
+                                // they come in pairs of two
+                                $c = 0;
+                                while (isset($string[$es_pos + $c]) && isset($string[$es_pos + $c + 1])
+                                    && $string[$es_pos + $c] == $this->language_data['ESCAPE_CHAR']
+                                    && $string[$es_pos + $c + 1] == $this->language_data['ESCAPE_CHAR']) {
+                                    $c += 2;
+                                }
+                                if ($c) {
+                                    $new_string .= "<span$escape_char_attributes>" .
+                                        str_repeat($escaped_escape_char, $c) .
+                                        '</span>';
+                                    $start = $es_pos + $c;
+                                } else {
+                                    // this is just a single lonely escape char...
+                                    $new_string .= $escaped_escape_char;
+                                    $start = $es_pos + 1;
+                                }
+                            }
+                            $string = $new_string . $this->hsc(substr($string, $start));
+                        } else {
+                            $string = $this->hsc($string);
+                        }
+
+                        if ($check_linenumbers) {
+                            // Are line numbers used? If, we should end the string before
+                            // the newline and begin it again (so when <li>s are put in the source
+                            // remains XHTML compliant)
+                            // note to self: This opens up possibility of config files specifying
+                            // that languages can/cannot have multiline strings???
+                            $string = str_replace("\n", "</span>\n<span$string_attributes>", $string);
+                        }
+
+                        $result .= "<span$string_attributes>" . $string . '</span>';
+                        $string = '';
+                        continue;
+                    } else {
+                        //Have a look for regexp comments
+                        if ($i == $next_comment_regexp_pos) {
+                            $COMMENT_MATCHED = true;
+                            $comment = $comment_regexp_cache_per_key[$next_comment_regexp_key];
+                            $test_str = $this->hsc(substr($part, $i, $comment['length']));
+
+                            //@todo If remove important do remove here
+                            if ($this->lexic_permissions['COMMENTS']['MULTI']) {
+                                if (!$this->use_classes) {
+                                    $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment['key']] . '"';
+                                } else {
+                                    $attributes = ' class="co' . $comment['key'] . '"';
+                                }
+
+                                $test_str = "<span$attributes>" . $test_str . "</span>";
+
+                                // Short-cut through all the multiline code
+                                if ($check_linenumbers) {
+                                    // strreplace to put close span and open span around multiline newlines
+                                    $test_str = str_replace(
+                                        "\n", "</span>\n<span$attributes>",
+                                        str_replace("\n ", "\n&nbsp;", $test_str)
+                                    );
+                                }
+                            }
+
+                            $i += $comment['length'] - 1;
+
+                            // parse the rest
+                            $result .= $this->parse_non_string_part($stuff_to_parse);
+                            $stuff_to_parse = '';
+                        }
+
+                        // If we haven't matched a regexp comment, try multi-line comments
+                        if (!$COMMENT_MATCHED) {
+                            // Is this a multiline comment?
+                            if (!empty($this->language_data['COMMENT_MULTI']) && $next_comment_multi_pos < $i) {
+                                $next_comment_multi_pos = $length;
+                                foreach ($this->language_data['COMMENT_MULTI'] as $open => $close) {
+                                    $match_i = false;
+                                    if (isset($comment_multi_cache_per_key[$open]) &&
+                                        ($comment_multi_cache_per_key[$open] >= $i ||
+                                         $comment_multi_cache_per_key[$open] === false)) {
+                                        // we have already matched something
+                                        if ($comment_multi_cache_per_key[$open] === false) {
+                                            // this comment is never matched
+                                            continue;
+                                        }
+                                        $match_i = $comment_multi_cache_per_key[$open];
+                                    } elseif (($match_i = stripos($part, $open, $i)) !== false) {
+                                        $comment_multi_cache_per_key[$open] = $match_i;
+                                    } else {
+                                        $comment_multi_cache_per_key[$open] = false;
+                                        continue;
+                                    }
+                                    if ($match_i !== false && $match_i < $next_comment_multi_pos) {
+                                        $next_comment_multi_pos = $match_i;
+                                        $next_open_comment_multi = $open;
+                                        if ($match_i === $i) {
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                            if ($i == $next_comment_multi_pos) {
+                                $open = $next_open_comment_multi;
+                                $close = $this->language_data['COMMENT_MULTI'][$open];
+                                $open_strlen = strlen($open);
+                                $close_strlen = strlen($close);
+                                $COMMENT_MATCHED = true;
+                                $test_str_match = $open;
+                                //@todo If remove important do remove here
+                                if ($this->lexic_permissions['COMMENTS']['MULTI'] ||
+                                    $open == GESHI_START_IMPORTANT) {
+                                    if ($open != GESHI_START_IMPORTANT) {
+                                        if (!$this->use_classes) {
+                                            $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS']['MULTI'] . '"';
+                                        } else {
+                                            $attributes = ' class="coMULTI"';
+                                        }
+                                        $test_str = "<span$attributes>" . $this->hsc($open);
+                                    } else {
+                                        if (!$this->use_classes) {
+                                            $attributes = ' style="' . $this->important_styles . '"';
+                                        } else {
+                                            $attributes = ' class="imp"';
+                                        }
+
+                                        // We don't include the start of the comment if it's an
+                                        // "important" part
+                                        $test_str = "<span$attributes>";
+                                    }
+                                } else {
+                                    $test_str = $this->hsc($open);
+                                }
+
+                                $close_pos = strpos( $part, $close, $i + $open_strlen );
+
+                                if ($close_pos === false) {
+                                    $close_pos = $length;
+                                }
+
+                                // Short-cut through all the multiline code
+                                $rest_of_comment = $this->hsc(substr($part, $i + $open_strlen, $close_pos - $i - $open_strlen + $close_strlen));
+                                if (($this->lexic_permissions['COMMENTS']['MULTI'] ||
+                                    $test_str_match == GESHI_START_IMPORTANT) &&
+                                    $check_linenumbers) {
+
+                                    // strreplace to put close span and open span around multiline newlines
+                                    $test_str .= str_replace(
+                                        "\n", "</span>\n<span$attributes>",
+                                        str_replace("\n ", "\n&nbsp;", $rest_of_comment)
+                                    );
+                                } else {
+                                    $test_str .= $rest_of_comment;
+                                }
+
+                                if ($this->lexic_permissions['COMMENTS']['MULTI'] ||
+                                    $test_str_match == GESHI_START_IMPORTANT) {
+                                    $test_str .= '</span>';
+                                }
+
+                                $i = $close_pos + $close_strlen - 1;
+
+                                // parse the rest
+                                $result .= $this->parse_non_string_part($stuff_to_parse);
+                                $stuff_to_parse = '';
+                            }
+                        }
+
+                        // If we haven't matched a multiline comment, try single-line comments
+                        if (!$COMMENT_MATCHED) {
+                            // cache potential single line comment occurances
+                            if (!empty($this->language_data['COMMENT_SINGLE']) && $next_comment_single_pos < $i) {
+                                $next_comment_single_pos = $length;
+                                foreach ($this->language_data['COMMENT_SINGLE'] as $comment_key => $comment_mark) {
+                                    $match_i = false;
+                                    if (isset($comment_single_cache_per_key[$comment_key]) &&
+                                        ($comment_single_cache_per_key[$comment_key] >= $i ||
+                                         $comment_single_cache_per_key[$comment_key] === false)) {
+                                        // we have already matched something
+                                        if ($comment_single_cache_per_key[$comment_key] === false) {
+                                            // this comment is never matched
+                                            continue;
+                                        }
+                                        $match_i = $comment_single_cache_per_key[$comment_key];
+                                    } elseif (
+                                        // case sensitive comments
+                                        ($this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] &&
+                                        ($match_i = stripos($part, $comment_mark, $i)) !== false) ||
+                                        // non case sensitive
+                                        (!$this->language_data['CASE_SENSITIVE'][GESHI_COMMENTS] &&
+                                          (($match_i = strpos($part, $comment_mark, $i)) !== false))) {
+                                        $comment_single_cache_per_key[$comment_key] = $match_i;
+                                    } else {
+                                        $comment_single_cache_per_key[$comment_key] = false;
+                                        continue;
+                                    }
+                                    if ($match_i !== false && $match_i < $next_comment_single_pos) {
+                                        $next_comment_single_pos = $match_i;
+                                        $next_comment_single_key = $comment_key;
+                                        if ($match_i === $i) {
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                            if ($next_comment_single_pos == $i) {
+                                $comment_key = $next_comment_single_key;
+                                $comment_mark = $this->language_data['COMMENT_SINGLE'][$comment_key];
+                                $com_len = strlen($comment_mark);
+
+                                // This check will find special variables like $# in bash
+                                // or compiler directives of Delphi beginning {$
+                                if ((empty($sc_disallowed_before) || ($i == 0) ||
+                                    (false === strpos($sc_disallowed_before, $part[$i-1]))) &&
+                                    (empty($sc_disallowed_after) || ($length <= $i + $com_len) ||
+                                    (false === strpos($sc_disallowed_after, $part[$i + $com_len]))))
+                                {
+                                    // this is a valid comment
+                                    $COMMENT_MATCHED = true;
+                                    if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
+                                        if (!$this->use_classes) {
+                                            $attributes = ' style="' . $this->language_data['STYLES']['COMMENTS'][$comment_key] . '"';
+                                        } else {
+                                            $attributes = ' class="co' . $comment_key . '"';
+                                        }
+                                        $test_str = "<span$attributes>" . $this->hsc($this->change_case($comment_mark));
+                                    } else {
+                                        $test_str = $this->hsc($comment_mark);
+                                    }
+
+                                    //Check if this comment is the last in the source
+                                    $close_pos = strpos($part, "\n", $i);
+                                    $oops = false;
+                                    if ($close_pos === false) {
+                                        $close_pos = $length;
+                                        $oops = true;
+                                    }
+                                    $test_str .= $this->hsc(substr($part, $i + $com_len, $close_pos - $i - $com_len));
+                                    if ($this->lexic_permissions['COMMENTS'][$comment_key]) {
+                                        $test_str .= "</span>";
+                                    }
+
+                                    // Take into account that the comment might be the last in the source
+                                    if (!$oops) {
+                                      $test_str .= "\n";
+                                    }
+
+                                    $i = $close_pos;
+
+                                    // parse the rest
+                                    $result .= $this->parse_non_string_part($stuff_to_parse);
+                                    $stuff_to_parse = '';
+                                }
+                            }
+                        }
+                    }
+
+                    // Where are we adding this char?
+                    if (!$COMMENT_MATCHED) {
+                        $stuff_to_parse .= $char;
+                    } else {
+                        $result .= $test_str;
+                        unset($test_str);
+                        $COMMENT_MATCHED = false;
+                    }
+                }
+                // Parse the last bit
+                $result .= $this->parse_non_string_part($stuff_to_parse);
+                $stuff_to_parse = '';
+            } else {
+                $result .= $this->hsc($part);
+            }
+            // Close the <span> that surrounds the block
+            if ($STRICTATTRS != '') {
+                $result = str_replace("\n", "</span>\n<span$STRICTATTRS>", $result);
+                $result .= '</span>';
+            }
+
+            $endresult .= $result;
+            unset($part, $parts[$key], $result);
+        }
+
+        //This fix is related to SF#1923020, but has to be applied regardless of
+        //actually highlighting symbols.
+        /** NOTE: memorypeak #3 */
+        $endresult = str_replace(array('<SEMI>', '<PIPE>'), array(';', '|'), $endresult);
+
+//        // Parse the last stuff (redundant?)
+//        $result .= $this->parse_non_string_part($stuff_to_parse);
+
+        // Lop off the very first and last spaces
+//        $result = substr($result, 1, -1);
+
+        // We're finished: stop timing
+        $this->set_time($start_time, microtime());
+
+        $this->finalise($endresult);
+        return $endresult;
+    }
+
+    /**
+     * Swaps out spaces and tabs for HTML indentation. Not needed if
+     * the code is in a pre block...
+     *
+     * @param  string The source to indent (reference!)
+     * @since  1.0.0
+     * @access private
+     */
+    function indent(&$result) {
+        /// Replace tabs with the correct number of spaces
+        if (false !== strpos($result, "\t")) {
+            $lines = explode("\n", $result);
+            $result = null;//Save memory while we process the lines individually
+            $tab_width = $this->get_real_tab_width();
+            $tab_string = '&nbsp;' . str_repeat(' ', $tab_width);
+
+            for ($key = 0, $n = count($lines); $key < $n; $key++) {
+                $line = $lines[$key];
+                if (false === strpos($line, "\t")) {
+                    continue;
+                }
+
+                $pos = 0;
+                $length = strlen($line);
+                $lines[$key] = ''; // reduce memory
+
+                $IN_TAG = false;
+                for ($i = 0; $i < $length; ++$i) {
+                    $char = $line[$i];
+                    // Simple engine to work out whether we're in a tag.
+                    // If we are we modify $pos. This is so we ignore HTML
+                    // in the line and only workout the tab replacement
+                    // via the actual content of the string
+                    // This test could be improved to include strings in the
+                    // html so that < or > would be allowed in user's styles
+                    // (e.g. quotes: '<' '>'; or similar)
+                    if ($IN_TAG) {
+                        if ('>' == $char) {
+                            $IN_TAG = false;
+                        }
+                        $lines[$key] .= $char;
+                    } elseif ('<' == $char) {
+                        $IN_TAG = true;
+                        $lines[$key] .= '<';
+                    } elseif ('&' == $char) {
+                        $substr = substr($line, $i + 3, 5);
+                        $posi = strpos($substr, ';');
+                        if (false === $posi) {
+                            ++$pos;
+                        } else {
+                            $pos -= $posi+2;
+                        }
+                        $lines[$key] .= $char;
+                    } elseif ("\t" == $char) {
+                        $str = '';
+                        // OPTIMISE - move $strs out. Make an array:
+                        // $tabs = array(
+                        //  1 => '&nbsp;',
+                        //  2 => '&nbsp; ',
+                        //  3 => '&nbsp; &nbsp;' etc etc
+                        // to use instead of building a string every time
+                        $tab_end_width = $tab_width - ($pos % $tab_width); //Moved out of the look as it doesn't change within the loop
+                        if (($pos & 1) || 1 == $tab_end_width) {
+                            $str .= substr($tab_string, 6, $tab_end_width);
+                        } else {
+                            $str .= substr($tab_string, 0, $tab_end_width+5);
+                        }
+                        $lines[$key] .= $str;
+                        $pos += $tab_end_width;
+
+                        if (false === strpos($line, "\t", $i + 1)) {
+                            $lines[$key] .= substr($line, $i + 1);
+                            break;
+                        }
+                    } elseif (0 == $pos && ' ' == $char) {
+                        $lines[$key] .= '&nbsp;';
+                        ++$pos;
+                    } else {
+                        $lines[$key] .= $char;
+                        ++$pos;
+                    }
+                }
+            }
+            $result = implode("\n", $lines);
+            unset($lines);//We don't need the lines separated beyond this --- free them!
+        }
+        // Other whitespace
+        // BenBE: Fix to reduce the number of replacements to be done
+        $result = preg_replace('/^ /m', '&nbsp;', $result);
+        $result = str_replace('  ', ' &nbsp;', $result);
+
+        if ($this->line_numbers == GESHI_NO_LINE_NUMBERS && $this->header_type != GESHI_HEADER_PRE_TABLE) {
+            if ($this->line_ending === null) {
+                $result = nl2br($result);
+            } else {
+                $result = str_replace("\n", $this->line_ending, $result);
+            }
+        }
+    }
+
+    /**
+     * Changes the case of a keyword for those languages where a change is asked for
+     *
+     * @param  string The keyword to change the case of
+     * @return string The keyword with its case changed
+     * @since  1.0.0
+     * @access private
+     */
+    function change_case($instr) {
+        switch ($this->language_data['CASE_KEYWORDS']) {
+            case GESHI_CAPS_UPPER:
+                return strtoupper($instr);
+            case GESHI_CAPS_LOWER:
+                return strtolower($instr);
+            default:
+                return $instr;
+        }
+    }
+
+    /**
+     * Handles replacements of keywords to include markup and links if requested
+     *
+     * @param  string The keyword to add the Markup to
+     * @return The HTML for the match found
+     * @since  1.0.8
+     * @access private
+     *
+     * @todo   Get rid of ender in keyword links
+     */
+    function handle_keyword_replace($match) {
+        $k = $this->_kw_replace_group;
+        $keyword = $match[0];
+        $keyword_match = $match[1];
+
+        $before = '';
+        $after = '';
+
+        if ($this->keyword_links) {
+            // Keyword links have been ebabled
+
+            if (isset($this->language_data['URLS'][$k]) &&
+                $this->language_data['URLS'][$k] != '') {
+                // There is a base group for this keyword
+
+                // Old system: strtolower
+                //$keyword = ( $this->language_data['CASE_SENSITIVE'][$group] ) ? $keyword : strtolower($keyword);
+                // New system: get keyword from language file to get correct case
+                if (!$this->language_data['CASE_SENSITIVE'][$k] &&
+                    strpos($this->language_data['URLS'][$k], '{FNAME}') !== false) {
+                    foreach ($this->language_data['KEYWORDS'][$k] as $word) {
+                        if (strcasecmp($word, $keyword_match) == 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    $word = $keyword_match;
+                }
+
+                $before = '<|UR1|"' .
+                    str_replace(
+                        array(
+                            '{FNAME}',
+                            '{FNAMEL}',
+                            '{FNAMEU}',
+                            '.'),
+                        array(
+                            str_replace('+', '%20', urlencode($this->hsc($word))),
+                            str_replace('+', '%20', urlencode($this->hsc(strtolower($word)))),
+                            str_replace('+', '%20', urlencode($this->hsc(strtoupper($word)))),
+                            '<DOT>'),
+                        $this->language_data['URLS'][$k]
+                    ) . '">';
+                $after = '</a>';
+            }
+        }
+
+        return $before . '<|/'. $k .'/>' . $this->change_case($keyword) . '|>' . $after;
+    }
+
+    /**
+     * handles regular expressions highlighting-definitions with callback functions
+     *
+     * @note this is a callback, don't use it directly
+     *
+     * @param array the matches array
+     * @return The highlighted string
+     * @since 1.0.8
+     * @access private
+     */
+    function handle_regexps_callback($matches) {
+        // before: "' style=\"' . call_user_func(\"$func\", '\\1') . '\"\\1|>'",
+        return  ' style="' . call_user_func($this->language_data['STYLES']['REGEXPS'][$this->_rx_key], $matches[1]) . '"'. $matches[1] . '|>';
+    }
+
+    /**
+     * handles newlines in REGEXPS matches. Set the _hmr_* vars before calling this
+     *
+     * @note this is a callback, don't use it directly
+     *
+     * @param array the matches array
+     * @return string
+     * @since 1.0.8
+     * @access private
+     */
+    function handle_multiline_regexps($matches) {
+        $before = $this->_hmr_before;
+        $after = $this->_hmr_after;
+        if ($this->_hmr_replace) {
+            $replace = $this->_hmr_replace;
+            $search = array();
+
+            foreach (array_keys($matches) as $k) {
+                $search[] = '\\' . $k;
+            }
+
+            $before = str_replace($search, $matches, $before);
+            $after = str_replace($search, $matches, $after);
+            $replace = str_replace($search, $matches, $replace);
+        } else {
+            $replace = $matches[0];
+        }
+        return $before
+                    . '<|!REG3XP' . $this->_hmr_key .'!>'
+                        . str_replace("\n", "|>\n<|!REG3XP" . $this->_hmr_key . '!>', $replace)
+                    . '|>'
+              . $after;
+    }
+
+    /**
+     * Takes a string that has no strings or comments in it, and highlights
+     * stuff like keywords, numbers and methods.
+     *
+     * @param string The string to parse for keyword, numbers etc.
+     * @since 1.0.0
+     * @access private
+     * @todo BUGGY! Why? Why not build string and return?
+     */
+    function parse_non_string_part($stuff_to_parse) {
+        $stuff_to_parse = ' ' . $this->hsc($stuff_to_parse);
+
+        // Highlight keywords
+        $disallowed_before = "(?<![a-zA-Z0-9\$_\|\#|^&";
+        $disallowed_after = "(?![a-zA-Z0-9_\|%\\-&;";
+        if ($this->lexic_permissions['STRINGS']) {
+            $quotemarks = preg_quote(implode($this->language_data['QUOTEMARKS']), '/');
+            $disallowed_before .= $quotemarks;
+            $disallowed_after .= $quotemarks;
+        }
+        $disallowed_before .= "])";
+        $disallowed_after .= "])";
+
+        $parser_control_pergroup = false;
+        if (isset($this->language_data['PARSER_CONTROL'])) {
+            if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'])) {
+                $x = 0; // check wether per-keyword-group parser_control is enabled
+                if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'])) {
+                    $disallowed_before = $this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_BEFORE'];
+                    ++$x;
+                }
+                if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'])) {
+                    $disallowed_after = $this->language_data['PARSER_CONTROL']['KEYWORDS']['DISALLOWED_AFTER'];
+                    ++$x;
+                }
+                $parser_control_pergroup = (count($this->language_data['PARSER_CONTROL']['KEYWORDS']) - $x) > 0;
+            }
+        }
+
+        foreach (array_keys($this->language_data['KEYWORDS']) as $k) {
+            if (!isset($this->lexic_permissions['KEYWORDS'][$k]) ||
+                $this->lexic_permissions['KEYWORDS'][$k]) {
+
+                $case_sensitive = $this->language_data['CASE_SENSITIVE'][$k];
+                $modifiers = $case_sensitive ? '' : 'i';
+
+                // NEW in 1.0.8 - per-keyword-group parser control
+                $disallowed_before_local = $disallowed_before;
+                $disallowed_after_local = $disallowed_after;
+                if ($parser_control_pergroup && isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k])) {
+                    if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_BEFORE'])) {
+                        $disallowed_before_local =
+                            $this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_BEFORE'];
+                    }
+
+                    if (isset($this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_AFTER'])) {
+                        $disallowed_after_local =
+                            $this->language_data['PARSER_CONTROL']['KEYWORDS'][$k]['DISALLOWED_AFTER'];
+                    }
+                }
+
+                $this->_kw_replace_group = $k;
+
+                //NEW in 1.0.8, the cached regexp list
+                // since we don't want PHP / PCRE to crash due to too large patterns we split them into smaller chunks
+                for ($set = 0, $set_length = count($this->language_data['CACHED_KEYWORD_LISTS'][$k]); $set <  $set_length; ++$set) {
+                    $keywordset =& $this->language_data['CACHED_KEYWORD_LISTS'][$k][$set];
+                    // Might make a more unique string for putting the number in soon
+                    // Basically, we don't put the styles in yet because then the styles themselves will
+                    // get highlighted if the language has a CSS keyword in it (like CSS, for example ;))
+                    $stuff_to_parse = preg_replace_callback(
+                        "/$disallowed_before_local({$keywordset})(?!\<DOT\>(?:htm|php|aspx?))$disallowed_after_local/$modifiers",
+                        array($this, 'handle_keyword_replace'),
+                        $stuff_to_parse
+                        );
+                }
+            }
+        }
+
+        // Regular expressions
+        foreach ($this->language_data['REGEXPS'] as $key => $regexp) {
+            if ($this->lexic_permissions['REGEXPS'][$key]) {
+                if (is_array($regexp)) {
+                    if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
+                        // produce valid HTML when we match multiple lines
+                        $this->_hmr_replace = $regexp[GESHI_REPLACE];
+                        $this->_hmr_before = $regexp[GESHI_BEFORE];
+                        $this->_hmr_key = $key;
+                        $this->_hmr_after = $regexp[GESHI_AFTER];
+                        $stuff_to_parse = preg_replace_callback(
+                            "/" . $regexp[GESHI_SEARCH] . "/{$regexp[GESHI_MODIFIERS]}",
+                            array($this, 'handle_multiline_regexps'),
+                            $stuff_to_parse);
+                        $this->_hmr_replace = false;
+                        $this->_hmr_before = '';
+                        $this->_hmr_after = '';
+                    } else {
+                        $stuff_to_parse = preg_replace(
+                            '/' . $regexp[GESHI_SEARCH] . '/' . $regexp[GESHI_MODIFIERS],
+                            $regexp[GESHI_BEFORE] . '<|!REG3XP'. $key .'!>' . $regexp[GESHI_REPLACE] . '|>' . $regexp[GESHI_AFTER],
+                            $stuff_to_parse);
+                    }
+                } else {
+                    if ($this->line_numbers != GESHI_NO_LINE_NUMBERS) {
+                        // produce valid HTML when we match multiple lines
+                        $this->_hmr_key = $key;
+                        $stuff_to_parse = preg_replace_callback( "/(" . $regexp . ")/",
+                                              array($this, 'handle_multiline_regexps'), $stuff_to_parse);
+                        $this->_hmr_key = '';
+                    } else {
+                        $stuff_to_parse = preg_replace( "/(" . $regexp . ")/", "<|!REG3XP$key!>\\1|>", $stuff_to_parse);
+                    }
+                }
+            }
+        }
+
+        // Highlight numbers. As of 1.0.8 we support different types of numbers
+        $numbers_found = false;
+
+        if ($this->lexic_permissions['NUMBERS'] && preg_match($this->language_data['PARSER_CONTROL']['NUMBERS']['PRECHECK_RX'], $stuff_to_parse )) {
+            $numbers_found = true;
+
+            //For each of the formats ...
+            foreach($this->language_data['NUMBERS_RXCACHE'] as $id => $regexp) {
+                //Check if it should be highlighted ...
+                $stuff_to_parse = preg_replace($regexp, "<|/NUM!$id/>\\1|>", $stuff_to_parse);
+            }
+        }
+
+        //
+        // Now that's all done, replace /[number]/ with the correct styles
+        //
+        foreach (array_keys($this->language_data['KEYWORDS']) as $k) {
+            if (!$this->use_classes) {
+                $attributes = ' style="' .
+                    (isset($this->language_data['STYLES']['KEYWORDS'][$k]) ?
+                    $this->language_data['STYLES']['KEYWORDS'][$k] : "") . '"';
+            } else {
+                $attributes = ' class="kw' . $k . '"';
+            }
+            $stuff_to_parse = str_replace("<|/$k/>", "<|$attributes>", $stuff_to_parse);
+        }
+
+        if ($numbers_found) {
+            // Put number styles in
+            foreach($this->language_data['NUMBERS_RXCACHE'] as $id => $regexp) {
+                //Commented out for now, as this needs some review ...
+                //                if ($numbers_permissions & $id) {
+                //Get the appropriate style ...
+                //Checking for unset styles is done by the style cache builder ...
+                if (!$this->use_classes) {
+                    $attributes = ' style="' . $this->language_data['STYLES']['NUMBERS'][$id] . '"';
+                } else {
+                    $attributes = ' class="nu'.$id.'"';
+                }
+
+                //Set in the correct styles ...
+                $stuff_to_parse = str_replace("/NUM!$id/", $attributes, $stuff_to_parse);
+                //                }
+            }
+        }
+
+        // Highlight methods and fields in objects
+        if ($this->lexic_permissions['METHODS'] && $this->language_data['OOLANG']) {
+            $oolang_spaces = "[\s]*";
+            $oolang_before = "";
+            $oolang_after = "[a-zA-Z][a-zA-Z0-9_]*";
+            if (isset($this->language_data['PARSER_CONTROL'])) {
+                if (isset($this->language_data['PARSER_CONTROL']['OOLANG'])) {
+                    if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_BEFORE'])) {
+                        $oolang_before = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_BEFORE'];
+                    }
+                    if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_AFTER'])) {
+                        $oolang_after = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_AFTER'];
+                    }
+                    if (isset($this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_SPACES'])) {
+                        $oolang_spaces = $this->language_data['PARSER_CONTROL']['OOLANG']['MATCH_SPACES'];
+                    }
+                }
+            }
+
+            foreach ($this->language_data['OBJECT_SPLITTERS'] as $key => $splitter) {
+                if (false !== strpos($stuff_to_parse, $splitter)) {
+                    if (!$this->use_classes) {
+                        $attributes = ' style="' . $this->language_data['STYLES']['METHODS'][$key] . '"';
+                    } else {
+                        $attributes = ' class="me' . $key . '"';
+                    }
+                    $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);
+                }