#!/usr/bin/php -dphar.readonly=1 2, 'c' => 'text/plain', 'cc' => 'text/plain', 'cpp' => 'text/plain', 'c++' => 'text/plain', 'dtd' => 'text/plain', 'h' => 'text/plain', 'log' => 'text/plain', 'rng' => 'text/plain', 'txt' => 'text/plain', 'xsd' => 'text/plain', 'php' => 1, 'inc' => 1, 'avi' => 'video/avi', 'bmp' => 'image/bmp', 'css' => 'text/css', 'gif' => 'image/gif', 'htm' => 'text/html', 'html' => 'text/html', 'htmls' => 'text/html', 'ico' => 'image/x-ico', 'jpe' => 'image/jpeg', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'js' => 'application/x-javascript', 'midi' => 'audio/midi', 'mid' => 'audio/midi', 'mod' => 'audio/mod', 'mov' => 'movie/quicktime', 'mp3' => 'audio/mp3', 'mpg' => 'video/mpeg', 'mpeg' => 'video/mpeg', 'pdf' => 'application/pdf', 'png' => 'image/png', 'swf' => 'application/shockwave-flash', 'tif' => 'image/tiff', 'tiff' => 'image/tiff', 'wav' => 'audio/wav', 'xbm' => 'image/xbm', 'xml' => 'text/xml', ); header("Cache-Control: no-cache, must-revalidate"); header("Pragma: no-cache"); $basename = basename(__FILE__); if (!strpos($_SERVER['REQUEST_URI'], $basename)) { chdir(Extract_Phar::$temp); include $web; return; } $pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename)); if (!$pt || $pt == '/') { $pt = $web; header('HTTP/1.1 301 Moved Permanently'); header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt); exit; } $a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt); if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) { header('HTTP/1.0 404 Not Found'); echo "\n \n File Not Found<title>\n </head>\n <body>\n <h1>404 - File ", $pt, " Not Found</h1>\n </body>\n</html>"; exit; } $b = pathinfo($a); if (!isset($b['extension'])) { header('Content-Type: text/plain'); header('Content-Length: ' . filesize($a)); readfile($a); exit; } if (isset($mimes[$b['extension']])) { if ($mimes[$b['extension']] === 1) { include $a; exit; } if ($mimes[$b['extension']] === 2) { highlight_file($a); exit; } header('Content-Type: ' .$mimes[$b['extension']]); header('Content-Length: ' . filesize($a)); readfile($a); exit; } } class Extract_Phar { static $temp; static $origdir; const GZ = 0x1000; const BZ2 = 0x2000; const MASK = 0x3000; const START = 'pharext_installer.php'; const LEN = 6697; static function go($return = false) { $fp = fopen(__FILE__, 'rb'); fseek($fp, self::LEN); $L = unpack('V', $a = (binary)fread($fp, 4)); $m = (binary)''; do { $read = 8192; if ($L[1] - strlen($m) < 8192) { $read = $L[1] - strlen($m); } $last = (binary)fread($fp, $read); $m .= $last; } while (strlen($last) && strlen($m) < $L[1]); if (strlen($m) < $L[1]) { die('ERROR: manifest length read was "' . strlen($m) .'" should be "' . $L[1] . '"'); } $info = self::_unpack($m); $f = $info['c']; if ($f & self::GZ) { if (!function_exists('gzinflate')) { die('Error: zlib extension is not enabled -' . ' gzinflate() function needed for zlib-compressed .phars'); } } if ($f & self::BZ2) { if (!function_exists('bzdecompress')) { die('Error: bzip2 extension is not enabled -' . ' bzdecompress() function needed for bz2-compressed .phars'); } } $temp = self::tmpdir(); if (!$temp || !is_writable($temp)) { $sessionpath = session_save_path(); if (strpos ($sessionpath, ";") !== false) $sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1); if (!file_exists($sessionpath) || !is_dir($sessionpath)) { die('Could not locate temporary directory to extract phar'); } $temp = $sessionpath; } $temp .= '/pharextract/'.basename(__FILE__, '.phar'); self::$temp = $temp; self::$origdir = getcwd(); @mkdir($temp, 0777, true); $temp = realpath($temp); if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) { self::_removeTmpFiles($temp, getcwd()); @mkdir($temp, 0777, true); @file_put_contents($temp . '/' . md5_file(__FILE__), ''); foreach ($info['m'] as $path => $file) { $a = !file_exists(dirname($temp . '/' . $path)); @mkdir(dirname($temp . '/' . $path), 0777, true); clearstatcache(); if ($path[strlen($path) - 1] == '/') { @mkdir($temp . '/' . $path, 0777); } else { file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp)); @chmod($temp . '/' . $path, 0666); } } } chdir($temp); if (!$return) { include self::START; } } static function tmpdir() { if (strpos(PHP_OS, 'WIN') !== false) { if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) { return $var; } if (is_dir('/temp') || mkdir('/temp')) { return realpath('/temp'); } return false; } if ($var = getenv('TMPDIR')) { return $var; } return realpath('/tmp'); } static function _unpack($m) { $info = unpack('V', substr($m, 0, 4)); $l = unpack('V', substr($m, 10, 4)); $m = substr($m, 14 + $l[1]); $s = unpack('V', substr($m, 0, 4)); $o = 0; $start = 4 + $s[1]; $ret['c'] = 0; for ($i = 0; $i < $info[1]; $i++) { $len = unpack('V', substr($m, $start, 4)); $start += 4; $savepath = substr($m, $start, $len[1]); $start += $len[1]; $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24))); $ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3] & 0xffffffff); $ret['m'][$savepath][7] = $o; $o += $ret['m'][$savepath][2]; $start += 24 + $ret['m'][$savepath][5]; $ret['c'] |= $ret['m'][$savepath][4] & self::MASK; } return $ret; } static function extractFile($path, $entry, $fp) { $data = ''; $c = $entry[2]; while ($c) { if ($c < 8192) { $data .= @fread($fp, $c); $c = 0; } else { $c -= 8192; $data .= @fread($fp, 8192); } } if ($entry[4] & self::GZ) { $data = gzinflate($data); } elseif ($entry[4] & self::BZ2) { $data = bzdecompress($data); } if (strlen($data) != $entry[0]) { die("Invalid internal .phar file (size error " . strlen($data) . " != " . $stat[7] . ")"); } if ($entry[3] != sprintf("%u", crc32((binary)$data) & 0xffffffff)) { die("Invalid internal .phar file (checksum error)"); } return $data; } static function _removeTmpFiles($temp, $origdir) { chdir($temp); foreach (glob('*') as $f) { if (file_exists($f)) { is_dir($f) ? @rmdir($f) : @unlink($f); if (file_exists($f) && is_dir($f)) { self::_removeTmpFiles($f, getcwd()); } } } @rmdir($temp); clearstatcache(); chdir($origdir); } } Extract_Phar::go(); __HALT_COMPILER(); ?> $������������� ��a:8:{s:6:"header";s:49:"pharext v3.0.1 (c) Michael Wallner <mike@php.net>";s:7:"version";s:5:"3.0.1";s:4:"name";s:8:"markdown";s:4:"date";s:10:"2015-05-01";s:4:"stub";s:21:"pharext_installer.php";s:7:"license";b:0;s:7:"release";s:5:"1.0.0";s:4:"type";s:9:"extension";}���pharext/Cli/Args.phpK��)QCUK��O���������pharext/Cli/Command.php��)QCU�� *۶���������pharext/Command.php��)QCU��td\���������pharext/Exception.phpc��)QCUc��U{���������pharext/ExecCmd.php��)QCU��lʶ���������pharext/Installer.php��)QCU��WJ���������pharext/Openssl/PrivateKey.php��)QCU��&P���������pharext/Packager.phpt!��)QCUt!��;c���������pharext/SourceDir/Basic.php��)QCU��zZ���������pharext/SourceDir/Git.php��)QCU��bc���������pharext/SourceDir/Pecl.php��)QCU�����������pharext/SourceDir.php]��)QCU]��S ���������pharext/Task/Activate.phpp ��)QCUp ��EE���������pharext/Task/Askpass.phpU��)QCUU��*������ ���pharext/Task/BundleGenerator.php}��)QCU}�� `Y���������pharext/Task/Cleanup.php���)QCU���ZͶ���������pharext/Task/Configure.phpT��)QCUT��}���������pharext/Task/Extract.php��)QCU��ն���������pharext/Task/GitClone.php��)QCU��CR(���������pharext/Task/Make.php��)QCU��6 ���������pharext/Task/PaxFixup.php��)QCU��y���������pharext/Task/PeclFixup.php��)QCU��et���������pharext/Task/PharBuild.php��)QCU��D垶���������pharext/Task/PharCompress.phpr��)QCUr�� ���������pharext/Task/PharRename.php��)QCU��[˶���������pharext/Task/PharSign.php��)QCU��ۺi���������pharext/Task/Phpize.php��)QCU�� 2Ѷ���������pharext/Task/StreamFetch.php��)QCU��s\���������pharext/Task.phpw���)QCUw��� IǶ���������pharext/Tempdir.php��)QCU��,���������pharext/Tempfile.php��)QCU��#���������pharext/Tempname.php`��)QCU`��<Np���������pharext/Version.php4���)QCU4���p���������pharext_installer.php���)QCU���pDZ���������pharext_packager.php���)QCU���1���������pharext_package.php2���)QCU2���vSTҶ������ ���package.xmlk>��)QCUk>��٣zB���������examples/definition_list.php��)QCU��h���������examples/dump_tree.php��)QCU��C���������examples/pandoc_headers.php��)QCU��]<'���������examples/pseudoprotos.phpR��)QCUR��d%}ݶ���������examples/simple_usage.php��)QCU��RY���������examples/smartypants.php��)QCU��bK���������examples/subclassing.php��)QCU��2ܖ���������examples/subclassing_2.phpP��)QCUP��o\������ ���lib/amalloc.c*���)QCU*���z������ ���lib/amalloc.hU��)QCUU��[W���������lib/basename.c*���)QCU*���z���������lib/blocktags_generated.c��)QCU�� o������ ���lib/config.h$��)QCU$��z���������lib/config.win32.hc��)QCUc��֏ñ������ ���lib/Csio.c��)QCU��e\e������ ���lib/css.cr��)QCUr��-[.������ ���lib/cstring.hv ��)QCUv ��?���������lib/docheader.cv��)QCUv�����������lib/dumptree.c2 ��)QCU2 ��\Ŷ������ ���lib/emmatch.c��)QCU��Ƈ������ ���lib/flags.c��)QCU��ZӶ���������lib/generate.c[��)QCU[��LK1˶������ ���lib/html5.c ��)QCU ��J���������lib/makepage.c*���)QCU*���z���������lib/markdown.ce��)QCUe��|���������lib/markdown.h��)QCU��yN���������lib/mkd2html.c*���)QCU*���z������ ���lib/mkdio.c ��)QCU ��D`������ ���lib/mkdio.h��)QCU��UC[������ ���lib/mktags.c��)QCU�� =���������lib/resource.c ��)QCU ��<B������ ���lib/setup.cp��)QCUp��!������ ���lib/tags.c��)QCU��Tե̶������ ���lib/tags.hX��)QCUX�� ������ ���lib/theme.c*���)QCU*���z������ ���lib/toc.c ��)QCU ��8(������ ���lib/version.c��)QCU��S*������ ���lib/xml.c��)QCU�������� ���lib/xmlpage.c��)QCU��P׶���������tests/compile_basic.phpt��)QCU�� ���������tests/compile_error.phpt9��)QCU9�����������tests/compile_error2.phpt"��)QCU"��0 ���������tests/compile_variation1.phpt��)QCU��/8���������tests/compile_variation2.phptE��)QCUE�����������tests/compile_variation3.phpt��)QCU�� ն���������tests/compile_variation4.phpt:��)QCU:��rJ���������tests/compile_variation5.phpt��)QCU��l���������tests/compile_variation6.phpt ��)QCU ��,rc���������tests/compile_variation7.phpt��)QCU��!���������tests/compile_variation8.phpt��)QCU��01Ub���������tests/compile_variation9.phpt|��)QCU|��y���������tests/compile_variation10.phpt#��)QCU#��γQ���������tests/compile_variation11.phpt��)QCU��pAp���������tests/compile_variation12.phptt��)QCUt��I���������tests/compile_variation13.phptI��)QCUI��ڴ���������tests/compile_variation14.phptT��)QCUT��H>���������tests/compile_variation15.phpt��)QCU�����������tests/compile_variation16.phpth��)QCUh��h|h���������tests/compile_variation17.phptp��)QCUp��1޽���������tests/compile_variation18.phpt��)QCU��$ԩ���������tests/compile_variation19.phpt��)QCU��]Z���������tests/compile_variation20.phpt��)QCU��)E���������tests/compile_variation21.phpt��)QCU�� JŶ������#���tests/create_from_stream_basic.phptJ��)QCUJ��  V������#���tests/create_from_stream_error.phpt��)QCU��d������(���tests/create_from_stream_variation1.phptu��)QCUu��xXK������(���tests/create_from_stream_variation2.phptI��)QCUI��p d������#���tests/create_from_string_basic.phpt��)QCU��:10������#���tests/create_from_string_error.phptZ��)QCUZ�����������tests/dump_tree_basic.phpt ��)QCU ��l˶���������tests/dump_tree_error.phpt��)QCU��J60���������tests/get_author_basic.phpt��)QCU��>���������tests/get_author_error.phpt��)QCU��/sK���������tests/get_css_basic.phpt8��)QCU8��&���������tests/get_css_error.phpt��)QCU��Pf���������tests/get_date_basic.phpt��)QCU��1%���������tests/get_date_error.phpt��)QCU�� i���������tests/get_html_basic.phpt��)QCU��t۶���������tests/get_html_error.phpt��)QCU��Ȝl���������tests/get_html_error2.phpt��)QCU��J|���������tests/get_html_error3.phpt��)QCU��b���������tests/get_title_basic.phptS��)QCUS��Q���������tests/get_title_error.phpt��)QCU�� a���������tests/get_toc_basic.phpt{��)QCU{��H/5���������tests/get_toc_error.phpt��)QCU��'���������tests/helpers.php.inc��)QCU��;2���������tests/inheritance_basic.phpt&��)QCU&��Ӷ���������tests/inheritance_error.phpt ��)QCU �� <���������tests/is_compiled_basic.phpt��)QCU��*⇍���������tests/is_compiled_error.phpt��)QCU��P2���������tests/pp_fenced_code.phpt��)QCU�����������tests/pp_with_github_tags.phptd��)QCUd�� uX������(���tests/set_attributes_callback_basic.phpt��)QCU�� aW������(���tests/set_attributes_callback_error.phpt`��)QCU`��B������%���tests/set_reference_prefix_basic.phptx��)QCUx��tm< ������%���tests/set_reference_prefix_error.phpt��)QCU��;�ö������!���tests/set_url_callback_basic.phpt��)QCU��r\Zr������!���tests/set_url_callback_error.phpt��)QCU�����������tests/simple_example.txt'���)QCU'���ض���������tests/syntax_start.txt��)QCU��Dz������"���tests/tranform_fragment_basic.phpt���)QCU���*�������"���tests/tranform_fragment_error.phpt��)QCU��f׶������'���tests/tranform_fragment_variation1.phpt��)QCU��k������'���tests/tranform_fragment_variation2.phpt��)QCU��R0���������tests/write_css_basic.phpt��)QCU��\nj���������tests/write_css_error.phpt'��)QCU'��m���������tests/write_fragment_basic.phptN��)QCUN�����������tests/write_fragment_error.phpt��)QCU��8������$���tests/write_fragment_variation1.phpt7��)QCU7��j������$���tests/write_fragment_variation2.phpt}��)QCU}��{���������tests/write_html_basic.phpt��)QCU��Q���������tests/write_html_error.phpt��)QCU��_#������ ���tests/write_html_variation1.phpt(��)QCU(��eଢ���������tests/write_toc_basic.phpt]��)QCU]�����������tests/write_toc_error.phpt ��)QCU ��ȴ������!���tests/write_xhtml_page_basic.phpt��)QCU��������!���tests/write_xhtml_page_error.phpt��)QCU��ö������ ���config.m4��)QCU��@������ ���config.w32��)QCU��w{*������ ���discount.cc��)QCUc��L@E���������markdowndoc_class.c0��)QCU0��$d���������markdowndoc_class.hi ��)QCUi �� ���������markdowndoc_meth_callbacks.c��)QCU��qW���������markdowndoc_meth_callbacks.hv��)QCUv��"���������markdowndoc_meth_document.cA��)QCUA��x���������markdowndoc_meth_document.h��)QCU��ddѶ���������markdowndoc_meth_header.c ��)QCU ��R���������markdowndoc_meth_header.h��)QCU��/ж���������markdowndoc_meth_input.c��)QCU��'���������markdowndoc_meth_input.h��)QCU��cU���������markdowndoc_meth_misc.c��)QCU�����������markdowndoc_meth_misc.h��)QCU��b;���������markdowndoc_meth_parts.c��)QCU��uz���������markdowndoc_meth_parts.h��)QCU��S]���������php_discount.h ��)QCU �����������README��)QCU��@x���������CREDITS|���)QCU|���9W/R���������MarkdownDocument.php��)QCU��5������<?php namespace pharext\Cli; /** * Command line arguments */ class Args implements \ArrayAccess { /** * Optional option */ const OPTIONAL = 0x000; /** * Required Option */ const REQUIRED = 0x001; /** * Only one value, even when used multiple times */ const SINGLE = 0x000; /** * Aggregate an array, when used multiple times */ const MULTI = 0x010; /** * Option takes no argument */ const NOARG = 0x000; /** * Option requires an argument */ const REQARG = 0x100; /** * Option takes an optional argument */ const OPTARG = 0x200; /** * Option halts processing */ const HALT = 0x10000000; /** * Original option spec * @var array */ private $orig = []; /** * Compiled spec * @var array */ private $spec = []; /** * Parsed args * @var array */ private $args = []; /** * Compile the original spec * @param array|Traversable $spec */ public function __construct($spec = null) { if (is_array($spec) || $spec instanceof Traversable) { $this->compile($spec); } } /** * Compile the original spec * @param array|Traversable $spec * @return pharext\CliArgs self */ public function compile($spec) { foreach ($spec as $arg) { if (isset($arg[0])) { $this->spec["-".$arg[0]] = $arg; } $this->spec["--".$arg[1]] = $arg; $this->orig[] = $arg; } return $this; } /** * Get original spec * @return array */ public function getSpec() { return $this->orig; } /** * Get compiled spec * @return array */ public function getCompiledSpec() { return $this->spec; } /** * Parse command line arguments according to the compiled spec. * * The Generator yields any parsing errors. * Parsing will stop when all arguments are processed or the first option * flagged CliArgs::HALT was encountered. * * @param int $argc * @param array $argv * @return Generator */ public function parse($argc, array $argv) { for ($i = 0; $i < $argc; ++$i) { $o = $argv[$i]; if ($o{0} === '-' && strlen($o) > 2 && $o{1} !== '-') { // multiple short opts, .e.g -vps $argc += strlen($o) - 2; array_splice($argv, $i, 1, array_map(function($s) { return "-$s"; }, str_split(substr($o, 1)))); $o = $argv[$i]; } elseif ($o{0} === '-' && strlen($o) > 2 && $o{1} === '-' && 0 < ($eq = strpos($o, "="))) { $argc++; array_splice($argv, $i, 1, [ substr($o, 0, $eq++), substr($o, $eq) ]); $o = $argv[$i]; } if (!isset($this->spec[$o])) { yield sprintf("Unknown option %s", $o); } elseif (!$this->optAcceptsArg($o)) { $this[$o] = true; } elseif ($i+1 < $argc && !isset($this->spec[$argv[$i+1]])) { $this[$o] = $argv[++$i]; } elseif ($this->optRequiresArg($o)) { yield sprintf("Option --%s requires an argument", $this->optLongName($o)); } else { // OPTARG $this[$o] = $this->optDefaultArg($o); } if ($this->optHalts($o)) { return; } } } /** * Validate that all required options were given. * * The Generator yields any validation errors. * * @return Generator */ public function validate() { $required = array_filter($this->orig, function($spec) { return $spec[3] & self::REQUIRED; }); foreach ($required as $req) { if (!strlen($this[$req[0]])) { yield sprintf("Option --%s is required", $req[1]); } } } /** * Retreive the default argument of an option * @param string $o * @return mixed */ private function optDefaultArg($o) { $o = $this->opt($o); if (isset($this->spec[$o][4])) { return $this->spec[$o][4]; } return null; } /** * Retrieve the help message of an option * @param string $o * @return string */ private function optHelp($o) { $o = $this->opt($o); if (isset($this->spec[$o][2])) { return $this->spec[$o][2]; } return ""; } /** * Retrieve option's flags * @param string $o * @return int */ private function optFlags($o) { $o = $this->opt($o); if (isset($this->spec[$o])) { return $this->spec[$o][3]; } return null; } /** * Check whether an option is flagged for halting argument processing * @param string $o * @return boolean */ private function optHalts($o) { return $this->optFlags($o) & self::HALT; } /** * Check whether an option needs an argument * @param string $o * @return boolean */ private function optRequiresArg($o) { return $this->optFlags($o) & self::REQARG; } /** * Check wether an option accepts any argument * @param string $o * @return boolean */ private function optAcceptsArg($o) { return $this->optFlags($o) & 0xf00; } /** * Check whether an option can be used more than once * @param string $o * @return boolean */ private function optIsMulti($o) { return $this->optFlags($o) & self::MULTI; } /** * Retreive the long name of an option * @param string $o * @return string */ private function optLongName($o) { $o = $this->opt($o); return $this->spec[$o][1]; } /** * Retreive the short name of an option * @param string $o * @return string */ private function optShortName($o) { $o = $this->opt($o); return $this->spec[$o][0]; } /** * Retreive the canonical name (--long-name) of an option * @param string $o * @return string */ private function opt($o) { if ($o{0} !== '-') { if (strlen($o) > 1) { $o = "-$o"; } $o = "-$o"; } return $o; } /**@+ * Implements ArrayAccess and virtual properties */ function offsetExists($o) { $o = $this->opt($o); return isset($this->args[$o]); } function __isset($o) { return $this->offsetExists($o); } function offsetGet($o) { $o = $this->opt($o); if (isset($this->args[$o])) { return $this->args[$o]; } return $this->optDefaultArg($o); } function __get($o) { return $this->offsetGet($o); } function offsetSet($o, $v) { $osn = $this->optShortName($o); $oln = $this->optLongName($o); if ($this->optIsMulti($o)) { if (isset($osn)) { $this->args["-$osn"][] = $v; } $this->args["--$oln"][] = $v; } else { if (isset($osn)) { $this->args["-$osn"] = $v; } $this->args["--$oln"] = $v; } } function __set($o, $v) { $this->offsetSet($o, $v); } function offsetUnset($o) { unset($this->args["-".$this->optShortName($o)]); unset($this->args["--".$this->optLongName($o)]); } function __unset($o) { $this->offsetUnset($o); } /**@-*/ } <?php namespace pharext\Cli; use pharext\Cli\Args as CliArgs; use Phar; if (!function_exists("array_column")) { function array_column(array $array, $col, $idx = null) { $result = []; foreach ($array as $el) { if (isset($idx)) { $result[$el[$idx]] = $el[$col]; } else { $result[] = $el[$col]; } } return $result; } } trait Command { /** * Command line arguments * @var pharext\CliArgs */ private $args; /** * @inheritdoc * @see \pharext\Command::getArgs() */ public function getArgs() { return $this->args; } /** * Retrieve metadata of the currently running phar * @param string $key * @return mixed */ public function metadata($key = null) { $running = new Phar(Phar::running(false)); if ($key === "signature") { $sig = $running->getSignature(); return sprintf("%s signature of %s\n%s", $sig["hash_type"], $this->metadata("name"), chunk_split($sig["hash"], 64, "\n")); } $metadata = $running->getMetadata(); if (isset($key)) { return $metadata[$key]; } return $metadata; } /** * Output pharext vX.Y.Z header */ public function header() { if (!headers_sent()) { /* only display header, if we didn't generate any output yet */ printf("%s\n\n", $this->metadata("header")); } } /** * @inheritdoc * @see \pharext\Command::debug() */ public function debug($fmt) { if ($this->args->verbose) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::info() */ public function info($fmt) { if (!$this->args->quiet) { vprintf($fmt, array_slice(func_get_args(), 1)); } } /** * @inheritdoc * @see \pharext\Command::warn() */ public function warn($fmt) { if (!$this->args->quiet) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "Warning: $fmt", $arg); } } /** * @inheritdoc * @see \pharext\Command::error() */ public function error($fmt) { if (!isset($fmt)) { $fmt = "%s\n"; $arg = error_get_last()["message"]; } else { $arg = array_slice(func_get_args(), 1); } vfprintf(STDERR, "ERROR: $fmt", $arg); } /** * Output command line help message * @param string $prog */ public function help($prog) { printf("Usage:\n\n \$ %s", $prog); $flags = []; $required = []; $optional = []; foreach ($this->args->getSpec() as $spec) { if ($spec[3] & CliArgs::REQARG) { if ($spec[3] & CliArgs::REQUIRED) { $required[] = $spec; } else { $optional[] = $spec; } } else { $flags[] = $spec; } } if ($flags) { printf(" [-%s]", implode("", array_column($flags, 0))); } foreach ($required as $req) { printf(" -%s <arg>", $req[0]); } if ($optional) { printf(" [-%s <arg>]", implode("|-", array_column($optional, 0))); } printf("\n\n"); $spc = $this->args->getSpec(); $max = max(array_map("strlen", array_column($spc, 1))); $max += $max % 8 + 2; foreach ($spc as $spec) { if (isset($spec[0])) { printf(" -%s|", $spec[0]); } else { printf(" "); } printf("--%s ", $spec[1]); if ($spec[3] & CliArgs::REQARG) { printf("<arg> "); } elseif ($spec[3] & CliArgs::OPTARG) { printf("[<arg>]"); } else { printf(" "); } printf("%s%s", str_repeat(" ", $max-strlen($spec[1])+3*!isset($spec[0])), $spec[2]); if ($spec[3] & CliArgs::REQUIRED) { printf(" (REQUIRED)"); } if (isset($spec[4])) { printf(" [%s]", $spec[4]); } printf("\n"); } printf("\n"); } /** * Verbosity * @return boolean */ public function verbosity() { if ($this->args->verbose) { return true; } elseif ($this->args->quiet) { return false; } else { return null; } } } <?php namespace pharext; /** * Command interface */ interface Command { /** * Argument error */ const EARGS = 1; /** * Build error */ const EBUILD = 2; /** * Signature error */ const ESIGN = 3; /** * Extract/unpack error */ const EEXTRACT = 4; /** * Install error */ const EINSTALL = 5; /** * Retrieve command line arguments * @return pharext\CliArgs */ public function getArgs(); /** * Print debug message * @param string $fmt * @param string ...$args */ public function debug($fmt); /** * Print info * @param string $fmt * @param string ...$args */ public function info($fmt); /** * Print warning * @param string $fmt * @param string ...$args */ public function warn($fmt); /** * Print error * @param string $fmt * @param string ...$args */ public function error($fmt); /** * Execute the command * @param int $argc command line argument count * @param array $argv command line argument list */ public function run($argc, array $argv); } <?php namespace pharext; class Exception extends \Exception { public function __construct($message = null, $code = 0, $previous = null) { if (!isset($message)) { $last_error = error_get_last(); $message = $last_error["message"]; if (!$code) { $code = $last_error["type"]; } } parent::__construct($message, $code, $previous); } } <?php namespace pharext; /** * Execute system command */ class ExecCmd { /** * Sudo command, if the cmd needs escalated privileges * @var string */ private $sudo; /** * Executable of the cmd * @var string */ private $command; /** * Passthrough cmd output * @var bool */ private $verbose; /** * Output of cmd run * @var string */ private $output; /** * Return code of cmd run * @var int */ private $status; /** * @param string $command * @param bool verbose */ public function __construct($command, $verbose = false) { $this->command = $command; $this->verbose = $verbose; } /** * (Re-)set sudo command * @param string $sudo */ public function setSu($sudo = false) { $this->sudo = $sudo; } /** * Execute a program with escalated privileges handling interactive password prompt * @param string $command * @param bool $verbose * @return int exit status */ private function suExec($command, $verbose = null) { if (!($proc = proc_open($command, [STDIN,["pipe","w"],["pipe","w"]], $pipes))) { $this->status = -1; throw new Exception("Failed to run {$command}"); } $stdout = $pipes[1]; $passwd = 0; $checks = 10; while (!feof($stdout)) { $R = [$stdout]; $W = []; $E = []; if (!stream_select($R, $W, $E, null)) { continue; } $data = fread($stdout, 0x1000); /* only check a few times */ if ($passwd < $checks) { $passwd++; if (stristr($data, "password")) { $passwd = $checks + 1; printf("\n%s", $data); continue; } } elseif ($passwd > $checks) { /* new line after pw entry */ printf("\n"); $passwd = $checks; } if ($verbose === null) { print $this->progress($data, 0); } else { if ($verbose) { printf("%s", $data); } $this->output .= $data; } } if ($verbose === null) { $this->progress("", PHP_OUTPUT_HANDLER_FINAL); } return $this->status = proc_close($proc); } /** * Output handler that displays some progress while soaking output * @param string $string * @param int $flags * @return string */ private function progress($string, $flags) { static $counter = 0; static $symbols = ["\\","|","/","-"]; $this->output .= $string; if (false !== strpos($string, "\n")) { ++$counter; } return $flags & PHP_OUTPUT_HANDLER_FINAL ? " \r" : sprintf(" %s\r", $symbols[$counter % 4]); } /** * Run the command * @param array $args * @return \pharext\ExecCmd self * @throws \pharext\Exception */ public function run(array $args = null) { $exec = escapeshellcmd($this->command); if ($args) { $exec .= " ". implode(" ", array_map("escapeshellarg", (array) $args)); } if ($this->sudo) { $this->suExec(sprintf($this->sudo." 2>&1", $exec), $this->verbose); } elseif ($this->verbose) { ob_start(function($s) { $this->output .= $s; return $s; }, 1); passthru($exec, $this->status); ob_end_flush(); } elseif ($this->verbose !== false /* !quiet */) { ob_start([$this, "progress"], 1); passthru($exec . " 2>&1", $this->status); ob_end_flush(); } else { exec($exec ." 2>&1", $output, $this->status); $this->output = implode("\n", $output); } if ($this->status) { throw new Exception("Command {$exec} failed ({$this->status})"); } return $this; } /** * Retrieve exit code of cmd run * @return int */ public function getStatus() { return $this->status; } /** * Retrieve output of cmd run * @return string */ public function getOutput() { return $this->output; } } <?php namespace pharext; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use Phar; use SplObjectStorage; /** * The extension install command executed by the extension phar */ class Installer implements Command { use CliCommand; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "prefix", "PHP installation prefix if phpize is not in \$PATH, e.g. /opt/php7", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["n", "common-name", "PHP common program name, e.g. php5 or zts-php", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "php"], ["c", "configure", "Additional extension configure flags, e.g. -c --with-flag", CliArgs::OPTIONAL|CliArgs::MULTI|CliArgs::REQARG], ["s", "sudo", "Installation might need increased privileges", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::OPTARG, "sudo -S %s"], ["i", "ini", "Activate in this php.ini instead of loaded default php.ini", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], [null, "signature", "Show package signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show package license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "name", "Show package name", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "date", "Show package release date", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "release", "Show package release version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } private function extract(Phar $phar) { $temp = (new Task\Extract($phar))->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($temp); return $temp; } private function hooks(SplObjectStorage $phars) { $hook = []; foreach ($phars as $phar) { if (isset($phar["pharext_package.php"])) { $sdir = include $phar["pharext_package.php"]; if ($sdir instanceof SourceDir) { $this->args->compile($sdir->getArgs()); $hook[] = $sdir; } } } return $hook; } private function load() { $list = new SplObjectStorage(); $phar = new Phar(Phar::running(false)); $temp = $this->extract($phar); foreach ($phar as $entry) { $dep_file = $entry->getBaseName(); if (fnmatch("*.ext.phar*", $dep_file)) { $dep_phar = new Phar("$temp/$dep_file"); $list[$dep_phar] = $this->extract($dep_phar); } } /* the actual ext.phar at last */ $list[$phar] = $temp; return $list; } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { try { /* load the phar(s) */ $list = $this->load(); /* installer hooks */ $hook = $this->hooks($list); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EEXTRACT); } /* standard arg stuff */ $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "name", "date", "license", "release", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } try { /* post process hooks */ foreach ($hook as $sdir) { $sdir->setArgs($this->args); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } /* install packages */ try { foreach ($list as $phar) { $this->info("Installing %s ...\n", basename($phar->getPath())); $this->install($list[$phar]); $this->activate($list[$phar]); $this->info("Successfully installed %s!\n", basename($phar->getPath())); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EINSTALL); } } /** * Phpize + trinity */ private function install($temp) { // phpize $phpize = new Task\Phpize($temp, $this->args->prefix, $this->args->{"common-name"}); $phpize->run($this->verbosity()); // configure $configure = new Task\Configure($temp, $this->args->configure, $this->args->prefix, $this->args{"common-name"}); $configure->run($this->verbosity()); // make $make = new Task\Make($temp); $make->run($this->verbosity()); // install $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $install = new Task\Make($temp, ["install"], $sudo); $install->run($this->verbosity()); } private function activate($temp) { if ($this->args->ini) { $files = [realpath($this->args->ini)]; } else { $files = array_filter(array_map("trim", explode(",", php_ini_scanned_files()))); $files[] = php_ini_loaded_file(); } $sudo = isset($this->args->sudo) ? $this->args->sudo : null; $type = $this->metadata("type") ?: "extension"; $activate = new Task\Activate($temp, $files, $type, $this->args->prefix, $this->args{"common-name"}, $sudo); if (!$activate->run($this->verbosity())) { $this->info("Extension already activated ...\n"); } } } <?php namespace pharext\Openssl; use pharext\Exception; class PrivateKey { /** * Private key * @var string */ private $key; /** * Public key * @var string */ private $pub; /** * Read a private key * @param string $file * @param string $password * @throws \pharext\Exception */ function __construct($file, $password) { /* there appears to be a bug with refcount handling of this * resource; when the resource is stored as property, it cannot be * "coerced to a private key" on openssl_sign() later in another method */ $key = openssl_pkey_get_private("file://$file", $password); if (!is_resource($key)) { throw new Exception("Could not load private key"); } openssl_pkey_export($key, $this->key); $this->pub = openssl_pkey_get_details($key)["key"]; } /** * Sign the PHAR * @param \Phar $package */ function sign(\Phar $package) { $package->setSignatureAlgorithm(\Phar::OPENSSL, $this->key); } /** * Export the public key to a file * @param string $file * @throws \pharext\Exception */ function exportPublicKey($file) { if (!file_put_contents("$file.tmp", $this->pub) || !rename("$file.tmp", $file)) { throw new Exception; } } } <?php namespace pharext; use Phar; use pharext\Cli\Args as CliArgs; use pharext\Cli\Command as CliCommand; use pharext\Exception; /** * The extension packaging command executed by bin/pharext */ class Packager implements Command { use CliCommand; /** * Extension source directory * @var pharext\SourceDir */ private $source; /** * Cleanups * @var array */ private $cleanup = []; /** * Create the command */ public function __construct() { $this->args = new CliArgs([ ["h", "help", "Display this help", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ["v", "verbose", "More output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["q", "quiet", "Less output", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["n", "name", "Extension name", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["r", "release", "Extension release version", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["s", "source", "Extension source directory", CliArgs::REQUIRED|CliArgs::SINGLE|CliArgs::REQARG], ["g", "git", "Use `git ls-tree` to determine file list", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["p", "pecl", "Use PECL package.xml to determine file list, name and release", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["d", "dest", "Destination directory", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG, "."], ["z", "gzip", "Create additional PHAR compressed with gzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["Z", "bzip", "Create additional PHAR compressed with bzip", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG], ["S", "sign", "Sign the PHAR with a private key", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::REQARG], ["E", "zend", "Mark as Zend Extension", CliArgs::OPTIONAL|CliARgs::SINGLE|CliArgs::NOARG], [null, "signature", "Show pharext signature", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "license", "Show pharext license", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], [null, "version", "Show pharext version", CliArgs::OPTIONAL|CliArgs::SINGLE|CliArgs::NOARG|CliArgs::HALT], ]); } /** * Perform cleaniup */ function __destruct() { foreach ($this->cleanup as $cleanup) { $cleanup->run(); } } /** * @inheritdoc * @see \pharext\Command::run() */ public function run($argc, array $argv) { $errs = []; $prog = array_shift($argv); foreach ($this->args->parse(--$argc, $argv) as $error) { $errs[] = $error; } if ($this->args["help"]) { $this->header(); $this->help($prog); exit; } try { foreach (["signature", "license", "version"] as $opt) { if ($this->args[$opt]) { printf("%s\n", $this->metadata($opt)); exit; } } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EARGS); } try { /* source needs to be evaluated before CliArgs validation, * so e.g. name and version can be overriden and CliArgs * does not complain about missing arguments */ $this->loadSource(); } catch (\Exception $e) { $errs[] = $e->getMessage(); } foreach ($this->args->validate() as $error) { $errs[] = $error; } if ($errs) { if (!$this->args["quiet"]) { $this->header(); } foreach ($errs as $err) { $this->error("%s\n", $err); } printf("\n"); if (!$this->args["quiet"]) { $this->help($prog); } exit(self::EARGS); } $this->createPackage(); } /** * Download remote source * @param string $source * @return string local source */ private function download($source) { if ($this->args->git) { $task = new Task\GitClone($source); } else { /* print newline only once */ $done = false; $task = new Task\StreamFetch($source, function($bytes_pct) use(&$done) { if (!$done) { $this->info(" %3d%% [%s>%s] \r", floor($bytes_pct*100), str_repeat("=", round(50*$bytes_pct)), str_repeat(" ", round(50*(1-$bytes_pct))) ); if ($bytes_pct == 1) { $done = true; printf("\n"); } } }); } $local = $task->run($this->verbosity()); $this->cleanup[] = new Task\Cleanup($local); return $local; } /** * Extract local archive * @param stirng $source * @return string extracted directory */ private function extract($source) { try { $task = new Task\Extract($source); $dest = $task->run($this->verbosity()); } catch (\Exception $e) { if (false === strpos($e->getMessage(), "checksum mismatch")) { throw $e; } $dest = (new Task\PaxFixup($source))->run($this->verbosity()); } $this->cleanup[] = new Task\Cleanup($dest); return $dest; } /** * Localize a possibly remote source * @param string $source * @return string local source directory */ private function localize($source) { if (!stream_is_local($source)) { $source = $this->download($source); $this->cleanup[] = new Task\Cleanup($source); } $source = realpath($source); if (!is_dir($source)) { $source = $this->extract($source); $this->cleanup[] = new Task\Cleanup($source); if (!$this->args->git) { $source = (new Task\PeclFixup($source))->run($this->verbosity()); } } return $source; } /** * Load the source dir * @throws \pharext\Exception */ private function loadSource(){ if ($this->args["source"]) { $source = $this->localize($this->args["source"]); if ($this->args["pecl"]) { $this->source = new SourceDir\Pecl($source); } elseif ($this->args["git"]) { $this->source = new SourceDir\Git($source); } elseif (is_file("$source/pharext_package.php")) { $this->source = include "$source/pharext_package.php"; } else { $this->source = new SourceDir\Basic($source); } if (!$this->source instanceof SourceDir) { throw new Exception("Unknown source dir $source"); } foreach ($this->source->getPackageInfo() as $key => $val) { $this->args->$key = $val; } } } /** * Creates the extension phar */ private function createPackage() { try { $meta = array_merge($this->metadata(), [ "date" => date("Y-m-d"), "name" => $this->args->name, "release" => $this->args->release, "license" => @file_get_contents(current(glob($this->source->getBaseDir()."/LICENSE*"))), "stub" => "pharext_installer.php", "type" => $this->args->zend ? "zend_extension" : "extension", ]); $file = (new Task\PharBuild($this->source, $meta))->run($this->verbosity()); } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } try { if ($this->args->sign) { $this->info("Using private key to sign phar ...\n"); $pass = (new Task\Askpass)->run($this->verbosity()); $sign = new Task\PharSign($file, $this->args->sign, $pass); $pkey = $sign->run($this->verbosity()); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::ESIGN); } if ($this->args->gzip) { try { $gzip = (new Task\PharCompress($file, Phar::GZ))->run(); $move = new Task\PharRename($gzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created gzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } if ($this->args->bzip) { try { $bzip = (new Task\PharCompress($file, Phar::BZ2))->run(); $move = new Task\PharRename($bzip, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created bzipped phar %s\n", $name); if ($this->args->sign) { $sign = new Task\PharSign($name, $this->args->sign, $pass); $sign->run($this->verbosity())->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->warn("%s\n", $e->getMessage()); } } try { $move = new Task\PharRename($file, $this->args->dest, $this->args->name ."-". $this->args->release); $name = $move->run($this->verbosity()); $this->info("Created executable phar %s\n", $name); if (isset($pkey)) { $pkey->exportPublicKey($name.".pubkey"); } } catch (\Exception $e) { $this->error("%s\n", $e->getMessage()); exit(self::EBUILD); } } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\SourceDir; use FilesystemIterator; use IteratorAggregate; use RecursiveCallbackFilterIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; class Basic implements IteratorAggregate, SourceDir { private $path; public function __construct($path) { $this->path = $path; } public function getBaseDir() { return $this->path; } public function getPackageInfo() { return []; } public function getArgs() { return []; } public function setArgs(Args $args) { } public function filter($current, $key, $iterator) { $sub = $current->getSubPath(); if ($sub === ".git" || $sub === ".hg" || $sub === ".svn") { return false; } return true; } public function getIterator() { $rdi = new RecursiveDirectoryIterator($this->path, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rci = new RecursiveCallbackFilterIterator($rdi, [$this, "filter"]); $rii = new RecursiveIteratorIterator($rci); foreach ($rii as $path => $child) { if (!$child->isDir()) { yield $path; } } } } <?php namespace pharext\SourceDir; use pharext\Command; use pharext\Cli\Args; use pharext\SourceDir; /** * Extension source directory which is a git repo */ class Git implements \IteratorAggregate, SourceDir { /** * Base directory * @var string */ private $path; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { $this->path = $path; } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * @inheritdoc * @return array */ public function getPackageInfo() { return []; } /** * @inheritdoc * @return array */ public function getArgs() { return []; } /** * @inheritdoc */ public function setArgs(Args $args) { } /** * Generate a list of files by `git ls-files` * @return Generator */ private function generateFiles() { $pwd = getcwd(); chdir($this->path); if (($pipe = popen("git ls-tree -r --name-only HEAD", "r"))) { $path = realpath($this->path); while (!feof($pipe)) { if (strlen($file = trim(fgets($pipe)))) { /* there may be symlinks, so no realpath here */ yield "$path/$file"; } } pclose($pipe); } chdir($pwd); } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext\SourceDir; use pharext\Cli\Args; use pharext\Exception; use pharext\SourceDir; use pharext\Tempfile; /** * A PECL extension source directory containing a v2 package.xml */ class Pecl implements \IteratorAggregate, SourceDir { /** * The package.xml * @var SimpleXmlElement */ private $sxe; /** * The base directory * @var string */ private $path; /** * The package.xml * @var string */ private $file; /** * @inheritdoc * @see \pharext\SourceDir::__construct() */ public function __construct($path) { if (is_file("$path/package2.xml")) { $sxe = simplexml_load_file($this->file = "$path/package2.xml"); } elseif (is_file("$path/package.xml")) { $sxe = simplexml_load_file($this->file = "$path/package.xml"); } else { throw new Exception("Missing package.xml in $path"); } $sxe->registerXPathNamespace("pecl", $sxe->getDocNamespaces()[""]); $this->sxe = $sxe; $this->path = realpath($path); } /** * @inheritdoc * @see \pharext\SourceDir::getBaseDir() */ public function getBaseDir() { return $this->path; } /** * Retrieve gathered package info * @return Generator */ public function getPackageInfo() { if (($name = $this->sxe->xpath("/pecl:package/pecl:name"))) { yield "name" => (string) $name[0]; } if (($release = $this->sxe->xpath("/pecl:package/pecl:version/pecl:release"))) { yield "release" => (string) $release[0]; } if ($this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease")) { yield "zend" => true; } } /** * @inheritdoc * @see \pharext\SourceDir::getArgs() */ public function getArgs() { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { yield [null, $cfg["name"], ucfirst($cfg["prompt"]), Args::OPTARG, strlen($cfg["default"]) ? $cfg["default"] : null]; } } /** * @inheritdoc * @see \pharext\SourceDir::setArgs() */ public function setArgs(Args $args) { $configure = $this->sxe->xpath("/pecl:package/pecl:extsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } $configure = $this->sxe->xpath("/pecl:package/pecl:zendextsrcrelease/pecl:configureoption"); foreach ($configure as $cfg) { if (isset($args[$cfg["name"]])) { $args->configure = "--{$cfg["name"]}={$args[$cfg["name"]]}"; } } } /** * Compute the path of a file by parent dir nodes * @param \SimpleXMLElement $ele * @return string */ private function dirOf($ele) { $path = ""; while (($ele = current($ele->xpath(".."))) && $ele->getName() == "dir") { $path = trim($ele["name"], "/") ."/". $path ; } return trim($path, "/"); } /** * Generate a list of files from the package.xml * @return Generator */ private function generateFiles() { /* hook */ $temp = tmpfile(); fprintf($temp, "<?php\nreturn new %s(__DIR__);\n", get_class($this)); rewind($temp); yield "pharext_package.php" => $temp; /* deps */ $dependencies = $this->sxe->xpath("/pecl:package/pecl:dependencies/pecl:required/pecl:package"); foreach ($dependencies as $key => $dep) { if (($glob = glob("{$this->path}/{$dep->name}-*.ext.phar*"))) { usort($glob, function($a, $b) { return version_compare( substr($a, strpos(".ext.phar", $a)), substr($b, strpos(".ext.phar", $b)) ); }); yield end($glob); } } /* files */ yield realpath($this->file); foreach ($this->sxe->xpath("//pecl:file") as $file) { yield realpath($this->path ."/". $this->dirOf($file) ."/". $file["name"]); } } /** * Implements IteratorAggregate * @see IteratorAggregate::getIterator() */ public function getIterator() { return $this->generateFiles(); } } <?php namespace pharext; /** * Source directory interface, which should yield file names to package on traversal */ interface SourceDir extends \Traversable { /** * Retrieve the base directory * @return string */ public function getBaseDir(); /** * Retrieve gathered package info * @return array|Traversable */ public function getPackageInfo(); /** * Provide installer command line args * @return array|Traversable */ public function getArgs(); /** * Process installer command line args * @param \pharext\Cli\Args $args */ public function setArgs(Cli\Args $args); } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; use pharext\Tempfile; /** * PHP INI activation */ class Activate implements Task { /** * @var string */ private $cwd; /** * @var array */ private $inis; /** * @var string */ private $type; /** * @var string */ private $php_config; /** * @var string */ private $sudo; /** * @param string $cwd working directory * @param array $inis custom INI or list of loaded/scanned INI files * @param string $type extension or zend_extension * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 * @param string $sudo sudo command * @throws \pharext\Exception */ public function __construct($cwd, array $inis, $type = "extension", $prefix = null, $common_name = "php", $sudo = null) { $this->cwd = $cwd; $this->type = $type; $this->sudo = $sudo; if (!$this->inis = $inis) { throw new Exception("No PHP INIs given"); } $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->php_config = $cmd; } /** * @param bool $verbose * @return boolean false, if extension was already activated */ public function run($verbose = false) { if ($verbose !== false) { printf("Running INI activation ...\n"); } $extension = basename(current(glob("{$this->cwd}/modules/*.so"))); if ($this->type === "zend_extension") { $pattern = preg_quote((new ExecCmd($this->php_config))->run(["--extension-dir"])->getOutput() . "/$extension", "/"); } else { $pattern = preg_quote($extension, "/"); } foreach ($this->inis as $file) { if ($verbose) { printf("Checking %s ...\n", $file); } $temp = new Tempfile("phpini"); foreach (file($file) as $line) { if (preg_match("/^\s*{$this->type}\s*=\s*[\"']?{$pattern}[\"']?\s*(;.*)?\$/", $line)) { return false; } fwrite($temp->getStream(), $line); } } /* not found; append to last processed file, which is the main by default */ if ($verbose) { printf("Activating in %s ...\n", $file); } fprintf($temp->getStream(), $this->type . "=%s\n", $extension); $temp->closeStream(); $path = $temp->getPathname(); $stat = stat($file); // owner transfer $ugid = sprintf("%d:%d", $stat["uid"], $stat["gid"]); $cmd = new ExecCmd("chown", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$ugid, $path]); // permission transfer $perm = decoct($stat["mode"] & 0777); $cmd = new ExecCmd("chmod", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$perm, $path]); // rename $cmd = new ExecCmd("mv", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $cmd->run([$path, $file]); if ($verbose) { printf("Replaced %s ...\n", $file); } return true; } } <?php namespace pharext\Task; use pharext\Task; /** * Ask password on console */ class Askpass implements Task { /** * @var string */ private $prompt; /** * @param string $prompt */ public function __construct($prompt = "Password:") { $this->prompt = $prompt; } /** * @param bool $verbose * @return string */ public function run($verbose = false) { system("stty -echo"); printf("%s ", $this->prompt); $pass = fgets(STDIN, 1024); printf("\n"); system("stty echo"); if (substr($pass, -1) == "\n") { $pass = substr($pass, 0, -1); } return $pass; } } <?php namespace pharext\Task; use pharext\Task; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * List all library files of pharext to bundle with a phar */ class BundleGenerator implements Task { /** * @param bool $verbose * @return Generator */ public function run($verbose = false) { if ($verbose) { printf("Packaging pharext ... \n"); } $rdi = new RecursiveDirectoryIterator(dirname(dirname(__DIR__))); $rii = new RecursiveIteratorIterator($rdi); for ($rii->rewind(); $rii->valid(); $rii->next()) { if (!$rii->isDot()) { yield $rii->getSubPathname() => $rii->key(); } } } } <?php namespace pharext\Task; use pharext\Task; use FilesystemIterator; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; /** * Recursively cleanup FS entries */ class Cleanup implements Task { /** * @var string */ private $rm; public function __construct($rm) { $this->rm = $rm; } /** * @param bool $verbose */ public function run($verbose = false) { if ($verbose) { printf("Cleaning up %s ...\n", $this->rm); } if ($this->rm instanceof Tempfile) { unset($this->rm); } elseif (is_dir($this->rm)) { $rdi = new RecursiveDirectoryIterator($this->rm, FilesystemIterator::CURRENT_AS_SELF | // needed for 5.5 FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::SKIP_DOTS); $rii = new RecursiveIteratorIterator($rdi, RecursiveIteratorIterator::CHILD_FIRST); foreach ($rii as $path => $child) { if ($child->isDir()) { rmdir($path); } else { unlink($path); } } rmdir($this->rm); } else { @unlink($this->rm); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Runs extension's configure */ class Configure implements Task { /** * @var array */ private $args; /** * @var string */ private $cwd; /** * @param string $cwd working directory * @param array $args configure args * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP programs common name, e.g. php5 */ public function __construct($cwd, array $args = null, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "-config"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->args = ["--with-php-config=$cmd"]; if ($args) { $this->args = array_merge($this->args, $args); } } public function run($verbose = false) { if ($verbose !== false) { printf("Running ./configure ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("./configure", $verbose); $cmd->run($this->args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Task; use pharext\Tempdir; use Phar; use PharData; /** * Extract a package archive */ class Extract implements Task { /** * @var Phar(Data) */ private $source; /** * @param mixed $source archive location */ public function __construct($source) { if ($source instanceof Phar || $source instanceof PharData) { $this->source = $source; } else { $this->source = new PharData($source); } } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose) { printf("Extracting %s ...\n", basename($this->source->getPath())); } $dest = new Tempdir("extract"); $this->source->extractTo($dest); return $dest; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Task; use pharext\Tempdir; /** * Clone a git repo */ class GitClone implements Task { /** * @var string */ private $source; /** * @param string $source git repo location */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return \pharext\Tempdir */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $local = new Tempdir("gitclone"); $cmd = new ExecCmd("git", $verbose); $cmd->run(["clone", $this->source, $local]); return $local; } } <?php namespace pharext\Task; use pharext\ExecCmd; use pharext\Exception; use pharext\Task; /** * Run make in the source dir */ class Make implements Task { /** * @var string */ private $cwd; /** * @var array */ private $args; /** * @var string */ private $sudo; /** * * @param string $cwd working directory * @param array $args make's arguments * @param string $sudo sudo command */ public function __construct($cwd, array $args = null, $sudo = null) { $this->cwd = $cwd; $this->sudo = $sudo; $this->args = $args; } /** * * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running make"); if ($this->args) { foreach ($this->args as $arg) { printf(" %s", $arg); } } printf(" ...\n"); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd("make", $verbose); if (isset($this->sudo)) { $cmd->setSu($this->sudo); } $args = $this->args; if (!$verbose) { $args = array_merge((array) $args, ["-s"]); } $cmd->run($args); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; class PaxFixup implements Task { private $source; public function __construct($source) { $this->source = $source; } private function openArchive($source) { $hdr = file_get_contents($source, false, null, 0, 3); if ($hdr === "\x1f\x8b\x08") { $fd = fopen("compress.zlib://$source", "r"); } elseif ($hdr === "BZh") { $fd = fopen("compress.bzip2://$source", "r"); } else { $fd = fopen($source, "r"); } if (!is_resource($fd)) { throw new Exception; } return $fd; } public function run($verbose = false) { if ($verbose !== false) { printf("Fixing up a tarball with global pax header ...\n"); } $temp = new Tempfile("paxfix"); stream_copy_to_stream($this->openArchive($this->source), $temp->getStream(), -1, 1024); $temp->closeStream(); return (new Extract((string) $temp))->run($verbose); } }<?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Fixup package.xml files in an extracted PECL dir */ class PeclFixup implements Task { /** * @var string */ private $source; /** * @param string $source source directory */ public function __construct($source) { $this->source = $source; } /** * @param bool $verbose * @return string sanitized source location * @throws \pahrext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Sanitizing PECL dir ...\n"); } $dirs = glob("{$this->source}/*", GLOB_ONLYDIR); $files = array_diff(glob("{$this->source}/*"), $dirs); $check = array_reduce($files, function($r, $v) { return $v && fnmatch("package*.xml", basename($v)); }, true); if (count($dirs) !== 1 || !$check) { throw new Exception("Does not look like an extracted PECL dir: {$this->source}"); } $dest = current($dirs); foreach ($files as $file) { if ($verbose) { printf("Moving %s into %s ...\n", basename($file), basename($dest)); } if (!rename($file, "$dest/" . basename($file))) { throw new Exception; } } return $dest; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\SourceDir; use pharext\Task; use pharext\Tempname; use Phar; /** * Build phar */ class PharBuild implements Task { /** * @var \pharext\SourceDir */ private $source; /** * @var array */ private $meta; /** * @var bool */ private $readonly; /** * @param SourceDir $source extension source directory * @param array $meta phar meta data * @param bool $readonly whether the stub has -dphar.readonly=1 set */ public function __construct(SourceDir $source = null, array $meta = null, $readonly = true) { $this->source = $source; $this->meta = $meta; $this->readonly = $readonly; } /** * @param bool $verbose * @return \pharext\Tempname * @throws \pharext\Exception */ public function run($verbose = false) { /* Phar::compress() and ::convert*() use strtok("."), ugh! * so, be sure to not use any other dots in the filename * except for .phar */ $temp = new Tempname("", "-pharext.phar"); $phar = new Phar($temp); $phar->startBuffering(); if ($this->meta) { $phar->setMetadata($this->meta); if (isset($this->meta["stub"])) { $phar->setDefaultStub($this->meta["stub"]); $phar->setStub("#!/usr/bin/php -dphar.readonly=" . intval($this->readonly) ."\n". $phar->getStub()); } } $phar->buildFromIterator((new Task\BundleGenerator)->run()); if ($this->source) { if ($verbose) { $bdir = $this->source->getBaseDir(); $blen = strlen($bdir); foreach ($this->source as $index => $file) { if (is_resource($file)) { printf("Packaging %s ...\n", $index); $phar[$index] = $file; } else { printf("Packaging %s ...\n", $index = trim(substr($file, $blen), "/")); $phar->addFile($file, $index); } } } else { $phar->buildFromIterator($this->source, $this->source->getBaseDir()); } } $phar->stopBuffering(); if (!chmod($temp, fileperms($temp) | 0111)) { throw new Exception; } return $temp; } }<?php namespace pharext\Task; use pharext\Task; use Phar; /** * Clone a compressed copy of a phar */ class PharCompress implements Task { /** * @var string */ private $file; /** * @var Phar */ private $package; /** * @var int */ private $encoding; /** * @var string */ private $extension; /** * @param string $file path to the original phar * @param int $encoding Phar::GZ or Phar::BZ2 */ public function __construct($file, $encoding) { $this->file = $file; $this->package = new Phar($file); $this->encoding = $encoding; switch ($encoding) { case Phar::GZ: $this->extension = ".gz"; break; case Phar::BZ2: $this->extension = ".bz2"; break; } } /** * @param bool $verbose * @return string */ public function run($verbose = false) { if ($verbose) { printf("Compressing %s ...\n", basename($this->package->getPath())); } $phar = $this->package->compress($this->encoding); $meta = $phar->getMetadata(); if (isset($meta["stub"])) { /* drop shebang */ $phar->setDefaultStub($meta["stub"]); } return $this->file . $this->extension; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; /** * Rename the phar archive */ class PharRename implements Task { /** * @var string */ private $phar; /** * @var string */ private $dest; /** * @var string */ private $name; /** * @param string $phar path to phar * @param string $dest destination dir * @param string $name package name */ public function __construct($phar, $dest, $name) { $this->phar = $phar; $this->dest = $dest; $this->name = $name; } /** * @param bool $verbose * @return string path to renamed phar * @throws \pharext\Exception */ public function run($verbose = false) { $extension = substr(strstr($this->phar, "-pharext.phar"), 8); $name = sprintf("%s/%s.ext%s", $this->dest, $this->name, $extension); if ($verbose) { printf("Renaming %s to %s ...\n", basename($this->phar), basename($name)); } if (!rename($this->phar, $name)) { throw new Exception; } return $name; } } <?php namespace pharext\Task; use pharext\Openssl; use pharext\Task; use Phar; /** * Sign the phar with a private key */ class PharSign implements Task { /** * @var Phar */ private $phar; /** * @var \pharext\Openssl\PrivateKey */ private $pkey; /** * * @param mixed $phar phar instance or path to phar * @param string $pkey path to private key * @param string $pass password for the private key */ public function __construct($phar, $pkey, $pass) { if ($phar instanceof Phar || $phar instanceof PharData) { $this->phar = $phar; } else { $this->phar = new Phar($phar); } $this->pkey = new Openssl\PrivateKey($pkey, $pass); } /** * @param bool $verbose * @return \pharext\Openssl\PrivateKey */ public function run($verbose = false) { if ($verbose) { printf("Signing %s ...\n", basename($this->phar->getPath())); } $this->pkey->sign($this->phar); return $this->pkey; } } <?php namespace pharext\Task; use pharext\Exception; use pharext\ExecCmd; use pharext\Task; /** * Run phpize in the extension source directory */ class Phpize implements Task { /** * @var string */ private $phpize; /** * * @var string */ private $cwd; /** * @param string $cwd working directory * @param string $prefix install prefix, e.g. /usr/local * @param string $common_name PHP program common name, e.g. php5 */ public function __construct($cwd, $prefix = null, $common_name = "php") { $this->cwd = $cwd; $cmd = $common_name . "ize"; if (isset($prefix)) { $cmd = $prefix . "/bin/" . $cmd; } $this->phpize = $cmd; } /** * @param bool $verbose * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Running %s ...\n", $this->phpize); } $pwd = getcwd(); if (!chdir($this->cwd)) { throw new Exception; } try { $cmd = new ExecCmd($this->phpize, $verbose); $cmd->run(); } finally { chdir($pwd); } } } <?php namespace pharext\Task; use pharext\Exception; use pharext\Task; use pharext\Tempfile; /** * Fetch a remote archive */ class StreamFetch implements Task { /** * @var string */ private $source; /** * @var callable */ private $progress; /** * @param string $source remote file location * @param callable $progress progress callback */ public function __construct($source, callable $progress) { $this->source = $source; $this->progress = $progress; } private function createStreamContext() { $progress = $this->progress; /* avoid bytes_max bug of older PHP versions */ $maxbytes = 0; return stream_context_create([],["notification" => function($notification, $severity, $message, $code, $bytes_cur, $bytes_max) use($progress, &$maxbytes) { if ($bytes_max > $maxbytes) { $maxbytes = $bytes_max; } switch ($notification) { case STREAM_NOTIFY_CONNECT: $progress(0); break; case STREAM_NOTIFY_PROGRESS: $progress($maxbytes > 0 ? $bytes_cur/$maxbytes : .5); break; case STREAM_NOTIFY_COMPLETED: /* this is sometimes not generated, why? */ $progress(1); break; } }]); } /** * @param bool $verbose * @return \pharext\Task\Tempfile * @throws \pharext\Exception */ public function run($verbose = false) { if ($verbose !== false) { printf("Fetching %s ...\n", $this->source); } $context = $this->createStreamContext(); if (!$remote = fopen($this->source, "r", false, $context)) { throw new Exception; } $local = new Tempfile("remote"); if (!stream_copy_to_stream($remote, $local->getStream())) { throw new Exception; } $local->closeStream(); /* STREAM_NOTIFY_COMPLETED is not generated, see above */ call_user_func($this->progress, 1); return $local; } } <?php namespace pharext; /** * Simple task interface */ interface Task { public function run($verbose = false); } <?php namespace pharext; /** * Create a temporary directory */ class Tempdir extends \SplFileInfo { /** * @param string $prefix prefix to uniqid() * @throws \pharext\Exception */ public function __construct($prefix) { $temp = new Tempname($prefix); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception("Could not create tempdir: ".error_get_last()["message"]); } parent::__construct($temp); } } <?php namespace pharext; /** * Create a new temporary file */ class Tempfile extends \SplFileInfo { /** * @var resource */ private $handle; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension * @throws \pharext\Exception */ public function __construct($prefix, $suffix = ".tmp") { $tries = 0; $omask = umask(077); do { $path = new Tempname($prefix, $suffix); $this->handle = fopen($path, "x"); } while (!is_resource($this->handle) && $tries++ < 10); umask($omask); if (!is_resource($this->handle)) { throw new Exception("Could not create temporary file"); } parent::__construct($path); } /** * Unlink the file */ public function __destruct() { @unlink($this->getPathname()); } /** * Close the stream */ public function closeStream() { fclose($this->handle); } /** * Retrieve the stream resource * @return resource */ public function getStream() { return $this->handle; } } <?php namespace pharext; use pharext\Exception; /** * A temporary file/directory name */ class Tempname { /** * @var string */ private $name; /** * @param string $prefix uniqid() prefix * @param string $suffix e.g. file extension */ public function __construct($prefix, $suffix = null) { $temp = sys_get_temp_dir() . "/pharext-" . posix_getlogin(); if (!is_dir($temp) && !mkdir($temp, 0700, true)) { throw new Exception; } $this->name = $temp ."/". uniqid($prefix) . $suffix; } /** * @return string */ public function __toString() { return (string) $this->name; } } <?php namespace pharext; const VERSION = "3.0.1"; <?php /** * The installer sub-stub for extension phars */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $installer = new pharext\Installer(); $installer->run($argc, $argv); <?php /** * The packager sub-stub for bin/pharext */ spl_autoload_register(function($c) { return include strtr($c, "\\_", "//") . ".php"; }); $packager = new pharext\Packager(); $packager->run($argc, $argv); <?php return new pharext\SourceDir\Pecl(__DIR__); <?xml version="1.0" encoding="UTF-8"?> <package packagerversion="1.9.4" version="2.1" xmlns="http://pear.php.net/dtd/package-2.1" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0 http://pear.php.net/dtd/tasks-1.0.xsd http://pear.php.net/dtd/package-2.1 http://pear.php.net/dtd/package-2.1.xsd"> <name>markdown</name> <channel>pecl.php.net</channel> <summary>Processes the Markdown language with the bundled discount library.</summary> <description>Markdown is a lightweight markup language created by John Gruber. It is also the name of the original tool written in Perl that converts such markup into HTML. This library is a wrapper for a modified version of the discount Markdown processor, created by David Parsons. It supports several extensions of the original Markdown language, including smartypants-style substitutions, pandoc- style document headers, and parts of Markdown Extra. The modifications to discount include discarding several parts important only to stand-alone applications that accompany the library code, better consistency in library interface functions, thread-safety without eager initialization of globals, usage of the Zend memory manager and support for Microsoft Windows. Discount is available at <http://www.pell.portland.or.us/~orc/Code/discount/>. The page of the original Markdown is available at <http://daringfireball.net/projects/markdown/>.</description> <lead> <name>Gustavo Lopes</name> <user>cataphract</user> <email>cataphract@php.net</email> <active>yes</active> </lead> <lead> <name>Pierre-Alain Joye</name> <user>pajoye</user> <email>pierre.php@gmail.com</email> <active>no</active> </lead> <date>2012-02-21</date> <time>10:09:49</time> <version> <release>1.0.0</release> <api>1.0.0</api> </version> <stability> <release>stable</release> <api>stable</api> </stability> <license uri="http://www.opensource.org/licenses/bsd-license.php">New BSD License</license> <notes> - Updated to post 2.1.3 discount version (commit 6eacbc931b) - Added MarkdownDocument::setReferencePrefix() method. </notes> <contents> <dir name="/"> <file md5sum="9bc2c5d5bb80dc0960e219e5493af8b3" name="examples/definition_list.php" role="doc" /> <file md5sum="4365802aa72c37e287871c063edff5ff" name="examples/dump_tree.php" role="doc" /> <file md5sum="ca7d8b59a981e96ac8b6f0a181e04f25" name="examples/pandoc_headers.php" role="doc" /> <file md5sum="b05cf011d7d3b5e4bf3d33693b9502b8" name="examples/pseudoprotos.php" role="doc" /> <file md5sum="83da0e3f7a467e7ee9a863b6177fc4d2" name="examples/simple_usage.php" role="doc" /> <file md5sum="cffddc99382a725f6e06c29ab3bf24fa" name="examples/smartypants.php" role="doc" /> <file md5sum="58f0820962b55f24fa4c3aa23ef1d359" name="examples/subclassing.php" role="doc" /> <file md5sum="5e2ca8b0a275599acbd6788acc573d7e" name="examples/subclassing_2.php" role="doc" /> <file md5sum="08987790008279ed410a5a2d0fb700c1" name="lib/amalloc.c" role="src" /> <file md5sum="da467d7e434d922b3d7c742428360783" name="lib/amalloc.h" role="src" /> <file md5sum="08987790008279ed410a5a2d0fb700c1" name="lib/basename.c" role="src" /> <file md5sum="ff7bcc632ee41cd1aba820817efc2f7a" name="lib/blocktags_generated.c" role="src" /> <file md5sum="b37fc072076eda22be0b83c3b9060127" name="lib/config.h" role="src" /> <file md5sum="d61aa6c4de0bfc3ceabcb936a4fa6928" name="lib/config.win32.h" role="src" /> <file md5sum="997a5295f05532ccd087cebc2a3718d6" name="lib/Csio.c" role="src" /> <file md5sum="2921b34e098e2e6e512bb6145364d7aa" name="lib/css.c" role="src" /> <file md5sum="f4210e2ba041e72fc075a552b605acce" name="lib/cstring.h" role="src" /> <file md5sum="b1d4cef51a627014b31dad5d39cde534" name="lib/docheader.c" role="src" /> <file md5sum="7f042d8cd08a999fd83a395c9147a80d" name="lib/dumptree.c" role="src" /> <file md5sum="36ec28452abb56dd6057553ac4ca9fd8" name="lib/emmatch.c" role="src" /> <file md5sum="18c1f71b1fba9f92912f316c7e102d64" name="lib/flags.c" role="src" /> <file md5sum="9166f682db91f1fcb8b091c39ad7859f" name="lib/generate.c" role="src" /> <file md5sum="1abe5b2a1e9dc9ac92fc8730bb5a9f5a" name="lib/html5.c" role="src" /> <file md5sum="08987790008279ed410a5a2d0fb700c1" name="lib/makepage.c" role="src" /> <file md5sum="ed0741a89810823744fdda827ff22ec9" name="lib/markdown.c" role="src" /> <file md5sum="fd267ab7355630243f7e27d4efecb003" name="lib/markdown.h" role="src" /> <file md5sum="08987790008279ed410a5a2d0fb700c1" name="lib/mkd2html.c" role="src" /> <file md5sum="a485a4d346d54b284013e87b94e71ea7" name="lib/mkdio.c" role="src" /> <file md5sum="508e8e7046ffecd6ce6cb9515e1f6ff3" name="lib/mkdio.h" role="src" /> <file md5sum="fda7f4eb98634b41435994583ecc91f9" name="lib/mktags.c" role="src" /> <file md5sum="cab2c436b5893a73c23f6b7f91000271" name="lib/resource.c" role="src" /> <file md5sum="42282242f1970e45becc3352ceaac74d" name="lib/setup.c" role="src" /> <file md5sum="f79451550fa3f5458deebf7e40039ca7" name="lib/tags.c" role="src" /> <file md5sum="b2c8d08d4a417562ed4f923505b3d04d" name="lib/tags.h" role="src" /> <file md5sum="08987790008279ed410a5a2d0fb700c1" name="lib/theme.c" role="src" /> <file md5sum="5b99d0b015ef556e2d8c50cf9f1a4a4a" name="lib/toc.c" role="src" /> <file md5sum="b3896eb9b24bf836535170af007ed300" name="lib/version.c" role="src" /> <file md5sum="6912718a21a13de875a996ed3d0a8477" name="lib/xml.c" role="src" /> <file md5sum="22091e6dc66358a61fd9250ff3b765c6" name="lib/xmlpage.c" role="src" /> <file md5sum="c4820cb525dbdfe43eea6451dc0ae068" name="tests/compile_basic.phpt" role="test" /> <file md5sum="3404e319dd8196e6c3c45894d97d51db" name="tests/compile_error.phpt" role="test" /> <file md5sum="e7dd54b0c2bc5fc1a32bf906e32fa59d" name="tests/compile_error2.phpt" role="test" /> <file md5sum="d9c29c409328d809135bfa004976b14e" name="tests/compile_variation1.phpt" role="test" /> <file md5sum="2dbfa02dc0e8efb8637d5c6b0980c328" name="tests/compile_variation2.phpt" role="test" /> <file md5sum="23ea6a2b2c45afd4d592ee177aba858f" name="tests/compile_variation3.phpt" role="test" /> <file md5sum="ae522736d8923ad0a0cd8e9c0e9e3d68" name="tests/compile_variation4.phpt" role="test" /> <file md5sum="90429687097d35872d9ffe19c98b396a" name="tests/compile_variation5.phpt" role="test" /> <file md5sum="142d6b00a22b39fc413fcd1e2eea685a" name="tests/compile_variation6.phpt" role="test" /> <file md5sum="a3a08c7def948c421ffeb8887fbfc47e" name="tests/compile_variation7.phpt" role="test" /> <file md5sum="824e2db9e7e2d1889b2f0288abcc7106" name="tests/compile_variation8.phpt" role="test" /> <file md5sum="29e2e9c2bb4fecac4f1f6f619e317943" name="tests/compile_variation9.phpt" role="test" /> <file md5sum="a48ed67bb464d787fd0a883cbdd0146b" name="tests/compile_variation10.phpt" role="test" /> <file md5sum="b59f27ca6f55f31248ea0d8b7d1b0001" name="tests/compile_variation11.phpt" role="test" /> <file md5sum="39e96127ff78807df91cad226b2be0e4" name="tests/compile_variation12.phpt" role="test" /> <file md5sum="bd4d3942f44ab3be3dad7723809dbdee" name="tests/compile_variation13.phpt" role="test" /> <file md5sum="504b426a4fa3bed79cbdcba53da1ed70" name="tests/compile_variation14.phpt" role="test" /> <file md5sum="523ab4603376ee6f8cb3a1338566e80d" name="tests/compile_variation15.phpt" role="test" /> <file md5sum="bb67afecf279b225f27efb99618e08c4" name="tests/compile_variation16.phpt" role="test" /> <file md5sum="135c340d513df77f2887aa66c5af03e2" name="tests/compile_variation17.phpt" role="test" /> <file md5sum="ad922a90d78ef80742c5182b35650525" name="tests/compile_variation18.phpt" role="test" /> <file md5sum="467bb828238d7144d221d73f74ad243b" name="tests/compile_variation19.phpt" role="test" /> <file md5sum="641940ca33311ad041c0d713ebacd848" name="tests/compile_variation20.phpt" role="test" /> <file md5sum="fea4a565e70d90a6e515c6ba9cdd40e3" name="tests/compile_variation21.phpt" role="test" /> <file md5sum="1abae66ce67c0e4031161e48b6b68100" name="tests/create_from_stream_basic.phpt" role="test" /> <file md5sum="0ba7da5b03b03b12f79254ca2d578b3e" name="tests/create_from_stream_error.phpt" role="test" /> <file md5sum="5ecf86b6f0a11c2a28d6565719ad3877" name="tests/create_from_stream_variation1.phpt" role="test" /> <file md5sum="f52856d79f270b899f5f4d5f4a5e6162" name="tests/create_from_stream_variation2.phpt" role="test" /> <file md5sum="6fd7687817f9117294ec16aba13d9112" name="tests/create_from_string_basic.phpt" role="test" /> <file md5sum="463ec20b4bfb3d6fc3d4a9a741b88b88" name="tests/create_from_string_error.phpt" role="test" /> <file md5sum="692b8c34fe428be3963ad81723e47f9b" name="tests/dump_tree_basic.phpt" role="test" /> <file md5sum="1078bda34e7252f14fddd975a74a92f1" name="tests/dump_tree_error.phpt" role="test" /> <file md5sum="6528dd7539c57913b670f9934763ea5f" name="tests/get_author_basic.phpt" role="test" /> <file md5sum="09669b0ba19e68a7a24398e18a94b2b6" name="tests/get_author_error.phpt" role="test" /> <file md5sum="cbeddf2874ddcc4f9669fcd817f4118b" name="tests/get_css_basic.phpt" role="test" /> <file md5sum="c8bbebc138e9bec7823426aa23cd8fec" name="tests/get_css_error.phpt" role="test" /> <file md5sum="d12f76f05ef016a36d4ff8de9e99af28" name="tests/get_date_basic.phpt" role="test" /> <file md5sum="d067a9d66a771665a1c79b69bdcf865f" name="tests/get_date_error.phpt" role="test" /> <file md5sum="d0174782a32921ae125964bddae430dd" name="tests/get_html_basic.phpt" role="test" /> <file md5sum="926a507c44748fdd996ec9c6eb750772" name="tests/get_html_error.phpt" role="test" /> <file md5sum="99d43fabaf64960189b7bb2691f09890" name="tests/get_html_error2.phpt" role="test" /> <file md5sum="37418576ce37cd3461c7f164b064fd64" name="tests/get_html_error3.phpt" role="test" /> <file md5sum="8546afec64814c3f37ac61f15b822bb0" name="tests/get_title_basic.phpt" role="test" /> <file md5sum="b3f52bbf734e4155c0dbe7c82c3985d5" name="tests/get_title_error.phpt" role="test" /> <file md5sum="622079d376dd29dc2a39280353566875" name="tests/get_toc_basic.phpt" role="test" /> <file md5sum="bdcc5715097742b6a791f784e6cbc81d" name="tests/get_toc_error.phpt" role="test" /> <file md5sum="9ff89ae512732c5dd556eea1f4dfeffa" name="tests/helpers.php.inc" role="test" /> <file md5sum="c8e822c586737a81cdafaf7950851743" name="tests/inheritance_basic.phpt" role="test" /> <file md5sum="1cdb7472ed4dba8df2e7e620d5683d1b" name="tests/inheritance_error.phpt" role="test" /> <file md5sum="d06f756a23fcdf7147ecbc665c2f6ab7" name="tests/is_compiled_basic.phpt" role="test" /> <file md5sum="14e61cc2a5da520180bc98a88e360494" name="tests/is_compiled_error.phpt" role="test" /> <file md5sum="76c6edda2da9503c07a64222f6b9ca79" name="tests/pp_fenced_code.phpt" role="test" /> <file md5sum="03acc7d316b3558b221c00fabfb1e738" name="tests/pp_with_github_tags.phpt" role="test" /> <file md5sum="ea45d27a96341236fcc7d1a96fe8e3dc" name="tests/set_attributes_callback_basic.phpt" role="test" /> <file md5sum="22d87dd84e9fd99850722044b7b16466" name="tests/set_attributes_callback_error.phpt" role="test" /> <file md5sum="977b1d3bd05d7e2e3af409674683665d" name="tests/set_reference_prefix_basic.phpt" role="test" /> <file md5sum="07ce7552968138faa5d8ed99c5fd08ee" name="tests/set_reference_prefix_error.phpt" role="test" /> <file md5sum="d5c4525fb7c226b80992975828f2407f" name="tests/set_url_callback_basic.phpt" role="test" /> <file md5sum="5074021c0f9d778d4ab9a5648887c0d7" name="tests/set_url_callback_error.phpt" role="test" /> <file md5sum="0043677f82df45ae1d37e8084ba1f1c8" name="tests/simple_example.txt" role="test" /> <file md5sum="6c7c6f5afc1a6a7732df9870f49da1ef" name="tests/syntax_start.txt" role="test" /> <file md5sum="67a1f37cdb14b253d11df75dec6447bb" name="tests/tranform_fragment_basic.phpt" role="test" /> <file md5sum="39df60a6e90352b72a3a88f0aa27079c" name="tests/tranform_fragment_error.phpt" role="test" /> <file md5sum="11d3012f4b2659df9371d27ea5809622" name="tests/tranform_fragment_variation1.phpt" role="test" /> <file md5sum="e4bdcfb6d26a6fbba576580d548ae702" name="tests/tranform_fragment_variation2.phpt" role="test" /> <file md5sum="39144be37d49295acfc0a90ccda60251" name="tests/write_css_basic.phpt" role="test" /> <file md5sum="dcb022e70974dc3d88ca741ad0887ca4" name="tests/write_css_error.phpt" role="test" /> <file md5sum="1f0164468ba163a30bc277d84e92d6fc" name="tests/write_fragment_basic.phpt" role="test" /> <file md5sum="87ec7f649e736191b9d5dce4065575b5" name="tests/write_fragment_error.phpt" role="test" /> <file md5sum="1702ec2d8dd64177635e98b364fa46e6" name="tests/write_fragment_variation1.phpt" role="test" /> <file md5sum="f1a72593e43df900b115409ed6d3a0d2" name="tests/write_fragment_variation2.phpt" role="test" /> <file md5sum="214c6b0f1dfb7d2c182464eacd46f992" name="tests/write_html_basic.phpt" role="test" /> <file md5sum="cb6262222478c3290c6aedc8d34e0b85" name="tests/write_html_error.phpt" role="test" /> <file md5sum="9ddae4f14eccd6593ccdcdc90b233fb6" name="tests/write_html_variation1.phpt" role="test" /> <file md5sum="5172ec09b133bf8ab867442dfbc8121b" name="tests/write_toc_basic.phpt" role="test" /> <file md5sum="da9415259df823b63728e5bb038a6388" name="tests/write_toc_error.phpt" role="test" /> <file md5sum="80a2e0f0d8e4a34d1619fafbd5eec290" name="tests/write_xhtml_page_basic.phpt" role="test" /> <file md5sum="a9fe77fa4157d6d1f3853855e743a252" name="tests/write_xhtml_page_error.phpt" role="test" /> <file md5sum="d442c7a082b40d375f5cae31f5f07ebf" name="config.m4" role="src" /> <file md5sum="37ba8f3c3e24aeda2f7d19fadf74e025" name="config.w32" role="src" /> <file md5sum="48a8a88432f7b682a86fd2b332cbe613" name="discount.c" role="src" /> <file md5sum="4e4f85128f7d89a2454464c4b25985d2" name="markdowndoc_class.c" role="src" /> <file md5sum="0daad43776623e73d66dc5101c684c3d" name="markdowndoc_class.h" role="src" /> <file md5sum="1f2412eca4e8d8feb7bc073c1ec321d3" name="markdowndoc_meth_callbacks.c" role="src" /> <file md5sum="6ec9917e8fc187514e74d027080a21a0" name="markdowndoc_meth_callbacks.h" role="src" /> <file md5sum="07be220e3bee112b122a5ecb47d2fe2f" name="markdowndoc_meth_document.c" role="src" /> <file md5sum="9b9fe7adfd4b75ea6a375cbc315c2dc7" name="markdowndoc_meth_document.h" role="src" /> <file md5sum="22dc3547c22b8dbc264eb18a9a0a0440" name="markdowndoc_meth_header.c" role="src" /> <file md5sum="dd2b23fa64b704c11d950c4cdf976a3b" name="markdowndoc_meth_header.h" role="src" /> <file md5sum="52edf0a51b36d7e19656a46219abd0e1" name="markdowndoc_meth_input.c" role="src" /> <file md5sum="a9b560bf196bbc18d4f56bc75a865c34" name="markdowndoc_meth_input.h" role="src" /> <file md5sum="1f3d632a0487fd6bf198bec9df4890a0" name="markdowndoc_meth_misc.c" role="src" /> <file md5sum="cea13634129b1cf102fc2517e0bebaff" name="markdowndoc_meth_misc.h" role="src" /> <file md5sum="eb94ecd3862751fdda77b68cbd156975" name="markdowndoc_meth_parts.c" role="src" /> <file md5sum="1057ac4ebc39b3eb8b0ed110bfc117ae" name="markdowndoc_meth_parts.h" role="src" /> <file md5sum="e4c392dded381aafa9df7bb53ece4801" name="php_discount.h" role="src" /> <file md5sum="d7714a7b0a65295e5f082b71ecefffe9" name="README" role="doc" /> <file md5sum="4de5ac807ae2c981c76d485c6f0f6d2f" name="CREDITS" role="doc" /> <file md5sum="66566669c5f18f290026297412c017e8" name="MarkdownDocument.php" role="doc" /> </dir> </contents> <dependencies> <required> <php> <min>5.2.0</min> </php> <pearinstaller> <min>1.4.8</min> </pearinstaller> </required> </dependencies> <providesextension>discount</providesextension> <extsrcrelease /> </package> <?php $t = <<<EOD Discount style definition list: =term one= definition 1 =term two= definition 2 Markdown extra style definition list Apple : Pomaceous fruit of plants of the genus Malus in the family Rosaceae. Orange : The fruit of an evergreen tree of the genus Citrus. EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; /* deactivated: */ echo "Now with NODLIST:\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NODLIST); echo $md->getHtml(); /* Expected output: <p>Discount style definition list:</p> <dl> <dt>term one</dt> <dd>definition 1</dd> <dt>term two</dt> <dd>definition 2</dd> </dl> <p>Markdown extra style definition list</p> <dl> <dt>Apple</dt> <dd>Pomaceous fruit of plants of the genus Malus in the family Rosaceae.</dd> <dt>Orange</dt> <dd>The fruit of an evergreen tree of the genus Citrus.</dd> </dl> Now with NODLIST: <p>Discount style definition list:</p> <p>=term one=</p> <pre><code>definition 1 </code></pre> <p>=term two=</p> <pre><code>definition 2 </code></pre> <p>Markdown extra style definition list</p> <p>Apple : Pomaceous fruit of plants of the genus Malus in the family Rosaceae.</p> <p>Orange : The fruit of an evergreen tree of the genus Citrus.</p> */ <?php $f = dirname(__FILE__)."/syntax_start.txt"; $md = MarkdownDocument::createFromStream($f); $md->compile(); var_dump($md->dumpTree($file = fopen('php://temp', 'r+'), "syntax_st")); echo stream_get_contents($file, -1, 0); /* if your platform provides the fopencookie native function, you can skip * stream_get_contents and the temporary file and do: * $md->dumpTree('php://output', "syntax_st"); */ /* Example of output: bool(true) syntax_st--+--[source]-----[header, <P>, 1 line] |--[html, 7 lines] `--[source]--+--[ul, <P>]--+--[item]--+--[markup, 1 line] | | `--[ul, <P>]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul, <P>]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul, <P>]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | `--[item]--+--[markup, 1 line] | `--[ul, <P>]--+--[item]-----[markup, 1 line] | `--[item]-----[markup, 1 line] |--[markup, <P>, 2 lines] `--[hr, <P>] */<?php $t = <<<EOD % This is the title % % 30 December 2010 Body of the document EOD; //Multi-line pandoc headers are not supported, e.g.: /* * % First line of title * Second line of title * % * % */ $md = MarkdownDocument::createFromString($t); echo "Title: ", $md->getTitle(), "\n\n"; //Complete page: $md->compile(); $md->writeXhtmlPage($f = fopen('php://temp', 'r+')); echo stream_get_contents($f, -1, 0); /* Expected output: Title: This is the title <?xml version="1.0" encoding="UTF-8"?> <!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>This is the title

