* @license http://www.gnu.org/copyleft/gpl.html GNU GPL * @copyright (C) 2004, 2005 Nigel McNie * @version 1.1.0 * */ /** * MAJOR TODOs: * * @todo [blocking 1.1.1] (bug 5) Support balanced context endings * @todo [blocking 1.1.1] (bug 14) OCCs should be able to modify their parent context * @todo [blocking 1.1.1] (bug 16, 17) Better Delphi and Codeworker support */ /** GeSHi Version */ define('GESHI_VERSION', '1.1.0'); /** Set the correct directory separator */ define('GESHI_DIR_SEPARATOR', ('WIN' != substr(PHP_OS, 0, 3)) ? '/' : '\\'); // Define the root directory for the GeSHi code tree if (!defined('GESHI_ROOT')) { /** The root directory for GeSHi (where class.geshi.php is located) */ define('GESHI_ROOT', dirname(__FILE__) . GESHI_DIR_SEPARATOR); } /**#@+ * @access private */ /** The data directory for GeSHi */ define('GESHI_DATA_ROOT', GESHI_ROOT . 'geshi' . GESHI_DIR_SEPARATOR); /** The classes directory for GeSHi */ define('GESHI_CLASSES_ROOT', GESHI_DATA_ROOT . 'classes' . GESHI_DIR_SEPARATOR); /** The languages directory for GeSHi */ define('GESHI_LANGUAGES_ROOT', GESHI_DATA_ROOT . 'languages' . GESHI_DIR_SEPARATOR); /** The context files directory for GeSHi */ define('GESHI_CONTEXTS_ROOT', GESHI_DATA_ROOT . 'contexts' . GESHI_DIR_SEPARATOR); /** The theme files directory for GeSHi */ define('GESHI_THEMES_ROOT', GESHI_DATA_ROOT . 'themes' . GESHI_DIR_SEPARATOR); /**#@-*/ /** Get required functions */ require GESHI_DATA_ROOT . 'functions.geshi.php'; /** Get styler class */ require GESHI_CLASSES_ROOT . 'class.geshistyler.php'; /** Get context class */ require GESHI_CLASSES_ROOT . 'class.geshicontext.php'; // // Constants // // Debugging constants. These will disappear in stable builds. Specify // as a bitfield, e.g. GESHI_DBG_API | GESHI_DBG_PARSE, by defining the // GESHI_DBG context before including this file /** No debugging */ define('GESHI_DBG_NONE', 0); /** Debug the API */ define('GESHI_DBG_API', 1); /** Debug actual parsing */ define('GESHI_DBG_PARSE', 2); /** Debug error handling */ define('GESHI_DBG_ERR', 4); /** Maximum debug level */ define('GESHI_DBG_ALL', GESHI_DBG_API | GESHI_DBG_PARSE | GESHI_DBG_ERR); // Set efault debug level if (!defined('GESHI_DBG')) { /** Default debug level */ define('GESHI_DBG', GESHI_DBG_NONE); } // These provide BACKWARD COMPATIBILITY ONLY // Use New Method setStyles(mixed identifiers, string styles); // e.g. setStyles('html/tag', 'styles'); // setStyles(array('html/tag', 'html/css-delimiters'), 'style'); /** Used to mark a context as having no equivalence in 1.0.X */ define('GESHI_STYLE_NONE', 0); /** Used to mark a context as being like a number in 1.0.X */ define('GESHI_STYLE_NUMBERS', 1); /** Used to mark a context as being like a comment in 1.0.X */ define('GESHI_STYLE_COMMENTS', 2); /** Used to mark a context as being like a string in 1.0.X */ define('GESHI_STYLE_STRINGS', 3); /** Used to mark a context as being like a symbol in 1.0.X */ define('GESHI_STYLE_SYMBOLS', 4); /** Used to mark a context as being like a method in 1.0.X */ define('GESHI_STYLE_METHODS', 5); /**#@+ * @access private */ // Error related constants, not needed by users of GeSHi /** No error has occured */ define('GESHI_ERROR_NONE', 0); /** There is no source code to highlight */ define('GESHI_ERROR_NO_INPUT', 1); /** You don't have a language file for the specified language */ define('GESHI_ERROR_NO_SUCH_LANG', 2); /** The language name contained illegal characters */ define('GESHI_ERROR_LANG_NAME_ILLEGAL_CHARS', 4); /** The path specified is not readable */ //define('GESHI_ERROR_ILLEGAL_PATH', 5); /** The theme specified does not exist */ //define('GESHI_ERROR_NO_SUCH_THEME', 6); /** Parsing is in strict mode, but the root context does not have a specific opener */ //define('GESHI_ERROR_STRICT_MODE_NO_OPENER', 7); /** getTime($type) was called with an invalid type */ define('GESHI_ERROR_INVALID_TIME_TYPE', 8); /** A file that was requested for loading data is not available */ define('GESHI_ERROR_FILE_UNAVAILABLE', 9); // Constants for specifying who (out of the parent or child) highlights the delimiter // between the parent and the child. Note that if you view the numbers as two bit binary, // a 1 indicates where the child parses and a 0 indicates where the parent should parse. // The default is GESHI_CHILD_PARSE_BOTH /** The child should parse neither delimiter (parent parses both) */ define('GESHI_CHILD_PARSE_NONE', 0); /** The child should parse the right (end) delimiter, the parent should parse the left delimiter */ define('GESHI_CHILD_PARSE_RIGHT', 1); /** The child should parse the left (beginning) delimiter, the parent should parse the right delimiter */ define('GESHI_CHILD_PARSE_LEFT', 2); /** The child should parse both delimiters (default) */ define('GESHI_CHILD_PARSE_BOTH', 3); // The name of integer/double number contexts define('GESHI_NUM_INT', 'num/int'); define('GESHI_NUM_DBL', 'num/dbl'); /** Default file extension */ define('GESHI_DEFAULT_FILE_EXTENSION', '.php'); /**#@-*/ /** * The GeSHi class * * @package core * @author Nigel McNie * @version 1.1.0 * @since 1.0.0 */ class GeSHi { /**#@+ * @access private * @var string */ /** * The source code to parse */ var $_source; /** * The name of the language to use when parsing the source */ var $language; /** * The humanised version of the language name */ var $humanLanguageName; /** * The error code of any error that has occured */ var $_error; /**#@-*/ /**#@+ * @access private */ /** * The root context to use for parsing the source * * @var GeSHiContext */ var $_rootContext; /** * Whether this object should be prepared as if it will be used * many times * * @var boolean */ var $_cacheRootContext; /** * The cached root context, if caching of context trees is enabled * * @var GeSHiContext */ var $_cachedRootContext; /** * The GeSHiStyler object used by this class and all contexts for * assisting parsing. * * @var GeSHiStyler */ /**#@-*/ /** * Sets the source and language name of the source to parse * * Also sets up other defaults, such as the default encoding * * USAGE: * *
$geshi =& new GeSHi($source, $language);
     * if (false !== ($msg = $geshi->error())) {
     *     // Handle error here: error message in $msg
     * }
     * $code = $geshi->parseCode();
