#!/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();
}