Body of the document

*/ text) [lang](lang:mylang) [abbr](abbr:myabbr) [class](class:myclass) EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); //To disable the special treatment for these pseudo-protocols, one could use //$md->compile(MarkdownDocument::NO_EXT); echo $md->getHtml(); /* Expected output:

normal link id text lang abbr class

*/ This is a blockquote. > > This is the second paragraph in the blockquote. > > ## This is an H2 in a blockquote EOD; $md = MarkdownDocument::createFromString($markdown_str); $md->compile(); echo $md->getHtml(); /* Expected output:

A First Level Header

A Second Level Header

Now is the time for all good men to come to the aid of their country. This is just a regular paragraph.

The quick brown fox jumped over the lazy dog’s back.

Header 3

This is a blockquote.

This is the second paragraph in the blockquote.

This is an H2 in a blockquote

*/ compile(); //To disable these substitutions, one could use //$md->compile(MarkdownDocument::NOPANTS); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOPANTS); echo $md->getHtml(), "\n\n"; /* Expected output:

“text” is translated to text. “double-quoted text” becomes double-quoted text ‘single-quoted text’ becomes single-quoted text don’t is dont. as well as anything-elset. (But foo'tbar is just foo'tbar.) And it’s is its, as well as anything-elses (except not foo'sbar and the like.) ™ becomes ® becomes © becomes ¼th ? th. Ditto for ¼ (), ½ (), ¾ths (ths), and ¾ (). … becomes … also becomes — becomes – becomes

*/ initFromString((string)$markdown, $flags & $inputMask); $this->compile(((int) $flags) & ~$inputMask); } public function __toString() { return $this->getHtml(); } } $markdown_str = << a quote > continuation of a quote EOD; echo(new SimpleMarkdown($markdown_str)); /* Expected output:

A First Level Header

a quote continuation of a quote

*/initFromString((string)$markdown, $flags & $inputMask); $this->compile( ((int) $flags) & ~($inputMask | MarkdownDocument::NOHEADER) | MarkdownDocument::TOC); } public function getHead() { $title = $this->getTitle(); if (empty($title)) $title = "(no title)"; return "".htmlspecialchars($title, 0, "UTF-8")."\n". $this->getCss(); } public function getBody() { return $this->getToc()."\n".$this->getHtml(); } } $markdown_str = << a quote > continuation of a quote EOD; $md = new MarkdownEx($markdown_str); ?> getHead(); ?> getBody(); ?> Example document