* * @param string The source code to highlight * @param string The language to highlight the source with * @param string The path to the GeSHi data files. This is no longer used! The path is detected * automatically by GeSHi, this paramters is only included for backward compatibility. If * you want to set the path to the GeSHi data directories yourself, you should define the * GESHI_ROOT constant before including class.geshi.php. * @since 1.0.0 */ function GeSHi ($source, $language_name, $path = '') { geshi_dbg('GeSHi::GeSHi (language='.$language_name.')', GESHI_DBG_API); // Initialise timing $initial_times = array(0 => '0 0', 1 => '0 0'); $this->_times = array( 'pre' => $initial_times, 'parse' => $initial_times, 'post' => $initial_times ); $this->_styler =& new GeSHiStyler; // @todo [blocking 1.1.5] Make third parameter an option array thing $this->setFileExtension(GESHI_DEFAULT_FILE_EXTENSION); //$this->setOutputFormat(GESHI_OUTPUT_HTML); //$this->setEncoding(GESHI_DEFAULT_ENCODING); // Set the initial source/language $this->setSource($source); $this->setLanguage($language_name); } /** * Returns an error message if there has been an error. Useful for debugging, * but not recommended for use on a live site. * * The last error that occured is returned by this method * @todo [blocking 1.1.9] Documentation: has this changed from 1.0.X? * * @return false|string A message if there is an error, else false * @since 1.0.0 */ function error () { geshi_dbg('GeSHi::error()', GESHI_DBG_ERR | GESHI_DBG_API); if ($this->_error) { $msg = $this->_getErrorMessage(); geshi_dbg(' ERR: ' . $this->_error . ': ' . $msg, GESHI_DBG_API | GESHI_DBG_ERR); return sprintf('
GeSHi Error: %s (code %s)
', $msg, '#' . $this->_error . '' ); } geshi_dbg(' No error', GESHI_DBG_ERR | GESHI_DBG_API); return false; } /** * Sets the source code to highlight * * @param string The source code to highlight * @since 1.0.0 */ function setSource ($source) { geshi_dbg('GeSHi::setSource(' . substr($source, 0, 15) . '...)', GESHI_DBG_API); $this->_source = $source; if (!$this->_sourceValid()) { geshi_dbg('@e Source code is not valid!', GESHI_DBG_API | GESHI_DBG_ERR); } } /** * Sets the source code to highlight. This method is deprecated, and will be * removed in 1.4/2.0. * * @param string The source code to highlight * @since 1.0.0 * @deprecated In favour of {@link setSource()} */ function set_source ($source) { $this->setSource($source); } /** * Sets the language to use for highlighting * * @param string The language to use for highlighting * @since 1.0.0 */ function setLanguage ($language_name) { $this->_times['pre'][0] = microtime(); geshi_dbg('GeSHi::setLanguage('.$language_name.')', GESHI_DBG_API); $this->_language = strtolower($language_name); // Make a legal language name if (false === strpos($this->_language, '/')) { $this->_language .= '/'.$this->_language; geshi_dbg(' Language name converted to '.$this->_language, GESHI_DBG_API); } if ($this->_languageNameValid()) { // load language now geshi_dbg('@o Language name fine, loading data', GESHI_DBG_API); $this->_parsePreProcess(); } else { geshi_dbg('@e Language name not OK! (code '.$this->_error.')', GESHI_DBG_API | GESHI_DBG_ERR); } $this->_times['pre'][1] = microtime(); geshi_dbg(' Language data loaded in ' . number_format($this->getTime('pre'), 3) . ' seconds', GESHI_DBG_API); } /** * Sets the language to use for highlighting. This method is deprecated, and * will be removed in 1.4/2.0. * * @param string The language to use for highlighting * @since 1.0.0 * @deprecated In favour of {@link setLanguage()} */ function set_language($language_name) { $this->setLanguage($language_name); } /** * Sets whether this object should cache the root context as loaded. Use * this if you're going to use the same language in this object to parse * multiple source codes. * * @param boolean true if caching of context data should be used */ function cacheRootContext ($flag = true) { geshi_dbg('GeSHi::cacheRootContext(' . $flag . ')', GESHI_DBG_API); $this->_cacheRootContext = ($flag) ? true : false; $this->_cachedRootContext = ($this->_cacheRootContext) ? $this->_rootContext : null; geshi_dbg(' Set caching to ' . $flag . ', cached root context size = ' . count($this->_cachedRootContext), GESHI_DBG_API); } /** * Sets the file extension to use when getting external php files * * @param string The file extension for PHP files. Can be specified with or without the leading "." */ function setFileExtension ($extension) { $this->_styler->fileExtension = ('.' == substr($extension, 0, 1)) ? $extension : '.' . $extension; geshi_dbg('GeSHi::setFileExtension(' . $this->_styler->fileExtension . ')', GESHI_DBG_API); } /** * Returns various timings related to this object. * * For example, how long it took to load a specific context, * or parse the source code. * * You can pass a string to this method, it will return various timings based * on what string you pass: * *