#!/usr/bin/php # # This program 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. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. /** * Abstract preprocessor, which deals with opening and reading files. * If inherited, the doProcessLine() function must be reimplemented, * which will do the actuall processing of a line. */ abstract class Preprocessor { private $_filename = ''; /** * Constructor. Prepares the preprocessor for a certain file given * by the filename. * @param string $filename The filename or URL the preprocessor will run on. */ public function __construct ($filename) { $this->_filename = $filename; } /** * Process the file. This will read the contents of the file to * be preprocessed, and work its magic. * * Internally this calls doProcess() which must be implemented * by inherited classes, which will do the actual processing. * * @return string The processed file. */ public function process() { $contents = file_get_contents($this->_filename); // Convert Mac/Win line breaks to Unix format. $contents = str_replace("\r\n", "\n", $contents); $contents = str_replace("\r", "\n", $contents); return $this->doProcess($contents); } /** * Process a line of the file * @param string $contents The contents of the line to process, including newline character * @return string The processed line */ protected abstract function doProcess($contents); } /** * This preprocessor reads a (Drupal) code file, and processes the doxygen documentationsuch that: * - Invalid Doxygen commands prefixed with '@' are escaped * - The '%' character is always escaped * - Backslash commands are escaped * - Drupal specific links (api/constants, api/globals) are replaced with Doxygen links * - External links are fixed for Doxygen usage */ class CodePreprocessor extends Preprocessor { public function __construct ($filename) { Preprocessor::__construct($filename); } protected function doProcess($contents) { // Beyond Drupal's API module: we also work on blocks started with "/*!" $contents = preg_replace_callback('@/\*[\*!](.*?)\*/@s', array($this, 'processCommentBlock'), $contents); // And those with at least two lines of /// or //! $contents = preg_replace_callback('@(//[/!])[^\\n]*\\n(\\1[^\\n]*\\n)+@s', array($this, 'processCommentBlock'), $contents); // Return processed file contents return $contents; } private function processCommentBlock($matches) { $contents = $matches[0]; $contents = $this->escapeUnknownCommands($contents); $contents = $this->makeAnchorLinks($contents, array( '/api/constants' => 'globals_enum.html', // '/api/globals' => 'globals_vars.html', )); $contents = $this->replaceLinks($contents, array( '/api/globals' => 'globals.php', )); $contents = $this->replaceExternalLinks($contents); return $contents; } /** * Escape all commands not known by Doxygen. * @param string $contents The line to escape commands on */ private function escapeUnknownCommands($contents) { static $commandsArray = array( 'a', 'addindex', 'addtogroup', 'anchor', 'arg', 'attention', 'author', 'b', 'brief', 'bug', 'c', 'callgraph', 'callgraph', 'callergraph', 'category', 'class', 'code', 'cond', 'copybrief', 'copydetails', 'copydoc', 'date', 'def', 'defgroup', 'deprecated', 'details', 'dir', 'dontinclude', 'dot', 'dotfile', 'e', 'else', 'elseif', 'em', 'endcode', 'endcond', 'enddot', 'endhtmlonly', 'endif', 'endlatexonly', 'endlink', 'endmanonly', 'endmsc', 'endverbatim', 'endxmlonly', 'enum', 'example', 'exception', 'extends', 'file', 'fn', 'headerfile', 'hideinitializer', 'htmlinclude', 'htmlonly', 'if', 'ifnot', 'image', 'implements', 'include', 'includelineno', 'ingroup', 'internal', 'invariant', 'interface', 'latexonly', 'li', 'line', 'link', 'mainpage', 'manonly', 'memberof', 'msc', 'n', 'name', 'namespace', 'nosubgrouping', 'note', 'overload', 'p', 'package', 'page', 'paragraph', 'param', 'post', 'pre', 'private', 'privatesection', 'property', 'protected', 'protectedsection', 'public', 'publicsection', 'protocol', 'ref', 'relates', 'relatesalso', 'remarks', 'return', 'retval', 'sa', 'section', 'see', 'showinitializer', 'since', 'skip', 'skipline', 'struct', 'subpage', 'subsection', 'subsubsection', 'test', 'throw', 'todo', 'tparam', 'typedef', 'union', 'until', 'var', 'verbatim', 'verbinclude', 'version', 'warning', 'weakgroup', 'xmlonly', 'xrefitem', 'annotatedclasslist', 'classhierarchy', 'define', 'functionindex', 'header', 'headerfilelist', 'inherit', 'l', 'postheader', ); static $backslashCommandsArray = array( 'addindex', 'addtogroup', 'anchor', 'arg', 'attention', 'author', 'brief', 'bug', 'callgraph', 'callgraph', 'callergraph', 'category', 'class', 'code', 'cond', 'copybrief', 'copydetails', 'copydoc', 'date', 'def', 'defgroup', 'deprecated', 'details', 'dir', 'dontinclude', 'dot', 'dotfile', 'else', 'elseif', 'em', 'endcode', 'endcond', 'enddot', 'endhtmlonly', 'endif', 'endlatexonly', 'endlink', 'endmanonly', 'endmsc', 'endverbatim', 'endxmlonly', 'enum', 'example', 'exception', 'extends', 'file', 'fn', 'headerfile', 'hideinitializer', 'htmlinclude', 'htmlonly', 'if', 'ifnot', 'image', 'implements', 'include', 'includelineno', 'ingroup', 'internal', 'invariant', 'interface', 'latexonly', 'li', 'line', 'link', 'mainpage', 'manonly', 'memberof', 'msc', 'name', 'namespace', 'nosubgrouping', 'note', 'overload', 'package', 'page', 'paragraph', 'param', 'post', 'pre', 'private', 'privatesection', 'property', 'protected', 'protectedsection', 'public', 'publicsection', 'protocol', 'ref', 'relates', 'relatesalso', 'remarks', 'return', 'retval', 'sa', 'section', 'see', 'showinitializer', 'since', 'skip', 'skipline', 'struct', 'subpage', 'subsection', 'subsubsection', 'test', 'throw', 'todo', 'tparam', 'typedef', 'union', 'until', 'var', 'verbatim', 'verbinclude', 'version', 'warning', 'weakgroup', 'xmlonly', 'xrefitem', 'annotatedclasslist', 'classhierarchy', 'define', 'functionindex', 'header', 'headerfilelist', 'inherit', 'postheader', ); static $noWordCommands = array( 'f[\$\[\]\{\}]', '[\$@\\\\&~<>#%"]', ); static $backslashCommandRegex = NULL; static $commandsRegex = NULL; static $noWordCommandsRegex = NULL; if (!isset($backslashCommandRegex)) { $commandsRegex = '/(^|(?<=\W))(? $new) { $re = '/@link ' . str_replace('/', '\/', $original) . '(\/\S*)?\s(.*\S)\s*@endlink/'; $contents = preg_replace($re, '\\2', $contents); } return $contents; } private function replaceLinks($contents, $links) { foreach($links as $original => $new) { $re = '/@link ' . str_replace('/', '\/', $original) . '(\/\S*)?\s(.*\S)\s*@endlink/'; $contents = preg_replace($re, '@link ' . $new . ' \\2 @endlink', $contents); } return $contents; } private function replaceExternalLinks($contents) { # Fix external links for any protocol return preg_replace('/@link (([a-zA-Z]+:\/\/|mailto\:)\S*)\s+(.*\S)\s*@endlink/', '\\3', $contents); } } // Default to processing stdin if no arguments are given if ($argc == 1) { $argc = 2; $argv[1] = '-'; } // Process all files in argument list for ($i = 1; $i < $argc; ++$i) { $filename = $argv[$i]; if ($filename == "-") { $filename = "php://stdin"; } // Find out type of file (based on filename) $processor = NULL; $info = pathinfo($filename); if (!empty($info['extension']) && in_array($info['extension'], array('html', 'htm', 'xhtml'))) { // HTML Processing is for later... } else { $processor = new CodePreprocessor($filename); } // Process file print $processor->process(); }