A First Level Header

a quote continuation of a quote

*//* on merge: this file is not important *//* * debugging malloc()/realloc()/calloc()/free() that attempts * to keep track of just what's been allocated today. */ #ifndef AMALLOC_D #define AMALLOC_D /* on merge: deleted amalloc part */ #define adump() (void)1 /* on merge: added to use zend memory manager's definitions of emalloc, etc. */ #include #endif/*AMALLOC_D*/ /* on merge: this file is not important */static struct kw blocktags[] = { { "P", 1, 0 }, { "BR", 2, 1 }, { "DL", 2, 0 }, { "H1", 2, 0 }, { "H2", 2, 0 }, { "H3", 2, 0 }, { "H4", 2, 0 }, { "H5", 2, 0 }, { "H6", 2, 0 }, { "HR", 2, 1 }, { "OL", 2, 0 }, { "UL", 2, 0 }, { "BDO", 3, 0 }, { "DFN", 3, 0 }, { "DIV", 3, 0 }, { "MAP", 3, 0 }, { "PRE", 3, 0 }, { "WBR", 3, 0 }, { "XMP", 3, 0 }, { "NOBR", 4, 0 }, { "STYLE", 5, 0 }, { "TABLE", 5, 0 }, { "CENTER", 6, 0 }, { "IFRAME", 6, 0 }, { "OBJECT", 6, 0 }, { "SCRIPT", 6, 0 }, { "ADDRESS", 7, 0 }, { "LISTING", 7, 0 }, { "PLAINTEXT", 9, 0 }, { "BLOCKQUOTE", 10, 0 }, }; #define NR_blocktags 30 /* on merge: not in original, where this is auto-generated */ #ifndef __AC_MARKDOWN_PRE_D #define __AC_MARKDOWN_PRE_D 1 #ifdef _WIN32 #include "config.win32.h" #else #include #define HAVE_STRCASECMP 1 #define HAVE_STRNCASECMP 1 #if ULONG_MAX == 0xFFFFFFFF #define DWORD unsigned long #elif UINT_MAX == 0xFFFFFFFF #define DWORD unsigned int #else #error Neither longs or ints have 32-bits. You can try editing this file and defining DWORD to a larger type #endif #define WORD unsigned short #define BYTE unsigned char #endif #endif/* on merge: not in original */ #ifndef __AC_MARKDOWN_WIN32_D #define __AC_MARKDOWN_WIN32_D 1 /* TODO: make configurable via PHP's config */ #define USE_DISCOUNT_DL 1 #define USE_EXTRA_DL 1 #define TABSTOP 4 #define WITH_ID_ANCHOR 1 #define WITH_FENCED_CODE 1 #define WITH_GITHUB_TAGS 1 #define DWORD unsigned long #define WORD unsigned short #define BYTE unsigned char /* removed the random stuff; in Windows rand() is thread-safe, * but on other platforms it may not be. Not worth it */ #define HAVE_STRCASECMP 1 #ifndef strncasecmp #define strncasecmp _strnicmp #endif #define HAVE_STRNCASECMP 1 #endif #include #include #include #include "cstring.h" #include "amalloc.h" /* on merge: added markdown.h (last!) */ #include "markdown.h" /* putc() into a cstring */ void Csputc(int c, Cstring *iot) { EXPAND(*iot) = c; } /* printf() into a cstring */ int Csprintf(Cstring *iot, char *fmt, ...) { va_list ptr; int siz=100; do { RESERVE(*iot, siz); va_start(ptr, fmt); siz = vsnprintf(T(*iot)+S(*iot), ALLOCATED(*iot)-S(*iot), fmt, ptr); va_end(ptr); } while ( siz > (ALLOCATED(*iot)-S(*iot)) ); S(*iot) += siz; return siz; } /* write() into a cstring */ int Cswrite(Cstring *iot, char *bfr, int size) { RESERVE(*iot, size); memcpy(T(*iot)+S(*iot), bfr, size); S(*iot) += size; return size; } /* reparse() into a cstring */ void Csreparse(Cstring *iot, char *buf, int size, int flags) { MMIOT f; ___mkd_initmmiot(&f, 0); ___mkd_reparse(buf, size, 0, &f, 0); ___mkd_emblock(&f); SUFFIX(*iot, T(f.out), S(f.out)); ___mkd_freemmiot(&f, 0); } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2009 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last */ #include "config.h" /* * dump out stylesheet sections. */ static void stylesheets(Paragraph *p, Cstring *f) { Line* q; for ( ; p ; p = p->next ) { if ( p->typ == STYLE ) { for ( q = p->text; q ; q = q->next ) { Cswrite(f, T(q->text), S(q->text)); Csputc('\n', f); } } if ( p->down ) stylesheets(p->down, f); } } /* dump any embedded styles to a string */ int mkd_css(Document *d, char **res) { Cstring f; int size; if ( res && d && d->compiled ) { CREATE(f); RESERVE(f, 100); stylesheets(d->code, &f); /* on merge: on empty output, null termination (i.e. null termination always) */ size = S(f); EXPAND(f) = 0; /* HACK ALERT! HACK ALERT! HACK ALERT! */ *res = T(f); /* we know that a T(Cstring) is a character pointer */ /* so we can simply pick it up and carry it away, */ /* leaving the husk of the Ctring on the stack */ /* END HACK ALERT */ return size; } return EOF; } /* dump any embedded styles to a file */ int mkd_generatecss(Document *d, FILE *f) { char *res; int written = EOF, size = mkd_css(d, &res); /* on merge: fixed bug in order of fwrite args */ if ( size > 0 ) written = fwrite(res, 1, size, f); if ( res ) efree(res); /* on merge: changed so it returns 0 on no data and EOF only if there's an error in fwrite */ if (size == 0) { return 0; } else { return (written == size) ? size : EOF; } } /* two template types: STRING(t) which defines a pascal-style string * of element (t) [STRING(char) is the closest to the pascal string], * and ANCHOR(t) which defines a baseplate that a linked list can be * built up from. [The linked list /must/ contain a ->next pointer * for linking the list together with.] */ #ifndef _CSTRING_D #define _CSTRING_D #include #include #ifndef __WITHOUT_AMALLOC # include "amalloc.h" #endif /* expandable Pascal-style string. */ #define STRING(type) struct { type *text; int size, alloc; } #define CREATE(x) ( (T(x) = (void*)0), (S(x) = (x).alloc = 0) ) #define EXPAND(x) (S(x)++)[(S(x) < (x).alloc) \ ? (T(x)) \ : (T(x) = T(x) ? erealloc(T(x), sizeof T(x)[0] * ((x).alloc += 100)) \ : emalloc(sizeof T(x)[0] * ((x).alloc += 100)) )] /* on merge: added this: */ #define EXPAND_PERMANENT(x) (S(x)++)[(S(x) < (x).alloc) \ ? (T(x)) \ : (T(x) = T(x) ? perealloc(T(x), sizeof T(x)[0] * ((x).alloc += 100), 1) \ : pemalloc(sizeof T(x)[0] * ((x).alloc += 100), 1) )] /* on merge: windows defines DELETE... */ #ifdef DELETE #undef DELETE #endif #define DELETE(x) ALLOCATED(x) ? (efree(T(x)), S(x) = (x).alloc = 0) \ : ( S(x) = 0 ) /* on merge: added this: */ #define DELETE_PERMANENT(x) ALLOCATED(x) ? (pefree(T(x), 1), S(x) = (x).alloc = 0) \ : ( S(x) = 0 ) #define CLIP(t,i,sz) \ S(t) -= ( ((i) >= 0) && ((sz) > 0) && (((i)+(sz)) <= S(t)) ) ? \ (memmove(&T(t)[i], &T(t)[i+sz], (S(t)-(i+sz)+1)*sizeof(T(t)[0])), \ (sz)) : 0 #define RESERVE(x, sz) T(x) = ((x).alloc > S(x) + (sz) \ ? T(x) \ : T(x) \ ? erealloc(T(x), sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x))) \ : emalloc(sizeof T(x)[0] * ((x).alloc = 100+(sz)+S(x)))) #define SUFFIX(t,p,sz) \ memcpy(((S(t) += (sz)) - (sz)) + \ (T(t) = T(t) ? erealloc(T(t), sizeof T(t)[0] * ((t).alloc += sz)) \ : emalloc(sizeof T(t)[0] * ((t).alloc += sz))), \ (p), sizeof(T(t)[0])*(sz)) #define PREFIX(t,p,sz) \ RESERVE( (t), (sz) ); \ if ( S(t) ) { memmove(T(t)+(sz), T(t), S(t)); } \ memcpy( T(t), (p), (sz) ); \ S(t) += (sz) /* reference-style links (and images) are stored in an array */ #define T(x) (x).text #define S(x) (x).size #define ALLOCATED(x) (x).alloc /* abstract anchor type that defines a list base * with a function that attaches an element to * the end of the list. * * the list base field is named .text so that the T() * macro will work with it. */ #define ANCHOR(t) struct { t *text, *end; } #define E(t) ((t).end) #define ATTACH(t, p) ( T(t) ? ( (E(t)->next = (p)), (E(t) = (p)) ) \ : ( (T(t) = E(t) = (p)) ) ) typedef STRING(char) Cstring; extern void Csputc(int, Cstring *); extern int Csprintf(Cstring *, char *, ...); extern int Cswrite(Cstring *, char *, int); extern void Csreparse(Cstring *, char *, int, int); #endif/*_CSTRING_D*/ /* * docheader -- get values from the document header * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" static char * onlyifset(Line *l) { char *ret = T(l->text) + l->dle; return ret[0] ? ret : 0; } char * mkd_doc_title(Document *doc) { if ( doc && doc->title ) return onlyifset(doc->title); return 0; } char * mkd_doc_author(Document *doc) { if ( doc && doc->author ) return onlyifset(doc->author); return 0; } char * mkd_doc_date(Document *doc) { if ( doc && doc->date ) return onlyifset(doc->date); return 0; } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include "markdown.h" #include "cstring.h" #include "amalloc.h" struct frame { int indent; char c; }; typedef STRING(struct frame) Stack; static char * Pptype(int typ) { switch (typ) { case WHITESPACE: return "whitespace"; case CODE : return "code"; case QUOTE : return "quote"; case MARKUP : return "markup"; case HTML : return "html"; case DL : return "dl"; case UL : return "ul"; case OL : return "ol"; case LISTITEM : return "item"; case HDR : return "header"; case HR : return "hr"; case TABLE : return "table"; case SOURCE : return "source"; case STYLE : return "style"; default : return "mystery node!"; } } static void pushpfx(int indent, char c, Stack *sp) { struct frame *q = &EXPAND(*sp); q->indent = indent; q->c = c; } static void poppfx(Stack *sp) { S(*sp)--; } static void changepfx(Stack *sp, char c) { char ch; if ( !S(*sp) ) return; ch = T(*sp)[S(*sp)-1].c; if ( ch == '+' || ch == '|' ) T(*sp)[S(*sp)-1].c = c; } static void printpfx(Stack *sp, FILE *f) { int i; char c; if ( !S(*sp) ) return; c = T(*sp)[S(*sp)-1].c; if ( c == '+' || c == '-' ) { fprintf(f, "--%c", c); T(*sp)[S(*sp)-1].c = (c == '-') ? ' ' : '|'; } else for ( i=0; i < S(*sp); i++ ) { if ( i ) fprintf(f, " "); fprintf(f, "%*s%c", T(*sp)[i].indent + 2, " ", T(*sp)[i].c); if ( T(*sp)[i].c == '`' ) T(*sp)[i].c = ' '; } fprintf(f, "--"); } static void dumptree(Paragraph *pp, Stack *sp, FILE *f) { int count; Line *p; int d; static char *Begin[] = { 0, "P", "center" }; while ( pp ) { if ( !pp->next ) changepfx(sp, '`'); printpfx(sp, f); d = fprintf(f, "[%s", Pptype(pp->typ)); if ( pp->ident ) d += fprintf(f, " %s", pp->ident); if ( pp->align > 1 ) d += fprintf(f, ", <%s>", Begin[pp->align]); for (count=0, p=pp->text; p; ++count, (p = p->next) ) ; if ( count ) d += fprintf(f, ", %d line%s", count, (count==1)?"":"s"); d += fprintf(f, "]"); if ( pp->down ) { pushpfx(d, pp->down->next ? '+' : '-', sp); dumptree(pp->down, sp, f); poppfx(sp); } else fputc('\n', f); pp = pp->next; } } /* on merge: removed flags argument and removed compilation * step. In the original, the flags may or may not have been used, * depending on whether compilation had already occurred. */ int mkd_dump(Document *doc, FILE *out, char *title) { Stack stack; /* on merge: line changed: */ if ( doc && doc->compiled ) { CREATE(stack); pushpfx(fprintf(out, "%s", title), doc->code->next ? '+' : '-', &stack); dumptree(doc->code, &stack, out); DELETE(stack); /* on merge: commented out */ /* mkd_cleanup(doc); */ return 0; } return -1; } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2010 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" /* emmatch: the emphasis mangler that's run after a block * of html has been generated. * * It should create MarkdownTest_1.0 (and _1.0.3) * compatable emphasis for non-pathological cases * and it should fail in a standards-compliant way * when someone attempts to feed it junk. * * Emmatching is done after the input has been * processed into a STRING (f->Q) of text and * emphasis blocks. After ___mkd_emblock() finishes, * it truncates f->Q and leaves the rendered paragraph * if f->out. */ /* empair() -- find the NEAREST matching emphasis token (or * subtoken of a 3+ long emphasis token. */ static int empair(MMIOT *f, int first, int last, int match) { int i; block *begin, *p; begin = &T(f->Q)[first]; for (i=first+1; i <= last; i++) { p = &T(f->Q)[i]; if ( (p->b_type != bTEXT) && (p->b_count <= 0) ) continue; /* break? */ if ( p->b_type == begin->b_type ) { if ( p->b_count == match ) /* exact match */ return i; if ( p->b_count > 2 ) /* fuzzy match */ return i; } } return 0; } /* empair */ /* emfill() -- if an emphasis token has leftover stars or underscores, * convert them back into character and append them to b_text. */ static void emfill(block *p) { int j; if ( p->b_type == bTEXT ) return; for (j=0; j < p->b_count; j++) EXPAND(p->b_text) = p->b_char; p->b_count = 0; } /* emfill */ static void emclose(MMIOT *f, int first, int last) { int j; for (j=first+1; jQ)[j]); } static struct emtags { char open[10]; char close[10]; int size; } emtags[] = { { "" , "", 5 }, { "", "", 9 } }; static void emblock(MMIOT*,int,int); /* emmatch() -- match emphasis for a single emphasis token. */ static void emmatch(MMIOT *f, int first, int last) { block *start = &T(f->Q)[first]; int e, e2, match; switch (start->b_count) { case 2: if ( e = empair(f,first,last,match=2) ) break; case 1: e = empair(f,first,last,match=1); break; case 0: return; default: e = empair(f,first,last,1); e2= empair(f,first,last,2); if ( e2 >= e ) { e = e2; match = 2; } else match = 1; break; } if ( e ) { /* if we found emphasis to match, match it, recursively call * emblock to match emphasis inside the new html block, add * the emphasis markers for the block, then (tail) recursively * call ourself to match any remaining emphasis on this token. */ block *end = &T(f->Q)[e]; end->b_count -= match; start->b_count -= match; emblock(f, first, e); PREFIX(start->b_text, emtags[match-1].open, emtags[match-1].size-1); SUFFIX(end->b_post, emtags[match-1].close, emtags[match-1].size); emmatch(f, first, last); } } /* emmatch */ /* emblock() -- walk a blocklist, attempting to match emphasis */ static void emblock(MMIOT *f, int first, int last) { int i; for ( i = first; i <= last; i++ ) if ( T(f->Q)[i].b_type != bTEXT ) emmatch(f, i, last); emclose(f, first, last); } /* emblock */ /* ___mkd_emblock() -- emblock a string of blocks, then concatenate the * resulting text onto f->out. */ void ___mkd_emblock(MMIOT *f) { int i; block *p; emblock(f, 0, S(f->Q)-1); for (i=0; i < S(f->Q); i++) { p = &T(f->Q)[i]; emfill(p); if ( S(p->b_post) ) { SUFFIX(f->out, T(p->b_post), S(p->b_post)); DELETE(p->b_post); } if ( S(p->b_text) ) { SUFFIX(f->out, T(p->b_text), S(p->b_text)); DELETE(p->b_text); } } S(f->Q) = 0; } /* ___mkd_emblock */ #include #include "markdown.h" struct flagnames { DWORD flag; char *name; }; static struct flagnames flagnames[] = { { MKD_NOLINKS, "!LINKS" }, { MKD_NOIMAGE, "!IMAGE" }, { MKD_NOPANTS, "!PANTS" }, { MKD_NOHTML, "!HTML" }, { MKD_STRICT, "STRICT" }, { MKD_TAGTEXT, "TAGTEXT" }, { MKD_NO_EXT, "!EXT" }, { MKD_CDATA, "CDATA" }, { MKD_NOSUPERSCRIPT, "!SUPERSCRIPT" }, { MKD_NORELAXED, "!RELAXED" }, { MKD_NOTABLES, "!TABLES" }, { MKD_NOSTRIKETHROUGH,"!STRIKETHROUGH" }, { MKD_TOC, "TOC" }, { MKD_1_COMPAT, "MKD_1_COMPAT" }, { MKD_AUTOLINK, "AUTOLINK" }, { MKD_SAFELINK, "SAFELINK" }, { MKD_NOHEADER, "!HEADER" }, { MKD_TABSTOP, "TABSTOP" }, { MKD_NODIVQUOTE, "!DIVQUOTE" }, { MKD_NOALPHALIST, "!ALPHALIST" }, { MKD_NODLIST, "!DLIST" }, { MKD_EXTRA_FOOTNOTE, "FOOTNOTE" }, }; #define NR(x) (sizeof x/sizeof x[0]) void mkd_flags_are(FILE *f, DWORD flags, int htmlplease) { int i; int not, set, even=1; char *name; if ( htmlplease ) fprintf(f, "\n"); for (i=0; i < NR(flagnames); i++) { set = flags & flagnames[i].flag; name = flagnames[i].name; if ( not = (*name == '!') ) { ++name; set = !set; } if ( htmlplease ) { if ( even ) fprintf(f, " "); fprintf(f, ""); if ( !even ) fprintf(f, "\n"); } even = !even; } if ( htmlplease ) { if ( even ) fprintf(f, "\n"); fprintf(f, "
"); } else fputc(' ', f); if ( !set ) fprintf(f, htmlplease ? "" : "!"); fprintf(f, "%s", name); if ( htmlplease ) { if ( !set ) fprintf(f, ""); fprintf(f, "
\n"); } } void mkd_mmiot_flags(FILE *f, MMIOT *m, int htmlplease) { if ( m ) mkd_flags_are(f, m->flags, htmlplease); } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" typedef int (*stfu)(const void*,const void*); typedef void (*spanhandler)(MMIOT*,int); /* forward declarations */ static void text(MMIOT *f); static Paragraph *display(Paragraph*, MMIOT*); /* externals from markdown.c */ int __mkd_footsort(Footnote *, Footnote *); /* * push text into the generator input buffer */ static void push(char *bfr, int size, MMIOT *f) { while ( size-- > 0 ) EXPAND(f->in) = *bfr++; } /* look characters ahead of the cursor. */ static inline int peek(MMIOT *f, int i) { i += (f->isp-1); return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF; } /* pull a byte from the input buffer */ static inline int pull(MMIOT *f) { return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF; } /* return a pointer to the current position in the input buffer. */ static inline char* cursor(MMIOT *f) { return T(f->in) + f->isp; } static inline int isthisspace(MMIOT *f, int i) { int c = peek(f, i); return isspace(c) || (c == EOF); } static inline int isthisalnum(MMIOT *f, int i) { int c = peek(f, i); return (c != EOF) && isalnum(c); } static inline int isthisnonword(MMIOT *f, int i) { return isthisspace(f, i) || ispunct(peek(f,i)); } /* return/set the current cursor position */ #define mmiotseek(f,x) (f->isp = x) #define mmiottell(f) (f->isp) /* move n characters forward ( or -n characters backward) in the input buffer. */ static void shift(MMIOT *f, int i) { if (f->isp + i >= 0 ) f->isp += i; } /* Qchar() */ static void Qchar(int c, MMIOT *f) { block *cur; if ( S(f->Q) == 0 ) { cur = &EXPAND(f->Q); memset(cur, 0, sizeof *cur); cur->b_type = bTEXT; } else cur = &T(f->Q)[S(f->Q)-1]; EXPAND(cur->b_text) = c; } /* Qstring() */ static void Qstring(char *s, MMIOT *f) { while (*s) Qchar(*s++, f); } /* Qwrite() */ static void Qwrite(char *s, int size, MMIOT *f) { while (size-- > 0) Qchar(*s++, f); } /* Qprintf() */ static void Qprintf(MMIOT *f, char *fmt, ...) { char bfr[80]; va_list ptr; va_start(ptr,fmt); vsnprintf(bfr, sizeof bfr, fmt, ptr); va_end(ptr); Qstring(bfr, f); } /* Qem() */ static void Qem(MMIOT *f, char c, int count) { block *p = &EXPAND(f->Q); memset(p, 0, sizeof *p); p->b_type = (c == '*') ? bSTAR : bUNDER; p->b_char = c; p->b_count = count; memset(&EXPAND(f->Q), 0, sizeof(block)); } /* generate html from a markup fragment */ void ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc) { MMIOT sub; struct escaped e; ___mkd_initmmiot(&sub, f->footnotes); sub.flags = f->flags | flags; sub.cb = f->cb; sub.ref_prefix = f->ref_prefix; if ( esc ) { sub.esc = &e; e.up = f->esc; e.text = esc; } else sub.esc = f->esc; push(bfr, size, &sub); EXPAND(sub.in) = 0; S(sub.in)--; text(&sub); ___mkd_emblock(&sub); Qwrite(T(sub.out), S(sub.out), f); ___mkd_freemmiot(&sub, f->footnotes); } /* * check the escape list for special cases */ static int escaped(MMIOT *f, char c) { struct escaped *thing = f->esc; while ( thing ) { if ( strchr(thing->text, c) ) return 1; thing = thing->up; } return 0; } /* * write out a url, escaping problematic characters */ static void puturl(char *s, int size, MMIOT *f, int display) { unsigned char c; while ( size-- > 0 ) { c = *s++; if ( c == '\\' && size-- > 0 ) { c = *s++; if ( !( ispunct(c) || isspace(c) ) ) Qchar('\\', f); } if ( c == '&' ) Qstring("&", f); else if ( c == '<' ) Qstring("<", f); else if ( c == '"' ) Qstring("%22", f); else if ( isalnum(c) || ispunct(c) || (display && isspace(c)) ) Qchar(c, f); else if ( c == 003 ) /* untokenize ^C */ Qstring(" ", f); else Qprintf(f, "%%%02X", c); } } /* advance forward until the next character is not whitespace */ static int eatspace(MMIOT *f) { int c; for ( ; ((c=peek(f, 1)) != EOF) && isspace(c); pull(f) ) ; return c; } /* (match (a (nested (parenthetical (string.))))) */ static int parenthetical(int in, int out, MMIOT *f) { int size, indent, c; for ( indent=1,size=0; indent; size++ ) { if ( (c = pull(f)) == EOF ) return EOF; else if ( (c == '\\') && (peek(f,1) == out || peek(f,1) == in) ) { ++size; pull(f); } else if ( c == in ) ++indent; else if ( c == out ) --indent; } return size ? (size-1) : 0; } /* extract a []-delimited label from the input stream. */ static int linkylabel(MMIOT *f, Cstring *res) { char *ptr = cursor(f); int size; if ( (size = parenthetical('[',']',f)) != EOF ) { T(*res) = ptr; S(*res) = size; return 1; } return 0; } /* see if the quote-prefixed linky segment is actually a title. */ static int linkytitle(MMIOT *f, char quote, Footnote *ref) { int whence = mmiottell(f); char *title = cursor(f); char *e; register int c; while ( (c = pull(f)) != EOF ) { e = cursor(f); if ( c == quote ) { if ( (c = eatspace(f)) == ')' ) { T(ref->title) = 1+title; S(ref->title) = (e-title)-2; return 1; } } } mmiotseek(f, whence); return 0; } /* extract a =HHHxWWW size from the input stream */ static int linkysize(MMIOT *f, Footnote *ref) { int height=0, width=0; int whence = mmiottell(f); int c; if ( isspace(peek(f,0)) ) { pull(f); /* eat '=' */ for ( c = pull(f); isdigit(c); c = pull(f)) width = (width * 10) + (c - '0'); if ( c == 'x' ) { for ( c = pull(f); isdigit(c); c = pull(f)) height = (height*10) + (c - '0'); if ( isspace(c) ) c = eatspace(f); if ( (c == ')') || ((c == '\'' || c == '"') && linkytitle(f, c, ref)) ) { ref->height = height; ref->width = width; return 1; } } } mmiotseek(f, whence); return 0; } /* extract a <...>-encased url from the input stream. * (markdown 1.0.2b8 compatibility; older versions * of markdown treated the < and > as syntactic * sugar that didn't have to be there. 1.0.2b8 * requires a closing >, and then falls into the * title or closing ) */ static int linkybroket(MMIOT *f, int image, Footnote *p) { int c; int good = 0; T(p->link) = cursor(f); for ( S(p->link)=0; (c = pull(f)) != '>'; ++S(p->link) ) { /* pull in all input until a '>' is found, or die trying. */ if ( c == EOF ) return 0; else if ( (c == '\\') && ispunct(peek(f,2)) ) { ++S(p->link); pull(f); } } c = eatspace(f); /* next nonspace needs to be a title, a size, or ) */ if ( ( c == '\'' || c == '"' ) && linkytitle(f,c,p) ) good=1; else if ( image && (c == '=') && linkysize(f,p) ) good=1; else good=( c == ')' ); if ( good ) { if ( peek(f, 1) == ')' ) pull(f); ___mkd_tidy(&p->link); } return good; } /* linkybroket */ /* extract a (-prefixed url from the input stream. * the label is either of the format ``, where I * extract until I find a >, or it is of the format * `text`, where I extract until I reach a ')', a quote, * or (if image) a '=' */ static int linkyurl(MMIOT *f, int image, Footnote *p) { int c; int mayneedtotrim=0; if ( (c = eatspace(f)) == EOF ) return 0; if ( c == '<' ) { pull(f); if ( !(f->flags & MKD_1_COMPAT) ) return linkybroket(f,image,p); mayneedtotrim=1; } T(p->link) = cursor(f); for ( S(p->link)=0; (c = peek(f,1)) != ')'; ++S(p->link) ) { if ( c == EOF ) return 0; else if ( (c == '"' || c == '\'') && linkytitle(f, c, p) ) break; else if ( image && (c == '=') && linkysize(f, p) ) break; else if ( (c == '\\') && ispunct(peek(f,2)) ) { ++S(p->link); pull(f); } pull(f); } if ( peek(f, 1) == ')' ) pull(f); ___mkd_tidy(&p->link); if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') ) --S(p->link); return 1; } /* prefixes for */ static struct _protocol { char *name; int nlen; } protocol[] = { #define _aprotocol(x) { x, (sizeof x)-1 } _aprotocol( "https:" ), _aprotocol( "http:" ), _aprotocol( "news:" ), _aprotocol( "ftp:" ), #undef _aprotocol }; #define NRPROTOCOLS (sizeof protocol / sizeof protocol[0]) static int isautoprefix(char *text, int size) { int i; struct _protocol *p; for (i=0, p=protocol; i < NRPROTOCOLS; i++, p++) if ( (size >= p->nlen) && strncasecmp(text, p->name, p->nlen) == 0 ) return 1; return 0; } /* * all the tag types that linkylinky can produce are * defined by this structure. */ typedef struct linkytype { char *pat; int szpat; char *link_pfx; /* tag prefix and link pointer (eg: "" */ char *text_sfx; /* text suffix (eg: "" */ int flags; /* reparse flags */ int kind; /* tag is url or something else? */ #define IS_URL 0x01 } linkytype; static linkytype imaget = { 0, 0, "\"",", MKD_NOIMAGE|MKD_TAGTEXT, IS_URL }; static linkytype linkt = { 0, 0, "", "", MKD_NOLINKS, IS_URL }; /* * pseudo-protocols for [][]; * * id: generates tag * class: generates tag * raw: just dump the link without any processing */ static linkytype specials[] = { { "id:", 3, "", "", 0, 0 }, { "raw:", 4, 0, 0, 0, 0, 0, MKD_NOHTML, 0 }, { "lang:", 5, "", "", 0, 0 }, { "abbr:", 5, "", "", 0, 0 }, { "class:", 6, "", "", 0, 0 }, } ; #define NR(x) (sizeof x / sizeof x[0]) /* see if t contains one of our pseudo-protocols. */ static linkytype * pseudo(Cstring t) { int i; linkytype *r; for ( i=0, r=specials; i < NR(specials); i++,r++ ) { if ( (S(t) > r->szpat) && (strncasecmp(T(t), r->pat, r->szpat) == 0) ) return r; } return 0; } /* print out the start of an `img' or `a' tag, applying callbacks as needed. */ static void printlinkyref(MMIOT *f, linkytype *tag, char *link, int size) { char *edit; if ( f->flags & IS_LABEL ) return; Qstring(tag->link_pfx, f); if ( tag->kind & IS_URL ) { if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) { puturl(edit, strlen(edit), f, 0); if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data); } else puturl(link + tag->szpat, size - tag->szpat, f, 0); } else ___mkd_reparse(link + tag->szpat, size - tag->szpat, MKD_TAGTEXT, f, 0); Qstring(tag->link_sfx, f); if ( f->cb && f->cb->e_flags && (edit = (*f->cb->e_flags)(link, size, f->cb->e_data)) ) { Qchar(' ', f); Qstring(edit, f); if ( f->cb->e_free ) (*f->cb->e_free)(edit, f->cb->e_data); } } /* printlinkyref */ /* helper function for php markdown extra footnotes; allow the user to * define a prefix tag instead of just `fn` */ static char * p_or_nothing(p) MMIOT *p; { return p->ref_prefix ? p->ref_prefix : "fn"; } /* php markdown extra/daring fireball style print footnotes */ static int extra_linky(MMIOT *f, Cstring text, Footnote *ref) { if ( ref->flags & REFERENCED ) return 0; if ( f->flags & IS_LABEL ) ___mkd_reparse(T(text), S(text), linkt.flags, f, 0); else { ref->flags |= REFERENCED; ref->refnumber = ++ f->reference; Qprintf(f, "%d", p_or_nothing(f), ref->refnumber, p_or_nothing(f), ref->refnumber, ref->refnumber); } return 1; } /* extra_linky */ /* print out a linky (or fail if it's Not Allowed) */ static int linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref) { linkytype *tag; if ( image || (ref == 0) ) tag = &imaget; else if ( tag = pseudo(ref->link) ) { if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) ) return 0; } else if ( (f->flags & MKD_SAFELINK) && T(ref->link) && (T(ref->link)[0] != '/') && !isautoprefix(T(ref->link), S(ref->link)) ) /* if MKD_SAFELINK, only accept links that are local or * a well-known protocol */ return 0; else tag = &linkt; if ( f->flags & tag->flags ) return 0; if ( f->flags & IS_LABEL ) ___mkd_reparse(T(text), S(text), tag->flags, f, 0); else if ( tag->link_pfx ) { printlinkyref(f, tag, T(ref->link), S(ref->link)); if ( tag->WxH ) { if ( ref->height ) Qprintf(f," height=\"%d\"", ref->height); if ( ref->width ) Qprintf(f, " width=\"%d\"", ref->width); } if ( S(ref->title) ) { Qstring(" title=\"", f); ___mkd_reparse(T(ref->title), S(ref->title), MKD_TAGTEXT, f, 0); Qchar('"', f); } Qstring(tag->text_pfx, f); ___mkd_reparse(T(text), S(text), tag->flags, f, 0); Qstring(tag->text_sfx, f); } else Qwrite(T(ref->link) + tag->szpat, S(ref->link) - tag->szpat, f); return 1; } /* linkyformat */ /* * process embedded links and images */ static int linkylinky(int image, MMIOT *f) { int start = mmiottell(f); Cstring name; Footnote key, *ref; int status = 0; int extra_footnote = 0; CREATE(name); memset(&key, 0, sizeof key); if ( linkylabel(f, &name) ) { if ( peek(f,1) == '(' ) { pull(f); if ( linkyurl(f, image, &key) ) status = linkyformat(f, name, image, &key); } else { int goodlink, implicit_mark = mmiottell(f); if ( isspace(peek(f,1)) ) pull(f); if ( peek(f,1) == '[' ) { pull(f); /* consume leading '[' */ goodlink = linkylabel(f, &key.tag); } else { /* new markdown implicit name syntax doesn't * require a second [] */ mmiotseek(f, implicit_mark); goodlink = !(f->flags & MKD_1_COMPAT); if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' ) extra_footnote = 1; } if ( goodlink ) { if ( !S(key.tag) ) { DELETE(key.tag); T(key.tag) = T(name); S(key.tag) = S(name); } if ( ref = bsearch(&key, T(*f->footnotes), S(*f->footnotes), sizeof key, (stfu)__mkd_footsort) ) { if ( extra_footnote ) status = extra_linky(f,name,ref); else status = linkyformat(f, name, image, ref); } else if ( f->flags & IS_LABEL ) status = linkyformat(f, name, image, 0); } } } DELETE(name); ___mkd_freefootnote(&key); if ( status == 0 ) mmiotseek(f, start); return status; } /* write a character to output, doing text escapes ( & -> &, * > -> > < -> < ) */ static void cputc(int c, MMIOT *f) { switch (c) { case '&': Qstring("&", f); break; case '>': Qstring(">", f); break; case '<': Qstring("<", f); break; default : Qchar(c, f); break; } } /* * convert an email address to a string of nonsense */ static void mangle(char *s, int len, MMIOT *f) { while ( len-- > 0 ) { Qstring("&#", f); /* on merge: removed coin toss */ Qprintf(f, "x%02x;", *((unsigned char*)(s++)) ); } } /* nrticks() -- count up a row of tick marks */ static int nrticks(int offset, int tickchar, MMIOT *f) { int tick = 0; while ( peek(f, offset+tick) == tickchar ) tick++; return tick; } /* nrticks */ /* matchticks() -- match a certain # of ticks, and if that fails * match the largest subset of those ticks. * * if a subset was matched, return the # of ticks * that were matched. */ static int matchticks(MMIOT *f, int tickchar, int ticks, int *endticks) { int size, count, c; int subsize=0, subtick=0; *endticks = ticks; for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) { if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) { if ( count == ticks ) return size; else if ( count ) { if ( (count > subtick) && (count < ticks) ) { subsize = size; subtick = count; } size += count; } } } if ( subsize ) { *endticks = subtick; return subsize; } return 0; } /* matchticks */ /* code() -- write a string out as code. The only characters that have * special meaning in a code block are * `<' and `&' , which * are /always/ expanded to < and & */ static void code(MMIOT *f, char *s, int length) { int i,c; for ( i=0; i < length; i++ ) if ( (c = s[i]) == 003) /* ^C: expand back to 2 spaces */ Qstring(" ", f); else if ( c == '\\' && (i < length-1) && escaped(f, s[i+1]) ) cputc(s[++i], f); else cputc(c, f); } /* code */ /* delspan() -- write out a chunk of text, blocking with ... */ static void delspan(MMIOT *f, int size) { Qstring("", f); ___mkd_reparse(cursor(f)-1, size, 0, f, 0); Qstring("", f); } /* codespan() -- write out a chunk of text as code, trimming one * space off the front and/or back as appropriate. */ static void codespan(MMIOT *f, int size) { int i=0; if ( size > 1 && peek(f, size-1) == ' ' ) --size; if ( peek(f,i) == ' ' ) ++i, --size; Qstring("", f); code(f, cursor(f)+(i-1), size); Qstring("", f); } /* codespan */ /* before letting a tag through, validate against * MKD_NOLINKS and MKD_NOIMAGE */ static int forbidden_tag(MMIOT *f) { int c = toupper(peek(f, 1)); if ( f->flags & MKD_NOHTML ) return 1; if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) ) return 1; if ( c == 'I' && (f->flags & MKD_NOIMAGE) && strncasecmp(cursor(f)+1, "MG", 2) == 0 && !isthisalnum(f,4) ) return 1; return 0; } /* Check a string to see if it looks like a mail address * "looks like a mail address" means alphanumeric + some * specials, then a `@`, then alphanumeric + some specials, * but with a `.` */ static int maybe_address(char *p, int size) { int ok = 0; for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size) ; if ( ! (size && *p == '@') ) return 0; --size, ++p; if ( size && *p == '.' ) return 0; for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size ) if ( *p == '.' && size > 1 ) ok = 1; return size ? 0 : ok; } /* The size-length token at cursor(f) is either a mailto:, an * implicit mailto:, one of the approved url protocols, or just * plain old text. If it's a mailto: or an approved protocol, * linkify it, otherwise say "no" */ static int process_possible_link(MMIOT *f, int size) { int address= 0; int mailto = 0; char *text = cursor(f); if ( f->flags & MKD_NOLINKS ) return 0; if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) { /* if it says it's a mailto, it's a mailto -- who am * I to second-guess the user? */ address = 1; mailto = 7; /* 7 is the length of "mailto:"; we need this */ } else address = maybe_address(text, size); if ( address ) { Qstring("", f); mangle(text+mailto, size-mailto, f); Qstring("", f); return 1; } else if ( isautoprefix(text, size) ) { printlinkyref(f, &linkt, text, size); Qchar('>', f); puturl(text,size,f, 1); Qstring("", f); return 1; } return 0; } /* process_possible_link */ /* a < may be just a regular character, the start of an embedded html * tag, or the start of an . If it's an automatic * link, we also need to know if it's an email address because if it * is we need to mangle it in our futile attempt to cut down on the * spaminess of the rendered page. */ static int maybe_tag_or_link(MMIOT *f) { int c, size; int maybetag = 1; if ( f->flags & MKD_TAGTEXT ) return 0; for ( size=0; (c = peek(f, size+1)) != '>'; size++) { if ( c == EOF ) return 0; else if ( c == '\\' ) { maybetag=0; if ( peek(f, size+2) != EOF ) size++; } else if ( isspace(c) ) break; #if WITH_GITHUB_TAGS else if ( ! (c == '/' || c == '-' || c == '_' || isalnum(c) ) ) #else else if ( ! (c == '/' || isalnum(c) ) ) #endif maybetag=0; } if ( size ) { if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) { /* It is not a html tag unless we find the closing '>' in * the same block. */ while ( (c = peek(f, size+1)) != '>' ) if ( c == EOF ) return 0; else size++; if ( forbidden_tag(f) ) return 0; Qchar('<', f); while ( ((c = peek(f, 1)) != EOF) && (c != '>') ) Qchar(pull(f), f); return 1; } else if ( !isspace(c) && process_possible_link(f, size) ) { shift(f, size+1); return 1; } } return 0; } /* autolinking means that all inline html is . A * autolink url is alphanumerics, slashes, periods, underscores, * the at sign, colon, and the % character. */ static int maybe_autolink(MMIOT *f) { register int c; int size; /* greedily scan forward for the end of a legitimate link. */ for ( size=0; (c=peek(f, size+1)) != EOF; size++ ) if ( c == '\\' ) { if ( peek(f, size+2) != EOF ) ++size; } else if ( isspace(c) || strchr("'\"()[]{}<>`", c) ) break; if ( (size > 1) && process_possible_link(f, size) ) { shift(f, size); return 1; } return 0; } /* smartyquote code that's common for single and double quotes */ static int smartyquote(int *flags, char typeofquote, MMIOT *f) { int bit = (typeofquote == 's') ? 0x01 : 0x02; if ( bit & (*flags) ) { if ( isthisnonword(f,1) ) { Qprintf(f, "&r%cquo;", typeofquote); (*flags) &= ~bit; return 1; } } else if ( isthisnonword(f,-1) && peek(f,1) != EOF ) { Qprintf(f, "&l%cquo;", typeofquote); (*flags) |= bit; return 1; } return 0; } static int islike(MMIOT *f, char *s) { int len; int i; if ( s[0] == '<' ) { if ( !isthisnonword(f, -1) ) return 0; ++s; } if ( !(len = strlen(s)) ) return 0; if ( s[len-1] == '>' ) { if ( !isthisnonword(f,len-1) ) return 0; len--; } for (i=1; i < len; i++) if (tolower(peek(f,i)) != s[i]) return 0; return 1; } static struct smarties { char c0; char *pat; char *entity; int shift; } smarties[] = { { '\'', "'s>", "rsquo", 0 }, { '\'', "'t>", "rsquo", 0 }, { '\'', "'re>", "rsquo", 0 }, { '\'', "'ll>", "rsquo", 0 }, { '\'', "'ve>", "rsquo", 0 }, { '\'', "'m>", "rsquo", 0 }, { '\'', "'d>", "rsquo", 0 }, { '-', "---", "mdash", 2 }, { '-', "--", "ndash", 1 }, { '.', "...", "hellip", 2 }, { '.', ". . .", "hellip", 4 }, { '(', "(c)", "copy", 2 }, { '(', "(r)", "reg", 2 }, { '(', "(tm)", "trade", 3 }, { '3', "<3/4>", "frac34", 2 }, { '3', "<3/4ths>", "frac34", 2 }, { '1', "<1/2>", "frac12", 2 }, { '1', "<1/4>", "frac14", 2 }, { '1', "<1/4th>", "frac14", 2 }, { '&', "�", 0, 3 }, } ; #define NRSMART ( sizeof smarties / sizeof smarties[0] ) /* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm) */ static int smartypants(int c, int *flags, MMIOT *f) { int i; if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) ) return 0; for ( i=0; i < NRSMART; i++) if ( (c == smarties[i].c0) && islike(f, smarties[i].pat) ) { if ( smarties[i].entity ) Qprintf(f, "&%s;", smarties[i].entity); shift(f, smarties[i].shift); return 1; } switch (c) { case '<' : return 0; case '\'': if ( smartyquote(flags, 's', f) ) return 1; break; case '"': if ( smartyquote(flags, 'd', f) ) return 1; break; case '`': if ( peek(f, 1) == '`' ) { int j = 2; while ( (c=peek(f,j)) != EOF ) { if ( c == '\\' ) j += 2; else if ( c == '`' ) break; else if ( c == '\'' && peek(f, j+1) == '\'' ) { Qstring("“", f); ___mkd_reparse(cursor(f)+1, j-2, 0, f, 0); Qstring("”", f); shift(f,j+1); return 1; } else ++j; } } break; } return 0; } /* smartypants */ /* process a body of text encased in some sort of tick marks. If it * works, generate the output and return 1, otherwise just return 0 and * let the caller figure it out. */ static int tickhandler(MMIOT *f, int tickchar, int minticks, int allow_space, spanhandler spanner) { int endticks, size; int tick = nrticks(0, tickchar, f); if ( !allow_space && isspace(peek(f,tick)) ) return 0; if ( (tick >= minticks) && (size = matchticks(f,tickchar,tick,&endticks)) ) { if ( endticks < tick ) { size += (tick - endticks); tick = endticks; } shift(f, tick); (*spanner)(f,size); shift(f, size+tick-1); return 1; } return 0; } #define tag_text(f) (f->flags & MKD_TAGTEXT) static void text(MMIOT *f) { int c, j; int rep; int smartyflags = 0; while (1) { if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) ) maybe_autolink(f); c = pull(f); if (c == EOF) break; if ( smartypants(c, &smartyflags, f) ) continue; switch (c) { case 0: break; case 3: Qstring(tag_text(f) ? " " : "
", f); break; case '>': if ( tag_text(f) ) Qstring(">", f); else Qchar(c, f); break; case '"': if ( tag_text(f) ) Qstring(""", f); else Qchar(c, f); break; case '!': if ( peek(f,1) == '[' ) { pull(f); if ( tag_text(f) || !linkylinky(1, f) ) Qstring("![", f); } else Qchar(c, f); break; case '[': if ( tag_text(f) || !linkylinky(0, f) ) Qchar(c, f); break; /* A^B -> AB */ case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT)) || (isthisnonword(f,-1) && peek(f,-1) != ')') || isthisspace(f,1) ) Qchar(c,f); else { char *sup = cursor(f); int len = 0; if ( peek(f,1) == '(' ) { int here = mmiottell(f); pull(f); if ( (len = parenthetical('(',')',f)) <= 0 ) { mmiotseek(f,here); Qchar(c, f); break; } sup++; } else { while ( isthisalnum(f,1+len) ) ++len; if ( !len ) { Qchar(c,f); break; } shift(f,len); } Qstring("",f); ___mkd_reparse(sup, len, 0, f, "()"); Qstring("", f); } break; case '_': /* Underscores don't count if they're in the middle of a word */ if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT)) && isthisalnum(f,-1) && isthisalnum(f,1) ) { Qchar(c, f); break; } case '*': /* Underscores & stars don't count if they're out in the middle * of whitespace */ if ( isthisspace(f,-1) && isthisspace(f,1) ) { Qchar(c, f); break; } /* else fall into the regular old emphasis case */ if ( tag_text(f) ) Qchar(c, f); else { for (rep = 1; peek(f,1) == c; pull(f) ) ++rep; Qem(f,c,rep); } break; case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || ! tickhandler(f,c,2,0, delspan) ) Qchar(c, f); break; case '`': if ( tag_text(f) || !tickhandler(f,c,1,1,codespan) ) Qchar(c, f); break; case '\\': switch ( c = pull(f) ) { case '&': Qstring("&", f); break; case '<': Qstring("<", f); break; case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case ':': case '|': if ( f->flags & MKD_NOTABLES ) { Qchar('\\', f); shift(f,-1); break; } Qchar(c, f); break; case EOF: Qchar('\\', f); break; default: if ( escaped(f,c) || strchr(">#.-+{}]![*_\\()`", c) ) Qchar(c, f); else { Qchar('\\', f); shift(f, -1); } break; } break; case '<': if ( !maybe_tag_or_link(f) ) Qstring("<", f); break; case '&': j = (peek(f,1) == '#' ) ? 2 : 1; while ( isthisalnum(f,j) ) ++j; if ( peek(f,j) != ';' ) Qstring("&", f); else Qchar(c, f); break; default: Qchar(c, f); break; } } /* truncate the input string after we've finished processing it */ S(f->in) = f->isp = 0; } /* text */ /* print a header block */ static void printheader(Paragraph *pp, MMIOT *f) { #if WITH_ID_ANCHOR Qprintf(f, "hnumber); if ( f->flags & MKD_TOC ) { Qstring(" id=\"", f); mkd_string_to_anchor(T(pp->text->text), S(pp->text->text), (mkd_sta_function_t)Qchar, f, 1); Qchar('"', f); } Qchar('>', f); #else if ( f->flags & MKD_TOC ) { Qstring("
text->text), S(pp->text->text), (mkd_sta_function_t)Qchar, f, 1); Qstring("\">\n", f); } Qprintf(f, "", pp->hnumber); #endif push(T(pp->text->text), S(pp->text->text), f); text(f); Qprintf(f, "", pp->hnumber); } enum e_alignments { a_NONE, a_CENTER, a_LEFT, a_RIGHT }; static char* alignments[] = { "", " align=\"center\"", " align=\"left\"", " align=\"right\"" }; typedef STRING(int) Istring; static int splat(Line *p, char *block, Istring align, int force, MMIOT *f) { int first, idx = p->dle, colno = 0; ___mkd_tidy(&p->text); if ( T(p->text)[S(p->text)-1] == '|' ) --S(p->text); Qstring("\n", f); while ( idx < S(p->text) ) { first = idx; if ( force && (colno >= S(align)-1) ) idx = S(p->text); else while ( (idx < S(p->text)) && (T(p->text)[idx] != '|') ) { if ( T(p->text)[idx] == '\\' ) ++idx; ++idx; } Qprintf(f, "<%s%s>", block, alignments[ (colno < S(align)) ? T(align)[colno] : a_NONE ]); ___mkd_reparse(T(p->text)+first, idx-first, 0, f, "|"); Qprintf(f, "\n", block); idx++; colno++; } if ( force ) while (colno < S(align) ) { Qprintf(f, "<%s>\n", block, block); ++colno; } Qstring("\n", f); return colno; } static int printtable(Paragraph *pp, MMIOT *f) { /* header, dashes, then lines of content */ Line *hdr, *dash, *body; Istring align; int hcols,start; char *p; enum e_alignments it; hdr = pp->text; dash= hdr->next; body= dash->next; if ( T(hdr->text)[hdr->dle] == '|' ) { /* trim leading pipe off all lines */ Line *r; for ( r = pp->text; r; r = r->next ) r->dle ++; } /* figure out cell alignments */ CREATE(align); for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) { char first, last; int end; last=first=0; for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) { if ( p[end] == '\\' ) ++ end; else if ( !isspace(p[end]) ) { if ( !first) first = p[end]; last = p[end]; } } it = ( first == ':' ) ? (( last == ':') ? a_CENTER : a_LEFT) : (( last == ':') ? a_RIGHT : a_NONE ); EXPAND(align) = it; start = 1+end; } Qstring("\n", f); Qstring("\n", f); hcols = splat(hdr, "th", align, 0, f); Qstring("\n", f); if ( hcols < S(align) ) S(align) = hcols; else while ( hcols > S(align) ) EXPAND(align) = a_NONE; Qstring("\n", f); for ( ; body; body = body->next) splat(body, "td", align, 1, f); Qstring("\n", f); Qstring("
\n", f); DELETE(align); return 1; } static int printblock(Paragraph *pp, MMIOT *f) { Line *t = pp->text; static char *Begin[] = { "", "

", "

" }; static char *End[] = { "", "

","

" }; while (t) { if ( S(t->text) ) { if ( t->next && S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' ' && T(t->text)[S(t->text)-1] == ' ' ) { push(T(t->text), S(t->text)-2, f); push("\003\n", 2, f); } else { ___mkd_tidy(&t->text); push(T(t->text), S(t->text), f); if ( t->next ) push("\n", 1, f); } } t = t->next; } Qstring(Begin[pp->align], f); text(f); Qstring(End[pp->align], f); return 1; } static void printcode(Line *t, MMIOT *f) { int blanks; Qstring("
", f);
    for ( blanks = 0; t ; t = t->next ) {
	if ( S(t->text) > t->dle ) {
	    while ( blanks ) {
		Qchar('\n', f);
		--blanks;
	    }
	    code(f, T(t->text), S(t->text));
	    Qchar('\n', f);
	}
	else blanks++;
    }
    Qstring("
", f); } static void printhtml(Line *t, MMIOT *f) { int blanks; for ( blanks=0; t ; t = t->next ) if ( S(t->text) ) { for ( ; blanks; --blanks ) Qchar('\n', f); Qwrite(T(t->text), S(t->text), f); Qchar('\n', f); } else blanks++; } static void htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f) { ___mkd_emblock(f); if ( block ) Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments); ___mkd_emblock(f); while (( p = display(p, f) )) { ___mkd_emblock(f); Qstring("\n\n", f); } if ( block ) Qprintf(f, "", block); ___mkd_emblock(f); } static void definitionlist(Paragraph *p, MMIOT *f) { Line *tag; if ( p ) { Qstring("
\n", f); for ( ; p ; p = p->next) { for ( tag = p->text; tag; tag = tag->next ) { Qstring("
", f); ___mkd_reparse(T(tag->text), S(tag->text), 0, f, 0); Qstring("
\n", f); } htmlify(p->down, "dd", p->ident, f); Qchar('\n', f); } Qstring("
", f); } } static void listdisplay(int typ, Paragraph *p, MMIOT* f) { if ( p ) { Qprintf(f, "<%cl", (typ==UL)?'u':'o'); if ( typ == AL ) Qprintf(f, " type=\"a\""); Qprintf(f, ">\n"); for ( ; p ; p = p->next ) { htmlify(p->down, "li", p->ident, f); Qchar('\n', f); } Qprintf(f, "\n", (typ==UL)?'u':'o'); } } /* dump out a Paragraph in the desired manner */ static Paragraph* display(Paragraph *p, MMIOT *f) { if ( !p ) return 0; switch ( p->typ ) { case STYLE: case WHITESPACE: break; case HTML: printhtml(p->text, f); break; case CODE: printcode(p->text, f); break; case QUOTE: htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f); break; case UL: case OL: case AL: listdisplay(p->typ, p->down, f); break; case DL: definitionlist(p->down, f); break; case HR: Qstring("
", f); break; case HDR: printheader(p, f); break; case TABLE: printtable(p, f); break; case SOURCE: htmlify(p->down, 0, 0, f); break; default: printblock(p, f); break; } return p->next; } /* dump out a list of footnotes */ static void mkd_extra_footnotes(MMIOT *m) { int j, i; Footnote *t; if ( m->reference == 0 ) return; Csprintf(&m->out, "\n
\n
\n
    \n"); for ( i=1; i <= m->reference; i++ ) { for ( j=0; j < S(*m->footnotes); j++ ) { t = &T(*m->footnotes)[j]; if ( (t->refnumber == i) && (t->flags & REFERENCED) ) { Csprintf(&m->out, "
  1. \n

    ", p_or_nothing(m), t->refnumber); Csreparse(&m->out, T(t->title), S(t->title), 0); Csprintf(&m->out, "", p_or_nothing(m), t->refnumber); Csprintf(&m->out, "

  2. \n"); } } } Csprintf(&m->out, "
\n
\n"); } /* return a pointer to the compiled markdown * document. */ int mkd_document(Document *p, char **res) { int size; if ( p && p->compiled ) { if ( ! p->html ) { htmlify(p->code, 0, 0, p->ctx); if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE ) mkd_extra_footnotes(p->ctx); p->html = 1; } size = S(p->ctx->out); if ( (size == 0) || T(p->ctx->out)[size-1] ) EXPAND(p->ctx->out) = 0; *res = T(p->ctx->out); /* on merge: changed so that the caller owns the memory */ p->html = 0; CREATE(p->ctx->out); /* marks as not allocated */ return size; } return EOF; } /* block-level tags for passing html5 blocks through the blender */ #include "tags.h" /* on merge: commented out, but consider using this in the future */ /*void mkd_with_html5_tags() { static int populated = 0; if ( populated ) return; populated = 1; mkd_define_tag("ASIDE", 0); mkd_define_tag("FOOTER", 0); mkd_define_tag("HEADER", 0); mkd_define_tag("HGROUP", 0); mkd_define_tag("NAV", 0); mkd_define_tag("SECTION", 0); mkd_define_tag("ARTICLE", 0); mkd_sort_tags(); } */ /* on merge: this file is not important *//* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" #include "tags.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" typedef int (*stfu)(const void*,const void*); typedef ANCHOR(Paragraph) ParagraphRoot; static Paragraph *Pp(ParagraphRoot *, Line *, int); static Paragraph *compile(Line *, int, MMIOT *); /* case insensitive string sort for Footnote tags. */ int __mkd_footsort(Footnote *a, Footnote *b) { int i; char ac, bc; if ( S(a->tag) != S(b->tag) ) return S(a->tag) - S(b->tag); for ( i=0; i < S(a->tag); i++) { ac = tolower(T(a->tag)[i]); bc = tolower(T(b->tag)[i]); if ( isspace(ac) && isspace(bc) ) continue; if ( ac != bc ) return ac - bc; } return 0; } /* find the first blank character after position */ static int nextblank(Line *t, int i) { while ( (i < S(t->text)) && !isspace(T(t->text)[i]) ) ++i; return i; } /* find the next nonblank character after position */ static int nextnonblank(Line *t, int i) { while ( (i < S(t->text)) && isspace(T(t->text)[i]) ) ++i; return i; } /* find the first nonblank character on the Line. */ int mkd_firstnonblank(Line *p) { return nextnonblank(p,0); } static inline int blankline(Line *p) { return ! (p && (S(p->text) > p->dle) ); } static Line * skipempty(Line *p) { while ( p && (p->dle == S(p->text)) ) p = p->next; return p; } void ___mkd_tidy(Cstring *t) { while ( S(*t) && isspace(T(*t)[S(*t)-1]) ) --S(*t); } static struct kw comment = { "!--", 3, 0 }; static struct kw * isopentag(Line *p) { int i=0, len; char *line; if ( !p ) return 0; line = T(p->text); len = S(p->text); if ( len < 3 || line[0] != '<' ) return 0; if ( line[1] == '!' && line[2] == '-' && line[3] == '-' ) /* comments need special case handling, because * the !-- doesn't need to end in a whitespace */ return &comment; /* find how long the tag is so we can check to see if * it's a block-level tag */ for ( i=1; i < len && T(p->text)[i] != '>' && T(p->text)[i] != '/' && !isspace(T(p->text)[i]); ++i ) ; return mkd_search_tags(T(p->text)+1, i-1); } typedef struct _flo { Line *t; int i; } FLO; #define floindex(x) (x.i) static int flogetc(FLO *f) { if ( f && f->t ) { if ( f->i < S(f->t->text) ) return T(f->t->text)[f->i++]; f->t = f->t->next; f->i = 0; return flogetc(f); } return EOF; } static void splitline(Line *t, int cutpoint) { if ( t && (cutpoint < S(t->text)) ) { Line *tmp = ecalloc(1, sizeof *tmp); tmp->next = t->next; t->next = tmp; tmp->dle = t->dle; SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint); S(t->text) = cutpoint; } } #define UNCHECK(l) ((l)->flags &= ~CHECKED) /* * walk a line, seeing if it's any of half a dozen interesting regular * types. */ static void checkline(Line *l) { int eol, i; int dashes = 0, spaces = 0, equals = 0, underscores = 0, stars = 0, tildes = 0; l->flags |= CHECKED; l->kind = chk_text; l->count = 0; if (l->dle >= 4) { l->kind=chk_code; return; } for ( eol = S(l->text); eol > l->dle && isspace(T(l->text)[eol-1]); --eol ) ; for (i=l->dle; itext)[i]; if ( c != ' ' ) l->count++; switch (c) { case '-': dashes = 1; break; case ' ': spaces = 1; break; case '=': equals = 1; break; case '_': underscores = 1; break; case '*': stars = 1; break; case '~': tildes = 1; break; default: return; } } if ( dashes + equals + underscores + stars + tildes > 1 ) return; if ( spaces ) { if ( (underscores || stars || dashes) ) l->kind = chk_hr; return; } if ( stars || underscores ) { l->kind = chk_hr; } else if ( dashes ) { l->kind = chk_dash; } else if ( tildes ) { l->kind = chk_tilde; } else if ( equals ) { l->kind = chk_equal; } } static Line * commentblock(Paragraph *p, int *unclosed) { Line *t, *ret; char *end; for ( t = p->text; t ; t = t->next) { if ( end = strstr(T(t->text), "-->") ) { splitline(t, 3 + (end - T(t->text)) ); ret = t->next; t->next = 0; return ret; } } *unclosed = 1; return t; } static Line * htmlblock(Paragraph *p, struct kw *tag, int *unclosed) { Line *ret; FLO f = { p->text, 0 }; int c; int i, closing, depth=0; *unclosed = 0; if ( tag == &comment ) return commentblock(p, unclosed); if ( tag->selfclose ) { ret = f.t->next; f.t->next = 0; return ret; } while ( (c = flogetc(&f)) != EOF ) { if ( c == '<' ) { /* tag? */ c = flogetc(&f); if ( c == '!' ) { /* comment? */ if ( flogetc(&f) == '-' && flogetc(&f) == '-' ) { /* yes */ while ( (c = flogetc(&f)) != EOF ) { if ( c == '-' && flogetc(&f) == '-' && flogetc(&f) == '>') /* consumed whole comment */ break; } } } else { if ( closing = (c == '/') ) c = flogetc(&f); for ( i=0; i < tag->size; c=flogetc(&f) ) { if ( tag->id[i++] != toupper(c) ) break; } if ( (i == tag->size) && !isalnum(c) ) { depth = depth + (closing ? -1 : 1); if ( depth == 0 ) { while ( c != EOF && c != '>' ) { /* consume trailing gunk in close tag */ c = flogetc(&f); } if ( c == EOF ) break; if ( !f.t ) return 0; splitline(f.t, floindex(f)); ret = f.t->next; f.t->next = 0; return ret; } } } } } *unclosed = 1; return 0; } /* footnotes look like ^{0,3}[stuff]: $ */ static int isfootnote(Line *t) { int i; if ( ( (i = t->dle) > 3) || (T(t->text)[i] != '[') ) return 0; for ( ++i; i < S(t->text) ; ++i ) { if ( T(t->text)[i] == '[' ) return 0; else if ( T(t->text)[i] == ']' ) return ( T(t->text)[i+1] == ':' ) ; } return 0; } static inline int isquote(Line *t) { return (t->dle < 4 && T(t->text)[t->dle] == '>'); } static inline int iscode(Line *t) { return (t->dle >= 4); } static inline int ishr(Line *t) { if ( ! (t->flags & CHECKED) ) checkline(t); if ( t->count > 2 ) return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal; return 0; } static int issetext(Line *t, int *htyp) { Line *n; /* check for setext-style HEADER * ====== */ if ( (n = t->next) ) { if ( !(n->flags & CHECKED) ) checkline(n); if ( n->kind == chk_dash || n->kind == chk_equal ) { *htyp = SETEXT; return 1; } } return 0; } static int ishdr(Line *t, int *htyp) { /* ANY leading `#`'s make this into an ETX header */ if ( (t->dle == 0) && (S(t->text) > 1) && (T(t->text)[0] == '#') ) { *htyp = ETX; return 1; } /* And if not, maybe it's a SETEXT header instead */ return issetext(t, htyp); } static inline int end_of_block(Line *t) { int dummy; if ( !t ) return 0; return ( (S(t->text) <= t->dle) || ishr(t) || ishdr(t, &dummy) ); } static Line* is_discount_dt(Line *t, int *clip) { #if USE_DISCOUNT_DL if ( t && t->next && (S(t->text) > 2) && (t->dle == 0) && (T(t->text)[0] == '=') && (T(t->text)[S(t->text)-1] == '=') ) { if ( t->next->dle >= 4 ) { *clip = 4; return t; } else return is_discount_dt(t->next, clip); } #endif return 0; } static int is_extra_dd(Line *t) { return (t->dle < 4) && (T(t->text)[t->dle] == ':') && isspace(T(t->text)[t->dle+1]); } static Line* is_extra_dt(Line *t, int *clip) { #if USE_EXTRA_DL if ( t && t->next && T(t->text)[0] != '=' && S(t->text) != 0 && T(t->text)[S(t->text)-1] != '=') { Line *x; if ( iscode(t) || end_of_block(t) ) return 0; if ( (x = skipempty(t->next)) && is_extra_dd(x) ) { *clip = x->dle+2; return t; } if ( x=is_extra_dt(t->next, clip) ) return x; } #endif return 0; } static Line* isdefinition(Line *t, int *clip, int *kind) { Line *ret; *kind = 1; if ( ret = is_discount_dt(t,clip) ) return ret; *kind=2; return is_extra_dt(t,clip); } static int islist(Line *t, int *clip, DWORD flags, int *list_type) { int i, j; char *q; if ( end_of_block(t) ) return 0; if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type) ) return DL; if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) { i = nextnonblank(t, t->dle+1); *clip = (i > 4) ? 4 : i; *list_type = UL; return AL; } if ( (j = nextblank(t,t->dle)) > t->dle ) { if ( T(t->text)[j-1] == '.' ) { if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT)) && (j == t->dle + 2) && isalpha(T(t->text)[t->dle]) ) { j = nextnonblank(t,j); *clip = (j > 4) ? 4 : j; *list_type = AL; return AL; } strtoul(T(t->text)+t->dle, &q, 10); if ( (q > T(t->text)+t->dle) && (q == T(t->text) + (j-1)) ) { j = nextnonblank(t,j); *clip = (j > 4) ? 4 : j; *list_type = OL; return AL; } } } return 0; } static Line * headerblock(Paragraph *pp, int htyp) { Line *ret = 0; Line *p = pp->text; int i, j; switch (htyp) { case SETEXT: /* p->text is header, p->next->text is -'s or ='s */ pp->hnumber = (T(p->next->text)[0] == '=') ? 1 : 2; ret = p->next->next; ___mkd_freeLine(p->next); p->next = 0; break; case ETX: /* p->text is ###header###, so we need to trim off * the leading and trailing `#`'s */ for (i=0; (T(p->text)[i] == T(p->text)[0]) && (i < S(p->text)-1) && (i < 6); i++) ; pp->hnumber = i; while ( (i < S(p->text)) && isspace(T(p->text)[i]) ) ++i; CLIP(p->text, 0, i); UNCHECK(p); for (j=S(p->text); (j > 1) && (T(p->text)[j-1] == '#'); --j) ; while ( j && isspace(T(p->text)[j-1]) ) --j; S(p->text) = j; ret = p->next; p->next = 0; break; } return ret; } static Line * codeblock(Paragraph *p) { Line *t = p->text, *r; for ( ; t; t = r ) { CLIP(t->text,0,4); t->dle = mkd_firstnonblank(t); if ( !( (r = skipempty(t->next)) && iscode(r)) ) { ___mkd_freeLineRange(t,r); t->next = 0; return r; } } return t; } #ifdef WITH_FENCED_CODE static int iscodefence(Line *r, int size) { if ( !(r->flags & CHECKED) ) checkline(r); return (r->kind == chk_tilde) && (r->count >= size); } static Paragraph * fencedcodeblock(ParagraphRoot *d, Line **ptr) { Line *first, *r; Paragraph *ret; first = (*ptr); /* don't allow zero-length code fences */ if ( (first->next == 0) || iscodefence(first->next, first->count) ) return 0; /* find the closing fence, discard the fences, * return a Paragraph with the contents */ for ( r = first; r && r->next; r = r->next ) if ( iscodefence(r->next, first->count) ) { (*ptr) = r->next->next; ret = Pp(d, first->next, CODE); ___mkd_freeLine(first); ___mkd_freeLine(r->next); r->next = 0; return ret; } return 0; } #endif static int centered(Line *first, Line *last) { if ( first&&last ) { int len = S(last->text); if ( (len > 2) && (strncmp(T(first->text), "->", 2) == 0) && (strncmp(T(last->text)+len-2, "<-", 2) == 0) ) { CLIP(first->text, 0, 2); S(last->text) -= 2; return CENTER; } } return 0; } static int endoftextblock(Line *t, int toplevelblock, DWORD flags) { int z; if ( end_of_block(t) || isquote(t) ) return 1; /* HORRIBLE STANDARDS KLUDGES: * 1. non-toplevel paragraphs absorb adjacent code blocks * 2. Toplevel paragraphs eat absorb adjacent list items, * but sublevel blocks behave properly. * (What this means is that we only need to check for code * blocks at toplevel, and only check for list items at * nested levels.) */ return toplevelblock ? 0 : islist(t,&z,flags,&z); } static Line * textblock(Paragraph *p, int toplevel, DWORD flags) { Line *t, *next; for ( t = p->text; t ; t = next ) { if ( ((next = t->next) == 0) || endoftextblock(next, toplevel, flags) ) { p->align = centered(p->text, t); t->next = 0; return next; } } return t; } /* length of the id: or class: kind in a special div-not-quote block */ static int szmarkerclass(char *p) { if ( strncasecmp(p, "id:", 3) == 0 ) return 3; if ( strncasecmp(p, "class:", 6) == 0 ) return 6; return 0; } /* * check if the first line of a quoted block is the special div-not-quote * marker %[kind:]name% */ #define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') ) static int isdivmarker(Line *p, int start, DWORD flags) { char *s; int last, i; if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) ) return 0; last= S(p->text) - (1 + start); s = T(p->text) + start; if ( (last <= 0) || (*s != '%') || (s[last] != '%') ) return 0; i = szmarkerclass(s+1); if ( !iscsschar(s[i+1]) ) return 0; while ( ++i < last ) if ( !(isdigit(s[i]) || iscsschar(s[i])) ) return 0; return 1; } /* * accumulate a blockquote. * * one sick horrible thing about blockquotes is that even though * it just takes ^> to start a quote, following lines, if quoted, * assume that the prefix is ``> ''. This means that code needs * to be indented *5* spaces from the leading '>', but *4* spaces * from the start of the line. This does not appear to be * documented in the reference implementation, but it's the * way the markdown sample web form at Daring Fireball works. */ static Line * quoteblock(Paragraph *p, DWORD flags) { Line *t, *q; int qp; for ( t = p->text; t ; t = q ) { if ( isquote(t) ) { /* clip leading spaces */ for (qp = 0; T(t->text)[qp] != '>'; qp ++) /* assert: the first nonblank character on this line * will be a > */; /* clip '>' */ qp++; /* clip next space, if any */ if ( T(t->text)[qp] == ' ' ) qp++; CLIP(t->text, 0, qp); UNCHECK(t); t->dle = mkd_firstnonblank(t); } q = skipempty(t->next); if ( (q == 0) || ((q != t->next) && (!isquote(q) || isdivmarker(q,1,flags))) ) { ___mkd_freeLineRange(t, q); t = q; break; } } if ( isdivmarker(p->text,0,flags) ) { char *prefix = "class"; int i; q = p->text; p->text = p->text->next; if ( (i = szmarkerclass(1+T(q->text))) == 3 ) /* and this would be an "%id:" prefix */ prefix="id"; if ( p->ident = emalloc(4+strlen(prefix)+S(q->text)) ) sprintf(p->ident, "%s=\"%.*s\"", prefix, S(q->text)-(i+2), T(q->text)+(i+1) ); ___mkd_freeLine(q); } return t; } typedef int (*linefn)(Line *); /* * pull in a list block. A list block starts with a list marker and * runs until the next list marker, the next non-indented paragraph, * or EOF. You do not have to indent nonblank lines after the list * marker, but multiple paragraphs need to start with a 4-space indent. */ static Line * listitem(Paragraph *p, int indent, DWORD flags, linefn check) { Line *t, *q; int clip = indent; int z; for ( t = p->text; t ; t = q) { CLIP(t->text, 0, clip); UNCHECK(t); t->dle = mkd_firstnonblank(t); if ( (q = skipempty(t->next)) == 0 ) { ___mkd_freeLineRange(t,q); return 0; } /* after a blank line, the next block needs to start with a line * that's indented 4(? -- reference implementation allows a 1 * character indent, but that has unfortunate side effects here) * spaces, but after that the line doesn't need any indentation */ if ( q != t->next ) { if (q->dle < indent) { q = t->next; t->next = 0; return q; } /* indent at least 2, and at most as * as far as the initial line was indented. */ indent = clip ? clip : 2; } if ( (q->dle < indent) && (ishr(q) || islist(q,&z,flags,&z) || (check && (*check)(q))) && !issetext(q,&z) ) { q = t->next; t->next = 0; return q; } clip = (q->dle > indent) ? indent : q->dle; } return t; } static Line * definition_block(Paragraph *top, int clip, MMIOT *f, int kind) { ParagraphRoot d = { 0, 0 }; Paragraph *p; Line *q = top->text, *text = 0, *labels; int z, para; while (( labels = q )) { if ( (q = isdefinition(labels, &z, &kind)) == 0 ) break; if ( (text = skipempty(q->next)) == 0 ) break; if ( para = (text != q->next) ) ___mkd_freeLineRange(q, text); q->next = 0; if ( kind == 1 /* discount dl */ ) for ( q = labels; q; q = q->next ) { CLIP(q->text, 0, 1); UNCHECK(q); S(q->text)--; } dd_block: p = Pp(&d, text, LISTITEM); text = listitem(p, clip, f->flags, (kind==2) ? is_extra_dd : 0); p->down = compile(p->text, 0, f); p->text = labels; labels = 0; if ( para && p->down ) p->down->align = PARA; if ( (q = skipempty(text)) == 0 ) break; if ( para = (q != text) ) { Line anchor; anchor.next = text; ___mkd_freeLineRange(&anchor,q); text = q; } if ( kind == 2 && is_extra_dd(q) ) goto dd_block; } top->text = 0; top->down = T(d); return text; } static Line * enumerated_block(Paragraph *top, int clip, MMIOT *f, int list_class) { ParagraphRoot d = { 0, 0 }; Paragraph *p; Line *q = top->text, *text; int para = 0, z; while (( text = q )) { p = Pp(&d, text, LISTITEM); text = listitem(p, clip, f->flags, 0); p->down = compile(p->text, 0, f); p->text = 0; if ( para && p->down ) p->down->align = PARA; if ( (q = skipempty(text)) == 0 || islist(q, &clip, f->flags, &z) != list_class ) break; if ( para = (q != text) ) { Line anchor; anchor.next = text; ___mkd_freeLineRange(&anchor, q); if ( p->down ) p->down->align = PARA; } } top->text = 0; top->down = T(d); return text; } static int tgood(char c) { switch (c) { case '\'': case '"': return c; case '(': return ')'; } return 0; } /* * add a new (image or link) footnote to the footnote table */ static Line* addfootnote(Line *p, MMIOT* f) { int j, i; int c; Line *np = p->next; Footnote *foot = &EXPAND(*f->footnotes); CREATE(foot->tag); CREATE(foot->link); CREATE(foot->title); foot->flags = foot->height = foot->width = 0; for (j=i=p->dle+1; T(p->text)[j] != ']'; j++) EXPAND(foot->tag) = T(p->text)[j]; EXPAND(foot->tag) = 0; S(foot->tag)--; j = nextnonblank(p, j+2); if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) { while ( j < S(p->text) ) EXPAND(foot->title) = T(p->text)[j++]; goto skip_to_end; } while ( (j < S(p->text)) && !isspace(T(p->text)[j]) ) EXPAND(foot->link) = T(p->text)[j++]; EXPAND(foot->link) = 0; S(foot->link)--; j = nextnonblank(p,j); if ( T(p->text)[j] == '=' ) { sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height); while ( (j < S(p->text)) && !isspace(T(p->text)[j]) ) ++j; j = nextnonblank(p,j); } if ( (j >= S(p->text)) && np && np->dle && tgood(T(np->text)[np->dle]) ) { ___mkd_freeLine(p); p = np; np = p->next; j = p->dle; } if ( (c = tgood(T(p->text)[j])) ) { /* Try to take the rest of the line as a comment; read to * EOL, then shrink the string back to before the final * quote. */ ++j; /* skip leading quote */ while ( j < S(p->text) ) EXPAND(foot->title) = T(p->text)[j++]; while ( S(foot->title) && T(foot->title)[S(foot->title)-1] != c ) --S(foot->title); if ( S(foot->title) ) /* skip trailing quote */ --S(foot->title); EXPAND(foot->title) = 0; --S(foot->title); } skip_to_end: ___mkd_freeLine(p); return np; } /* * allocate a paragraph header, link it to the * tail of the current document */ static Paragraph * Pp(ParagraphRoot *d, Line *ptr, int typ) { Paragraph *ret = ecalloc(sizeof *ret, 1); ret->text = ptr; ret->typ = typ; return ATTACH(*d, ret); } static Line* consume(Line *ptr, int *eaten) { Line *next; int blanks=0; for (; ptr && blankline(ptr); ptr = next, blanks++ ) { next = ptr->next; ___mkd_freeLine(ptr); } if ( ptr ) *eaten = blanks; return ptr; } /* * top-level compilation; break the document into * style, html, and source blocks with footnote links * weeded out. */ static Paragraph * compile_document(Line *ptr, MMIOT *f) { ParagraphRoot d = { 0, 0 }; ANCHOR(Line) source = { 0, 0 }; Paragraph *p = 0; struct kw *tag; int eaten, unclosed; while ( ptr ) { if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) { /* If we encounter a html/style block, compile and save all * of the cached source BEFORE processing the html/style. */ if ( T(source) ) { E(source)->next = 0; p = Pp(&d, 0, SOURCE); p->down = compile(T(source), 1, f); T(source) = E(source) = 0; } p = Pp(&d, ptr, strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML); ptr = htmlblock(p, tag, &unclosed); if ( unclosed ) { p->typ = SOURCE; p->down = compile(p->text, 1, f); p->text = 0; } } else if ( isfootnote(ptr) ) { /* footnotes, like cats, sleep anywhere; pull them * out of the input stream and file them away for * later processing */ ptr = consume(addfootnote(ptr, f), &eaten); } else { /* source; cache it up to wait for eof or the * next html/style block */ ATTACH(source,ptr); ptr = ptr->next; } } if ( T(source) ) { /* if there's any cached source at EOF, compile * it now. */ E(source)->next = 0; p = Pp(&d, 0, SOURCE); p->down = compile(T(source), 1, f); } return T(d); } static int first_nonblank_before(Line *j, int dle) { return (j->dle < dle) ? j->dle : dle; } static int actually_a_table(MMIOT *f, Line *pp) { Line *r; int j; int c; /* tables need to be turned on */ if ( f->flags & (MKD_STRICT|MKD_NOTABLES) ) return 0; /* tables need three lines */ if ( !(pp && pp->next && pp->next->next) ) { return 0; } /* all lines must contain |'s */ for (r = pp; r; r = r->next ) if ( !(r->flags & PIPECHAR) ) { return 0; } /* if the header has a leading |, all lines must have leading |'s */ if ( T(pp->text)[pp->dle] == '|' ) { for ( r = pp; r; r = r->next ) if ( T(r->text)[first_nonblank_before(r,pp->dle)] != '|' ) { return 0; } } /* second line must be only whitespace, -, |, or : */ r = pp->next; for ( j=r->dle; j < S(r->text); ++j ) { c = T(r->text)[j]; if ( !(isspace(c)||(c=='-')||(c==':')||(c=='|')) ) { return 0; } } return 1; } /* * break a collection of markdown input into * blocks of lists, code, html, and text to * be marked up. */ static Paragraph * compile(Line *ptr, int toplevel, MMIOT *f) { ParagraphRoot d = { 0, 0 }; Paragraph *p = 0; Line *r; int para = toplevel; int blocks = 0; int hdr_type, list_type, list_class, indent; ptr = consume(ptr, ¶); while ( ptr ) { if ( iscode(ptr) ) { p = Pp(&d, ptr, CODE); if ( f->flags & MKD_1_COMPAT) { /* HORRIBLE STANDARDS KLUDGE: the first line of every block * has trailing whitespace trimmed off. */ ___mkd_tidy(&p->text->text); } ptr = codeblock(p); } #if WITH_FENCED_CODE else if ( iscodefence(ptr,3) && (p=fencedcodeblock(&d, &ptr)) ) /* yay, it's already done */ ; #endif else if ( ishr(ptr) ) { p = Pp(&d, 0, HR); r = ptr; ptr = ptr->next; ___mkd_freeLine(r); } else if ( list_class = islist(ptr, &indent, f->flags, &list_type) ) { if ( list_class == DL ) { p = Pp(&d, ptr, DL); ptr = definition_block(p, indent, f, list_type); } else { p = Pp(&d, ptr, list_type); ptr = enumerated_block(p, indent, f, list_class); } } else if ( isquote(ptr) ) { p = Pp(&d, ptr, QUOTE); ptr = quoteblock(p, f->flags); p->down = compile(p->text, 1, f); p->text = 0; } else if ( ishdr(ptr, &hdr_type) ) { p = Pp(&d, ptr, HDR); ptr = headerblock(p, hdr_type); } else { p = Pp(&d, ptr, MARKUP); ptr = textblock(p, toplevel, f->flags); /* tables are a special kind of paragraph */ if ( actually_a_table(f, p->text) ) p->typ = TABLE; } if ( (para||toplevel) && !p->align ) p->align = PARA; blocks++; para = toplevel || (blocks > 1); ptr = consume(ptr, ¶); if ( para && !p->align ) p->align = PARA; } return T(d); } /* * the guts of the markdown() function, ripped out so I can do * debugging. */ /* * prepare and compile `text`, returning a Paragraph tree. */ int mkd_compile(Document *doc, DWORD flags) { if ( !doc ) return 0; if ( doc->compiled ) return 1; doc->compiled = 1; memset(doc->ctx, 0, sizeof(MMIOT) ); doc->ctx->ref_prefix= doc->ref_prefix; doc->ctx->cb = &(doc->cb); doc->ctx->flags = flags & USER_FLAGS; CREATE(doc->ctx->in); doc->ctx->footnotes = emalloc(sizeof doc->ctx->footnotes[0]); CREATE(*doc->ctx->footnotes); mkd_initialize(); doc->code = compile_document(T(doc->content), doc->ctx); qsort(T(*doc->ctx->footnotes), S(*doc->ctx->footnotes), sizeof T(*doc->ctx->footnotes)[0], (stfu)__mkd_footsort); memset(&doc->content, 0, sizeof doc->content); return 1; } /* on merge: added by me */ int mkd_is_compiled(Document *doc) { return doc && doc->compiled; } #ifndef _MARKDOWN_D #define _MARKDOWN_D #include "cstring.h" #include "config.h" /* reference-style links (and images) are stored in an array * of footnotes. */ typedef struct footnote { Cstring tag; /* the tag for the reference link */ Cstring link; /* what this footnote points to */ Cstring title; /* what it's called (TITLE= attribute) */ int height, width; /* dimensions (for image link) */ int dealloc; /* deallocation needed? */ int refnumber; int flags; #define EXTRA_BOOKMARK 0x01 #define REFERENCED 0x02 } Footnote; /* each input line is read into a Line, which contains the line, * the offset of the first non-space character [this assumes * that all tabs will be expanded to spaces!], and a pointer to * the next line. */ typedef struct line { Cstring text; struct line *next; int dle; /* leading indent on the line */ int flags; /* special attributes for this line */ #define PIPECHAR 0x01 /* line contains a | */ #define CHECKED 0x02 enum { chk_text, chk_code, chk_hr, chk_dash, chk_tilde, chk_equal } kind; int count; } Line; /* a paragraph is a collection of Lines, with links to the next paragraph * and (if it's a QUOTE, UL, or OL) to the reparsed contents of this * paragraph. */ typedef struct paragraph { struct paragraph *next; /* next paragraph */ struct paragraph *down; /* recompiled contents of this paragraph */ struct line *text; /* all the text in this paragraph */ char *ident; /* %id% tag for QUOTE */ enum { WHITESPACE=0, CODE, QUOTE, MARKUP, HTML, STYLE, DL, UL, OL, AL, LISTITEM, HDR, HR, TABLE, SOURCE } typ; enum { IMPLICIT=0, PARA, CENTER} align; int hnumber; /* for typ == HDR */ } Paragraph; enum { ETX, SETEXT }; /* header types */ typedef struct block { enum { bTEXT, bSTAR, bUNDER } b_type; int b_count; char b_char; Cstring b_text; Cstring b_post; } block; typedef STRING(block) Qblock; typedef char* (*mkd_callback_t)(const char*, const int, void*); typedef void (*mkd_free_t)(char*, void*); typedef struct callback_data { void *e_data; /* private data for callbacks */ mkd_callback_t e_url; /* url edit callback */ mkd_callback_t e_flags; /* extra href flags callback */ mkd_free_t e_free; /* edit/flags callback memory deallocator */ } Callback_data; struct escaped { char *text; struct escaped *up; } ; /* a magic markdown io thing holds all the data structures needed to * do the backend processing of a markdown document */ typedef struct mmiot { Cstring out; Cstring in; Qblock Q; int isp; int reference; struct escaped *esc; char *ref_prefix; STRING(Footnote) *footnotes; DWORD flags; #define MKD_NOLINKS 0x00000001 #define MKD_NOIMAGE 0x00000002 #define MKD_NOPANTS 0x00000004 #define MKD_NOHTML 0x00000008 #define MKD_STRICT 0x00000010 #define MKD_TAGTEXT 0x00000020 #define MKD_NO_EXT 0x00000040 #define MKD_CDATA 0x00000080 #define MKD_NOSUPERSCRIPT 0x00000100 #define MKD_NORELAXED 0x00000200 #define MKD_NOTABLES 0x00000400 #define MKD_NOSTRIKETHROUGH 0x00000800 #define MKD_TOC 0x00001000 #define MKD_1_COMPAT 0x00002000 #define MKD_AUTOLINK 0x00004000 #define MKD_SAFELINK 0x00008000 #define MKD_NOHEADER 0x00010000 #define MKD_TABSTOP 0x00020000 #define MKD_NODIVQUOTE 0x00040000 #define MKD_NOALPHALIST 0x00080000 #define MKD_NODLIST 0x00100000 #define MKD_EXTRA_FOOTNOTE 0x00200000 #define IS_LABEL 0x08000000 #define USER_FLAGS 0x0FFFFFFF #define INPUT_MASK (MKD_NOHEADER|MKD_TABSTOP) Callback_data *cb; } MMIOT; /* * the mkdio text input functions return a document structure, * which contains a header (retrieved from the document if * markdown was configured * with the * --enable-pandoc-header * and the document begins with a pandoc-style header) and the * root of the linked list of Lines. */ typedef struct document { int magic; /* "I AM VALID" magic number */ #define VALID_DOCUMENT 0x19600731 Line *title; Line *author; Line *date; ANCHOR(Line) content; /* uncompiled text, not valid after compile() */ Paragraph *code; /* intermediate code generated by compile() */ int compiled; /* set after mkd_compile() */ int html; /* set after (internal) htmlify() */ int tabstop; /* for properly expanding tabs (ick) */ char *ref_prefix; MMIOT *ctx; /* backend buffers, flags, and structures */ Callback_data cb; /* callback functions & private data */ } Document; extern int mkd_firstnonblank(Line *); extern int mkd_compile(Document *, DWORD); extern int mkd_document(Document *, char **); extern int mkd_generatehtml(Document *, FILE *); extern int mkd_css(Document *, char **); extern int mkd_generatecss(Document *, FILE *); #define mkd_style mkd_generatecss extern int mkd_xml(char *, int , char **); extern int mkd_generatexml(char *, int, FILE *); extern void mkd_cleanup(Document *); extern int mkd_line(char *, int, char **, DWORD); extern int mkd_generateline(char *, int, FILE*, DWORD); #define mkd_text mkd_generateline extern void mkd_basename(Document*, char *); typedef int (*mkd_sta_function_t)(const int,const void*); extern void mkd_string_to_anchor(char*,int, mkd_sta_function_t, void*, int); extern Document *mkd_in(FILE *, DWORD); extern Document *mkd_string(const char*,int, DWORD); extern void mkd_initialize(); extern void mkd_shlib_destructor(); extern void mkd_ref_prefix(Document*, char*); /* internal resource handling functions. */ extern void ___mkd_freeLine(Line *); extern void ___mkd_freeLines(Line *); extern void ___mkd_freeParagraph(Paragraph *); extern void ___mkd_freefootnote(Footnote *); extern void ___mkd_freefootnotes(MMIOT *); extern void ___mkd_initmmiot(MMIOT *, void *); extern void ___mkd_freemmiot(MMIOT *, void *); extern void ___mkd_freeLineRange(Line *, Line *); extern void ___mkd_xml(char *, int, FILE *); extern void ___mkd_reparse(char *, int, int, MMIOT*, char*); extern void ___mkd_emblock(MMIOT*); extern void ___mkd_tidy(Cstring *); #endif/*_MARKDOWN_D*/ /* on merge: this file is not important *//* * mkdio -- markdown front end input functions * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" typedef ANCHOR(Line) LineAnchor; /* create a new blank Document */ static Document* new_Document() { Document *ret = ecalloc(sizeof(Document), 1); if ( ret ) { if ( ret->ctx = ecalloc(sizeof(MMIOT), 1) ) { ret->magic = VALID_DOCUMENT; return ret; } efree(ret); } return 0; } /* add a line to the markdown input chain, expanding tabs and * noting the presence of special characters as we go. */ static void queue(Document* a, Cstring *line) { Line *p = ecalloc(sizeof *p, 1); unsigned char c; int xp = 0; int size = S(*line); unsigned char *str = (unsigned char*)T(*line); CREATE(p->text); ATTACH(a->content, p); while ( size-- ) { if ( (c = *str++) == '\t' ) { /* expand tabs into ->tabstop spaces. We use ->tabstop * because the ENTIRE FREAKING COMPUTER WORLD uses editors * that don't do ^T/^D, but instead use tabs for indentation, * and, of course, set their tabs down to 4 spaces */ do { EXPAND(p->text) = ' '; } while ( ++xp % a->tabstop ); } else if ( c >= ' ' ) { if ( c == '|' ) p->flags |= PIPECHAR; EXPAND(p->text) = c; ++xp; } } EXPAND(p->text) = 0; S(p->text)--; p->dle = mkd_firstnonblank(p); } /* trim leading blanks from a header line */ static void header_dle(Line *p) { CLIP(p->text, 0, 1); p->dle = mkd_firstnonblank(p); } /* build a Document from any old input. */ typedef int (*getc_func)(void*); Document * populate(getc_func getc, void* ctx, int flags) { Cstring line; Document *a = new_Document(); int c; int pandoc = 0; if ( !a ) return 0; a->tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP; CREATE(line); while ( (c = (*getc)(ctx)) != EOF ) { if ( c == '\n' ) { if ( pandoc != EOF && pandoc < 3 ) { if ( S(line) && (T(line)[0] == '%') ) pandoc++; else pandoc = EOF; } queue(a, &line); S(line) = 0; } else if ( isprint(c) || isspace(c) || (c & 0x80) ) EXPAND(line) = c; } if ( S(line) ) queue(a, &line); DELETE(line); if ( (pandoc == 3) && !(flags & (MKD_NOHEADER|MKD_STRICT)) ) { /* the first three lines started with %, so we have a header. * clip the first three lines out of content and hang them * off header. */ Line *headers = T(a->content); a->title = headers; header_dle(a->title); a->author= headers->next; header_dle(a->author); a->date = headers->next->next; header_dle(a->date); T(a->content) = headers->next->next->next; } return a; } /* convert a file into a linked list */ Document * mkd_in(FILE *f, DWORD flags) { return populate((getc_func)fgetc, f, flags & INPUT_MASK); } /* return a single character out of a buffer */ struct string_ctx { const char *data; /* the unread data */ int size; /* and how much is there? */ } ; static int strget(struct string_ctx *in) { if ( !in->size ) return EOF; --(in->size); return *(in->data)++; } /* convert a block of text into a linked list */ Document * mkd_string(const char *buf, int len, DWORD flags) { struct string_ctx about; about.data = buf; about.size = len; return populate((getc_func)strget, &about, flags & INPUT_MASK); } /* write the html to a file (xmlified if necessary) */ int mkd_generatehtml(Document *p, FILE *output) { char *doc; int szdoc; /* on merge: changed meaning of return value */ if ( (szdoc = mkd_document(p, &doc)) != EOF ) { int ret = 0; if ( p->ctx->flags & MKD_CDATA ) ret |= mkd_generatexml(doc, szdoc, output); else ret = !fwrite(doc, szdoc, 1, output); ret |= putc('\n', output); /* on merge: added call to efree */ efree(doc); return ret; } return EOF; } /* convert some markdown text to html */ int markdown(Document *document, FILE *out, int flags) { if ( mkd_compile(document, flags) ) { mkd_generatehtml(document, out); mkd_cleanup(document); return 0; } return -1; } /* write out a Cstring, mangled into a form suitable for `0) && !isalpha(line[0]) ) (*outchar)('L',out); for ( i=0; i < size ; i++ ) { c = line[i]; if ( labelformat ) { if ( isalnum(c) || (c == '_') || (c == ':') || (c == '-') || (c == '.' ) ) (*outchar)(c, out); else (*outchar)('.', out); } else (*outchar)(c,out); } if (line) efree(line); } /* ___mkd_reparse() a line */ static void mkd_parse_line(char *bfr, int size, MMIOT *f, int flags) { ___mkd_initmmiot(f, 0); f->flags = flags & USER_FLAGS; ___mkd_reparse(bfr, size, 0, f, 0); ___mkd_emblock(f); } /* ___mkd_reparse() a line, returning it in malloc()ed memory */ int mkd_line(char *bfr, int size, char **res, DWORD flags) { MMIOT f; int len; mkd_parse_line(bfr, size, &f, flags); if ( len = S(f.out) ) { /* kludge alert; we know that T(f.out) is malloced memory, * so we can just steal it away. This is awful -- there * should be an opaque method that transparently moves * the pointer out of the embedded Cstring. */ EXPAND(f.out) = 0; *res = T(f.out); T(f.out) = 0; S(f.out) = ALLOCATED(f.out) = 0; } else { *res = 0; len = EOF; } ___mkd_freemmiot(&f, 0); return len; } /* ___mkd_reparse() a line, writing it to a FILE */ int mkd_generateline(char *bfr, int size, FILE *output, DWORD flags) { MMIOT f; int ret; /* on merge: changed so the return value has some meaning */ mkd_parse_line(bfr, size, &f, flags); if ( flags & MKD_CDATA ) { ret = mkd_generatexml(T(f.out), S(f.out), output); } else { if (S(f.out) == 0) ret = 0; else ret = fwrite(T(f.out), S(f.out), 1, output) == 1 ? 0 : EOF; } ___mkd_freemmiot(&f, 0); return ret; } /* set the url display callback */ void mkd_e_url(Document *f, mkd_callback_t edit) { if ( f ) f->cb.e_url = edit; } /* set the url options callback */ void mkd_e_flags(Document *f, mkd_callback_t edit) { if ( f ) f->cb.e_flags = edit; } /* set the url display/options deallocator */ void mkd_e_free(Document *f, mkd_free_t dealloc) { if ( f ) f->cb.e_free = dealloc; } /* set the url display/options context data field */ void mkd_e_data(Document *f, void *data) { if ( f ) f->cb.e_data = data; } /* set the href prefix for markdown extra style footnotes */ void mkd_ref_prefix(Document *f, char *data) { if ( f ) f->ref_prefix = data; } #ifndef _MKDIO_D #define _MKDIO_D /* on merge: from mkdio.h.in */ #include #include "config.h" typedef void MMIOT; typedef DWORD mkd_flag_t; /* line builder for markdown() */ MMIOT *mkd_in(FILE*,mkd_flag_t); /* assemble input from a file */ MMIOT *mkd_string(const char*,int,mkd_flag_t); /* assemble input from a buffer */ /*void mkd_basename(MMIOT*,char*);*/ void mkd_tags_on_startup(INIT_FUNC_ARGS); void mkd_tags_on_shutdown(SHUTDOWN_FUNC_ARGS); void mkd_initialize(); void mkd_with_html5_tags(); void mkd_shlib_destructor(); /* compilation, debugging, cleanup */ int mkd_compile(MMIOT*, mkd_flag_t); int mkd_cleanup(MMIOT*); /* on merge: added: */ int mkd_is_compiled(MMIOT*); /* markup functions */ int mkd_dump(MMIOT*, FILE*, char*); int markdown(MMIOT*, FILE*, mkd_flag_t); /* XXX: generatehtml + cleanup */ int mkd_line(char *, int, char **, mkd_flag_t); typedef int (*mkd_sta_function_t)(const int,const void*); void mkd_string_to_anchor(char *, int, mkd_sta_function_t, void*, int); int mkd_xhtmlpage(MMIOT*,FILE*); /* on merge: removed 2nd param */ /* header block access */ char* mkd_doc_title(MMIOT*); /* return private data */ char* mkd_doc_author(MMIOT*); /* return private data */ char* mkd_doc_date(MMIOT*); /* return private data */ /* compiled data access */ int mkd_document(MMIOT*, char**); /* compile doc, return private data */ int mkd_toc(MMIOT*, char**); /* non-private data */ int mkd_css(MMIOT*, char **); /* non-private data */ int mkd_xml(char *, int, char **); /* XXX; encodes data and writes to string */ /* write-to-file functions */ int mkd_generatehtml(MMIOT*,FILE*); /* mkd_document, mayve encodes data and writes to file */ int mkd_generatetoc(MMIOT*,FILE*); /* mkd_toc to file */ int mkd_generatexml(char *, int,FILE*); /* XXX; encodes data and writes to file */ int mkd_generatecss(MMIOT*,FILE*); /* mkd_css to file */ #define mkd_style mkd_generatecss int mkd_generateline(char *, int, FILE*, mkd_flag_t); /* mkd_line, but maybe encoding and writes file */ #define mkd_text mkd_generateline /* url generator callbacks */ typedef char * (*mkd_callback_t)(const char*, const int, void*); typedef void (*mkd_free_t)(char*, void*); void mkd_e_url(void *, mkd_callback_t); void mkd_e_flags(void *, mkd_callback_t); void mkd_e_free(void *, mkd_free_t ); void mkd_e_data(void *, void *); /* version#. */ extern const char markdown_version[]; void mkd_mmiot_flags(FILE *, MMIOT *, int); void mkd_flags_are(FILE*, mkd_flag_t, int); void mkd_ref_prefix(MMIOT*, char*); /* special flags for markdown() and mkd_text() */ #define MKD_NOLINKS 0x00000001 /* don't do link processing, block tags */ #define MKD_NOIMAGE 0x00000002 /* don't do image processing, block */ #define MKD_NOPANTS 0x00000004 /* don't run smartypants() */ #define MKD_NOHTML 0x00000008 /* don't allow raw html through AT ALL */ #define MKD_STRICT 0x00000010 /* disable SUPERSCRIPT, RELAXED_EMPHASIS */ #define MKD_TAGTEXT 0x00000020 /* process text inside an html tag; no * , no , no html or [] expansion */ #define MKD_NO_EXT 0x00000040 /* don't allow pseudo-protocols */ #define MKD_NOEXT MKD_NO_EXT /* ^^^ (aliased for user convenience) */ #define MKD_CDATA 0x00000080 /* generate code for xml ![CDATA[...]] */ #define MKD_NOSUPERSCRIPT 0x00000100 /* no A^B */ #define MKD_NORELAXED 0x00000200 /* emphasis happens /everywhere/ */ #define MKD_NOTABLES 0x00000400 /* disallow tables */ #define MKD_NOSTRIKETHROUGH 0x00000800 /* forbid ~~strikethrough~~ */ #define MKD_TOC 0x00001000 /* do table-of-contents processing */ #define MKD_1_COMPAT 0x00002000 /* compatibility with MarkdownTest_1.0 */ #define MKD_AUTOLINK 0x00004000 /* make http://foo.com link even without <>s */ #define MKD_SAFELINK 0x00008000 /* paranoid check for link protocol */ #define MKD_NOHEADER 0x00010000 /* don't process header blocks */ #define MKD_TABSTOP 0x00020000 /* expand tabs to 4 spaces */ #define MKD_NODIVQUOTE 0x00040000 /* forbid >%class% blocks */ #define MKD_NOALPHALIST 0x00080000 /* forbid alphabetic lists */ #define MKD_NODLIST 0x00100000 /* forbid definition lists */ #define MKD_EXTRA_FOOTNOTE 0x00200000 /* enable markdown extra-style footnotes */ #define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT /* special flags for mkd_in() and mkd_string() */ #endif/*_MKDIO_D*/ /* On merge: This file is to be run to generate blocktags_generated.c every time it changes */ /* block-level tags for passing html blocks through the blender */ #include /* on merge: added these three lines */ #include "config.h" #define emalloc malloc #define erealloc realloc #define __WITHOUT_AMALLOC 1 #include "cstring.h" #include "tags.h" STRING(struct kw) blocktags; /* define a html block tag */ static void define_one_tag(char *id, int selfclose) { struct kw *p = &EXPAND(blocktags); p->id = id; p->size = strlen(id); p->selfclose = selfclose; } /* case insensitive string sort (for qsort() and bsearch() of block tags) */ static int casort(struct kw *a, struct kw *b) { if ( a->size != b->size ) return a->size - b->size; return strncasecmp(a->id, b->id, b->size); } /* stupid cast to make gcc shut up about the function types being * passed into qsort() and bsearch() */ typedef int (*stfu)(const void*,const void*); /* load in the standard collection of html tags that markdown supports */ main() { int i; #define KW(x) define_one_tag(x, 0) #define SC(x) define_one_tag(x, 1) KW("STYLE"); KW("SCRIPT"); KW("ADDRESS"); KW("BDO"); KW("BLOCKQUOTE"); KW("CENTER"); KW("DFN"); KW("DIV"); KW("OBJECT"); KW("H1"); KW("H2"); KW("H3"); KW("H4"); KW("H5"); KW("H6"); KW("LISTING"); KW("NOBR"); KW("UL"); KW("P"); KW("OL"); KW("DL"); KW("PLAINTEXT"); KW("PRE"); KW("TABLE"); KW("WBR"); KW("XMP"); SC("HR"); KW("IFRAME"); KW("MAP"); qsort(T(blocktags), S(blocktags), sizeof(struct kw), (stfu)casort); printf("static struct kw blocktags[] = {\n"); for (i=0; i < S(blocktags); i++) printf(" { \"%s\", %d, %d },\n", T(blocktags)[i].id, T(blocktags)[i].size, T(blocktags)[i].selfclose ); printf("};\n\n"); printf("#define NR_blocktags %d\n", S(blocktags)); exit(0); } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" /* free a (single) line */ void ___mkd_freeLine(Line *ptr) { DELETE(ptr->text); efree(ptr); } /* free a list of lines */ void ___mkd_freeLines(Line *p) { if (p->next) ___mkd_freeLines(p->next); ___mkd_freeLine(p); } /* bye bye paragraph. */ void ___mkd_freeParagraph(Paragraph *p) { if (p->next) ___mkd_freeParagraph(p->next); if (p->down) ___mkd_freeParagraph(p->down); if (p->text) ___mkd_freeLines(p->text); if (p->ident) efree(p->ident); efree(p); } /* bye bye footnote. */ void ___mkd_freefootnote(Footnote *f) { DELETE(f->tag); DELETE(f->link); DELETE(f->title); } /* bye bye footnotes. */ void ___mkd_freefootnotes(MMIOT *f) { int i; if ( f->footnotes ) { for (i=0; i < S(*f->footnotes); i++) ___mkd_freefootnote( &T(*f->footnotes)[i] ); DELETE(*f->footnotes); efree(f->footnotes); } } /* initialize a new MMIOT */ void ___mkd_initmmiot(MMIOT *f, void *footnotes) { if ( f ) { memset(f, 0, sizeof *f); CREATE(f->in); CREATE(f->out); CREATE(f->Q); if ( footnotes ) f->footnotes = footnotes; else { f->footnotes = emalloc(sizeof f->footnotes[0]); CREATE(*f->footnotes); } } } /* free the contents of a MMIOT, but leave the object alone. */ void ___mkd_freemmiot(MMIOT *f, void *footnotes) { if ( f ) { DELETE(f->in); DELETE(f->out); DELETE(f->Q); if ( f->footnotes != footnotes ) ___mkd_freefootnotes(f); memset(f, 0, sizeof *f); } } /* free lines up to an barrier. */ void ___mkd_freeLineRange(Line *anchor, Line *stop) { Line *r = anchor->next; if ( r != stop ) { while ( r && (r->next != stop) ) r = r->next; if ( r ) r->next = 0; ___mkd_freeLines(anchor->next); } anchor->next = 0; } /* clean up everything allocated in __mkd_compile() */ void mkd_cleanup(Document *doc) { if ( doc && (doc->magic == VALID_DOCUMENT) ) { if ( doc->ctx ) { ___mkd_freemmiot(doc->ctx, 0); efree(doc->ctx); } if ( doc->code) ___mkd_freeParagraph(doc->code); if ( doc->title) ___mkd_freeLine(doc->title); if ( doc->author) ___mkd_freeLine(doc->author); if ( doc->date) ___mkd_freeLine(doc->date); if ( T(doc->content) ) ___mkd_freeLines(T(doc->content)); memset(doc, 0, sizeof doc[0]); efree(doc); } } /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" #include "tags.h" /* on merge: moved down */ #include "config.h" /* on merge: commented out; not needed */ /*static int need_to_setup = 1; static int need_to_initrng = 1;*/ /* on merge: added mutex */ #ifdef ZTS static MUTEX_T tags_mutex; #endif /* on merge: added function */ void mkd_tags_on_startup(INIT_FUNC_ARGS) { #ifdef ZTS tags_mutex = tsrm_mutex_alloc(); #endif } /* on merge: added function */ void mkd_tags_on_shutdown(SHUTDOWN_FUNC_ARGS) { mkd_shlib_destructor(); #ifdef ZTS tsrm_mutex_free(tags_mutex); #endif } void mkd_initialize() { /* on merge: nothing needed */ /*#ifdef ZTS tsrm_mutex_lock(tags_mutex); #endif*/ /* if ( need_to_initrng ) { need_to_initrng = 0; INITRNG(time(0)); }*/ /*#ifdef ZTS tsrm_mutex_unlock(tags_mutex); #endif*/ } void mkd_shlib_destructor() { /* on merge: added critical section */ #ifdef ZTS tsrm_mutex_lock(tags_mutex); #endif mkd_deallocate_tags(); #ifdef ZTS tsrm_mutex_unlock(tags_mutex); #endif } /* block-level tags for passing html blocks through the blender */ /* on merge: commented out define */ /* #define __WITHOUT_AMALLOC 1 */ #include "cstring.h" #include "tags.h" #include "config.h" /* on merge: added */ /* on merge: added static */ static STRING(struct kw) extratags; /* the standard collection of tags are built and sorted when * discount is configured, so all we need to do is pull them * in and use them. * * Additional tags still need to be allocated, sorted, and deallocated. */ #include "blocktags_generated.c" /* on merge: changed file name */ /* define an additional html block tag */ /* on merge: commented out because html5.c is commented out */ /* static void mkd_define_tag(char *id, int selfclose) { struct kw *p; *//* only add the new tag if it doesn't exist in * either the standard or extra tag tables. *//* if ( !(p = mkd_search_tags(id, strlen(id))) ) { p = &EXPAND_PERMANENT(extratags); *//* in merge permanent *//* p->id = id; p->size = strlen(id); p->selfclose = selfclose; } } */ /* case insensitive string sort (for qsort() and bsearch() of block tags) */ static int casort(struct kw *a, struct kw *b) { if ( a->size != b->size ) return a->size - b->size; return strncasecmp(a->id, b->id, b->size); } /* stupid cast to make gcc shut up about the function types being * passed into qsort() and bsearch() */ typedef int (*stfu)(const void*,const void*); /* sort the list of extra html block tags for later searching */ /* on merge: commented out because html5.c is commented out */ /* static void mkd_sort_tags() { qsort(T(extratags), S(extratags), sizeof(struct kw), (stfu)casort); } */ /* look for a token in the html block tag list */ struct kw* mkd_search_tags(char *pat, int len) { struct kw key; struct kw *ret; key.id = pat; key.size = len; if ( (ret=bsearch(&key,blocktags,NR_blocktags,sizeof key,(stfu)casort)) ) return ret; if ( S(extratags) ) return bsearch(&key,T(extratags),S(extratags),sizeof key,(stfu)casort); return 0; } /* destroy the extratags list (for shared libraries) */ void mkd_deallocate_tags() { if ( S(extratags) > 0 ) DELETE_PERMANENT(extratags); /* on merge: permanent */ } /* mkd_deallocate_tags */ /* block-level tags for passing html blocks through the blender */ #ifndef _TAGS_D #define _TAGS_D struct kw { char *id; int size; int selfclose; } ; struct kw* mkd_search_tags(char *, int); void mkd_deallocate_tags(); /* on merge: these were deleted */ /*void mkd_sort_tags();*/ /*void mkd_define_tag(char *, int);*/ #endif /* on merge: this file is not important *//* * toc -- spit out a table of contents based on header blocks * * Copyright (C) 2008 Jjgod Jiang, David L Parsons * portions Copyright (C) 2011 Stefano D'Angelo * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* write an header index */ int mkd_toc(Document *p, char **doc) { Paragraph *tp, *srcp; int last_hnumber = 0; Cstring res; int size; int first = 1; if ( !(doc && p && p->ctx) ) return -1; *doc = 0; if ( ! (p->ctx->flags & MKD_TOC) ) return 0; CREATE(res); RESERVE(res, 100); for ( tp = p->code; tp ; tp = tp->next ) { if ( tp->typ == SOURCE ) { for ( srcp = tp->down; srcp; srcp = srcp->next ) { if ( srcp->typ == HDR && srcp->text ) { while ( last_hnumber > srcp->hnumber ) { if ( (last_hnumber - srcp->hnumber) > 1 ) Csprintf(&res, "\n"); Csprintf(&res, "\n%*s\n%*s", last_hnumber-1, "", last_hnumber-1, ""); --last_hnumber; } if ( last_hnumber == srcp->hnumber ) Csprintf(&res, "\n"); else if ( (srcp->hnumber > last_hnumber) && !first ) Csprintf(&res, "\n"); while ( srcp->hnumber > last_hnumber ) { Csprintf(&res, "%*s\n%*s", last_hnumber, "", last_hnumber, ""); } /* on merge: changed comparison operator to == from < */ if ( (size = S(res)) == 0 ) EXPAND(res) = 0; /* HACK ALERT! HACK ALERT! HACK ALERT! */ *doc = T(res); /* we know that a T(Cstring) is a character pointer * so we can simply pick it up and carry it away, * leaving the husk of the Ctring on the stack * END HACK ALERT */ return size; } /* write an header index */ int mkd_generatetoc(Document *p, FILE *out) { char *buf = 0; int sz = mkd_toc(p, &buf); int ret = EOF; /* on merge: changed so it returns 1 on normal/no data, 0 on no MKD_TOC and EOF only if there's an error in fwrite */ if ( sz > 0 ) ret = fwrite(buf, 1, sz, out) == (size_t)sz ? 1 : EOF; /* on merge: added to allow distinguishing empty toc from no MKD_TOC */ if (sz == 0) { ret = (buf != 0); } if ( buf ) efree(buf); return ret; } #include "config.h" /* on merge: check against version.c.in */ /* on merge: obtain version from VERSION */ const char markdown_version[] = "2.1.3.1-dev" #if TABSTOP != 4 " TAB=" #TABSTOP #endif /* on merge: commented out: */ /*#if USE_AMALLOC " DEBUG" #endif*/ #if USE_DISCOUNT_DL # if USE_EXTRA_DL " DL=BOTH" # else " DL=DISCOUNT" # endif #elif USE_EXTRA_DL " DL=EXTRA" #else " DL=NONE" #endif #if WITH_ID_ANCHOR " ID-ANCHOR" #endif #if WITH_GITHUB_TAGS " GITHUB-TAGS" #endif #if WITH_FENCED_CODE " FENCED-CODE" #endif ; /* markdown: a C implementation of John Gruber's Markdown markup language. * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" /* return the xml version of a character */ static char * mkd_xmlchar(unsigned char c) { switch (c) { case '<': return "<"; case '>': return ">"; case '&': return "&"; case '"': return """; case '\'': return "'"; default: if ( isascii(c) || (c & 0x80) ) return 0; return ""; } } /* write output in XML format */ int mkd_generatexml(char *p, int size, FILE *out) { unsigned char c; char *entity; int ret = 0; /* on merge: changed so the return value has some meaning */ while ( size-- > 0 ) { c = *p++; if ( entity = mkd_xmlchar(c) ) ret |= fputs(entity, out); else ret |= fputc(c, out); } return ret; } /* build a xml'ed version of a string */ int mkd_xml(char *p, int size, char **res) { unsigned char c; char *entity; Cstring f; CREATE(f); RESERVE(f, 100); while ( size-- > 0 ) { c = *p++; if ( entity = mkd_xmlchar(c) ) Cswrite(&f, entity, strlen(entity)); else Csputc(c, &f); } /* HACK ALERT! HACK ALERT! HACK ALERT! */ *res = T(f); /* we know that a T(Cstring) is a character pointer */ /* so we can simply pick it up and carry it away, */ return S(f); /* leaving the husk of the Ctring on the stack */ /* END HACK ALERT */ } /* * xmlpage -- write a skeletal xhtml page * * Copyright (C) 2007 David L Parsons. * The redistribution terms are provided in the COPYRIGHT file that must * be distributed with this source code. */ #include #include #include #include "cstring.h" #include "markdown.h" #include "amalloc.h" /* on merge: moved config.h last to avoid conflict with windows headers */ #include "config.h" /* on merge: removed flags argument and compilation, since it made * the behavior of this function depend on state (whether or not * the document had been compiled before, if it had been, the flags * would be ignored) */ int mkd_xhtmlpage(Document *p, FILE *out) { char *title; extern char *mkd_doc_title(Document *); /* on merge: added error handling */ if ( p->compiled ) { int ret = 0; #define MKD_ERRH(s) ret |= (s) < 0 ? -1 : 0 MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( fprintf(out, "\n") ); if ( title = mkd_doc_title(p) ) MKD_ERRH( fprintf(out, "%s\n", title)) ; MKD_ERRH( mkd_generatecss(p, out) ); MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( mkd_generatehtml(p, out) ); MKD_ERRH( fprintf(out, "\n") ); MKD_ERRH( fprintf(out, "\n") ); #undef MKD_ERRH /* on merge: commented out */ /*mkd_cleanup(p);*/ return ret; } return -1; } --TEST-- MarkdownDocument::compile basic test --SKIPIF-- compile()); $md->dumpTree('php://stdout'); echo "\nDone.\n"; --EXPECTF-- bool(true) -----[source]--+--[header, 1 line] `--[markup, 2 lines] Done. --TEST-- MarkdownDocument::compile: error in arguments --SKIPIF-- compile("hhh")); var_dump($md->compile(0,0)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::compile() expects parameter 1 to be long, string given in %s on line 4 bool(false) Warning: MarkdownDocument::compile() expects at most 1 parameter, 2 given in %s on line 5 bool(false) Done. --TEST-- MarkdownDocument::compile: method called twice --SKIPIF-- compile(); $md->compile(); echo "\nDone.\n"; --EXPECTF-- Fatal error: Uncaught exception 'LogicException' with message 'Invalid state: the markdown document has already been compiled' in %s:%d Stack trace: #0 %s(%d): MarkdownDocument->compile() #1 {main} thrown in %s on line %d --TEST-- MarkdownDocument::compile: test NOLINKS flag --COMMENT-- There's a bug of the library here. With the NOLINKS, the closing in the original is not escaped. --SKIPIF-- link text 3. New Par . [1]: http://www.example2.com/ EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOLINKS); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

Test link text link text 2.

New par; link text 3.

New Par http://www.example3.com/.

=====================

Test [link text](http://www.example.com/) [link text 2][1].

New par; <a href=“http://www.example3.com/”>link text 3.

New Par <http://www.example3.com/>.

Done. --TEST-- MarkdownDocument::compile: test NOIMAGE flag --COMMENT-- Same bug that in variation1 --SKIPIF-- link text 3. New par; link text 3. [1]: http://www.example2.com/img EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOIMAGE); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

Test alt text image 2.

New par; link text 3.

New par; link text 3.

=====================

Test ![alt text](http://www.example.com/img “title”) ![image 2]1.

New par; <img src=“http://www.example3.com/img” />link text 3.

New par; <img src=“http://www.example4.com/img”>link text 3.

Done. --TEST-- MarkdownDocument::compile: test NOPANTS flag --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOPANTS); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

“text” is translated to text. “double-quoted text” becomes double-quoted text ‘single-quoted text’ becomes single-quoted text don’t is dont. as well as anything-elset. (But foo'tbar is just foo'tbar.) And it’s is its, as well as anything-elses (except not foo'sbar and the like.) ™ becomes ® becomes © becomes ¼th ? th. Ditto for ¼ (), ½ (), ¾ths (ths), and ¾ (). … becomes … also becomes — becomes – becomes

=====================

``text'' is translated to text. "double-quoted text" becomes double-quoted text 'single-quoted text' becomes single-quoted text don't is dont. as well as anything-elset. (But foo'tbar is just foo'tbar.) And it's is its, as well as anything-elses (except not foo'sbar and the like.) (tm) becomes (r) becomes (c) becomes 1/4th ? th. Ditto for 1/4 (), 1/2 (), 3/4ths (ths), and 3/4 (). ... becomes . . . also becomes --- becomes -- becomes

Done. --TEST-- MarkdownDocument::compile: test NOHTML flag --SKIPIF--
  • -d is, as previously mentioned, the flag that makes markdown produce a parse tree instead of a html document.
  • -F <flags> sets various flags that change how markdown works. The flags argument is a somewhat less than obvious bitmask — for example, -F 0x4 tells markdown to not do the smartypants translations on the output. (there are cases — like running the test suite — where this is a useful feature.)
  • -o file tells markdown to write the output to file
  • -V tells you a markdown version number and how the package was configured. For example

  • $ markdown -V
    markdown: discount 1.0.0 DL_TAG HEADER TAB=8
    
    EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOHTML); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    • -d is, as previously mentioned, the flag that makes markdown produce a parse tree instead of a html document.
    • -F <flags> sets various flags that change how markdown works. The flags argument is a somewhat less than obvious bitmask — for example, -F 0x4 tells markdown to not do the smartypants translations on the output. (there are cases — like running the test suite — where this is a useful feature.)
    • -o file tells markdown to write the output to file
    • -V tells you a markdown version number and how the package was configured. For example

    $ markdown -V
    markdown: discount 1.0.0 DL_TAG HEADER TAB=8
    
    =====================

    <ul> <li><code>-d</code> is, as previously mentioned, the flag that makes markdown produce a parse tree instead of a html document.</li> <li><code>-F <flags></code> sets various <a href=“#flags”>flags</a> that change how markdown works. The flags argument is a somewhat less than obvious bitmask — for example, <code>-F 0x4</code> tells <code>markdown</code> to <strong>not</strong> do the <a href=“http://daringfireball.net/projects/smartypants/”>smartypants</a> translations on the output. (there are cases — like running the <a href=“http://six.pairlist.net/pipermail/markdown-discuss/2006-June/000079.html”>test suite</a> — where this is a useful feature.)</li> <li><code>-o file</code> tells markdown to write the output to <em><code>file</code></em></li> <li><p><code>-V</code> tells you a markdown version number and how the package was configured. For example</p></li> </ul>

    <pre><code>$ markdown -V markdown: discount 1.0.0 DL_TAG HEADER TAB=8 </code></pre>

    Done. --TEST-- MarkdownDocument::compile: test STRICT flag --COMMENT-- Combination of NOSUPERSCRIPT, NORELAXED, NOSTRIKETHROUGH, NODLIST, NOALPHALIST, NODIVQUOTE and MKD_NOTABLES --SKIPIF-- %class% aaa | bbbb -----|------ hello|sailor EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::STRICT); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    Superscript: AB, ABC

    Relaxed emphasis: kk_kk_

    Strike-through: strikethrough

    Lists:

    . sdfsd . wesdf

    hey!
    This is a definition list
    aaa bbbb
    hello sailor
    =====================

    Superscript: A^B, A^(BC)

    Relaxed emphasis: kkkk

    Strike-through: ~~strikethrough~~

    Lists:

    . sdfsd . wesdf

    =hey!= This is a definition list

    %class%

    aaa | bbbb —–|—— hello|sailor

    Done. --TEST-- MarkdownDocument::compile: test TAGTEXT flag --COMMENT-- No [] expansion for images or links (or HTML for them), smarty pants, ticks, autolink (even with AUTOLINK), emphasis; transformation of > and " into > and "e --SKIPIF-- [link text](http://www.example.com/) link text2
    html (except images and links) is allowed
    Smarty pants: (c) (tm) 1/4 "ooo" Superscript: A^B A^(BC) Strike through: ~~kkk~~ Ticks: `sdfsdf` Some chars: > " http://www.autolink.com/ Emphasis: *emphasis* EOD; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::AUTOLINK); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::TAGTEXT | MarkdownDocument::AUTOLINK); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    alt text alt text2

    link text link text2 http://www.bugabuga.com/

    html (except images and links) is allowed

    Smarty pants: © ™ ¼ “ooo”

    Superscript: AB ABC

    Strike through: kkk

    Ticks: sdfsdf

    Some chars: > "

    http://www.autolink.com/

    Emphasis: emphasis

    =====================

    ![alt text](http://www.example.com/img "title!!") <img src="http://www.example.com/img2" alt="alt text2" />

    [link text](http://www.example.com/) <a href="http://www.example2.com/">link text2</a> <http://www.bugabuga.com/>

    html (except images and links) is allowed

    Smarty pants: (c) (tm) 1/4 "ooo"

    Superscript: A^B A^(BC)

    Strike through: ~~kkk~~

    Ticks: `sdfsdf`

    Some chars: > "

    http://www.autolink.com/

    Emphasis: *emphasis*

    Done. --TEST-- MarkdownDocument::compile: test NO_EXT flag --SKIPIF-- text) [lang](lang:mylang) [abbr](abbr:myabbr) [class](class:myclass) EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NO_EXT); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    normal link id text lang abbr class

    =====================

    normal link [id](id:myid) [raw](raw:text) [lang](lang:mylang) [abbr](abbr:myabbr) [class](class:myclass)

    Done.--TEST-- MarkdownDocument::compile: test NOSUPERSCRIPT flag --SKIPIF-- B allowed though EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOSUPERSCRIPT); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    AB ABC aB allowed though

    =====================

    A^B A^(BC) aB allowed though

    Done. --TEST-- MarkdownDocument::compile: test NORELAXED flag --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NORELAXED); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    normal emphasis spe_cial emphasis_

    =====================

    normal emphasis special emphasis

    Done. --TEST-- MarkdownDocument::compile: test NOTABLES flag --SKIPIF-- aaabbbb hellosailor EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOTABLES); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    aaa bbbb
    hello sailor

    But allow this

    aaabbbb
    hellosailor
    =====================

    aaa | bbbb —–|—— hello|sailor

    But allow this

    aaabbbb
    hellosailor
    Done. --TEST-- MarkdownDocument::compile: test NOSTRIKETHROUGH flag --SKIPIF-- sdfsdf EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOSTRIKETHROUGH); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    sdfsdf

    But allow this sdfsdf

    =====================

    ~~sdfsdf~~

    But allow this sdfsdf

    Done. --TEST-- MarkdownDocument::compile: test TOC flag --SKIPIF-- compile(); var_dump($md->getToc()); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::TOC); echo $md->getToc(), "\n"; echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT-- bool(false)

    header 1

    header 1.1

    header 1.1.1

    header 1.1.2

    header 1.2

    header 2

    buga buga

    =====================

    header 1

    header 1.1

    header 1.1.1

    header 1.1.2

    header 1.2

    header 2

    buga buga

    Done. --TEST-- MarkdownDocument::compile: test ONE_COMPAT flag --COMMENT-- Mostly useless: 1) the first line of every block has trailing whitespace trimmed off and 2) require second [] for links/images instead of using label as key in the absence of it 3) more lax algorithm if content of []() link/image starts with < --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::ONE_COMPAT); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    buga   
    guga   
    

    Par key. [link text2](<http://www.example2.com)

    =====================
    buga
    guga   
    

    Par [key]. link text2

    Done. --TEST-- MarkdownDocument::compile: test AUTOLINK flag --SKIPIF-- EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::AUTOLINK); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    http://www.example.com news://news.php.net http://www.example2.com

    =====================

    http://www.example.com news://news.php.net http://www.example2.com

    Done. --TEST-- MarkdownDocument::compile: test SAFELINK flag --SKIPIF-- l [m](class:myclass) EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::SAFELINK); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    a b c d e f g h i j k l m

    =====================

    a b [c](a/b/example) d [e](../example) f g h [i](newss://example) j [k](irc://example) l [m](class:myclass)

    Done. --TEST-- MarkdownDocument::compile: test NODIVQUOTE flag --SKIPIF-- %myclass% > adsdf > sfdf EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NODIVQUOTE); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    adsdf sfdf

    =====================

    %myclass% adsdf sfdf

    Done. --TEST-- MarkdownDocument::compile: test NOALPHALIST flag --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NOALPHALIST); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    1. first
    2. second
    3. third
    =====================

    a. first b. second c. third

    Done. --TEST-- MarkdownDocument::compile: test NODLIST flag (markdown style) --COMMENT-- This is currently leaking memory. --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NODLIST); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    Apple
    Pomaceous fruit of plants of the genus Malus in the family Rosaceae.
    Orange
    The fruit of an evergreen tree of the genus Citrus.
    =====================

    Apple : Pomaceous fruit of plants of the genus Malus in the family Rosaceae.

    Orange : The fruit of an evergreen tree of the genus Citrus.

    Done. --TEST-- MarkdownDocument::compile: test NODLIST flag (discount style) --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::NODLIST); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--
    hey!
    This is a definition list
    =====================

    =hey!= This is a definition list

    Done. --TEST-- MarkdownDocument::compile: empty input --SKIPIF-- compile()); echo "\nDone.\n"; --EXPECT-- bool(true) Done. --TEST-- MarkdownDocument::compile: test EXTRA_FOOTNOTE flag --SKIPIF-- compile(); echo $md->getHtml(), "\n\n"; echo "=====================\n"; $md = MarkdownDocument::createFromString($t); $md->compile(MarkdownDocument::EXTRA_FOOTNOTE); echo $md->getHtml(), "\n\n"; echo "\nDone.\n"; --EXPECT--

    I haz a footnote^1

    =====================

    I haz a footnote1


    1. yes?

    Done. --TEST-- MarkdownDocument::createFromStream basic test --SKIPIF-- tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP; * TABSTOP is defined as 4 */ /* NOHEADER and MKD_STRICT should have the same effect */ $f = fopen("php://temp", "r+b"); $t1 = <<compile(); echo $md->getHtml(), "\n"; $f = fopen("php://temp", "r+b"); fwrite($f, $t1); fseek($f, 0); $md = MarkdownDocument::createFromStream($f, MarkdownDocument::NOHEADER); $md->compile(); echo $md->getHtml(), "\n"; echo "Done.\n"; --EXPECTF--

    test

    % the title % the author % the date

    test

    Done. --TEST-- MarkdownDocument::createFromStream: empty stream --SKIPIF-- compile(); var_dump($md->dumpTree('php://stdout')); var_dump($md->dumpTree('php://stdout', "my title")); echo "\nDone.\n"; --EXPECT-- --+--[source]-----[header, 1 line] |--[html, 7 lines] `--[source]--+--[ul]--+--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | `--[item]--+--[markup, 1 line] | `--[ul]--+--[item]-----[markup, 1 line] | `--[item]-----[markup, 1 line] |--[markup, 2 lines] `--[hr] bool(true) my title--+--[source]-----[header, 1 line] |--[html, 7 lines] `--[source]--+--[ul]--+--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | |--[item]--+--[markup, 1 line] | | `--[ul]--+--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | |--[item]-----[markup, 1 line] | | `--[item]-----[markup, 1 line] | `--[item]--+--[markup, 1 line] | `--[ul]--+--[item]-----[markup, 1 line] | `--[item]-----[markup, 1 line] |--[markup, 2 lines] `--[hr] bool(true) Done. --TEST-- MarkdownDocument::compile: error conditions --SKIPIF-- dumpTree('php://stdout'); } ); $md->compile(); var_dump($md->dumpTree()); var_dump($md->dumpTree(1,2,3)); var_dump($md->dumpTree(1,array())); echo "\nDone.\n"; --EXPECTF-- LogicException: Invalid state: the markdown document has not been compiled Warning: MarkdownDocument::dumpTree() expects at least 1 parameter, 0 given in %s on line %d bool(false) Warning: MarkdownDocument::dumpTree() expects at most 2 parameters, 3 given in %s on line %d bool(false) Warning: MarkdownDocument::dumpTree() expects parameter 2 to be string, array given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getAuthor basic test --SKIPIF-- getAuthor()); $md = MarkdownDocument::createFromString($t2); var_dump($md->getAuthor()); $md = MarkdownDocument::createFromString($t3); $md->compile(); //should have no effect in getAuthor var_dump($md->getAuthor()); $md = MarkdownDocument::createFromString($t4); var_dump($md->getAuthor()); $md = MarkdownDocument::createFromString($t2, MarkdownDocument::NOHEADER); var_dump($md->getAuthor()); echo "\nDone.\n"; --EXPECT-- string(0) "" string(18) "Author 1; Author 2" string(0) "" string(0) "" string(0) "" Done. --TEST-- MarkdownDocument::getAuthor error in arguments --SKIPIF-- getAuthor(6)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::getAuthor() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getCss basic test --COMMENT-- Possible bug that the second block style's not detected --SKIPIF-- .mystyle {} dfd ====== string(0) "" ====== string(0) "" Done. --TEST-- MarkdownDocument::getCss error in arguments --SKIPIF-- compile(); var_dump($md->getCss(6)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::getCss() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getDate basic test --SKIPIF-- getDate()); $md = MarkdownDocument::createFromString($t2); var_dump($md->getDate()); $md = MarkdownDocument::createFromString($t3); $md->compile(); //should have no effect in getDate var_dump($md->getDate()); $md = MarkdownDocument::createFromString($t4); var_dump($md->getDate()); $md = MarkdownDocument::createFromString($t2, MarkdownDocument::NOHEADER); var_dump($md->getDate()); echo "\nDone.\n"; --EXPECT-- string(0) "" string(16) "30 December 2010" string(0) "" string(0) "" string(0) "" Done. --TEST-- MarkdownDocument::getDate error in arguments --SKIPIF-- getDate(6)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::getDate() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getHtml basic test --SKIPIF-- compile(); echo $md->getHtml(); echo "\n==================\n"; echo $md->getHtml(); echo "\n==================\n"; $md = MarkdownDocument::createFromString(''); $md->compile(); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT--

    Markdown: Syntax

    Note: This document is itself written using Markdown; you can see the source for it by adding ‘.text’ to the URL.


    ==================

    Markdown: Syntax

    Note: This document is itself written using Markdown; you can see the source for it by adding ‘.text’ to the URL.


    ================== Done. --TEST-- MarkdownDocument::getHtml error in arguments and no compile --SKIPIF-- getHtml(); }); $md->compile(); var_dump($md->getHtml(6)); echo "\nDone.\n"; --EXPECTF-- LogicException: Invalid state: the markdown document has not been compiled Warning: MarkdownDocument::getHtml() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getHtml: exception in callback --SKIPIF-- setUrlCallback('cb'); $md->compile(); show_exc(array($md, 'getHtml')); echo "\nDone.\n"; --EXPECT-- string(23) "http://www.example.com/" RuntimeException: Call to PHP URL callback has failed (exception) Exception: my exception Done. --TEST-- MarkdownDocument::getHtml: callback calls object method --SKIPIF-- getHtml()); return NULL; } $md->setUrlCallback('cb'); $md->compile(); show_exc(array($md, 'getHtml')); echo "\nDone.\n"; --EXPECT-- RuntimeException: Call to PHP URL callback has failed (exception) LogicException: Attempt to call object method from inside callback Done. --TEST-- MarkdownDocument::getTitle basic test --COMMENT-- There's a bug (or at least a missing feature) in that multi-line titles are not supported see http://johnmacfarlane.net/pandoc/README.html#title-blocks --SKIPIF-- getTitle()); $md = MarkdownDocument::createFromString($t2); var_dump($md->getTitle()); $md = MarkdownDocument::createFromString($t3); $md->compile(); //should have no effect in getTitle var_dump($md->getTitle()); $md = MarkdownDocument::createFromString($t4); var_dump($md->getTitle()); $md = MarkdownDocument::createFromString($t2, MarkdownDocument::NOHEADER); var_dump($md->getTitle()); echo "\nDone.\n"; --EXPECT-- string(0) "" string(24) "This is second the title" string(0) "" string(0) "" string(0) "" Done. --TEST-- MarkdownDocument::getTitle error in arguments --SKIPIF-- getTitle(6)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::getTitle() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- MarkdownDocument::getToc basic test --SKIPIF-- compile(MarkdownDocument::TOC); echo $md->getToc(); echo "\n======\n"; $md = MarkdownDocument::createFromString($t1); $md->compile(); var_dump($md->getToc()); echo "\n======\n"; $md = MarkdownDocument::createFromString(''); $md->compile(MarkdownDocument::TOC); var_dump($md->getToc()); echo "\nDone.\n"; --EXPECT-- ====== bool(false) ====== string(0) "" Done. --TEST-- MarkdownDocument::getToc error in arguments and no compilation --SKIPIF-- getToc(); } ); $md->compile(); var_dump($md->getToc(6)); echo "\nDone.\n"; --EXPECTF-- LogicException: Invalid state: the markdown document has not been compiled Warning: MarkdownDocument::getToc() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. getMessage(),"\n"; if ($e->getPrevious() !== null) print_exc($e->getPrevious(), $indent + 2); } function show_exc($c) { try { call_user_func($c); } catch (Exception $e) { print_exc($e); } } function bad_stream() { $path = dirname(__FILE__) . "/temp_file"; cleanup_file(); //create the file $f = fopen($path, "w"); fclose($f); //read-only (windows has emulation for this) chmod($path, 0444); $f = fopen($path, "r"); return $f; } function cleanup_file() { $path = dirname(__FILE__) . "/temp_file"; if (file_exists($path)) { chmod($path, 0777); @unlink(dirname(__FILE__) . "/temp_file"); } } --TEST-- MarkdownDocument inheritance error test --SKIPIF-- isCompiled()) $this->compile(); return $this->getHtml(); } } $t = file_get_contents(dirname(__FILE__)."/simple_example.txt"); echo (new Markdown($t)); echo "\n========\n"; class Markdown2 extends MarkdownDocument { function __construct($str) { parent::__construct(); $str = (string) $str; var_dump(parent::initFromStream($str)); } public function __toString() { if (!$this->isCompiled()) $this->compile(); return $this->getHtml(); } } echo (new Markdown2(dirname(__FILE__)."/simple_example.txt")); echo "\nDone.\n"; --EXPECT-- bool(true)

    This is an H1

    para line2

    ======== bool(true)

    This is an H1

    para line2

    Done. --TEST-- MarkdownDocument inheritance error test --SKIPIF-- isCompiled()) $this->compile(); return $this->getHtml(); } } $t = file_get_contents(dirname(__FILE__)."/simple_example.txt"); show_exc(function () use ($t) { $a = new MarkdownBogus1($t); $a->__toString(); }); class MarkdownBogus2 extends MarkdownDocument { function __construct($str) { parent::__construct(); var_dump(parent::initFromString()); var_dump(parent::initFromString(5,4,6)); var_dump(parent::initFromStream()); var_dump(parent::initFromStream(5,4,6)); parent::initFromStream(dirname(__FILE__).'/non-existent-file'); } public function __toString() { if (!$this->isCompiled()) $this->compile(); return $this->getHtml(); } } show_exc(function () use ($t) { $a = new MarkdownBogus2($t); $a->__toString(); }); class MarkdownBogus3 extends MarkdownDocument { function __construct($str) { parent::__construct(); $str = (string) $str; parent::initFromString($str); parent::initFromString($str); } public function __toString() { if (!$this->isCompiled()) $this->compile(); return $this->getHtml(); } } $t = file_get_contents(dirname(__FILE__)."/simple_example.txt"); show_exc(function () use ($t) { $a = new MarkdownBogus3($t); $a->__toString(); }); echo "\nDone.\n"; --EXPECTF-- LogicException: Invalid state: the markdown document is not initialized Warning: MarkdownDocument::initFromString() expects at least 1 parameter, 0 given in %s on line %d bool(false) Warning: MarkdownDocument::initFromString() expects at most 2 parameters, 3 given in %s on line %d bool(false) Warning: MarkdownDocument::initFromStream() expects at least 1 parameter, 0 given in %s on line %d bool(false) Warning: MarkdownDocument::initFromStream() expects at most 2 parameters, 3 given in %s on line %d bool(false) InvalidArgumentException: Could not open path "%snon-existent-file" for reading LogicException: This object has already been initialized. Done. --TEST-- MarkdownDocument::isCompiled basic test --SKIPIF-- isCompiled()); $md->compile(); var_dump($md->isCompiled()); echo "\nDone.\n"; --EXPECTF-- bool(false) bool(true) Done. --TEST-- MarkdownDocument::compile: error in arguments --SKIPIF-- compile(); var_dump($md->isCompiled(88)); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::isCompiled() expects exactly 0 parameters, 1 given in %s on line %d bool(false) Done. --TEST-- Compile-time options: check WITH_FENCED_CODE effect --SKIPIF-- compile(); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT--

    First line

    My code
    Foo bar
    
    Done. --TEST-- Compile-time options: check WITH_GITHUB_TAGS effect --SKIPIF-- EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT--

    Test <_arghfoo_bar>

    Done. --TEST-- MarkdownDocument::setAttributesCallback basic test --SKIPIF-- two: [e-mail](mailto:buga@mail.com) EOD; $md = MarkdownDocument::createFromString($t1); $md->setAttributesCallback( function ($url) { var_dump($url); return "onclick=\"been_here\""; } ); $md->compile(); echo $md->getHtml(); echo "\n======\n"; $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setAttributesCallback( function ($url) { var_dump($url); return ""; } ); echo $md->getHtml(); echo "\n======\n"; function retnull() { return NULL; } $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setAttributesCallback("retnull"); echo $md->getHtml(); echo "\n======\n"; class StringWrapper { function __construct($str) { $this->str = $str; } function __toString() { return $this->str; } } $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setAttributesCallback( function ($url) { return new StringWrapper("target=\"_blank\""); } ); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT-- string(19) "http://aurl.com/jjj" string(20) "mailto:buga@mail.com"

    one: http://aurl.com/jjj two: e-mail

    ====== string(19) "http://aurl.com/jjj" string(20) "mailto:buga@mail.com"

    one: http://aurl.com/jjj two: e-mail

    ======

    one: http://aurl.com/jjj two: e-mail

    ======

    one: http://aurl.com/jjj two: e-mail

    Done. --TEST-- MarkdownDocument::setAttributesCallback test some errors --SKIPIF-- two: [e-mail](mailto:buga@mail.com) EOD; $md = MarkdownDocument::createFromString($t1); $md->compile(); var_dump($md->setAttributesCallback()); var_dump($md->setAttributesCallback(5)); var_dump($md->setAttributesCallback(5,6)); var_dump($md->setAttributesCallback( function ($a, $b) {} )); echo $md->getHtml(); echo "\n=========\n"; var_dump($md->setAttributesCallback( function ($a) { throw new RuntimeException("exception message"); } )); show_exc( function() use ($md) { echo $md->getHtml(); } ); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::setAttributesCallback() expects exactly 1 parameter, 0 given in %s on line %d bool(false) Warning: MarkdownDocument::setAttributesCallback() expects parameter 1 to be a valid callback, no array or string given in %s on line %d bool(false) Warning: MarkdownDocument::setAttributesCallback() expects exactly 1 parameter, 2 given in %s on line %d bool(false) bool(true) Warning: Missing argument 2 for {closure}() in %s on line %d Warning: Missing argument 2 for {closure}() in %s on line %d

    one: http://aurl.com/jjj two: e-mail

    ========= bool(true) RuntimeException: Call to PHP attributes callback has failed (exception) RuntimeException: exception message Done. --TEST-- MarkdownDocument::setReferencePrefix basic test --SKIPIF-- setReferencePrefix('foobar'); $md->compile(MarkdownDocument::EXTRA_FOOTNOTE); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT--

    I haz a footnote1


    1. yes?

    Done. --TEST-- MarkdownDocument::setReferencePrefix: error conditions --SKIPIF-- setReferencePrefix(); $md->setReferencePrefix(1, 2); $md->setReferencePrefix(fopen('php://memory', 'r+')); $md->compile(MarkdownDocument::EXTRA_FOOTNOTE); $md->setReferencePrefix('foobar'); --EXPECTF-- Warning: MarkdownDocument::setReferencePrefix() expects exactly 1 parameter, 0 given in %s on line %d Warning: MarkdownDocument::setReferencePrefix() expects exactly 1 parameter, 2 given in %s on line %d Warning: MarkdownDocument::setReferencePrefix() expects parameter 1 to be string, resource given in %s on line %d Fatal error: Uncaught exception 'LogicException' with message 'Invalid state: the markdown document has already been compiled' in %s:%d Stack trace: #0 %s(%d): MarkdownDocument->setReferencePrefix('foobar') #1 {main} thrown in %s on line %d --TEST-- MarkdownDocument::setUrlCallback basic test --SKIPIF-- two: [e-mail](mailto:buga@mail.com) EOD; $md = MarkdownDocument::createFromString($t1); $md->setUrlCallback( function ($url) { var_dump($url); return $url . "/been_here"; } ); $md->compile(); echo $md->getHtml(); echo "\n======\n"; $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setUrlCallback( function ($url) { var_dump($url); return ""; } ); echo $md->getHtml(); echo "\n======\n"; function retnull() { return NULL; } $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setUrlCallback("retnull"); echo $md->getHtml(); echo "\n======\n"; class StringWrapper { function __construct($str) { $this->str = $str; } function __toString() { return $this->str; } } $md = MarkdownDocument::createFromString($t1); $md->compile(); $md->setUrlCallback( function ($url) { return new StringWrapper("pre/".$url."/after"); } ); echo $md->getHtml(); echo "\nDone.\n"; --EXPECT-- string(19) "http://aurl.com/jjj" string(20) "mailto:buga@mail.com"

    one: http://aurl.com/jjj two: e-mail

    ====== string(19) "http://aurl.com/jjj" string(20) "mailto:buga@mail.com"

    one: http://aurl.com/jjj two: e-mail

    ======

    one: http://aurl.com/jjj two: e-mail

    ======

    one: http://aurl.com/jjj two: e-mail

    Done. --TEST-- MarkdownDocument::setUrlCallback test some errors --SKIPIF-- two: [e-mail](mailto:buga@mail.com) EOD; $md = MarkdownDocument::createFromString($t1); $md->compile(); var_dump($md->setUrlCallback()); var_dump($md->setUrlCallback(5)); var_dump($md->setUrlCallback(5,6)); var_dump($md->setUrlCallback( function ($a, $b) {} )); echo $md->getHtml(); echo "\n=========\n"; var_dump($md->setUrlCallback( function ($a) { throw new RuntimeException("exception message"); } )); show_exc( function() use ($md) { echo $md->getHtml(); } ); echo "\nDone.\n"; --EXPECTF-- Warning: MarkdownDocument::setUrlCallback() expects exactly 1 parameter, 0 given in %s on line %d bool(false) Warning: MarkdownDocument::setUrlCallback() expects parameter 1 to be a valid callback, no array or string given in %s on line %d bool(false) Warning: MarkdownDocument::setUrlCallback() expects exactly 1 parameter, 2 given in %s on line %d bool(false) bool(true) Warning: Missing argument 2 for {closure}() in %s on line %d Warning: Missing argument 2 for {closure}() in %s on line %d

    one: http://aurl.com/jjj two: e-mail

    ========= bool(true) RuntimeException: Call to PHP URL callback has failed (exception) RuntimeException: exception message Done. This is an H1 ============= para line2Markdown: Syntax ================ * [Overview](#overview) * [Philosophy](#philosophy) * [Inline HTML](#html) * [Automatic Escaping for Special Characters](#autoescape) * [Block Elements](#block) * [Paragraphs and Line Breaks](#p) * [Headers](#header) * [Blockquotes](#blockquote) * [Lists](#list) * [Code Blocks](#precode) * [Horizontal Rules](#hr) * [Span Elements](#span) * [Links](#link) * [Emphasis](#em) * [Code](#code) * [Images](#img) * [Miscellaneous](#misc) * [Backslash Escapes](#backslash) * [Automatic Links](#autolink) **Note:** This document is itself written using Markdown; you can [see the source for it by adding '.text' to the URL][src]. [src]: /projects/markdown/syntax.text * * *--TEST-- MarkdownDocument::transformFragment basic test --SKIPIF-- hh Doesn't support: multiple paragraphs > quoting > quoting 1. lists 2. lists code code *** [references][1] [1]: http://www.example.com EOD; echo MarkdownDocument::transformFragment($frag); echo "\nDone.\n"; --EXPECT-- Stuff supported: emphasis emphasis strike AB hh Doesn’t support: multiple paragraphs > quoting > quoting 1. lists 2. lists code code *** [references][1] [1]: http://www.example.com Done. --TEST-- MarkdownDocument::transformFragment error in arguments --SKIPIF-- emphasis emp_hasis_ don’t do. emphasis emphasis don't do. Done. --TEST-- MarkdownDocument::transformFragment with empty string --SKIPIF-- .mystyle {} dfd ====== bool(true) ====== bool(true) Done. --TEST-- MarkdownDocument::writeCss: some errors --SKIPIF-- .mystyle {} dfd bla2 *bla2* EOD; $md = MarkdownDocument::createFromString($t); $md->compile(); var_dump($md->writeXhtmlPage('php://stdout')); echo "\n"; $md = MarkdownDocument::createFromString(''); $md->compile(); var_dump($md->writeXhtmlPage('php://stdout')); echo "\nDone.\n"; --EXPECT-- This is the title

    bla bla

    bla2 bla2

    bool(true) bool(true) Done. --TEST-- MarkdownDocument::writeXhtmlPage error in arguments --SKIPIF-- writeXhtmlPage("php://stdout"); }); $md->compile(); var_dump($md->writeXhtmlPage(bad_stream())); var_dump($md->writeXhtmlPage()); show_exc(function () use ($md) { $md->writeXhtmlPage('inex/sdfs'); }); var_dump($md->writeXhtmlPage(6,7)); echo "\nDone.\n"; --EXPECTF-- LogicException: Invalid state: the markdown document has not been compiled Warning: MarkdownDocument::writeXhtmlPage(): I/O error in library function mkd_xhtmlpage: %s (%d) in %s on line %d bool(false) Warning: MarkdownDocument::writeXhtmlPage() expects exactly 1 parameter, 0 given in %s on line %d bool(false) InvalidArgumentException: Could not open path "inex/sdfs" for writing Warning: MarkdownDocument::writeXhtmlPage() expects exactly 1 parameter, 2 given in %s on line %d bool(false) Done.dnl $Id: 442ee0e5b508ccb26d5cdf31fe6e88c335fa0773 $ dnl config.m4 for extension discount PHP_ARG_ENABLE(discount, whether to enable discount support, [ --enable-discount Enable dicount markdown support]) discountlib_sources="lib/Csio.c lib/css.c lib/docheader.c \ lib/dumptree.c lib/emmatch.c lib/flags.c \ lib/generate.c lib/html5.c lib/markdown.c \ lib/mkdio.c lib/resource.c lib/tags.c \ lib/toc.c lib/version.c lib/xml.c \ lib/xmlpage.c lib/setup.c" if test "$PHP_DISCOUNT" != "no"; then AC_DEFINE(HAVE_DISCOUNT, 1, [Whether you have discount markdown support]) PHP_SUBST(DISCOUNT_SHARED_LIBADD) PHP_NEW_EXTENSION(discount, discount.c markdowndoc_class.c markdowndoc_meth_callbacks.c markdowndoc_meth_document.c markdowndoc_meth_header.c markdowndoc_meth_input.c markdowndoc_meth_misc.c markdowndoc_meth_parts.c $discountlib_sources, $ext_shared,,-DUSE_DISCOUNT_DL=1 -DUSE_EXTRA_DL=1 -DTABSTOP=4 -DWITH_ID_ANCHOR=1 -DWITH_FENCED_CODE=1 -DWITH_GITHUB_TAGS=1 -Wall -Wno-parentheses) PHP_ADD_BUILD_DIR($ext_builddir/lib) fi // $Id: 4227c94da9b28ce7a5780d984c1b04d71b5cb4c7 $ // vim:ft=javascript ARG_ENABLE("discount", "Discount markdown support", "no"); if (PHP_DISCOUNT != "no") { EXTENSION("discount", "discount.c markdowndoc_class.c markdowndoc_meth_callbacks.c markdowndoc_meth_document.c markdowndoc_meth_header.c markdowndoc_meth_input.c markdowndoc_meth_misc.c markdowndoc_meth_parts.c ", PHP_DISCOUNT_SHARED, "/D_WSTDIO_DEFINED"); ADD_SOURCES(configure_module_dirname + "/lib", "Csio.c css.c docheader.c \ dumptree.c emmatch.c flags.c \ generate.c html5.c markdown.c \ mkdio.c resource.c tags.c \ toc.c version.c xml.c \ xmlpage.c setup.c", "discount"); AC_DEFINE("HAVE_DISCOUNT", 1, "Discount markdown support"); } /* * Copyright (c) 2011, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 41cb6ef79b60bb03ea2607f7700c2bd6710df903 $ */ #include #include #include "php_discount.h" #include "lib/mkdio.h" #include "lib/tags.h" #include "markdowndoc_class.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef COMPILE_DL_DISCOUNT ZEND_GET_MODULE(discount) #endif /* {{{ discount_functions[] */ static zend_function_entry discount_functions[] = { {NULL, NULL, NULL} }; /* }}} */ #ifdef DISCOUNT_GLOBALS /* {{{ Globals' related activities */ ZEND_DECLARE_MODULE_GLOBALS(discount); static void ZEND_MODULE_GLOBALS_CTOR_N(discount)(void *arg TSRMLS_DC) /* {{{ */ { zend_discount_globals *discount_globals = arg; /* empty */ } /* }}} */ static void ZEND_MODULE_GLOBALS_DTOR_N(discount)(void *arg TSRMLS_DC) /* {{{ */ { /* empty */ } /* }}} */ /* end globals }}} */ #endif /* {{{ ZEND_MODULE_STARTUP */ ZEND_MODULE_STARTUP_D(discount) { markdowndoc_module_start(INIT_FUNC_ARGS_PASSTHRU); mkd_tags_on_startup(INIT_FUNC_ARGS_PASSTHRU); return SUCCESS; } /* }}} */ /* {{{ ZEND_MODULE_SHUTDOWN */ ZEND_MODULE_SHUTDOWN_D(discount) { markdowndoc_module_start(SHUTDOWN_FUNC_ARGS_PASSTHRU); mkd_tags_on_shutdown(SHUTDOWN_FUNC_ARGS_PASSTHRU); return SUCCESS; } /* }}} */ /* {{{ ZEND_MODULE_INFO */ ZEND_MODULE_INFO_D(discount) { php_info_print_table_start(); php_info_print_table_header(2, "Discount markdown", "enabled"); php_info_print_table_row(2, "Discount Ext. version", PHP_DISCOUNT_VERSION); php_info_print_table_row(2, "Discount lib version", markdown_version); php_info_print_table_end(); } /* }}} */ /* {{{ discount_module_entry */ zend_module_entry discount_module_entry = { STANDARD_MODULE_HEADER, "discount", discount_functions, ZEND_MODULE_STARTUP_N(discount), ZEND_MODULE_SHUTDOWN_N(discount), /* ZEND_MODULE_ACTIVATE_N(discount), */ NULL, /* ZEND_MODULE_DEACTIVATE_N(discount), */ NULL, ZEND_MODULE_INFO_N(discount), PHP_DISCOUNT_VERSION, #ifdef DISCOUNT_GLOBALS ZEND_MODULE_GLOBALS(discount), ZEND_MODULE_GLOBALS_CTOR_N(discount), ZEND_MODULE_GLOBALS_DTOR_N(discount), #else NO_MODULE_GLOBALS, #endif NULL, //post_deactivate_func STANDARD_MODULE_PROPERTIES_EX, }; /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 0ca38b6552279cb4b9db5438f76089bd79b457e4 $ */ #include #include
    #include #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_input.h" #include "markdowndoc_meth_misc.h" #include "markdowndoc_meth_header.h" #include "markdowndoc_meth_document.h" #include "markdowndoc_meth_parts.h" #include "markdowndoc_meth_callbacks.h" zend_class_entry *markdowndoc_ce; static zend_object_handlers object_handlers; /* {{{ allusions */ static PHP_METHOD(markdowndoc, __construct); /* }}} */ /* {{{ arginfo */ ZEND_BEGIN_ARG_INFO_EX(arginfo_void, 0, 0, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_createfromstream, 0, 0, 1) ZEND_ARG_INFO(0, markdown_stream) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_createfromstring, 0, 0, 1) ZEND_ARG_INFO(0, markdown_doc) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_compile, 0, 0, 0) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_dumptree, 0, 0, 1) ZEND_ARG_INFO(0, out_stream) ZEND_ARG_INFO(0, title) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_transformfragment, 0, 0, 1) ZEND_ARG_INFO(0, markdown_fragment) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_writefragment, 0, 0, 2) ZEND_ARG_INFO(0, markdown_fragment) ZEND_ARG_INFO(0, out_stream) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_setreferenceprefix, 0, 0, 1) ZEND_ARG_INFO(0, prefix) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_outstream, 0, 0, 1) ZEND_ARG_INFO(0, markdown_outstream) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_callback, 0, 0, 1) ZEND_ARG_INFO(0, callback) ZEND_END_ARG_INFO() /* }}} */ static zend_function_entry class_methods[] = { PHP_ME(markdowndoc, __construct, arginfo_void, ZEND_ACC_PROTECTED) PHP_ME(markdowndoc, createFromStream, arginfo_createfromstream, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(markdowndoc, createFromString, arginfo_createfromstring, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(markdowndoc, initFromStream, arginfo_createfromstream, ZEND_ACC_PROTECTED | ZEND_ACC_FINAL) PHP_ME(markdowndoc, initFromString, arginfo_createfromstring, ZEND_ACC_PROTECTED | ZEND_ACC_FINAL) PHP_ME(markdowndoc, compile, arginfo_compile, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, isCompiled, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, dumpTree, arginfo_dumptree, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, transformFragment, arginfo_transformfragment, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(markdowndoc, writeFragment, arginfo_writefragment, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC) PHP_ME(markdowndoc, setReferencePrefix, arginfo_setreferenceprefix, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getTitle, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getAuthor, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getDate, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getHtml, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, writeHtml, arginfo_outstream, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, writeXhtmlPage, arginfo_outstream, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getToc, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, getCss, arginfo_void, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, writeToc, arginfo_outstream, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, writeCss, arginfo_outstream, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, setUrlCallback, arginfo_callback, ZEND_ACC_PUBLIC) PHP_ME(markdowndoc, setAttributesCallback,arginfo_callback, ZEND_ACC_PUBLIC) {NULL, NULL, NULL, 0, 0} }; static void free_object_storage(void *object TSRMLS_DC) { discount_object *dobj = object; if (dobj->markdoc != NULL) { mkd_cleanup(dobj->markdoc); dobj->markdoc = NULL; } if (dobj->ref_prefix != NULL) { efree(dobj->ref_prefix); dobj->ref_prefix = NULL; } markdowndoc_free_callback(&dobj->url_fci, &dobj->url_fcc); markdowndoc_free_callback(&dobj->attr_fci, &dobj->attr_fcc); zend_objects_free_object_storage(object TSRMLS_CC); } static zend_object_value ce_create_object(zend_class_entry *class_type TSRMLS_DC) { zend_object_value zov; discount_object *dobj; dobj = emalloc(sizeof *dobj); zend_object_std_init((zend_object *) dobj, class_type TSRMLS_CC); #if PHP_VERSION_ID < 50399 zend_hash_copy(dobj->std.properties, &(class_type->default_properties), (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*)); #else object_properties_init(&dobj->std, class_type); #endif dobj->markdoc = NULL; dobj->in_callback = 0; dobj->url_fci = NULL; dobj->url_fcc = NULL; dobj->attr_fci = NULL; dobj->attr_fcc = NULL; dobj->ref_prefix = NULL; zov.handle = zend_objects_store_put(dobj, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) free_object_storage, NULL TSRMLS_CC); zov.handlers = &object_handlers; return zov; } /* {{{ Constructor; protected no-op. * Subclasses may call this, but they don't have to, they can just call one * of the init protected methods. */ static PHP_METHOD(markdowndoc, __construct) { if (zend_parse_parameters_none() == FAILURE) { return; } return; /* no-op method */ } /* }}} */ /* {{{ Public functions */ discount_object* markdowndoc_get_object(zval *zobj, int require_compiled TSRMLS_DC) { discount_object *dobj; if (zobj == NULL) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Unexpected null pointer. This should not happen"); return NULL; } dobj = zend_object_store_get_object(zobj TSRMLS_CC); if (dobj->markdoc == NULL) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Invalid state: the markdown document is not initialized"); return NULL; } if (dobj->in_callback) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Attempt to call object method from inside callback"); return NULL; } if (require_compiled && !mkd_is_compiled(dobj->markdoc)) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Invalid state: the markdown document has not been compiled"); return NULL; } return dobj; } php_stream *markdowndoc_get_stream(zval *arg, int write, int *must_close TSRMLS_DC) { php_stream *ret; *must_close = 0; if (Z_TYPE_P(arg) == IS_RESOURCE) { php_stream_from_zval_no_verify(ret, &arg); if (ret == NULL) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "The resource passed is not a stream"); } } else if (Z_TYPE_P(arg) == IS_STRING) { const char *mode; is_string: mode = write?"w":"r"; ret = php_stream_open_wrapper_ex(Z_STRVAL_P(arg), (char *) mode, 0, NULL, NULL); if (ret == NULL) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Could not open path \"%s\" for %s", Z_STRVAL_P(arg), write?"writing":"reading"); } else { *must_close = 1; } } else { /* not a string or a resource; convert to string */ SEPARATE_ZVAL(&arg); convert_to_string(arg); goto is_string; } return ret; } int markdowndoc_get_file(zval *arg, int write, php_stream **stream, int *must_close, FILE **file TSRMLS_DC) { *stream = NULL; *must_close = 0; *file = NULL; *stream = markdowndoc_get_stream(arg, write, must_close TSRMLS_CC); if (*stream == NULL) { return FAILURE; } if (php_stream_cast(*stream, PHP_STREAM_AS_STDIO, (void**) file, 0) == FAILURE) { if (must_close) { php_stream_close(*stream); } *stream = NULL; *must_close = 0; zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Could not cast stream into an stdlib file pointer"); return FAILURE; } return SUCCESS; } int markdown_sync_stream_and_file(php_stream *stream, int close, FILE *file TSRMLS_DC) { long pos; int status; fflush(file); /* ignore return */ if (close) { status = php_stream_close(stream); return status ? FAILURE : SUCCESS; } pos = ftell(file); if (pos < 0) { return FAILURE; } /* don't do simply php_stream_seek(strem, 0L, SEEK_CUR) because * PHP turns the SEEK_CUR into a SEEK_SET using an out-of-date position * to calculate the offset */ status = php_stream_seek(stream, (off_t) pos, SEEK_SET); return status ? FAILURE : SUCCESS; } int markdown_handle_io_error(int status, const char *lib_func TSRMLS_DC) { if (status < 0) { if (errno == 0) { zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Unspecified error in library function %s", lib_func); return FAILURE; } else { php_error_docref0(NULL TSRMLS_CC, E_WARNING, "I/O error in library " "function %s: %s (%d)", lib_func, strerror(errno), errno); errno = 0; return FAILURE; } } return SUCCESS; } void markdowndoc_store_callback( zend_fcall_info *fci_in, zend_fcall_info_cache *fcc_in, zend_fcall_info **fci_out, zend_fcall_info_cache **fcc_out ) { markdowndoc_free_callback(fci_out, fcc_out); if (fci_in) { *fci_out = emalloc(sizeof **fci_out); **fci_out = *fci_in; Z_ADDREF_P((**fci_out).function_name); #if PHP_VERSION_ID >= 50300 if ((**fci_out).object_ptr != NULL) { Z_ADDREF_P((**fci_out).object_ptr); } #endif } if (fcc_in) { *fcc_out = emalloc(sizeof **fcc_out); **fcc_out = *fcc_in; } } void markdowndoc_free_callback(zend_fcall_info **fci, zend_fcall_info_cache **fcc) { if (*fci != NULL) { zval_ptr_dtor(&(*fci)->function_name); #if PHP_VERSION_ID >= 50300 if ((*fci)->object_ptr != NULL) { zval_ptr_dtor(&(*fci)->object_ptr); } #endif efree(*fci); *fci = NULL; } if (*fcc != NULL) { efree(*fcc); *fcc = NULL; } } void markdowndoc_module_start(INIT_FUNC_ARGS) { zend_class_entry ce; memcpy(&object_handlers, zend_get_std_object_handlers(), sizeof object_handlers); object_handlers.clone_obj = NULL; INIT_CLASS_ENTRY(ce, "MarkdownDocument", class_methods); markdowndoc_ce = zend_register_internal_class(&ce TSRMLS_CC); markdowndoc_ce->create_object = ce_create_object; #define DISCOUNT_CONST(name) \ zend_declare_class_constant_long(markdowndoc_ce, #name, sizeof(#name) -1, \ MKD_ ## name TSRMLS_CC) DISCOUNT_CONST(NOLINKS); DISCOUNT_CONST(NOIMAGE); DISCOUNT_CONST(NOPANTS); DISCOUNT_CONST(NOHTML); DISCOUNT_CONST(STRICT); DISCOUNT_CONST(TAGTEXT); DISCOUNT_CONST(NO_EXT); DISCOUNT_CONST(CDATA); DISCOUNT_CONST(NOSUPERSCRIPT); DISCOUNT_CONST(NORELAXED); DISCOUNT_CONST(NOTABLES); DISCOUNT_CONST(NOSTRIKETHROUGH); DISCOUNT_CONST(TOC); #define MKD_ONE_COMPAT MKD_1_COMPAT DISCOUNT_CONST(ONE_COMPAT); #undef MKD_ONE_COMPAT DISCOUNT_CONST(AUTOLINK); DISCOUNT_CONST(SAFELINK); DISCOUNT_CONST(NOHEADER); DISCOUNT_CONST(TABSTOP); DISCOUNT_CONST(NODIVQUOTE); DISCOUNT_CONST(NOALPHALIST); DISCOUNT_CONST(NODLIST); DISCOUNT_CONST(EMBED); DISCOUNT_CONST(EXTRA_FOOTNOTE); #undef DISCOUNT_CONST } /* end of public functions }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 2ea51a92e71df183dfc9425d7b3dab76883f7bb3 $ */ #include "lib/mkdio.h" typedef struct discount_object { zend_object std; MMIOT *markdoc; int in_callback; zend_fcall_info *url_fci; zend_fcall_info_cache *url_fcc; zend_fcall_info *attr_fci; zend_fcall_info_cache *attr_fcc; char *ref_prefix; } discount_object; extern zend_class_entry *markdowndoc_ce; discount_object* markdowndoc_get_object(zval *zobj, int require_compiled TSRMLS_DC); php_stream *markdowndoc_get_stream(zval *arg, int write, int *must_close TSRMLS_DC); /* on failure, no cleanup necessary: */ int markdowndoc_get_file(zval *arg, int write, php_stream **stream, int *must_close, FILE **file TSRMLS_DC); int markdown_sync_stream_and_file(php_stream *stream, int close, FILE *file TSRMLS_DC); int markdown_handle_io_error(int status, const char *lib_func TSRMLS_DC); void markdowndoc_store_callback(zend_fcall_info *fci_in, zend_fcall_info_cache *fcc_in, zend_fcall_info **fci_out, zend_fcall_info_cache **fcc_out); void markdowndoc_free_callback(zend_fcall_info **fci, zend_fcall_info_cache **fcc); void markdowndoc_module_start(INIT_FUNC_ARGS); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 524ffff30b791ac46ef01ca7b982f81320f9e17b $ */ #include #include #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_callbacks.h" static char *proxy_callback( const char *url, const int url_len, zend_fcall_info *fci, zend_fcall_info_cache *fcc, char *callback_name ) { zval *zurl, *retval_ptr = NULL, **params; int retval; char *result = NULL; TSRMLS_FETCH(); if (fci == NULL || fci->size == 0) return NULL; /* should not happen */ MAKE_STD_ZVAL(zurl); ZVAL_STRINGL(zurl, url, url_len, 1); params = &zurl; fci->retval_ptr_ptr = &retval_ptr; fci->params = ¶ms; fci->param_count = 1; fci->no_separation = 1; retval = zend_call_function(fci, fcc TSRMLS_CC); if (retval != SUCCESS || fci->retval_ptr_ptr == NULL) { /* failure was most likely due to a previous exception (probably * in a previous URL), so don't throw yet another exception on * top of it */ if (!EG(exception)) { zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Call to PHP %s callback has failed", callback_name); } } else { /* success in zend_call_function, but there may've been an exception */ /* may have been changed by return by reference */ retval_ptr = *fci->retval_ptr_ptr; if (retval_ptr == NULL) { /* no retval - most likely an exception, but we feel confortable * stacking an exception here */ zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Call to PHP %s callback has failed (%s)", callback_name, EG(exception)?"exception":"no return value"); } else if (Z_TYPE_P(retval_ptr) == IS_NULL) { /* use the default string for the url */ } else { if (Z_TYPE_P(retval_ptr) != IS_STRING) { SEPARATE_ZVAL(&retval_ptr); convert_to_string(retval_ptr); } result = estrndup(Z_STRVAL_P(retval_ptr), Z_STRLEN_P(retval_ptr)); } } zval_ptr_dtor(&zurl); if (retval_ptr != NULL) { zval_ptr_dtor(&retval_ptr); } return result; } /* {{{ proxy_url_callback */ static char *proxy_url_callback(const char *url, const int url_len, void *data) { discount_object *dobj = data; char *retval; dobj->in_callback = 1; retval = proxy_callback(url, url_len, dobj->url_fci, dobj->url_fcc, "URL"); dobj->in_callback = 0; return retval; } /* }}} */ /* {{{ proxy_attributes_callback */ static char *proxy_attributes_callback(const char *url, const int url_len, void *data) { discount_object *dobj = data; char *retval; dobj->in_callback = 1; retval = proxy_callback(url, url_len, dobj->attr_fci, dobj->attr_fcc, "attributes"); dobj->in_callback = 0; return retval; } /* }}} */ /* {{{ free_proxy_return */ static void free_proxy_return(char *buffer, void *doc) { (void) doc; /* don't care */ /* PHP doesn't like efree called on null pointers */ if (buffer) { efree(buffer); } } /* }}} */ /* {{{ proto bool MarkdownDocument::setUrlCallback(callback $url_callback) */ PHP_METHOD(markdowndoc, setUrlCallback) { zend_fcall_info fci; zend_fcall_info_cache fcc; discount_object *dobj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", &fci, &fcc) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (fci.size > 0) { /* non-NULL passed */ markdowndoc_store_callback(&fci, &fcc, &dobj->url_fci, &dobj->url_fcc); mkd_e_url(dobj->markdoc, proxy_url_callback); mkd_e_free(dobj->markdoc, free_proxy_return); mkd_e_data(dobj->markdoc, dobj); } else { /* NULL */ markdowndoc_free_callback(&dobj->url_fci, &dobj->url_fcc); mkd_e_url(dobj->markdoc, NULL); } RETURN_TRUE; } /* }}} */ /* {{{ proto bool MarkdownDocument::setAttributesCallback(callback $attributes_callback) */ PHP_METHOD(markdowndoc, setAttributesCallback) { zend_fcall_info fci; zend_fcall_info_cache fcc; discount_object *dobj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f!", &fci, &fcc) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (fci.size > 0) { /* non-NULL passed */ markdowndoc_store_callback(&fci, &fcc, &dobj->attr_fci, &dobj->attr_fcc); mkd_e_flags(dobj->markdoc, proxy_attributes_callback); mkd_e_free(dobj->markdoc, free_proxy_return); mkd_e_data(dobj->markdoc, dobj); } else { /* NULL */ markdowndoc_free_callback(&dobj->attr_fci, &dobj->attr_fcc); mkd_e_url(dobj->markdoc, NULL); } RETURN_TRUE; } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: f272bb5cc81765a28d6fe7202f97102fd7a34282 $ */ PHP_METHOD(markdowndoc, setUrlCallback); PHP_METHOD(markdowndoc, setAttributesCallback); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 09cc496e5989e6ac60f9b3169e5777956cf63eeb $ */ #include #include #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_document.h" /* {{{ proto string MarkdownDocument::getHtml() */ PHP_METHOD(markdowndoc, getHtml) { discount_object *dobj; char *data = NULL; int status; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } status = mkd_document(dobj->markdoc, &data); if (status < 0) { /* should never happen, but... */ zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Call to library function mkd_document() failed (should not happen!)"); RETURN_FALSE; } assert(data != NULL); RETURN_STRINGL(data, status, 0); } /* }}} */ /* {{{ proto bool MarkdownDocument::writeHtml(mixed $out_stream) */ PHP_METHOD(markdowndoc, writeHtml) { discount_object *dobj; zval *zstream; php_stream *stream; int close; FILE *f; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_generatehtml(dobj->markdoc, f); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (markdown_handle_io_error(status, "mkd_generatehtml" TSRMLS_CC) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto bool MarkdownDocument::writeXhtmlPage(mixed $out_stream) */ PHP_METHOD(markdowndoc, writeXhtmlPage) { discount_object *dobj; zval *zstream; php_stream *stream; int close; FILE *f; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_xhtmlpage(dobj->markdoc, f); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (markdown_handle_io_error(status, "mkd_xhtmlpage" TSRMLS_CC) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 357941de34cb9999e363b34188cc4f78cfdbec33 $ */ PHP_METHOD(markdowndoc, getHtml); PHP_METHOD(markdowndoc, writeHtml); PHP_METHOD(markdowndoc, writeXhtmlPage); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 27bff05f9446d4915ced615c3512c3297f337e30 $ */ #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_header.h" /* {{{ proto string MarkdownDocument::getTitle() */ PHP_METHOD(markdowndoc, getTitle) { discount_object *dobj; char *data; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } data = mkd_doc_title(dobj->markdoc); if (data == NULL) { RETURN_EMPTY_STRING() } else { RETURN_STRING(data, 1); /* must dup */ } } /* }}} */ /* {{{ proto string MarkdownDocument::getAuthor() */ PHP_METHOD(markdowndoc, getAuthor) { discount_object *dobj; char *data = NULL; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } data = mkd_doc_author(dobj->markdoc); if (data == NULL) { RETURN_EMPTY_STRING() } else { RETURN_STRING(data, 1); /* must dup */ } } /* }}} */ /* {{{ proto string MarkdownDocument::getDate() */ PHP_METHOD(markdowndoc, getDate) { discount_object *dobj; char *data = NULL; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } data = mkd_doc_date(dobj->markdoc); if (data == NULL) { RETURN_EMPTY_STRING() } else { RETURN_STRING(data, 1); /* must dup */ } } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 5154fc682491b0795e636e0f7a3d07fce407af86 $ */ PHP_METHOD(markdowndoc, getTitle); PHP_METHOD(markdowndoc, getAuthor); PHP_METHOD(markdowndoc, getDate); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 27186aca5b092b26d24d3f7cd77bc0ca06bbb3c1 $ */ #include #include #include
    #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_input.h" /* {{{ markdown_check_input_flags */ static int markdown_check_input_flags(mkd_flag_t flags TSRMLS_DC) { if (flags & ~(MKD_TABSTOP|MKD_NOHEADER)) { zend_throw_exception_ex(spl_ce_InvalidArgumentException, 0 TSRMLS_CC, "Only the flags TABSTOP and NOHEADER are allowed."); return FAILURE; } return SUCCESS; } /* }}} */ /* {{{ markdown_init_from_stream */ static int markdown_init_from_stream(zval* obj, zval *zstream, long flags TSRMLS_DC) { discount_object *dobj = zend_object_store_get_object(obj TSRMLS_CC); MMIOT *mmiot; php_stream *stream; int close; FILE *f; int ret; if (dobj->markdoc != NULL) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "This object has already been initialized."); return FAILURE; } if (markdown_check_input_flags((mkd_flag_t) flags TSRMLS_CC) == FAILURE) { return FAILURE; } if (markdowndoc_get_file(zstream, 0, &stream, &close, &f TSRMLS_CC) == FAILURE) { return FAILURE; } mmiot = mkd_in(f, (mkd_flag_t) flags); if (mmiot == NULL) { zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Error initializing markdown document: call to the library routine " "mkd_in() failed"); ret = FAILURE; } else { dobj->markdoc = mmiot; ret = SUCCESS; } if (close) { php_stream_close(stream); } return ret; } /* }}} */ /* {{{ markdown_init_from_string */ static int markdown_init_from_string(zval* obj, const char *string, int len, long flags TSRMLS_DC) { discount_object *dobj = zend_object_store_get_object(obj TSRMLS_CC); MMIOT *mmiot; if (dobj->markdoc != NULL) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "This object has already been initialized."); return FAILURE; } if (markdown_check_input_flags((mkd_flag_t) flags TSRMLS_CC) == FAILURE) { return FAILURE; } mmiot = mkd_string((char*) string, len, (mkd_flag_t) flags); if (mmiot == NULL) { /* should not happen */ zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Error initializing markdown document: call to the library routine " "mkd_string() failed"); return FAILURE; } dobj->markdoc = mmiot; return SUCCESS; } /* }}} */ /* {{{ proto MarkdownDoc MarkdownDocument::createFromStream(mixed $markdown_stream [, int $flags = 0]) * Creates and initializes a markdown document from a stream. */ PHP_METHOD(markdowndoc, createFromStream) { zval *zstream; long flags = 0; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|l", &zstream, &flags) == FAILURE) { RETURN_FALSE; } object_init_ex(return_value, markdowndoc_ce); ret = markdown_init_from_stream(return_value, zstream, flags TSRMLS_CC); if (ret == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ proto MarkdownDoc MarkdownDocument::createFromString(string $markdown_string [, int $flags = 0]) * Creates and initializes a markdown document from a string. */ PHP_METHOD(markdowndoc, createFromString) { char *string; int string_len; long flags = 0; int ret; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &string, &string_len, &flags) == FAILURE) { RETURN_FALSE; } object_init_ex(return_value, markdowndoc_ce); ret = markdown_init_from_string(return_value, string, string_len, flags TSRMLS_CC); if (ret == FAILURE) { zval_dtor(return_value); RETURN_FALSE; } } /* }}} */ /* {{{ proto bool MarkdownDocument::initFromStream(mixed $markdown_stream [, int $flags = 0]) * Initializes a markdown document from a stream. */ PHP_METHOD(markdowndoc, initFromStream) { zval *this, *zstream; long flags = 0; int ret; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oz|l", &this, markdowndoc_ce, &zstream, &flags) == FAILURE) { RETURN_FALSE; } ret = markdown_init_from_stream(this, zstream, flags TSRMLS_CC); if (ret == FAILURE) { RETURN_FALSE; /* no rollback needed */ } RETURN_TRUE; } /* }}} */ /* {{{ proto bool MarkdownDocument::initFromString(string $markdown_string [, int $flags = 0]) * Initializes a markdown document from a string. */ PHP_METHOD(markdowndoc, initFromString) { zval *this; char *string; int string_len; long flags = 0; int ret; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os|l", &this, markdowndoc_ce, &string, &string_len, &flags) == FAILURE) { RETURN_FALSE; } ret = markdown_init_from_string(this, string, string_len, flags TSRMLS_CC); if (ret == FAILURE) { RETURN_FALSE; /* no rollback needed */ } RETURN_TRUE; } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 37c131b178f406b814a8b50eb38e328744c37615 $ */ PHP_METHOD(markdowndoc, createFromStream); PHP_METHOD(markdowndoc, createFromString); PHP_METHOD(markdowndoc, initFromStream); PHP_METHOD(markdowndoc, initFromString); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 6317c148d0cc9ad5a2f4bb4f5c56d9300d8814e1 $ */ #include #include #include
    #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_misc.h" /* {{{ proto bool MarkdownDocument::compile([int $flags = 0]) */ PHP_METHOD(markdowndoc, compile) { discount_object *dobj; long flags = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (mkd_is_compiled(dobj->markdoc)) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Invalid state: the markdown document has already been compiled"); RETURN_FALSE; } /* always returns success (unless fed a null pointer) */ mkd_compile(dobj->markdoc, (mkd_flag_t) flags); RETURN_TRUE; } /* }}} */ /* {{{ proto bool MarkdownDocument::isCompiled() */ PHP_METHOD(markdowndoc, isCompiled) { discount_object *dobj; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } RETURN_BOOL(mkd_is_compiled(dobj->markdoc)); } /* }}} */ /* {{{ proto bool MarkdownDocument::dumpTree(mixed $out_stream [, string $title = "" ]) */ PHP_METHOD(markdowndoc, dumpTree) { discount_object *dobj; zval *zstream; php_stream *stream; int close; FILE *f; char *title = ""; int title_len = 0; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|s", &zstream, &title, &title_len) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_dump(dobj->markdoc, f, title); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (status == -1) { /* should never happen */ zend_throw_exception(spl_ce_RuntimeException, "Error dumping tree: call to the library failed", 0 TSRMLS_CC); RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto string MarkdownDocument::transformFragment(string $markdown_fragment [, int $flags = 0 ]) */ PHP_METHOD(markdowndoc, transformFragment) { char *markdown; int markdown_len; long flags = 0; char *out = NULL; int out_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &markdown, &markdown_len, &flags) == FAILURE) { RETURN_FALSE; } if (markdown_len == 0) { RETURN_EMPTY_STRING(); } out_len = mkd_line(markdown, markdown_len, &out, (mkd_flag_t) flags); if (out_len < 0) { zend_throw_exception(spl_ce_RuntimeException, "Error parsing the fragment", 0 TSRMLS_CC); RETVAL_FALSE; } else { RETVAL_STRINGL(out, out_len, 0); } if (Z_TYPE_P(return_value) == IS_BOOL && out != NULL) { efree(out); } } /* }}} */ /* {{{ proto bool MarkdownDoc::writeFragment(string $markdown_fragment, mixed $out_stream [, int $flags = 0 ]) */ PHP_METHOD(markdowndoc, writeFragment) { char *markdown; int markdown_len; long flags = 0; zval *zstream; php_stream *stream; int close; FILE *f; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &markdown, &markdown_len, &zstream, &flags) == FAILURE) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_generateline(markdown, markdown_len, f, (mkd_flag_t) flags); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (markdown_handle_io_error(status, "mkd_generateline" TSRMLS_CC) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* {{{ proto bool MarkdownDocument::setReferencePrefix(string) */ PHP_METHOD(markdowndoc, setReferencePrefix) { char *prefix; int prefix_len; discount_object *dobj; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prefix, &prefix_len) == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 0 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (mkd_is_compiled(dobj->markdoc)) { zend_throw_exception_ex(spl_ce_LogicException, 0 TSRMLS_CC, "Invalid state: the markdown document has already been compiled"); RETURN_FALSE; } if (dobj->ref_prefix != NULL) { efree(dobj->ref_prefix); } dobj->ref_prefix = estrndup(prefix, prefix_len); mkd_ref_prefix(dobj->markdoc, dobj->ref_prefix); RETURN_TRUE; } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: effd5175b0f9ea7c94828aab28817e6a4984d9c5 $ */ PHP_METHOD(markdowndoc, compile); PHP_METHOD(markdowndoc, isCompiled); PHP_METHOD(markdowndoc, dumpTree); PHP_METHOD(markdowndoc, transformFragment); PHP_METHOD(markdowndoc, writeFragment); PHP_METHOD(markdowndoc, setReferencePrefix); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 475b0296002da90ac4d35e50ebbff8235c1587c9 $ */ #include #include #include #include "lib/mkdio.h" #include "php_discount.h" #include "markdowndoc_class.h" #include "markdowndoc_meth_parts.h" /* {{{ proto string MarkdownDocument::getToc() */ PHP_METHOD(markdowndoc, getToc) { discount_object *dobj; char *data = NULL; int status; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } status = mkd_toc(dobj->markdoc, &data); if (status < 0) { /* no doc->ctx, shouldn't happen */ zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Call to library function mkd_toc() failed (should not happen!)"); RETURN_FALSE; } /* status == 0 can indicate either empty string or no MKD_TOC, we * must use data to disambiguate */ if (data == NULL) { RETURN_FALSE; /* no MKD_TOC */ } /* empty string included in general case */ RETURN_STRINGL(data, status, 0); } /* }}} */ /* {{{ proto string MarkdownDocument::getCss() */ PHP_METHOD(markdowndoc, getCss) { discount_object *dobj; char *data = NULL; int status; if (zend_parse_parameters_none() == FAILURE) { RETURN_FALSE; } if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } status = mkd_css(dobj->markdoc, &data); if (status < 0) { /* should never happen, but... */ zend_throw_exception_ex(spl_ce_RuntimeException, 0 TSRMLS_CC, "Call to library function mkd_css() failed (should not happen!)"); RETURN_FALSE; } assert(data != NULL); RETURN_STRINGL(data, status, 0); } /* }}} */ /* {{{ proto bool MarkdownDocument::writeToc(mixed $out_stream) */ PHP_METHOD(markdowndoc, writeToc) { discount_object *dobj; zval *zstream; php_stream *stream; int close; FILE *f; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) == FAILURE) { RETURN_FALSE; } /* no compilation required */ if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_generatetoc(dobj->markdoc, f); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (markdown_handle_io_error(status, "mkd_generatetoc" TSRMLS_CC) == FAILURE) { RETURN_FALSE; } RETURN_BOOL(status == 1); /* 1 for no data; 0 for no MKD_TOC */ } /* }}} */ /* {{{ proto bool MarkdownDocument::writecSS(mixed $out_stream) */ PHP_METHOD(markdowndoc, writeCss) { discount_object *dobj; zval *zstream; php_stream *stream; int close; FILE *f; int status; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zstream) == FAILURE) { RETURN_FALSE; } /* no compilation required */ if ((dobj = markdowndoc_get_object(getThis(), 1 TSRMLS_CC)) == NULL) { RETURN_FALSE; } if (markdowndoc_get_file(zstream, 1, &stream, &close, &f TSRMLS_CC) == FAILURE) { RETURN_FALSE; } status = mkd_generatecss(dobj->markdoc, f); markdown_sync_stream_and_file(stream, close, f TSRMLS_CC); if (markdown_handle_io_error(status, "mkd_generatecss" TSRMLS_CC) == FAILURE) { RETURN_FALSE; } RETURN_TRUE; } /* }}} */ /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: effbfe0d87b6e8dc548e1259d3a1c1f263782ebd $ */ PHP_METHOD(markdowndoc, getToc); PHP_METHOD(markdowndoc, getCss); PHP_METHOD(markdowndoc, writeToc); PHP_METHOD(markdowndoc, writeCss); /* * Copyright (c) 2012, Gustavo Lopes * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * The names of its contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* $Id: 7be1fc57105752480e95a404af833839cbd8fd6b $ */ #ifndef PHP_DISCOUNT_H #define PHP_DISCOUNT_H extern zend_module_entry discount_module_entry; #define phpext_discount_ptr &discount_module_entry #define PHP_DISCOUNT_VERSION "1.0.0" #ifdef PHP_WIN32 #define PHP_DISCOUNT_API __declspec(dllexport) #else #define PHP_DISCOUNT_API #endif #ifdef ZTS #include "TSRM.h" #endif #ifdef DISCOUNT_GLOBALS ZEND_BEGIN_MODULE_GLOBALS(discount) void *dummy; ZEND_END_MODULE_GLOBALS(discount) ZEND_EXTERN_MODULE_GLOBALS(discount); #ifdef ZTS # define DISCOUNT_G(v) TSRMG(discount_globals_id, zend_discount_globals *, v) #else # define DISCOUNT_G(v) (discount_globals.v) #endif #endif /* PHP 5.2 compatibility */ #if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 #define zend_parse_parameters_none() \ zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") #define Z_DELREF_P ZVAL_DELREF #define Z_ADDREF_P ZVAL_ADDREF #define STREAM_ASSUME_REALPATH 0 #define ALLOC_PERMANENT_ZVAL(z) \ (z) = (zval*) malloc(sizeof(zval)); #undef ZEND_BEGIN_ARG_INFO_EX #define ZEND_BEGIN_ARG_INFO_EX(name, pass_rest_by_reference, return_reference, required_num_args) \ static const zend_arg_info name[] = { \ { NULL, 0, NULL, 0, 0, 0, pass_rest_by_reference, return_reference, required_num_args }, #endif /* discount.c */ PHP_MINIT_FUNCTION(discount); PHP_MSHUTDOWN_FUNCTION(discount); PHP_RINIT_FUNCTION(discount); PHP_RSHUTDOWN_FUNCTION(discount); PHP_MINFO_FUNCTION(discount); #endif /* PHP_DISCOUNT_H */ Processes the Markdown language with the bundled discount library. Markdown is a lightweight markup language created by John Gruber. It is also the name of the original tool written in Perl that converts such markup into HTML. This library is a wrapper for a modified version of the discount Markdown processor, created by David Parsons. It supports several extensions of the original Markdown language, including smartypants-style substitutions, pandoc- style document headers, and parts of Markdown Extra. The modifications to discount include discarding several parts important only to stand-alone applications that accompany the library code, better consistency in library interface functions, thread-safety without eager initialization of globals, usage of the Zend memory manager and support for Microsoft Windows. Discount is available at . The page of the original Markdown is available at . ;; discount Gustavo Lopes [cataphract] (lead) Pierre-Alain Joye [pajoye] (lead) *
  • Return FALSE if there is a wrong number of arguments or if * the type of the argument is unexpected and PHP does not provide an automatic * conversion to the required type.
  • *
  • Throw LogicException if the method is an instance method and, * except for {@link MarkdownDocument::initFromStream()} and * {@link MarkdownDocument::initFromString()}, is used without the document * having been initialized. This can only happen in subclasses whose constructor * doesn't call either {@link MarkdownDocument::initFromStream()} or * {@link MarkdownDocument::initFromString()} (or calls to them fail but the * constructor doesn't abort the object construction with an exception).
  • *
  • Throw LogicException if the method is called from within a * callback provided to {@link MarkdownDocument::setUrlCallback()} or * {@link MarkdownDocument::setAttributeCallback()}.
  • *
  • Throw LogicException if the method requires the Markdown * document to be compiled, but it isn't (or not to be compiled, but it * is).
  • * * * The methods that take a stream can instead take a URL from which such * stream can be opened, but beware that if the method is to write a result, * the stream will be opened with mode 'w', most likely discarding * the previous contents of the file. * * Explaining the Markdown syntax is beyond the scope of this documentation. * For that purpose, you can consult the * {@link http://daringfireball.net/projects/markdown/syntax syntax of the * original Markdown}. Discount also implements * {@link http://www.pell.portland.or.us/~orc/Code/discount/#Language.extensions * several extensions}, which you can deactivate with great granularity by using * the flags described in this document. * * @package Discount * @author Gustavo Lopes * @example simple_usage.php A simple example of the usage of this class. * @example subclassing.php Simple subclassing to simplify usage. * @example subclassing_2.php Subclassing example with table of contents & CSS. * @link http://pecl.php.net/markdown Project homepage * @link http://cataphract.github.com/php-discount/ Git development repository * @link http://www.pell.portland.or.us/~orc/kbd/discount/ Discount library * @since 0.1.0 */ class MarkdownDocument { /** * A compile flag that disables processing the markdown elements that would * otherwise create HTML links (like in * [text](http://www.example.com)); * additionally, it escapes any A (anchor) element it finds. * @var int Compile flag to forbid links (HTML anchors). * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::SAFELINK * @see MarkdownDocument::NO_EXT * @see MarkdownDocument::AUTOLINK * @since 0.1.0 */ const NOLINKS = 1; /** * A compile flag that disables processing the markdown elements that would * otherwise create HTML images (like in * ![alt text](http://www.example.com/myimg.jpg "title")); * additionally, it escapes any IMG element it finds. * @var int Compile flag to forbid images (HTML img element) * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ const NOIMAGE = 2; /** * A compile flag that deactivates smartypants substitutions such as turning * (tm) into , don't into * don’t, among others (see the example). * @var int Compile flag to deactivate smartypants substitutions. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @link http://daringfireball.net/projects/smartypants/ Page for original * SmartyPants project * @example smartypants.php Smartypants-style substitutions in action * @since 0.1.0 */ const NOPANTS = 4; /** * A compile flag that disables all literal HTML present in the input by * encoding all the tags. * @var int Compile flag to deactivate literal HTML. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ const NOHTML = 8; /** * A compile flags that has the effects of approximating the behavior of * discount to that of the original Markdown, by disabling several * extensions. * * In particular, it has the effect of combining * {@link MarkdownDocument::NOSUPERSCRIPT}, * {@link MarkdownDocument::NORELAXED}, * {@link MarkdownDocument::NOSTRIKETHROUGH}, * {@link MarkdownDocument::NODLIST}, * {@link MarkdownDocument::NOALPHALIST}, * {@link MarkdownDocument::NODIVQUOTE} and, * {@link MarkdownDocument::NOTABLES}, * @var into Compile flag to disable most discount extensions and * increase compatibility with the original Markdown. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ const STRICT = 16; /** * A compile flag that aims to process attribute-safe fragments. * * This flag disables [] expansion for images or links, * (including literal HTML for them) smarty pants, ticks, autolink (even * with {@link MarkdownDocument::AUTOLINK}), emphasis; it also transforms > * into > and " into "e;. Its purpose is to transform text * that could go into tag attributes. Best used with * {@link MarkdownDocument::transformFragment()} or * {@link MarkdownDocument::writeFragment()}, as many substitutions done * by {@link MarkdownDocument::compile()} + * {@link MarkdownDocument::getHtml()} are not covered. * @var int Flag to make fragments more attribute-safe. * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ const TAGTEXT = 32; /** * A compile flag that disables the use of pseudo-protocols for links. * Pseudo-protocols are link protocols that result in HTML elements other * than anchors being generated. They are a discount extension. See the * example for more details. * * @var int Compile flag to disable the use of pseudo-protocols. * @example pseudoprotos.php Pseudo-protocols in action. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::NOLINKS * @see MarkdownDocument::SAFELINK * @since 0.1.0 */ const NO_EXT = 64; /** * A compile flag that, for {@link MarkdownDocument::writeFragment()} and * {@link MarkdownDocument::writeHtml()}, causes any < , * >, &, " and ' characters * in the final output to be converted to their corresponding XML entities. * * @var int Compile flag to enable conversion into basic XML entities. * @see MarkdownDocument::compile() * @see MarkdownDocument::writeHtml() * @see MarkdownDocument::writeFragment() * @since 0.1.0 */ const CDATA = 128; /** * A compile flag that deactivates the conversion of superscripts expressed * with ^, as in A^B or A^(BC) (a discount * extension). * * SUP tags can still be included literally (but see * {@link MarkdownDocument::NOHTML}). * * @var int Compile flag to disable superscripts. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::STRICT * @since 0.1.0 */ const NOSUPERSCRIPT = 256; /** * A compile flag that forces discount to substitute with EM elements all * the pairs of underscores it finds. In normal circumstances, discount will * ignore underscores that are surrounded by alphanumeric characters. * * For instance, this flag forces emphasis in c d for the input * string ab_c d_2, which would otherwise not happen. * * The "relaxed" parsing of underscores is a discount extension borrowed * from Markdown extra; this flag forces compliant behavior. * * @var int Compile flag to disable relaxed parsing of underscores. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::STRICT * @link http://michelf.com/projects/php-markdown/extra/#em Markdown extra * description of the relaxed emphasis rules. * @since 0.1.0 */ const NORELAXED = 512; /** * A compile flag that disables the parsing of tables. Tables are a discount * extension borrowed by Markdown Extra. * * Tables can still be written with literal HTML (but see * {@link MarkdownDocument::NOHTML}). * * @var int Compile flag to deactivate Markdown Extra tables. * @see MarkdownDocument::STRICT * @see MarkdownDocument::compile() * @link http://michelf.com/projects/php-markdown/extra/#table Syntax for * Markdown Extra tables. * @since 0.1.0 */ const NOTABLES = 1024; /** * A compile flag that deactivates the conversion of striken-through text, * as in ~~text~~, to be converted to text. * This conversion is a discount extension. * * The DEL element can still be introduced with literal HTML * (but see {@link MarkdownDocument::NOHTML}). * * @var int Compile flag to disable strike-through. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::STRICT * @since 0.1.0 */ const NOSTRIKETHROUGH = 2048; /** * A compile flag that forces a table of contents to be generated and the * headings to have attributed an id. * * The generated table of contents can then be retrieved with * {@link MarkdownDocument::getToc()} or written with * {@link MarkdownDocument::writeToc()}. * * @var int Compile flag to activate table of contents generation. * @see MarkdownDocument::compile() * @see MarkdownDocument::getToc() * @see MarkdownDocument::writeToc() * @since 0.1.0 */ const TOC = 4096; /** * A compile flag that applies some compatibility quirks in order to force * discount passing some compatibility tests. * * Particularly, it makes: *
      *
    1. the first line of every block has trailing whitespace trimmed * off;
    2. *
    3. require second [] for links/images instead of using label as key in * the absence of it;
    4. *
    5. more lax algorithm if content of []() link/image starts with <.
    6. *
    * * @var int Compile flag to turn on compatibility quirks. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ const ONE_COMPAT = 8192; /** * A compile flag that turns every found URL into a link. * * In general, for a URL to linkified, it needs < and > around it. This flag * drops that requirement and linkifies all the recognized URLs. * * @var int Compile flag to linkify every URL. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::NOLINKS * @since 0.1.0 */ const AUTOLINK = 16384; /** * A compile flag that limits generated links to certain protocols. * * The allowed protocols are http, https, * news and ftp. Links starting with /, but * not relative links, are also allowed. * * Pseudo-protocols are also deactivated, there is no need to include * {@link MarkdownDocument::NO_EXT}. * * Links with arbitrary destinations are still allowed with literal HTML, * but see {@link MarkdownDocument::NOHTML}. * * @var int Compile flag to generate links only for some protocols. * @see MarkdownDocument::compile() * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::NOLINKS * @see MarkdownDocument::NO_EXT * @since 0.1.0 */ const SAFELINK = 32768; /** * An input flag that deactivates parsing of pandoc-style headers. * * Note that Markdown does not currently support multi-line headers. * * @var int Input flag to process pandoc-style headers. * @see MarkdownDocument::createFromString() * @see MarkdownDocument::createFromStream() * @see MarkdownDocument::initFromStream() * @see MarkdownDocument::initFromString() * @example pandoc_headers.php Pandoc headers in action. * @link http://johnmacfarlane.net/pandoc/README.html#title-blocks * Documentation on pandoc-style headers. * @since 0.1.0 */ const NOHEADER = 65536; /** * An input flag to treat tab stops as 4 spaces � has no effect unless the * extension was compiled with a different tab stop. * * The default tab stop is 4 spaces, so this flag usually has no effect. * * @var int Input flag to force tab stops to be 4 spaces longs, even when * discount was compiled otherwise. * @since 0.1.0 */ const TABSTOP = 131072; /** * Compile flag that deactivates turning certain special block quotes into * DIV elements with a certain class. * * In discount, blockquotes whose first line has the form * > %classname% will be transformed into a DIV * element with the indicated class instead of a BLOCKQUOTE * element. This is a discount extension that can be deactivated with this * class. * * @var int Compile flag to deactivate turning certain quoted blocks into * DIV elements with an arbitrary class. * @see MarkdownDocument::compile() * @see MarkdownDocument::STRICT * @since 0.1.0 */ const NODIVQUOTE = 262144; /** * Compile flag that deactivates alphabetically ordered lists. These lists * are a discount extension. * * @var int Compile flag to deactivate alphabetically ordered lists. * @see MarkdownDocument::compile() * @see MarkdownDocument::STRICT * @since 0.1.0 */ const NOALPHALIST = 524288; /** * Compile flag that deactivates definition lists, either discount-style or * Markdown-extra style. * * @var int Compile flag to deactivate definition lists. These lists are a * discount extension. * * @see MarkdownDocument::STRICT * @see MarkdownDocument::compile() * @example definition_list.php Definitions list and NODLIST. * @since 0.1.0 */ const NODLIST = 1048576; /** * A combination of the compile flags {@link MarkdownDocument::NOLINKS}, * {@link MarkdownDocument::NOIMAGE} and {@link MarkdownDocument::TAGTEXT}. * Effectively, the same effect as {@link MarkdownDocument::TAGTEXT}. * * It's unclear why discount includes this flag since the effects of * {@link MarkdownDocument::NOLINKS} and {@link MarkdownDocument::NOIMAGE} * are included in those of {@link MarkdownDocument::TAGTEXT}. * * @var int Combination of compile flags with the same effect as * {@link MarkdownDocument::TAGTEXT}. * @see MarkdownDocument::transformFragment() * @see MarkdownDocument::TAGTEXT * @see MarkdownDocument::NOIMAGE * @see MarkdownDocument::NOLINKS * @since 0.1.0 */ const EMBED = 35; /** * Compile flag that enables the use of PHP Markdown Extra-style footnotes. * * @var int Compile flag that enables footnotes. Footnotes are a discount * extension borrowed from Markdown Extra. * * @link http://michelf.com/projects/php-markdown/extra/#footnotes Syntax * for Markdown Extra footnotes * @see MarkdownDocument::compile() * @since 1.0.0 */ const EXTRA_FOOTNOTE = 2097152; /** * Creates a {@link MarkdownDocument} from a stream. * * This is one of the two public methods available for creating an object of * this type, which is required as an initial step for full markdown * processing. * * @param mixed $markdown_stream Either a stream resource opened with * reading permissions or a URL from which such a stream can be opened. * @param integer $flags Data input type flags. Only the flags * {@link MarkdownDocument::NOHEADER} and {@link MarkdownDocument::TABSTOP} * are allowed. * @return MarkdownDocument An object of the type of this class, created * from data read from the specified stream. * @see MarkdownDocument::createFromString() * @see MarkdownDocument::initFromStream() * @since 0.1.0 */ static public function createFromStream($markdown_stream, $flags = 0) {} /** * Creates a {@link MarkdownDocument} from a string. * * This is one of the two public methods available for creating an object of * this type, which is required as an initial step for full markdown * processing. * * @param mixed $markdown_doc A string containing the document expressed * in the markdown markup language. * @param integer $flags Data input type flags. Only the flags * {@link MarkdownDocument::NOHEADER} and {@link MarkdownDocument::TABSTOP} * are allowed. * @return MarkdownDocument An object of the type of this class, created * from data read from the specified string. * @see MarkdownDocument::createFromStream() * @see MarkdownDocument::initFromString() * @since 0.1.0 */ static public function createFromString($markdown_doc, $flags = 0) {} /** * Reads markdown from a string and transforms it to HTML without creating * any block elements. * * In this form (inline markdown), the transformations made are much more * limited than with the combination of * {@link MarkdownDocument::createFromString()}, * {@link MarkdownDocument::compile()} and * {@link MarkdownDocument::getHtml()}. No paragraph tags are added and no * tables, block quotes, code blocks, pandoc headers or reference-style * links/images or lists are processed. * * Arbitrary HTML is still allowed, unless the * {@link MarkdownDocument::NOHTML} flag is given. * * @param string $markdown_fragment The markdown fragment to convert. * @param integer $flags Compile flags appropriate for inline markdown * (several features are disabled in inline markdown, so many flags have * no effect). * @return string A string with the HTML resulting from transforming the * markdown fragment. * @see MarkdownDocument::writeFragment() * @since 0.1.0 */ static public function transformFragment($markdown_fragment, $flags = 0) {} /** * Reads markdown from a string, transforms it to HTML without creating * any block elements and writes the result to a stream. * * In this form (inline markdown), the transformations made are much more * limited than with the combination of * {@link MarkdownDocument::createFromString()}, * {@link MarkdownDocument::compile()} and * {@link MarkdownDocument::writeHtml()}. No paragraph tags are added and no * tables, block quotes, code blocks, pandoc headers or reference-style * links/images or lists are processed. * * Arbitrary HTML is still allowed, unless the * {@link MarkdownDocument::NOHTML} flag is given. * * @param string $markdown_fragment The markdown fragment to convert. * @param mixed $out_stream A resource stream that can be written to or a * URL with which a stream with writing permissions can be opened. * @param integer $flags Compile flags appropriate for inline markdown * (several features are disabled in inline markdown, so many flags have * no effect). * @return boolean TRUE if all the data is successfully written; * if there is an error when writing to the stream, FALSE is * returned and a warning is raised. The usual error handling for * {@link MarkdownDocument} objects still apply. * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ static public function writeFragment($markdown_fragment, $out_stream, $flags = 0) {} /** * A no-op protected constructor. * * This method prevents instantiation of the {@link MarkdownDocument} * class with new, forcing the usage of * {@link MarkdownDocument::createFromString()} or * {@link MarkdownDocument::createFromStream()}. * * It is protected to, nevertheless, allow subclassing in a straightforward * manner by overriding this constructor. The overriding constructor need * not call this method, as it is a no-op. It should, however, call either * the {@link MarkdownDocument::initFromString()} or * {@link MarkdownDocument::initFromStream()}. * * @see MarkdownDocument::createFromString() * @see MarkdownDocument::createFromStream() * @see MarkdownDocument::initFromString() * @see MarkdownDocument::initFromStream() * @since 0.1.0 */ protected function __construct() {} /** * Initialize the native part of the object using an input stream. * * This method reads markdown data from the passed stream and initializes * the native discount data structure. It should be called in the * constructor of subclasses; if not, it should at least be called before * any of the {@link MarkdownDocument} methods be called. Alternatively, * {@link MarkdownDocument::initFromString()} can be called instead. * * @param mixed $markdown_stream Either a stream resource opened with * reading permissions or a URL from which such a stream can be opened. * @param integer $flags Data input type flags. Only the flags * {@link MarkdownDocument::NOHEADER} and {@link MarkdownDocument::TABSTOP} * are allowed. * @return boolean TRUE unless one of the usual error * conditions for the {@link MarkdownDocument} class is triggered. * @throws LogicException If the object has already been initialized. * @see MarkdownDocument::initFromString() * @see MarkdownDocument::createFromStream() * @since 0.1.0 */ final protected function initFromStream ($markdown_stream, $flags = 0) {} /** * Initialize the native part of the object using a string. * * This method reads markdown data from the passed string and initializes * the native discount data structure. It should be called in the * constructor of subclasses; if not, it should at least be called before * any of the {@link MarkdownDocument} methods be called. Alternatively, * {@link MarkdownDocument::initFromStream()} can be called instead. * * @param mixed $markdown_doc Either a stream resource opened with * reading permissions or a URL from which such a stream can be opened. * @param integer $flags Data input type flags. Only the flags * {@link MarkdownDocument::NOHEADER} and {@link MarkdownDocument::TABSTOP} * are allowed. * @return boolean TRUE unless one of the usual error * conditions for the {@link MarkdownDocument} class is triggered. * @throws LogicException if the object has already been initialized. * @see MarkdownDocument::initFromString() * @since 0.1.0 */ final protected function initFromString($markdown_doc, $flags = 0) {} /** * Compiles this document, preparing the HTML data to be retrieved. * * * @param integer $flags A combination of the compile flags, i.e., all but * {@link MarkdownDocument::NOHEADER} and {@link MarkdownDocument::TABSTOP}. * @return boolean TRUE unless one of the usual error * conditions for the {@link MarkdownDocument} class is triggered. * @throws LogicException if the object has already been compiled. * @since 0.1.0 * */ public function compile($flags) {} /** * Tells whether this document has already been compiled. * * This method will return TRUE if * {@link MarkdownDocument::compile()} has already been successfully called * and FALSE otherwise. * * @return boolean Whether the document has already been compiled. * @see MarkdownDocument::compile() * @since 0.1.0 */ public function isCompiled() {} /** * Write an outline view of the document. * * @param mixed $out_stream A resource stream opened with write permissions * or a URL that can be opened to yield such a stream, for writing the * output. * @param string $title A string to be prefixed to the tree. * @return boolean Returns TRUE, except for the situations covered * in the class summary. * @example dump_tree.php dumpTree() in action. * @since 0.1.0 */ public function dumpTree($out_stream, $title = "") {} /** * The title found in the pandoc-style header. * * Note that discount does not currently support multi-line titles. * * The document need not have been compiled. * * @example pandoc_headers.php getTitle() in action. * @link http://johnmacfarlane.net/pandoc/README.html#title-blocks * Documentation on pandoc-style headers. * @return string The title found in the pandoc-style header. An empty * string is returned if no title was found or if * {@link MarkdownDocument::NOHEADER} was used. * @see MarkdownDocument::getAuthor() * @see MarkdownDocument::getDate() * @see MarkdownDocument::NOHEADER * @since 0.1.0 */ public function getTitle() {} /** * The author or authors found in the pandoc-style hader. * * Note that discount does not currently support multi-line authors. * * The document need not have been compiled. * * @link http://johnmacfarlane.net/pandoc/README.html#title-blocks * Documentation on pandoc-style headers. * @return string The authors found in the pandoc-style header. An empty * string is returned if no authors were found or if * {@link MarkdownDocument::NOHEADER} was used. * @see MarkdownDocument::getTitle() * @see MarkdownDocument::getDate() * @see MarkdownDocument::NOHEADER * @since 0.1.0 */ public function getAuthor() {} /** * The date found in the pandoc-style header. * * The document need not have been compiled. * * @link http://johnmacfarlane.net/pandoc/README.html#title-blocks * Documentation on pandoc-style headers. * @return string The date found in the pandoc-style header. An empty * string is returned if no date was found or if * {@link MarkdownDocument::NOHEADER} was used. * @see MarkdownDocument::getTitle() * @see MarkdownDocument::getAuthor() * @see MarkdownDocument::NOHEADER * @since 0.1.0 */ public function getDate() {} /** * Generate and return the body HTML data that results from processing this * document. * * This includes all the data present in the given markup with the exception * of the pandoc-style headers and the style blocks. The unprocessed headers * will, however, show up if {@link MarkdownDocument::NOHEADER} is given, * and the escaped style blocks will show up if * {@link MarkdownDocument::NOHTML} is given. * * The document should already have been compiled. * * If defined, this method calls the callbacks specified through * {@link MarkdownDocument::setUrlCallback()} and * {@link MarkdownDocument::setAttributesCallback()} for each link * generated. If any of these callbacks throws an exception, this method * throws another exception on top of it on the first time. * * @return string The body of the final HTML that results from processing * this document. * @see MarkdownDocument::writeHtml() * @see MarkdownDocument::writeXhtmlPage() * @see MarkdownDocument::transformFragment() * @since 0.1.0 */ public function getHtml() {} /** * Writes the HTML contents of the body of the processed markup document. * * This includes all the data present in the given markup with the exception * of the pandoc-style headers and the style blocks. The unprocessed headers * will, however, show up if {@link MarkdownDocument::NOHEADER} is given, * and the escaped style blocks will show up if * {@link MarkdownDocument::NOHTML} is given. * * The document should already have been compiled. * * If defined, this method calls the callbacks specified through * {@link MarkdownDocument::setUrlCallback()} and * {@link MarkdownDocument::setAttributesCallback()} for each link * generated. If any of these callbacks throws an exception, this method * throws another exception on top of it on the first time. * * @param mixed $markdown_outstream A resource stream opened with write * permissions or a URL that can be opened to yield such a stream, for * writing the output. * @return boolean TRUE on normal conditions or * FALSE and raises a warning if there was an I/O error when * writing to the given stream (or to a successfully opened stream, if a URL * was provided instead). The usual error handling described in the class * synopsis still applies. * @see MarkdownDocument::compile() * @see MarkdownDocument::getHtml() * @see MarkdownDocument::writeCss() * @see MarkdownDocument::writeXhtmlPage() * @since 0.1.0 */ public function writeHtml($markdown_outstream) {} /** * Writes a complete XHTML page. * * The style blocks found in the markdown data are put in the * HEAD element, the TITLE element is built from the * title extracted from the pandoc-style header and the * BODY is composed by what * {@link MarkdownDocument::writeHtml()} would return. * * The document should already have been compiled. * * @param mixed $markdown_outstream A resource stream opened with write * permissions or a URL that can be opened to yield such a stream. * @return boolean TRUE on normal conditions or * FALSE and raises a warning if there was an I/O error when * writing to the given stream or to a successfully opened stream, if a URL * was provided instead. * @see MarkdownDocument::writeHtml() * @see MarkdownDocument::writeCss() * @see MarkdownDocument::getTitle() * @since 0.1.0 */ public function writeXhtmlPage($markdown_outstream) {} /** * Returns the table of contents HTML data. * * This method must be called after the document has been compiled with * {@link MarkdownDocument::TOC}. * * @return string the table of contents HTML data or FALSE if * {@link MarkdownDocument::TOC} was not given to * {@link MarkdownDocument::compile()}. * @see MarkdownDocument::TOC * @since 0.1.0 */ public function getToc() {} /** * Give all the