]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #9499 : brson/rust/relnotes, r=cmr
authorbors <bors@rust-lang.org>
Thu, 26 Sep 2013 02:50:56 +0000 (19:50 -0700)
committerbors <bors@rust-lang.org>
Thu, 26 Sep 2013 02:50:56 +0000 (19:50 -0700)
121 files changed:
.gitattributes
configure
mk/rt.mk
mk/tests.mk
src/etc/pkg/rust-logo.ico
src/libextra/arc.rs
src/libextra/base64.rs
src/libextra/crypto/cryptoutil.rs
src/libextra/crypto/digest.rs
src/libextra/extra.rs
src/libextra/flatpipes.rs
src/libextra/future.rs
src/libextra/getopts.rs
src/libextra/glob.rs
src/libextra/hex.rs
src/libextra/sync.rs
src/libextra/uuid.rs
src/librustc/middle/resolve.rs
src/librustc/middle/typeck/infer/doc.rs
src/librustdoc/clean.rs
src/librustdoc/doctree.rs
src/librustdoc/html/format.rs
src/librustdoc/html/markdown.rs
src/librustdoc/html/render.rs
src/librustdoc/html/static/main.css
src/librustdoc/passes.rs
src/librustdoc/rustdoc.rs
src/librustdoc/visit_ast.rs
src/librustpkg/package_source.rs
src/librustpkg/path_util.rs
src/librustpkg/tests.rs
src/librustpkg/testsuite/pass/src/fancy-lib/foo.rs
src/librustpkg/testsuite/pass/src/install-paths/bench.rs
src/librustpkg/testsuite/pass/src/simple-lib/src/foo.rs
src/librustpkg/util.rs
src/libstd/bool.rs
src/libstd/c_str.rs
src/libstd/cast.rs
src/libstd/condition.rs
src/libstd/fmt/mod.rs
src/libstd/io.rs
src/libstd/iter.rs
src/libstd/local_data.rs
src/libstd/num/f32.rs
src/libstd/num/f64.rs
src/libstd/num/float.rs
src/libstd/num/int_macros.rs
src/libstd/num/num.rs
src/libstd/option.rs
src/libstd/path.rs
src/libstd/prelude.rs
src/libstd/rand/distributions.rs
src/libstd/rand/mod.rs
src/libstd/rt/io/buffered.rs
src/libstd/rt/io/file.rs
src/libstd/rt/io/mock.rs
src/libstd/rt/kill.rs
src/libstd/std.rs
src/libstd/str.rs
src/libstd/task/mod.rs
src/libstd/tuple.rs
src/libstd/unstable/finally.rs
src/libstd/vec.rs
src/libsyntax/ext/deriving/generic.rs
src/libsyntax/ext/expand.rs
src/libsyntax/parse/comments.rs
src/rt/rustrt.def.in
src/rt/sundown/.gitignore [new file with mode: 0644]
src/rt/sundown/CONTRIBUTING.md [new file with mode: 0644]
src/rt/sundown/Makefile [new file with mode: 0644]
src/rt/sundown/Makefile.win [new file with mode: 0644]
src/rt/sundown/README.markdown [new file with mode: 0644]
src/rt/sundown/examples/smartypants.c [new file with mode: 0644]
src/rt/sundown/examples/sundown.c [new file with mode: 0644]
src/rt/sundown/html/houdini.h [new file with mode: 0644]
src/rt/sundown/html/houdini_href_e.c [new file with mode: 0644]
src/rt/sundown/html/houdini_html_e.c [new file with mode: 0644]
src/rt/sundown/html/html.c [new file with mode: 0755]
src/rt/sundown/html/html.h [new file with mode: 0644]
src/rt/sundown/html/html_smartypants.c [new file with mode: 0644]
src/rt/sundown/html_block_names.txt [new file with mode: 0644]
src/rt/sundown/src/autolink.c [new file with mode: 0644]
src/rt/sundown/src/autolink.h [new file with mode: 0644]
src/rt/sundown/src/buffer.c [new file with mode: 0644]
src/rt/sundown/src/buffer.h [new file with mode: 0644]
src/rt/sundown/src/html_blocks.h [new file with mode: 0644]
src/rt/sundown/src/markdown.c [new file with mode: 0644]
src/rt/sundown/src/markdown.h [new file with mode: 0644]
src/rt/sundown/src/stack.c [new file with mode: 0644]
src/rt/sundown/src/stack.h [new file with mode: 0644]
src/test/compile-fail/borrowck-anon-fields-struct.rs
src/test/compile-fail/borrowck-anon-fields-tuple.rs
src/test/compile-fail/borrowck-anon-fields-variant.rs
src/test/compile-fail/borrowck-bad-nested-calls-free.rs
src/test/compile-fail/borrowck-bad-nested-calls-move.rs
src/test/compile-fail/borrowck-move-in-irrefut-pat.rs
src/test/compile-fail/borrowck-move-mut-base-ptr.rs
src/test/compile-fail/borrowck-swap-mut-base-ptr.rs
src/test/compile-fail/cast-immutable-mutable-trait.rs
src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs
src/test/compile-fail/closure-bounds-not-builtin.rs
src/test/compile-fail/coherence_inherent.rs
src/test/compile-fail/coherence_inherent_cc.rs
src/test/compile-fail/deprecated-auto-code.rs
src/test/compile-fail/issue-2995.rs
src/test/compile-fail/issue-4736.rs
src/test/compile-fail/issue-6762.rs
src/test/compile-fail/lub-if.rs
src/test/compile-fail/lub-match.rs
src/test/compile-fail/main-wrong-location.rs
src/test/compile-fail/regions-free-region-ordering-callee.rs
src/test/compile-fail/regions-free-region-ordering-caller.rs
src/test/compile-fail/regions-ref-in-fn-arg.rs
src/test/compile-fail/suppressed-error.rs
src/test/compile-fail/tag-variant-disr-dup.rs
src/test/compile-fail/type-parameter-names.rs
src/test/run-pass/borrowck-scope-of-deref-issue-4666.rs
src/test/run-pass/deriving-via-extension-struct-empty.rs
src/test/run-pass/issue-4252.rs
src/test/run-pass/xc_conditions_client.rs
src/test/run-pass/xc_conditions_client_4.rs

index 39221d3928ee3c681eeb753107c6facf9ae918b3..57f8083a5568ab249f88315c2c4c3fbe6f2b92c9 100644 (file)
@@ -4,6 +4,7 @@
 *.cpp rust
 *.h rust
 *.rs rust
+src/etc/pkg/rust-logo.ico binary
 src/rt/msvc/* -whitespace
 src/rt/vg/* -whitespace
 src/rt/linenoise/* -whitespace
index 55f5afda1a0143cdb413e6fa839dd78ff5d97faa..70b2bb75a38fc57d34e5a3bd9d035aef483ffe9d 100755 (executable)
--- a/configure
+++ b/configure
@@ -684,7 +684,7 @@ do
       isaac linenoise sync test \
       arch/i386 arch/x86_64 arch/arm arch/mips  \
       libuv libuv/src/ares libuv/src/eio libuv/src/ev \
-      jemalloc
+      jemalloc sundown/src sundown/html
     do
       make_dir $t/rt/stage$s/$i
     done
index 1b818bfee415103d642f0c8a1c9f5bd5cd1644c9..fb4d8f7c734e84d7c39d152178a1a7f5abbcf345 100644 (file)
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -82,7 +82,16 @@ RUNTIME_CXXS_$(1)_$(2) := \
               rt/rust_android_dummy.cpp \
               rt/rust_test_helpers.cpp
 
-RUNTIME_CS_$(1)_$(2) := rt/linenoise/linenoise.c rt/linenoise/utf8.c
+RUNTIME_CS_$(1)_$(2) := rt/linenoise/linenoise.c \
+                       rt/linenoise/utf8.c \
+                       rt/sundown/src/autolink.c \
+                       rt/sundown/src/buffer.c \
+                       rt/sundown/src/stack.c \
+                       rt/sundown/src/markdown.c \
+                       rt/sundown/html/houdini_href_e.c \
+                       rt/sundown/html/houdini_html_e.c \
+                       rt/sundown/html/html_smartypants.c \
+                       rt/sundown/html/html.c
 
 RUNTIME_S_$(1)_$(2) := rt/arch/$$(HOST_$(1))/_context.S \
                        rt/arch/$$(HOST_$(1))/ccall.S \
@@ -117,6 +126,8 @@ RUNTIME_DEF_$(1)_$(2) := $$(RT_OUTPUT_DIR_$(1))/rustrt$$(CFG_DEF_SUFFIX_$(1))
 RUNTIME_INCS_$(1)_$(2) := -I $$(S)src/rt -I $$(S)src/rt/isaac -I $$(S)src/rt/uthash \
                      -I $$(S)src/rt/arch/$$(HOST_$(1)) \
                      -I $$(S)src/rt/linenoise \
+                     -I $$(S)src/rt/sundown/src \
+                     -I $$(S)src/rt/sundown/html \
                      -I $$(S)src/libuv/include
 RUNTIME_OBJS_$(1)_$(2) := $$(RUNTIME_CXXS_$(1)_$(2):rt/%.cpp=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \
                      $$(RUNTIME_CS_$(1)_$(2):rt/%.c=$$(RT_BUILD_DIR_$(1)_$(2))/%.o) \
index 9fe163e509b5e627abcee457c5bc7a3a0f05b7c6..be1d3ac057cd7bca6491c1561ab5a3c8c0473d67 100644 (file)
@@ -227,8 +227,9 @@ ALL_CS := $(wildcard $(S)src/rt/*.cpp \
                      $(S)src/rt/*/*/*.cpp \
                      $(S)src/rustllvm/*.cpp)
 ALL_CS := $(filter-out $(S)src/rt/miniz.cpp \
-                       $(S)src/rt/linenoise/linenoise.c \
-                       $(S)src/rt/linenoise/utf8.c \
+                      $(wildcard $(S)src/rt/linenoise/*.c) \
+                      $(wildcard $(S)src/rt/sundown/src/*.c) \
+                      $(wildcard $(S)src/rt/sundown/html/*.c) \
        ,$(ALL_CS))
 ALL_HS := $(wildcard $(S)src/rt/*.h \
                      $(S)src/rt/*/*.h \
@@ -241,6 +242,8 @@ ALL_HS := $(filter-out $(S)src/rt/vg/valgrind.h \
                        $(S)src/rt/msvc/inttypes.h \
                        $(S)src/rt/linenoise/linenoise.h \
                        $(S)src/rt/linenoise/utf8.h \
+                      $(wildcard $(S)src/rt/sundown/src/*.h) \
+                      $(wildcard $(S)src/rt/sundown/html/*.h) \
        ,$(ALL_HS))
 
 # Run the tidy script in multiple parts to avoid huge 'echo' commands
index 4de8f87ae269c423b08cab18c5e98b5c23496e87..4749b398c2d2c063219f8e5b5c99bf1547ec0d99 100644 (file)
Binary files a/src/etc/pkg/rust-logo.ico and b/src/etc/pkg/rust-logo.ico differ
index 28a067a782bc400976ef038c36da70bac94fcee8..be55af697c66acc6ec4dd51ae8afe188d3ca6a26 100644 (file)
@@ -17,7 +17,7 @@
  * In this example, a large vector of floats is shared between several tasks.
  * With simple pipes, without Arc, a copy would have to be made for each task.
  *
- * ~~~ {.rust}
+ * ```rust
  * extern mod std;
  * use extra::arc;
  * let numbers=vec::from_fn(100, |ind| (ind as float)*rand::random());
@@ -34,7 +34,7 @@
  *           // Work with the local numbers
  *       }
  *   }
- * ~~~
+ * ```
  */
 
 #[allow(missing_doc)];
@@ -440,7 +440,7 @@ pub fn read<U>(&self, blk: &fn(x: &T) -> U) -> U {
      *
      * # Example
      *
-     * ~~~ {.rust}
+     * ```rust
      * do arc.write_downgrade |mut write_token| {
      *     do write_token.write_cond |state, condvar| {
      *         ... exclusive access with mutable state ...
@@ -450,7 +450,7 @@ pub fn read<U>(&self, blk: &fn(x: &T) -> U) -> U {
      *         ... shared access with immutable state ...
      *     }
      * }
-     * ~~~
+     * ```
      */
     pub fn write_downgrade<U>(&self, blk: &fn(v: RWWriteMode<T>) -> U) -> U {
         unsafe {
index 191ecbe8d16b42969b9ce7e6ff5c50f95b7be28f..f26554c42f4f9894b78dba7372b488cf574d7364 100644 (file)
@@ -62,7 +62,7 @@ impl<'self> ToBase64 for &'self [u8] {
      *
      * # Example
      *
-     * ~~~ {.rust}
+     * ```rust
      * extern mod extra;
      * use extra::base64::{ToBase64, standard};
      *
@@ -70,7 +70,7 @@ impl<'self> ToBase64 for &'self [u8] {
      *     let str = [52,32].to_base64(standard);
      *     printfln!("%s", str);
      * }
-     * ~~~
+     * ```
      */
     fn to_base64(&self, config: Config) -> ~str {
         let bytes = match config.char_set {
@@ -170,7 +170,7 @@ impl<'self> FromBase64 for &'self str {
      *
      * This converts a string literal to base64 and back.
      *
-     * ~~~ {.rust}
+     * ```rust
      * extern mod extra;
      * use extra::base64::{ToBase64, FromBase64, standard};
      * use std::str;
@@ -183,7 +183,7 @@ impl<'self> FromBase64 for &'self str {
      *     let result_str = str::from_utf8(bytes);
      *     printfln!("%s", result_str);
      * }
-     * ~~~
+     * ```
      */
     fn from_base64(&self) -> Result<~[u8], ~str> {
         let mut r = ~[];
index 4eba3e13eea5985e3978bc190cd89c733bb1d5bf..3573bb55948f109b7f77df587572853b0fc17e74 100644 (file)
@@ -352,6 +352,7 @@ mod test {
 
     use cryptoutil::{add_bytes_to_bits, add_bytes_to_bits_tuple};
     use digest::Digest;
+    use hex::FromHex;
 
     /// Feed 1,000,000 'a's into the digest with varying input sizes and check that the result is
     /// correct.
@@ -372,8 +373,10 @@ pub fn test_digest_1million_random<D: Digest>(digest: &mut D, blocksize: uint, e
         }
 
         let result_str = digest.result_str();
+        let result_bytes = digest.result_bytes();
 
-        assert!(expected == result_str);
+        assert_eq!(expected, result_str.as_slice());
+        assert_eq!(expected.from_hex().unwrap(), result_bytes);
     }
 
     // A normal addition - no overflow occurs
index d2d6b540cff4cf50bc6b10f67974776823e02d28..85c256c47a3805c9728d2fb7bf22a8a684dd0d9a 100644 (file)
@@ -10,6 +10,8 @@
 
 use std::vec;
 
+use hex::ToHex;
+
 
 /**
  * The Digest trait specifies an interface common to digest functions, such as SHA-1 and the SHA-2
@@ -58,23 +60,20 @@ fn input_str(&mut self, input: &str) {
 
     /**
      * Convenience function that retrieves the result of a digest as a
-     * ~str in hexadecimal format.
+     * newly allocated vec of bytes.
      */
-    fn result_str(&mut self) -> ~str {
+    fn result_bytes(&mut self) -> ~[u8] {
         let mut buf = vec::from_elem((self.output_bits()+7)/8, 0u8);
         self.result(buf);
-        return to_hex(buf);
+        buf
     }
-}
 
-fn to_hex(rr: &[u8]) -> ~str {
-    let mut s = ~"";
-    for b in rr.iter() {
-        let hex = (*b as uint).to_str_radix(16u);
-        if hex.len() == 1 {
-            s.push_char('0');
-        }
-        s.push_str(hex);
+    /**
+     * Convenience function that retrieves the result of a digest as a
+     * ~str in hexadecimal format.
+     */
+    fn result_str(&mut self) -> ~str {
+        self.result_bytes().to_hex()
     }
-    return s;
 }
+
index fc4b2947d1b3402b6415c8abfc26e5958494e804..f55eff9360daec29421dba6ded9aed971405f528 100644 (file)
@@ -26,8 +26,7 @@
        url = "https://github.com/mozilla/rust/tree/master/src/libextra")];
 
 #[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
-      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
-      passes = "strip-hidden")];
+      html_favicon_url = "http://www.rust-lang.org/favicon.ico")];
 
 #[comment = "Rust extras"];
 #[license = "MIT/ASL2"];
index d102f6068f5613a49b2f85d9da80be798343dcf9..886a28ac9793f657478846e6aa700be4dc7534fa 100644 (file)
@@ -25,7 +25,7 @@
 
 This example sends boxed integers across tasks using serialization.
 
-~~~ {.rust}
+```rust
 let (port, chan) = serial::pipe_stream();
 
 do task::spawn || {
@@ -37,7 +37,7 @@
 for i in range(0, 10) {
     assert @i == port.recv()
 }
-~~~
+ ```
 
 # Safety Note
 
index 4e8bc37891dcb7597bf1047be7423a54270cf585..72c6db6fb72daa667fb8a828295dbe75c0f1342f 100644 (file)
  *
  * # Example
  *
- * ~~~ {.rust}
+ * ```rust
  * # fn fib(n: uint) -> uint {42};
  * # fn make_a_sandwich() {};
  * let mut delayed_fib = extra::future::spawn (|| fib(5000) );
  * make_a_sandwich();
  * printfln!("fib(5000) = %?", delayed_fib.get())
- * ~~~
+ * ```
  */
 
 #[allow(missing_doc)];
index 0116c5a1f66013df4f01744ea2f6dafaf30eb88f..f73c34224ee5fb0e9fadb4edde25393e01c407f7 100644 (file)
@@ -29,7 +29,7 @@
 //! that requires an input file to be specified, accepts an optional output
 //! file name following -o, and accepts both -h and --help as optional flags.
 //!
-//! ```
+//! ~~~{.rust}
 //! exter mod extra;
 //! use extra::getopts::*;
 //! use std::os;
@@ -75,7 +75,7 @@
 //!     };
 //!     do_work(input, output);
 //! }
-//! ```
+//! ~~~
 
 use std::cmp::Eq;
 use std::result::{Err, Ok};
index 72368aeff230ff3d02b59ccc7ce0145f36401d7a..76ac7c91832bd8acad8a1df455bff4c7deb5c98a 100644 (file)
@@ -51,18 +51,18 @@ pub struct GlobIterator {
  * Consider a directory `/media/pictures` containing only the files `kittens.jpg`,
  * `puppies.jpg` and `hamsters.gif`:
  *
- * ~~~ {.rust}
+ * ```rust
  * for path in glob("/media/pictures/*.jpg") {
  *     println(path.to_str());
  * }
- * ~~~
+ * ```
  *
  * The above code will print:
  *
- * ~~~
+ * ```
  * /media/pictures/kittens.jpg
  * /media/pictures/puppies.jpg
- * ~~~
+ * ```
  */
 pub fn glob(pattern: &str) -> GlobIterator {
     glob_with(pattern, MatchOptions::new())
@@ -270,11 +270,11 @@ pub fn escape(s: &str) -> ~str {
      *
      * # Example
      *
-     * ~~~ {.rust}
+     * ```rust
      * assert!(Pattern::new("c?t").matches("cat"));
      * assert!(Pattern::new("k[!e]tteh").matches("kitteh"));
      * assert!(Pattern::new("d*g").matches("doog"));
-     * ~~~
+     * ```
      */
     pub fn matches(&self, str: &str) -> bool {
         self.matches_with(str, MatchOptions::new())
@@ -492,13 +492,13 @@ impl MatchOptions {
      *
      * This function always returns this value:
      *
-     * ~~~ {.rust}
+     * ```rust
      * MatchOptions {
      *     case_sensitive: true,
      *     require_literal_separator: false.
      *     require_literal_leading_dot: false
      * }
-     * ~~~
+     * ```
      */
     pub fn new() -> MatchOptions {
         MatchOptions {
index b93b5fb43d445cdfd986ddd38db3ccc335c392b7..d5b89cafced9326c887576650218990750c35e1f 100644 (file)
@@ -27,7 +27,7 @@ impl<'self> ToHex for &'self [u8] {
      *
      * # Example
      *
-     * ~~~ {.rust}
+     * ```rust
      * extern mod extra;
      * use extra::hex::ToHex;
      *
@@ -35,7 +35,7 @@ impl<'self> ToHex for &'self [u8] {
      *     let str = [52,32].to_hex();
      *     printfln!("%s", str);
      * }
-     * ~~~
+     * ```
      */
     fn to_hex(&self) -> ~str {
         let mut v = vec::with_capacity(self.len() * 2);
@@ -70,7 +70,7 @@ impl<'self> FromHex for &'self str {
      *
      * This converts a string literal to hexadecimal and back.
      *
-     * ~~~ {.rust}
+     * ```rust
      * extern mod extra;
      * use extra::hex::{FromHex, ToHex};
      * use std::str;
@@ -83,7 +83,7 @@ impl<'self> FromHex for &'self str {
      *     let result_str = str::from_utf8(bytes);
      *     printfln!("%s", result_str);
      * }
-     * ~~~
+     * ```
      */
     fn from_hex(&self) -> Result<~[u8], ~str> {
         // This may be an overestimate if there is any whitespace
index 99235e3029cc43b663335838087c762bab3cfba2..31ae5e70a9942136ea5f42485c63a837f71c6a4e 100644 (file)
@@ -578,7 +578,7 @@ pub fn write_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U {
      *
      * # Example
      *
-     * ~~~ {.rust}
+     * ```rust
      * do lock.write_downgrade |mut write_token| {
      *     do write_token.write_cond |condvar| {
      *         ... exclusive access ...
@@ -588,7 +588,7 @@ pub fn write_cond<U>(&self, blk: &fn(c: &Condvar) -> U) -> U {
      *         ... shared access ...
      *     }
      * }
-     * ~~~
+     * ```
      */
     pub fn write_downgrade<U>(&self, blk: &fn(v: RWLockWriteMode) -> U) -> U {
         // Implementation slightly different from the slicker 'write's above.
index 8eff9a022995a7f193b3ddfad52afe1aa70f31a8..77f8edc10aa5c679d868be2ba1b07b4ee1cf00ad 100644 (file)
@@ -28,7 +28,7 @@
 
 To create a new random (V4) UUID and print it out in hexadecimal form:
 
-~~~ {.rust}
+```rust
 extern mod extra;
 use extra::uuid::Uuid;
 
@@ -36,7 +36,7 @@ fn main() {
     let uuid1 = Uuid::new_v4();
     println(uuid1.to_str());
 }
-~~~
+ ```
 
 # Strings
 
index 320baf3318164c5bfeb3113762fb8615fe82b344..f4772d7adc3d38a45efbe14d82badde8ed34270f 100644 (file)
@@ -2499,6 +2499,26 @@ fn get_binding(this: @mut Resolver,
         assert!(import_resolution.outstanding_references >= 1);
         import_resolution.outstanding_references -= 1;
 
+        // record what this import resolves to for later uses in documentation,
+        // this may resolve to either a value or a type, but for documentation
+        // purposes it's good enough to just favor one over the other.
+        match i.value_target {
+            Some(target) => {
+                self.def_map.insert(i.value_id,
+                                    target.bindings.value_def.get_ref().def);
+            }
+            None => {}
+        }
+        match i.type_target {
+            Some(target) => {
+                match target.bindings.type_def.get_ref().type_def {
+                    Some(def) => { self.def_map.insert(i.type_id, def); }
+                    None => {}
+                }
+            }
+            None => {}
+        }
+
         debug!("(resolving single import) successfully resolved import");
         return Success(());
     }
@@ -2626,6 +2646,14 @@ pub fn resolve_glob_import(@mut self,
             merge_import_resolution(name, name_bindings);
         }
 
+        // Record the destination of this import
+        match containing_module.def_id {
+            Some(did) => {
+                self.def_map.insert(id, DefMod(did));
+            }
+            None => {}
+        }
+
         debug!("(resolving glob import) successfully resolved import");
         return Success(());
     }
index 11bfbc637169e20a81a36397d493acc2c88592b9..539418dbcb2282c9aac244bba4526c693f468efc 100644 (file)
@@ -240,4 +240,4 @@ fn bar() {
 duplicated code between subtypes, GLB, and LUB computations.  See the
 section on "Type Combining" below for details.
 
-*/
\ No newline at end of file
+*/
index 97a599196e72fc55b57ba605775755a1bd6daf3b..ae7cbf76bcc83f85c560bdd4aabd998e374dbe60 100644 (file)
@@ -15,6 +15,7 @@
 
 use syntax;
 use syntax::ast;
+use syntax::ast_util;
 use syntax::attr::AttributeMethods;
 
 use std;
@@ -283,7 +284,7 @@ fn clean(&self) -> Item {
             attrs: self.attrs.clean(),
             source: self.span.clean(),
             id: self.self_id.clone(),
-            visibility: None,
+            visibility: self.vis.clean(),
             inner: MethodItem(Method {
                 generics: self.generics.clean(),
                 self_: self.explicit_self.clean(),
@@ -345,6 +346,7 @@ fn clean(&self) -> SelfTy {
 pub struct Function {
     decl: FnDecl,
     generics: Generics,
+    purity: ast::purity,
 }
 
 impl Clean<Item> for doctree::Function {
@@ -358,6 +360,7 @@ fn clean(&self) -> Item {
             inner: FunctionItem(Function {
                 decl: self.decl.clean(),
                 generics: self.generics.clean(),
+                purity: self.purity,
             }),
         }
     }
@@ -468,8 +471,7 @@ fn clean(&self) -> Item {
 
 impl Clean<Type> for ast::trait_ref {
     fn clean(&self) -> Type {
-        let t = Unresolved(self.path.clean(), None, self.ref_id);
-        resolve_type(&t)
+        resolve_type(self.path.clean(), None, self.ref_id)
     }
 }
 
@@ -514,9 +516,6 @@ fn clean(&self) -> TraitMethod {
 /// it does not preserve mutability or boxes.
 #[deriving(Clone, Encodable, Decodable)]
 pub enum Type {
-    /// Most types start out as "Unresolved". It serves as an intermediate stage between cleaning
-    /// and type resolution.
-    Unresolved(Path, Option<~[TyParamBound]>, ast::NodeId),
     /// structs/enums/traits (anything that'd be an ast::ty_path)
     ResolvedPath { path: Path, typarams: Option<~[TyParamBound]>, id: ast::NodeId },
     /// Reference to an item in an external crate (fully qualified path)
@@ -555,25 +554,25 @@ fn clean(&self) -> Type {
         debug!("cleaning type `%?`", self);
         let codemap = local_data::get(super::ctxtkey, |x| *x.unwrap()).sess.codemap;
         debug!("span corresponds to `%s`", codemap.span_to_str(self.span));
-        let t = match self.node {
+        match self.node {
             ty_nil => Unit,
-            ty_ptr(ref m) =>  RawPointer(m.mutbl.clean(), ~resolve_type(&m.ty.clean())),
+            ty_ptr(ref m) => RawPointer(m.mutbl.clean(), ~m.ty.clean()),
             ty_rptr(ref l, ref m) =>
                 BorrowedRef {lifetime: l.clean(), mutability: m.mutbl.clean(),
-                             type_: ~resolve_type(&m.ty.clean())},
-            ty_box(ref m) => Managed(m.mutbl.clean(), ~resolve_type(&m.ty.clean())),
-            ty_uniq(ref m) => Unique(~resolve_type(&m.ty.clean())),
-            ty_vec(ref m) => Vector(~resolve_type(&m.ty.clean())),
-            ty_fixed_length_vec(ref m, ref e) => FixedVector(~resolve_type(&m.ty.clean()),
+                             type_: ~m.ty.clean()},
+            ty_box(ref m) => Managed(m.mutbl.clean(), ~m.ty.clean()),
+            ty_uniq(ref m) => Unique(~m.ty.clean()),
+            ty_vec(ref m) => Vector(~m.ty.clean()),
+            ty_fixed_length_vec(ref m, ref e) => FixedVector(~m.ty.clean(),
                                                              e.span.to_src()),
-            ty_tup(ref tys) => Tuple(tys.iter().map(|x| resolve_type(&x.clean())).collect()),
-            ty_path(ref p, ref tpbs, id) => Unresolved(p.clean(), tpbs.clean(), id),
+            ty_tup(ref tys) => Tuple(tys.iter().map(|x| x.clean()).collect()),
+            ty_path(ref p, ref tpbs, id) =>
+                resolve_type(p.clean(), tpbs.clean(), id),
             ty_closure(ref c) => Closure(~c.clean()),
             ty_bare_fn(ref barefn) => BareFunction(~barefn.clean()),
             ty_bot => Bottom,
             ref x => fail!("Unimplemented type %?", x),
-        };
-        resolve_type(&t)
+        }
     }
 }
 
@@ -927,26 +926,45 @@ fn clean(&self) -> ViewItemInner {
 
 #[deriving(Clone, Encodable, Decodable)]
 pub enum ViewPath {
-    SimpleImport(~str, Path, ast::NodeId),
-    GlobImport(Path, ast::NodeId),
-    ImportList(Path, ~[ViewListIdent], ast::NodeId)
+    // use str = source;
+    SimpleImport(~str, ImportSource),
+    // use source::*;
+    GlobImport(ImportSource),
+    // use source::{a, b, c};
+    ImportList(ImportSource, ~[ViewListIdent]),
+}
+
+#[deriving(Clone, Encodable, Decodable)]
+pub struct ImportSource {
+    path: Path,
+    did: Option<ast::DefId>,
 }
 
 impl Clean<ViewPath> for ast::view_path {
     fn clean(&self) -> ViewPath {
         match self.node {
-            ast::view_path_simple(ref i, ref p, ref id) => SimpleImport(i.clean(), p.clean(), *id),
-            ast::view_path_glob(ref p, ref id) => GlobImport(p.clean(), *id),
-            ast::view_path_list(ref p, ref pl, ref id) => ImportList(p.clean(), pl.clean(), *id),
+            ast::view_path_simple(ref i, ref p, id) =>
+                SimpleImport(i.clean(), resolve_use_source(p.clean(), id)),
+            ast::view_path_glob(ref p, id) =>
+                GlobImport(resolve_use_source(p.clean(), id)),
+            ast::view_path_list(ref p, ref pl, id) =>
+                ImportList(resolve_use_source(p.clean(), id), pl.clean()),
         }
     }
 }
 
-pub type ViewListIdent = ~str;
+#[deriving(Clone, Encodable, Decodable)]
+pub struct ViewListIdent {
+    name: ~str,
+    source: Option<ast::DefId>,
+}
 
 impl Clean<ViewListIdent> for ast::path_list_ident {
     fn clean(&self) -> ViewListIdent {
-        self.node.name.clean()
+        ViewListIdent {
+            name: self.node.name.clean(),
+            source: resolve_def(self.node.id),
+        }
     }
 }
 
@@ -1017,14 +1035,10 @@ fn remove_comment_tags(s: &str) -> ~str {
 }
 
 /// Given a Type, resolve it using the def_map
-fn resolve_type(t: &Type) -> Type {
+fn resolve_type(path: Path, tpbs: Option<~[TyParamBound]>,
+                id: ast::NodeId) -> Type {
     use syntax::ast::*;
 
-    let (path, tpbs, id) = match t {
-        &Unresolved(ref path, ref tbps, id) => (path, tbps, id),
-        _ => return (*t).clone(),
-    };
-
     let dm = local_data::get(super::ctxtkey, |x| *x.unwrap()).tycx.def_map;
     debug!("searching for %? in defmap", id);
     let d = match dm.find(&id) {
@@ -1089,6 +1103,18 @@ fn resolve_type(t: &Type) -> Type {
         let cname = cratedata.name.to_owned();
         External(cname + "::" + path, ty)
     } else {
-        ResolvedPath {path: path.clone(), typarams: tpbs.clone(), id: def_id.node}
+        ResolvedPath {path: path.clone(), typarams: tpbs, id: def_id.node}
+    }
+}
+
+fn resolve_use_source(path: Path, id: ast::NodeId) -> ImportSource {
+    ImportSource {
+        path: path,
+        did: resolve_def(id),
     }
 }
+
+fn resolve_def(id: ast::NodeId) -> Option<ast::DefId> {
+    let dm = local_data::get(super::ctxtkey, |x| *x.unwrap()).tycx.def_map;
+    dm.find(&id).map_move(|&d| ast_util::def_id_of_def(d))
+}
index 67722b1e1777a6d929baa235fa265b742284bee7..a4e3f79549824bbd49326493d35860e89e446a00 100644 (file)
@@ -107,6 +107,7 @@ pub struct Function {
     id: NodeId,
     name: Ident,
     vis: ast::visibility,
+    purity: ast::purity,
     where: Span,
     generics: ast::Generics,
 }
index 4d0f6928d50e3534bf546867aea2c2f74659a37b..7010e7fa4eab6a51de94ba65a3ced8ce9d1f65cf 100644 (file)
 use std::rt::io;
 
 use syntax::ast;
+use syntax::ast_util;
 
 use clean;
 use html::render::{cache_key, current_location_key};
 
 pub struct VisSpace(Option<ast::visibility>);
+pub struct PuritySpace(ast::purity);
 pub struct Method<'self>(&'self clean::SelfTy, &'self clean::FnDecl);
 
 impl fmt::Default for clean::Generics {
@@ -95,7 +97,8 @@ fn fmt(path: &clean::Path, f: &mut fmt::Formatter) {
     }
 }
 
-fn resolved_path(w: &mut io::Writer, id: ast::NodeId, path: &clean::Path) {
+fn resolved_path(w: &mut io::Writer, id: ast::NodeId,
+                 path: &clean::Path, print_all: bool) {
     // The generics will get written to both the title and link
     let mut generics = ~"";
     let last = path.segments.last();
@@ -117,47 +120,73 @@ fn resolved_path(w: &mut io::Writer, id: ast::NodeId, path: &clean::Path) {
     // Did someone say rightward-drift?
     do local_data::get(current_location_key) |loc| {
         let loc = loc.unwrap();
+
+        if print_all {
+            let mut root = match path.segments[0].name.as_slice() {
+                "super" => ~"../",
+                "self" => ~"",
+                _ => "../".repeat(loc.len() - 1),
+            };
+            let amt = path.segments.len() - 1;
+            for seg in path.segments.slice_to(amt).iter() {
+                if "super" == seg.name || "self" == seg.name {
+                    write!(w, "{}::", seg.name);
+                } else {
+                    root.push_str(seg.name);
+                    root.push_str("/");
+                    write!(w, "<a class='mod'
+                                  href='{}index.html'>{}</a>::",
+                           root,
+                           seg.name);
+                }
+            }
+        }
+
         do local_data::get(cache_key) |cache| {
             do cache.unwrap().read |cache| {
                 match cache.paths.find(&id) {
                     // This is a documented path, link to it!
                     Some(&(ref fqp, shortty)) => {
                         let fqn = fqp.connect("::");
-                        let mut same = 0;
-                        for (a, b) in loc.iter().zip(fqp.iter()) {
-                            if *a == *b {
-                                same += 1;
-                            } else {
-                                break;
-                            }
-                        }
+                        let same = loc.iter().zip(fqp.iter())
+                                      .take_while(|&(a, b)| *a == *b).len();
 
                         let mut url = ~"";
-                        for _ in range(same, loc.len()) {
+                        if "super" == path.segments[0].name {
                             url.push_str("../");
+                        } else if "self" != path.segments[0].name {
+                            url.push_str("../".repeat(loc.len() - same));
                         }
-                        if same == fqp.len() {
-                            url.push_str(shortty);
-                            url.push_str(".");
-                            url.push_str(*fqp.last());
-                            url.push_str(".html");
-                        } else {
+                        if same < fqp.len() {
                             let remaining = fqp.slice_from(same);
                             let to_link = remaining.slice_to(remaining.len() - 1);
                             for component in to_link.iter() {
                                 url.push_str(*component);
                                 url.push_str("/");
                             }
-                            url.push_str(shortty);
-                            url.push_str(".");
-                            url.push_str(*remaining.last());
-                            url.push_str(".html");
                         }
-
+                        match shortty {
+                            "mod" => {
+                                url.push_str(*fqp.last());
+                                url.push_str("/index.html");
+                            }
+                            _ => {
+                                url.push_str(shortty);
+                                url.push_str(".");
+                                url.push_str(*fqp.last());
+                                url.push_str(".html");
+                            }
+                        }
                         write!(w, "<a class='{}' href='{}' title='{}'>{}</a>{}",
                                shortty, url, fqn, last.name, generics);
                     }
                     None => {
+                        if print_all {
+                            let amt = path.segments.len() - 1;
+                            for seg in path.segments.iter().take(amt) {
+                                write!(w, "{}::", seg.name);
+                            }
+                        }
                         write!(w, "{}{}", last.name, generics);
                     }
                 };
@@ -176,9 +205,8 @@ fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
                     }
                 }
             }
-            clean::Unresolved(*) => unreachable!(),
             clean::ResolvedPath{id, typarams: ref typarams, path: ref path} => {
-                resolved_path(f.buf, id, path);
+                resolved_path(f.buf, id, path, false);
                 match *typarams {
                     Some(ref params) => {
                         f.buf.write("&lt;".as_bytes());
@@ -228,11 +256,7 @@ fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
                     None => {}
                 }
                 write!(f.buf, "{}{}fn{}",
-                       match decl.purity {
-                           ast::unsafe_fn => "unsafe ",
-                           ast::extern_fn => "extern ",
-                           ast::impure_fn => ""
-                       },
+                       PuritySpace(decl.purity),
                        match decl.onceness {
                            ast::Once => "once ",
                            ast::Many => "",
@@ -242,11 +266,7 @@ fn fmt(g: &clean::Type, f: &mut fmt::Formatter) {
             }
             clean::BareFunction(ref decl) => {
                 write!(f.buf, "{}{}fn{}{}",
-                       match decl.purity {
-                           ast::unsafe_fn => "unsafe ",
-                           ast::extern_fn => "extern ",
-                           ast::impure_fn => ""
-                       },
+                       PuritySpace(decl.purity),
                        match decl.abi {
                            ~"" | ~"\"Rust\"" => ~"",
                            ref s => " " + *s + " ",
@@ -362,3 +382,73 @@ fn fmt(v: &VisSpace, f: &mut fmt::Formatter) {
         }
     }
 }
+
+impl fmt::Default for PuritySpace {
+    fn fmt(p: &PuritySpace, f: &mut fmt::Formatter) {
+        match **p {
+            ast::unsafe_fn => write!(f.buf, "unsafe "),
+            ast::extern_fn => write!(f.buf, "extern "),
+            ast::impure_fn => {}
+        }
+    }
+}
+
+impl fmt::Default for clean::ViewPath {
+    fn fmt(v: &clean::ViewPath, f: &mut fmt::Formatter) {
+        match *v {
+            clean::SimpleImport(ref name, ref src) => {
+                if *name == src.path.segments.last().name {
+                    write!(f.buf, "use {};", *src);
+                } else {
+                    write!(f.buf, "use {} = {};", *name, *src);
+                }
+            }
+            clean::GlobImport(ref src) => {
+                write!(f.buf, "use {}::*;", *src);
+            }
+            clean::ImportList(ref src, ref names) => {
+                write!(f.buf, "use {}::\\{", *src);
+                for (i, n) in names.iter().enumerate() {
+                    if i > 0 { write!(f.buf, ", "); }
+                    write!(f.buf, "{}", *n);
+                }
+                write!(f.buf, "\\};");
+            }
+        }
+    }
+}
+
+impl fmt::Default for clean::ImportSource {
+    fn fmt(v: &clean::ImportSource, f: &mut fmt::Formatter) {
+        match v.did {
+            Some(did) if ast_util::is_local(did) => {
+                resolved_path(f.buf, did.node, &v.path, true);
+            }
+            _ => {
+                for (i, seg) in v.path.segments.iter().enumerate() {
+                    if i > 0 { write!(f.buf, "::") }
+                    write!(f.buf, "{}", seg.name);
+                }
+            }
+        }
+    }
+}
+
+impl fmt::Default for clean::ViewListIdent {
+    fn fmt(v: &clean::ViewListIdent, f: &mut fmt::Formatter) {
+        match v.source {
+            Some(did) if ast_util::is_local(did) => {
+                let path = clean::Path {
+                    global: false,
+                    segments: ~[clean::PathSegment {
+                        name: v.name.clone(),
+                        lifetime: None,
+                        types: ~[],
+                    }]
+                };
+                resolved_path(f.buf, did.node, &path, false);
+            }
+            _ => write!(f.buf, "{}", v.name),
+        }
+    }
+}
index 14e2327550b324d568984c17637aa824d138e129..ce7e7f64dcc88de26f3ad965df831cb0717b2810 100644 (file)
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[allow(cstack)]; // each rendering task runs on a fixed stack segment.
+
 use std::fmt;
-use std::rt::io::Reader;
-use std::rt::io::pipe::PipeStream;
-use std::rt::io::process::{ProcessConfig, Process, CreatePipe};
+use std::libc;
 use std::rt::io;
+use std::vec;
 
 pub struct Markdown<'self>(&'self str);
 
+static OUTPUT_UNIT: libc::size_t = 64;
+static MKDEXT_NO_INTRA_EMPHASIS: libc::c_uint = 1 << 0;
+static MKDEXT_TABLES: libc::c_uint = 1 << 1;
+static MKDEXT_FENCED_CODE: libc::c_uint = 1 << 2;
+static MKDEXT_AUTOLINK: libc::c_uint = 1 << 3;
+static MKDEXT_STRIKETHROUGH: libc::c_uint = 1 << 4;
+static MKDEXT_SPACE_HEADERS: libc::c_uint = 1 << 6;
+static MKDEXT_SUPERSCRIPT: libc::c_uint = 1 << 7;
+static MKDEXT_LAX_SPACING: libc::c_uint = 1 << 8;
+
+type sd_markdown = libc::c_void;  // this is opaque to us
+
+// this is a large struct of callbacks we don't use
+type sd_callbacks = [libc::size_t, ..26];
+
+struct html_toc_data {
+    header_count: libc::c_int,
+    current_level: libc::c_int,
+    level_offset: libc::c_int,
+}
+
+struct html_renderopt {
+    toc_data: html_toc_data,
+    flags: libc::c_uint,
+    link_attributes: Option<extern "C" fn(*buf, *buf, *libc::c_void)>,
+}
+
+struct buf {
+    data: *u8,
+    size: libc::size_t,
+    asize: libc::size_t,
+    unit: libc::size_t,
+}
+
+// sundown FFI
+extern {
+    fn sdhtml_renderer(callbacks: *sd_callbacks,
+                       options_ptr: *html_renderopt,
+                       render_flags: libc::c_uint);
+    fn sd_markdown_new(extensions: libc::c_uint,
+                       max_nesting: libc::size_t,
+                       callbacks: *sd_callbacks,
+                       opaque: *libc::c_void) -> *sd_markdown;
+    fn sd_markdown_render(ob: *buf,
+                          document: *u8,
+                          doc_size: libc::size_t,
+                          md: *sd_markdown);
+    fn sd_markdown_free(md: *sd_markdown);
+
+    fn bufnew(unit: libc::size_t) -> *buf;
+    fn bufrelease(b: *buf);
+
+}
+
+fn render(w: &mut io::Writer, s: &str) {
+    // This code is all lifted from examples/sundown.c in the sundown repo
+    unsafe {
+        let ob = bufnew(OUTPUT_UNIT);
+        let extensions = MKDEXT_NO_INTRA_EMPHASIS | MKDEXT_TABLES |
+                         MKDEXT_FENCED_CODE | MKDEXT_AUTOLINK |
+                         MKDEXT_STRIKETHROUGH;
+        let options = html_renderopt {
+            toc_data: html_toc_data {
+                header_count: 0,
+                current_level: 0,
+                level_offset: 0,
+            },
+            flags: 0,
+            link_attributes: None,
+        };
+        let callbacks: sd_callbacks = [0, ..26];
+
+        sdhtml_renderer(&callbacks, &options, 0);
+        let markdown = sd_markdown_new(extensions, 16, &callbacks,
+                                       &options as *html_renderopt as *libc::c_void);
+
+        do s.as_imm_buf |data, len| {
+            sd_markdown_render(ob, data, len as libc::size_t, markdown);
+        }
+        sd_markdown_free(markdown);
+
+        do vec::raw::buf_as_slice((*ob).data, (*ob).size as uint) |buf| {
+            w.write(buf);
+        }
+
+        bufrelease(ob);
+    }
+}
+
 impl<'self> fmt::Default for Markdown<'self> {
     fn fmt(md: &Markdown<'self>, fmt: &mut fmt::Formatter) {
+        // This is actually common enough to special-case
         if md.len() == 0 { return; }
 
-        // Create the pandoc process
-        do io::io_error::cond.trap(|err| {
-            fail2!("Error executing `pandoc`: {}", err.desc);
-        }).inside {
-            let io = ~[CreatePipe(PipeStream::new().unwrap(), true, false),
-                       CreatePipe(PipeStream::new().unwrap(), false, true)];
-            let args = ProcessConfig {
-                program: "pandoc",
-                args: [],
-                env: None,
-                cwd: None,
-                io: io,
-            };
-            let mut p = Process::new(args).expect("couldn't fork for pandoc");
-
-            // Write the markdown to stdin and close it.
-            p.io[0].get_mut_ref().write(md.as_bytes());
-            p.io[0] = None;
-
-            // Ferry the output from pandoc over to the destination buffer.
-            let mut buf = [0, ..1024];
-            loop {
-                match p.io[1].get_mut_ref().read(buf) {
-                    None | Some(0) => { break }
-                    Some(n) => {
-                        fmt.buf.write(buf.slice_to(n));
-                    }
-                }
-            }
-        }
+        render(fmt.buf, md.as_slice());
     }
 }
index 464a4e4b736553c94f422368e084d7ca93849a93..4f8c11d828544f7af638cb489716a3e7163bac9d 100644 (file)
@@ -32,7 +32,7 @@
 use clean;
 use doctree;
 use fold::DocFolder;
-use html::format::{VisSpace, Method};
+use html::format::{VisSpace, Method, PuritySpace};
 use html::layout;
 use html::markdown::Markdown;
 
@@ -288,7 +288,9 @@ fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
         } else { false };
         match item.inner {
             clean::StructItem(*) | clean::EnumItem(*) |
-            clean::TypedefItem(*) | clean::TraitItem(*) => {
+            clean::TypedefItem(*) | clean::TraitItem(*) |
+            clean::FunctionItem(*) | clean::ModuleItem(*) |
+            clean::VariantItem(*) => {
                 self.paths.insert(item.id, (self.stack.clone(), shortty(&item)));
             }
             _ => {}
@@ -401,8 +403,16 @@ enum Progress { JobNew, JobDone }
             let mut task = task::task();
             task.unlinked(); // we kill things manually
             task.name(format!("worker{}", i));
-            do task.spawn_with(cache.clone()) |cache| {
+            task.spawn_with(cache.clone(),
+                            |cache| worker(cache, &port, &chan, &prog_chan));
+
+            fn worker(cache: RWArc<Cache>,
+                      port: &SharedPort<Work>,
+                      chan: &SharedChan<Work>,
+                      prog_chan: &SharedChan<Progress>) {
+                #[fixed_stack_segment]; // we hit markdown FFI *a lot*
                 local_data::set(cache_key, cache);
+
                 loop {
                     match port.recv() {
                         Process(cx, item) => {
@@ -425,28 +435,20 @@ enum Progress { JobNew, JobDone }
             }
         }
 
-        let watcher_chan = chan.clone();
-        let (done_port, done_chan) = comm::stream();
-        do task::spawn {
-            let mut jobs = 0;
-            loop {
-                match prog_port.recv() {
-                    JobNew => jobs += 1,
-                    JobDone => jobs -= 1,
-                }
-
-                if jobs == 0 { break }
+        chan.send(Process(self, item));
+        let mut jobs = 1;
+        loop {
+            match prog_port.recv() {
+                JobNew => jobs += 1,
+                JobDone => jobs -= 1,
             }
 
-            for _ in range(0, WORKERS) {
-                watcher_chan.send(Die);
-            }
-            done_chan.send(());
+            if jobs == 0 { break }
         }
 
-        prog_chan.send(JobNew);
-        chan.send(Process(self, item));
-        done_port.recv();
+        for _ in range(0, WORKERS) {
+            chan.send(Die);
+        }
     }
 
     fn item(&mut self, item: clean::Item, f: &fn(&mut Context, clean::Item)) {
@@ -479,6 +481,8 @@ fn render(w: io::file::FileWriter, cx: &mut Context, it: &clean::Item,
         }
 
         match item.inner {
+            // modules are special because they add a namespace. We also need to
+            // recurse into the items of the module as well.
             clean::ModuleItem(*) => {
                 let name = item.name.get_ref().to_owned();
                 let item = Cell::new(item);
@@ -498,11 +502,29 @@ fn render(w: io::file::FileWriter, cx: &mut Context, it: &clean::Item,
                     }
                 }
             }
+
+            // Things which don't have names (like impls) don't get special
+            // pages dedicated to them.
             _ if item.name.is_some() => {
                 let dst = self.dst.push(item_path(&item));
                 let writer = dst.open_writer(io::CreateOrTruncate);
                 render(writer.unwrap(), self, &item, true);
+
+                // recurse if necessary
+                let name = item.name.get_ref().clone();
+                match item.inner {
+                    clean::EnumItem(e) => {
+                        let mut it = e.variants.move_iter();
+                        do self.recurse(name) |this| {
+                            for item in it {
+                                f(this, item);
+                            }
+                        }
+                    }
+                    _ => {}
+                }
             }
+
             _ => {}
         }
     }
@@ -567,6 +589,7 @@ fn fmt(it: &Item<'self>, fmt: &mut fmt::Formatter) {
             clean::StructItem(ref s) => item_struct(fmt.buf, it.item, s),
             clean::EnumItem(ref e) => item_enum(fmt.buf, it.item, e),
             clean::TypedefItem(ref t) => item_typedef(fmt.buf, it.item, t),
+            clean::VariantItem(*) => item_variant(fmt.buf, it.cx, it.item),
             _ => {}
         }
     }
@@ -615,13 +638,21 @@ fn document(w: &mut io::Writer, item: &clean::Item) {
 fn item_module(w: &mut io::Writer, cx: &Context,
                item: &clean::Item, items: &[clean::Item]) {
     document(w, item);
+    debug2!("{:?}", items);
     let mut indices = vec::from_fn(items.len(), |i| i);
 
-    fn lt(i1: &clean::Item, i2: &clean::Item) -> bool {
+    fn lt(i1: &clean::Item, i2: &clean::Item, idx1: uint, idx2: uint) -> bool {
         if shortty(i1) == shortty(i2) {
             return i1.name < i2.name;
         }
         match (&i1.inner, &i2.inner) {
+            (&clean::ViewItemItem(ref a), &clean::ViewItemItem(ref b)) => {
+                match (&a.inner, &b.inner) {
+                    (&clean::ExternMod(*), _) => true,
+                    (_, &clean::ExternMod(*)) => false,
+                    _ => idx1 < idx2,
+                }
+            }
             (&clean::ViewItemItem(*), _) => true,
             (_, &clean::ViewItemItem(*)) => false,
             (&clean::ModuleItem(*), _) => true,
@@ -638,18 +669,19 @@ fn lt(i1: &clean::Item, i2: &clean::Item) -> bool {
             (_, &clean::FunctionItem(*)) => false,
             (&clean::TypedefItem(*), _) => true,
             (_, &clean::TypedefItem(*)) => false,
-            _ => false,
+            _ => idx1 < idx2,
         }
     }
 
+    debug2!("{:?}", indices);
     do sort::quick_sort(indices) |&i1, &i2| {
-        lt(&items[i1], &items[i2])
+        lt(&items[i1], &items[i2], i1, i2)
     }
 
+    debug2!("{:?}", indices);
     let mut curty = "";
     for &idx in indices.iter() {
         let myitem = &items[idx];
-        if myitem.name.is_none() { loop }
 
         let myty = shortty(myitem);
         if myty != curty {
@@ -687,17 +719,43 @@ fn fmt(s: &Initializer<'self>, f: &mut fmt::Formatter) {
 
                 write!(w, "
                     <tr>
-                        <td><code>{}: {} = </code>{}</td>
+                        <td><code>{}static {}: {} = </code>{}</td>
                         <td class='docblock'>{}&nbsp;</td>
                     </tr>
                 ",
+                VisSpace(myitem.visibility),
                 *myitem.name.get_ref(),
                 s.type_,
                 Initializer(s.expr),
                 Markdown(blank(myitem.doc_value())));
             }
 
+            clean::ViewItemItem(ref item) => {
+                match item.inner {
+                    clean::ExternMod(ref name, ref src, _, _) => {
+                        write!(w, "<tr><td><code>extern mod {}",
+                               name.as_slice());
+                        match *src {
+                            Some(ref src) => write!(w, " = \"{}\"",
+                                                    src.as_slice()),
+                            None => {}
+                        }
+                        write!(w, ";</code></td></tr>");
+                    }
+
+                    clean::Import(ref imports) => {
+                        for import in imports.iter() {
+                            write!(w, "<tr><td><code>{}{}</code></td></tr>",
+                                   VisSpace(myitem.visibility),
+                                   *import);
+                        }
+                    }
+                }
+
+            }
+
             _ => {
+                if myitem.name.is_none() { loop }
                 write!(w, "
                     <tr>
                         <td><a class='{class}' href='{href}'
@@ -717,8 +775,9 @@ fn fmt(s: &Initializer<'self>, f: &mut fmt::Formatter) {
 }
 
 fn item_function(w: &mut io::Writer, it: &clean::Item, f: &clean::Function) {
-    write!(w, "<pre class='fn'>{vis}fn {name}{generics}{decl}</pre>",
+    write!(w, "<pre class='fn'>{vis}{purity}fn {name}{generics}{decl}</pre>",
            vis = VisSpace(it.visibility),
+           purity = PuritySpace(f.purity),
            name = it.name.get_ref().as_slice(),
            generics = f.generics,
            decl = f.decl);
@@ -830,8 +889,8 @@ fn fun(w: &mut io::Writer, it: &clean::Item, purity: ast::purity,
            g: &clean::Generics, selfty: &clean::SelfTy, d: &clean::FnDecl,
            withlink: bool) {
         write!(w, "{}fn {withlink, select,
-                            true{<a href='\\#fn.{name}'>{name}</a>}
-                            other{{name}}
+                            true{<a href='\\#fn.{name}' class='fnname'>{name}</a>}
+                            other{<span class='fnname'>{name}</span>}
                         }{generics}{decl}",
                match purity {
                    ast::unsafe_fn => "unsafe ",
@@ -872,7 +931,8 @@ fn item_enum(w: &mut io::Writer, it: &clean::Item, e: &clean::Enum) {
     } else {
         write!(w, " \\{\n");
         for v in e.variants.iter() {
-            let name = v.name.get_ref().as_slice();
+            let name = format!("<a name='variant.{0}'>{0}</a>",
+                               v.name.get_ref().as_slice());
             match v.inner {
                 clean::VariantItem(ref var) => {
                     match var.kind {
@@ -1101,3 +1161,12 @@ fn build_sidebar(m: &clean::Module) -> HashMap<~str, ~[~str]> {
     }
     return map;
 }
+
+fn item_variant(w: &mut io::Writer, cx: &Context, it: &clean::Item) {
+    write!(w, "<DOCTYPE html><html><head>\
+                <meta http-equiv='refresh' content='0; \
+                      url=../enum.{}.html\\#variant.{}'>\
+               </head><body></body></html>",
+           *cx.current.last(),
+           it.name.get_ref().as_slice());
+}
index 2b9651681295682a83276a5a4aee1246118ad017..97f597982406bbd416967690fdf831ce561b0461 100644 (file)
@@ -221,6 +221,7 @@ a {
 .content a.enum, .block a.current.enum { color: #5e9766; }
 .content a.struct, .block a.current.struct { color: #e53700; }
 .content a.fn, .block a.current.fn { color: #8c6067; }
+.content .fnname { color: #8c6067; }
 
 .search-input {
     border: 2px solid #f2f2f2;
index e580ab0719c8effc6155ed3c2b9030cf828e46ec..91e2b3bfcf187c75fb80ba5af657d726e2d1b583 100644 (file)
@@ -11,8 +11,9 @@
 use std::num;
 use std::uint;
 
-use clean;
 use syntax::ast;
+
+use clean;
 use clean::Item;
 use plugins;
 use fold;
@@ -47,6 +48,78 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
     (crate, None)
 }
 
+/// Strip private items from the point of view of a crate or externally from a
+/// crate, specified by the `xcrate` flag.
+pub fn strip_private(crate: clean::Crate) -> plugins::PluginResult {
+    struct Stripper;
+    impl fold::DocFolder for Stripper {
+        fn fold_item(&mut self, i: Item) -> Option<Item> {
+            match i.inner {
+                // These items can all get re-exported
+                clean::TypedefItem(*) | clean::StaticItem(*) |
+                clean::StructItem(*) | clean::EnumItem(*) |
+                clean::TraitItem(*) | clean::FunctionItem(*) |
+                clean::ViewItemItem(*) | clean::MethodItem(*) => {
+                    // XXX: re-exported items should get surfaced in the docs as
+                    //      well (using the output of resolve analysis)
+                    if i.visibility != Some(ast::public) {
+                        return None;
+                    }
+                }
+
+                // These are public-by-default (if the enum was public)
+                clean::VariantItem(*) => {
+                    if i.visibility == Some(ast::private) {
+                        return None;
+                    }
+                }
+
+                // We show these regardless of whether they're public/private
+                // because it's useful to see sometimes
+                clean::StructFieldItem(*) => {}
+
+                // handled below
+                clean::ModuleItem(*) => {}
+
+                // impls/tymethods have no control over privacy
+                clean::ImplItem(*) | clean::TyMethodItem(*) => {}
+            }
+
+            let fastreturn = match i.inner {
+                // nothing left to do for traits (don't want to filter their
+                // methods out, visibility controlled by the trait)
+                clean::TraitItem(*) => true,
+
+                // implementations of traits are always public.
+                clean::ImplItem(ref imp) if imp.trait_.is_some() => true,
+
+                _ => false,
+            };
+
+            let i = if fastreturn {
+                return Some(i);
+            } else {
+                self.fold_item_recur(i)
+            };
+
+            match i {
+                Some(i) => {
+                    match i.inner {
+                        // emptied modules/impls have no need to exist
+                        clean::ModuleItem(ref m) if m.items.len() == 0 => None,
+                        clean::ImplItem(ref i) if i.methods.len() == 0 => None,
+                        _ => Some(i),
+                    }
+                }
+                None => None,
+            }
+        }
+    }
+    let mut stripper = Stripper;
+    let crate = stripper.fold_crate(crate);
+    (crate, None)
+}
+
 pub fn unindent_comments(crate: clean::Crate) -> plugins::PluginResult {
     struct CommentCleaner;
     impl fold::DocFolder for CommentCleaner {
@@ -69,27 +142,6 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
     (crate, None)
 }
 
-pub fn collapse_privacy(crate: clean::Crate) -> plugins::PluginResult {
-    struct PrivacyCollapser {
-        stack: ~[clean::Visibility]
-    }
-    impl fold::DocFolder for PrivacyCollapser {
-        fn fold_item(&mut self, mut i: Item) -> Option<Item> {
-            if i.visibility.is_some() {
-                if i.visibility == Some(ast::inherited) {
-                    i.visibility = Some(self.stack.last().clone());
-                } else {
-                    self.stack.push(i.visibility.clone().unwrap());
-                }
-            }
-            self.fold_item_recur(i)
-        }
-    }
-    let mut privacy = PrivacyCollapser { stack: ~[] };
-    let crate = privacy.fold_crate(crate);
-    (crate, None)
-}
-
 pub fn collapse_docs(crate: clean::Crate) -> plugins::PluginResult {
     struct Collapser;
     impl fold::DocFolder for Collapser {
@@ -110,7 +162,7 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
                 _ => true
             }).map(|x| x.clone()).collect();
             if "" != docstr {
-                a.push(clean::NameValue(~"doc", docstr.trim().to_owned()));
+                a.push(clean::NameValue(~"doc", docstr));
             }
             i.attrs = a;
             self.fold_item_recur(i)
@@ -121,7 +173,6 @@ fn fold_item(&mut self, i: Item) -> Option<Item> {
     (crate, None)
 }
 
-// n.b. this is copied from src/librustdoc/unindent_pass.rs
 pub fn unindent(s: &str) -> ~str {
     let lines = s.any_line_iter().collect::<~[&str]>();
     let mut saw_first_line = false;
index c3d8cdf028e0c67aec1e3601ee49d601c743ef35..ccab6dad835ab129f212d6104ba1e93cab613c99 100644 (file)
@@ -45,6 +45,28 @@ pub mod html {
 
 pub static SCHEMA_VERSION: &'static str = "0.8.0";
 
+type Pass = (&'static str,                                      // name
+             extern fn(clean::Crate) -> plugins::PluginResult,  // fn
+             &'static str);                                     // description
+
+static PASSES: &'static [Pass] = &[
+    ("strip-hidden", passes::strip_hidden,
+     "strips all doc(hidden) items from the output"),
+    ("unindent-comments", passes::unindent_comments,
+     "removes excess indentation on comments in order for markdown to like it"),
+    ("collapse-docs", passes::collapse_docs,
+     "concatenates all document attributes into one document attribute"),
+    ("strip-private", passes::strip_private,
+     "strips all private items from a crate which cannot be seen externally"),
+];
+
+static DEFAULT_PASSES: &'static [&'static str] = &[
+    "strip-hidden",
+    "strip-private",
+    "collapse-docs",
+    "unindent-comments",
+];
+
 local_data_key!(pub ctxtkey: @core::DocContext)
 
 enum OutputFormat {
@@ -61,7 +83,8 @@ pub fn opts() -> ~[groups::OptGroup] {
         optmulti("L", "library-path", "directory to add to crate search path",
                  "DIR"),
         optmulti("", "plugin-path", "directory to load plugins from", "DIR"),
-        optmulti("", "passes", "space separated list of passes to also run",
+        optmulti("", "passes", "space separated list of passes to also run, a \
+                                value of `list` will print available passes",
                  "PASSES"),
         optmulti("", "plugins", "space separated list of plugins to also load",
                  "PLUGINS"),
@@ -86,6 +109,22 @@ pub fn main_args(args: &[~str]) -> int {
         return 0;
     }
 
+    let mut default_passes = !matches.opt_present("nodefaults");
+    let mut passes = matches.opt_strs("passes");
+    let mut plugins = matches.opt_strs("plugins");
+
+    if passes == ~[~"list"] {
+        println("Available passes for running rustdoc:");
+        for &(name, _, description) in PASSES.iter() {
+            println!("{:>20s} - {}", name, description);
+        }
+        println("\nDefault passes for rustdoc:");
+        for &name in DEFAULT_PASSES.iter() {
+            println!("{:>20s}", name);
+        }
+        return 0;
+    }
+
     let (format, cratefile) = match matches.free.clone() {
         [~"json", crate] => (JSON, crate),
         [~"html", crate] => (HTML, crate),
@@ -118,9 +157,6 @@ pub fn main_args(args: &[~str]) -> int {
 
     // Process all of the crate attributes, extracting plugin metadata along
     // with the passes which we are supposed to run.
-    let mut default_passes = !matches.opt_present("nodefaults");
-    let mut passes = matches.opt_strs("passes");
-    let mut plugins = matches.opt_strs("plugins");
     match crate.module.get_ref().doc_list() {
         Some(nested) => {
             for inner in nested.iter() {
@@ -145,19 +181,20 @@ pub fn main_args(args: &[~str]) -> int {
         None => {}
     }
     if default_passes {
-        passes.unshift(~"collapse-docs");
-        passes.unshift(~"unindent-comments");
+        for name in DEFAULT_PASSES.rev_iter() {
+            passes.unshift(name.to_owned());
+        }
     }
 
     // Load all plugins/passes into a PluginManager
     let mut pm = plugins::PluginManager::new(Path("/tmp/rustdoc_ng/plugins"));
     for pass in passes.iter() {
-        let plugin = match pass.as_slice() {
-            "strip-hidden" => passes::strip_hidden,
-            "unindent-comments" => passes::unindent_comments,
-            "collapse-docs" => passes::collapse_docs,
-            "collapse-privacy" => passes::collapse_privacy,
-            s => { error!("unknown pass %s, skipping", s); loop },
+        let plugin = match PASSES.iter().position(|&(p, _, _)| p == *pass) {
+            Some(i) => PASSES[i].n1(),
+            None => {
+                error2!("unknown pass {}, skipping", *pass);
+                loop
+            },
         };
         pm.add_plugin(plugin);
     }
index 14be80f0f15336e89fc85438f7726d237e268001..c4b9b9efe566eb78a6e3c6e6792ae7e6303112bb 100644 (file)
@@ -75,7 +75,7 @@ fn visit_enum_def(it: &ast::item, def: &ast::enum_def, params: &ast::Generics) -
             }
         }
 
-        fn visit_fn(item: &ast::item, fd: &ast::fn_decl, _purity: &ast::purity,
+        fn visit_fn(item: &ast::item, fd: &ast::fn_decl, purity: &ast::purity,
                      _abi: &AbiSet, gen: &ast::Generics) -> Function {
             debug!("Visiting fn");
             Function {
@@ -86,6 +86,7 @@ fn visit_fn(item: &ast::item, fd: &ast::fn_decl, _purity: &ast::purity,
                 name: item.ident,
                 where: item.span,
                 generics: gen.clone(),
+                purity: *purity,
             }
         }
 
index 4bf647b011d9dbc3b7cf24ae872b6e0ec21439a8..c2fddafd5fe221bf217e73631d7c263432fd214e 100644 (file)
@@ -59,7 +59,8 @@ pub fn new(workspace: Path, use_rust_path_hack: bool, id: PkgId) -> PkgSrc {
         use conditions::nonexistent_package::cond;
 
         debug!("Checking package source for package ID %s, \
-               workspace = %s", id.to_str(), workspace.to_str());
+               workspace = %s use_rust_path_hack = %?",
+               id.to_str(), workspace.to_str(), use_rust_path_hack);
 
         let mut to_try = ~[];
         if use_rust_path_hack {
index 3ed1b7a3a9ca6ffce4f2d0f399e8754874da8630..7061345341f9c4b9767cbb3af7dc4564e4035c51 100644 (file)
@@ -421,11 +421,16 @@ fn dir_has_file(dir: &Path, file: &str) -> bool {
 pub fn find_dir_using_rust_path_hack(p: &PkgId) -> Option<Path> {
     let rp = rust_path();
     for dir in rp.iter() {
-        debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());
-        if dir_has_file(dir, "lib.rs") || dir_has_file(dir, "main.rs")
-            || dir_has_file(dir, "test.rs") || dir_has_file(dir, "bench.rs") {
-            debug!("Did find id %s in dir %s", p.to_str(), dir.to_str());
-            return Some(dir.clone());
+        // Require that the parent directory match the package ID
+        // Note that this only matches if the package ID being searched for
+        // has a name that's a single component
+        if dir.is_parent_of(&p.path) || dir.is_parent_of(&versionize(&p.path, &p.version)) {
+            debug!("In find_dir_using_rust_path_hack: checking dir %s", dir.to_str());
+            if dir_has_file(dir, "lib.rs") || dir_has_file(dir, "main.rs")
+                || dir_has_file(dir, "test.rs") || dir_has_file(dir, "bench.rs") {
+                debug!("Did find id %s in dir %s", p.to_str(), dir.to_str());
+                return Some(dir.clone());
+            }
         }
         debug!("Didn't find id %s in dir %s", p.to_str(), dir.to_str())
     }
@@ -440,3 +445,11 @@ pub fn user_set_rust_path() -> bool {
         Some(_)         => true
     }
 }
+
+/// Append the version string onto the end of the path's filename
+fn versionize(p: &Path, v: &Version) -> Path {
+    let q = p.file_path().to_str();
+    p.with_filename(fmt!("%s-%s", q, v.to_str()))
+}
+
+
index 79f137de853a3cc7b5f0f85a92858258948c8fe4..52545d60420f9f4aaf80fbdccd6d1606936a3900 100644 (file)
@@ -1267,7 +1267,9 @@ fn test_rust_path_can_contain_package_dirs_without_flag() {
 #[test]
 fn rust_path_hack_cwd() {
    // Same as rust_path_hack_test, but the CWD is the dir to build out of
-   let cwd = mkdtemp(&os::tmpdir(), "pkg_files").expect("rust_path_hack_cwd");
+   let cwd = mkdtemp(&os::tmpdir(), "foo").expect("rust_path_hack_cwd");
+   let cwd = cwd.push("foo");
+   assert!(os::mkdir_recursive(&cwd, U_RWX));
    writeFile(&cwd.push("lib.rs"), "pub fn f() { }");
 
    let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
@@ -1763,6 +1765,41 @@ fn reinstall() {
     assert_built_executable_exists(&workspace, b.short_name);
 }
 
+#[test]
+fn correct_package_name_with_rust_path_hack() {
+    /*
+    Set rust_path_hack flag
+
+    Try to install bar
+    Check that:
+    - no output gets produced in any workspace
+    - there's an error
+    */
+
+    // Set RUST_PATH to something containing only the sources for foo
+    let foo_id = PkgId::new("foo");
+    let bar_id = PkgId::new("bar");
+    let foo_workspace = create_local_package(&foo_id);
+    let dest_workspace = mk_empty_workspace(&Path("bar"), &NoVersion, "dest_workspace");
+
+    writeFile(&dest_workspace.push_many(["src", "bar-0.1", "main.rs"]),
+              "extern mod blat; fn main() { let _x = (); }");
+
+    let rust_path = Some(~[(~"RUST_PATH", fmt!("%s:%s", dest_workspace.to_str(),
+                        foo_workspace.push_many(["src", "foo-0.1"]).to_str()))]);
+    // bar doesn't exist, but we want to make sure rustpkg doesn't think foo is bar
+    command_line_test_with_env([~"install", ~"--rust-path-hack", ~"bar"],
+                               &dest_workspace, rust_path);
+    assert!(!executable_exists(&dest_workspace, "bar"));
+    assert!(!lib_exists(&dest_workspace, &bar_id.path.clone(), bar_id.version.clone()));
+    assert!(!executable_exists(&dest_workspace, "foo"));
+    assert!(!lib_exists(&dest_workspace, &foo_id.path.clone(), foo_id.version.clone()));
+    assert!(!executable_exists(&foo_workspace, "bar"));
+    assert!(!lib_exists(&foo_workspace, &bar_id.path.clone(), bar_id.version.clone()));
+    assert!(!executable_exists(&foo_workspace, "foo"));
+    assert!(!lib_exists(&foo_workspace, &foo_id.path.clone(), foo_id.version.clone()));
+}
+
 /// Returns true if p exists and is executable
 fn is_executable(p: &Path) -> bool {
     use std::libc::consts::os::posix88::{S_IXUSR};
index 542a6af402d055053afffe3c366fb145e51c4b83..3b233c9f6a88ac9788d1a74a4b3dad26911fb7b6 100644 (file)
@@ -9,4 +9,4 @@
 // except according to those terms.
 
 pub fn do_nothing() {
-}
\ No newline at end of file
+}
index e1641ccf0749309426e57a911ce21ceed13d75fd..3d22ddc57faa30021e03b924b1aed2e12798797a 100644 (file)
@@ -14,4 +14,4 @@ fn g() {
     while(x < 1000) {
         x += 1;
     }
-}
\ No newline at end of file
+}
index 542a6af402d055053afffe3c366fb145e51c4b83..3b233c9f6a88ac9788d1a74a4b3dad26911fb7b6 100644 (file)
@@ -9,4 +9,4 @@
 // except according to those terms.
 
 pub fn do_nothing() {
-}
\ No newline at end of file
+}
index 5d5e895a5adae88384ad695e3383bfd55c9ae04a..b30ad6f2c92970ac6d3152606935a4270a134dd5 100644 (file)
@@ -439,26 +439,30 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
                         let pkg_id = PkgId::new(lib_name);
                         let workspaces = pkg_parent_workspaces(&self.context.context,
                                                                &pkg_id);
-                        let dep_workspace = if workspaces.is_empty() {
-                            error(fmt!("Couldn't find package %s, which is needed by %s, \
-                                            in any of the workspaces in the RUST_PATH (%?)",
-                                            lib_name,
-                                            self.parent.to_str(),
-                                            rust_path()));
+                        let source_workspace = if workspaces.is_empty() {
+                            error(fmt!("Couldn't find package %s \
+                                       in any of the workspaces in the RUST_PATH (%s)",
+                                       lib_name,
+                                       rust_path().map(|s| s.to_str()).connect(":")));
                             cond.raise((pkg_id.clone(), ~"Dependency not found"))
                         }
-                        else {
+                            else {
                             workspaces[0]
                         };
                         let (outputs_disc, inputs_disc) =
-                            self.context.install(PkgSrc::new(dep_workspace.clone(),
-                                                             false,
+                            self.context.install(PkgSrc::new(source_workspace.clone(),
+                            // Use the rust_path_hack to search for dependencies iff
+                            // we were already using it
+                            self.context.context.use_rust_path_hack,
                                                              pkg_id),
                                                  &JustOne(Path(
-                                                    lib_crate_filename)));
+                                    lib_crate_filename)));
                         debug!("Installed %s, returned %? dependencies and \
                                %? transitive dependencies",
                                lib_name, outputs_disc.len(), inputs_disc.len());
+                        // It must have installed *something*...
+                        assert!(!outputs_disc.is_empty());
+                        let target_workspace = outputs_disc[0].pop();
                         for dep in outputs_disc.iter() {
                             debug!("Discovering a binary input: %s", dep.to_str());
                             self.exec.discover_input("binary",
@@ -471,31 +475,24 @@ fn visit_view_item(&mut self, vi: &ast::view_item, env: ()) {
                                                          *dep,
                                                          digest_file_with_date(&Path(*dep)));
                             }
-                            else if *what == ~"binary" {
+                                else if *what == ~"binary" {
                                 self.exec.discover_input(*what,
                                                          *dep,
                                                          digest_only_date(&Path(*dep)));
                             }
-                            else {
+                                else {
                                 fail!("Bad kind: %s", *what);
                             }
                         }
                         // Also, add an additional search path
-                        debug!("Adding additional search path: %s", lib_name);
-                        let installed_library =
-                            installed_library_in_workspace(&Path(lib_name), &dep_workspace)
-                                .expect(fmt!("rustpkg failed to install dependency %s",
-                                              lib_name));
-                        let install_dir = installed_library.pop();
-                        debug!("Installed %s into %s [%?]", lib_name, install_dir.to_str(),
-                               datestamp(&installed_library));
-                        (self.save)(install_dir);
+                        debug!("Installed %s into %s", lib_name, target_workspace.to_str());
+                        (self.save)(target_workspace);
                     }
-                }}
+                }
+            }
             // Ignore `use`s
             _ => ()
         }
-
         visit::walk_view_item(self, vi, env)
     }
 }
index 4ef50094139aa749bc5fe244bbefae4a9cde4fa9..1d59e63e702f5be9f4bc9dfa382bc4391da5176a 100644 (file)
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::not(true)
 * false
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::not(false)
 * true
-* ~~~
+* ```
 */
 pub fn not(v: bool) -> bool { !v }
 
@@ -69,15 +69,15 @@ pub fn not(v: bool) -> bool { !v }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::and(true, false)
 * false
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::and(true, true)
 * true
-* ~~~
+* ```
 */
 pub fn and(a: bool, b: bool) -> bool { a && b }
 
@@ -86,15 +86,15 @@ pub fn and(a: bool, b: bool) -> bool { a && b }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::or(true, false)
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::or(false, false)
 * false
-* ~~~
+* ```
 */
 pub fn or(a: bool, b: bool) -> bool { a || b }
 
@@ -105,15 +105,15 @@ pub fn or(a: bool, b: bool) -> bool { a || b }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::xor(true, false)
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::xor(true, true)
 * false
-* ~~~
+* ```
 */
 pub fn xor(a: bool, b: bool) -> bool { (a && !b) || (!a && b) }
 
@@ -126,15 +126,15 @@ pub fn xor(a: bool, b: bool) -> bool { (a && !b) || (!a && b) }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::implies(true, true)
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::implies(true, false)
 * false
-* ~~~
+* ```
 */
 pub fn implies(a: bool, b: bool) -> bool { !a || b }
 
@@ -143,15 +143,15 @@ pub fn implies(a: bool, b: bool) -> bool { !a || b }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::is_true(true)
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::is_true(false)
 * false
-* ~~~
+* ```
 */
 pub fn is_true(v: bool) -> bool { v }
 
@@ -160,15 +160,15 @@ pub fn is_true(v: bool) -> bool { v }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::is_false(false)
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::is_false(true)
 * false
-* ~~~
+* ```
 */
 pub fn is_false(v: bool) -> bool { !v }
 
@@ -179,20 +179,20 @@ pub fn is_false(v: bool) -> bool { !v }
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> FromStr::from_str::<bool>("true")
 * Some(true)
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> FromStr::from_str::<bool>("false")
 * Some(false)
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> FromStr::from_str::<bool>("not even a boolean")
 * None
-* ~~~
+* ```
 */
 impl FromStr for bool {
     fn from_str(s: &str) -> Option<bool> {
@@ -209,15 +209,15 @@ fn from_str(s: &str) -> Option<bool> {
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> true.to_str()
 * "true"
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> false.to_str()
 * "false"
-* ~~~
+* ```
 */
 impl ToStr for bool {
     #[inline]
@@ -232,11 +232,11 @@ fn to_str(&self) -> ~str {
 * There are no guarantees about the order values will be given.
 *
 * # Examples
-* ~~~
+* ```
 * do std::bool::all_values |x: bool| {
 *     println(x.to_str())
 * }
-* ~~~
+* ```
 */
 pub fn all_values(blk: &fn(v: bool)) {
     blk(true);
@@ -248,15 +248,15 @@ pub fn all_values(blk: &fn(v: bool)) {
 *
 * # Examples
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::to_bit(true)
 * 1
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> std::bool::to_bit(false)
 * 0
-* ~~~
+* ```
 */
 #[inline]
 pub fn to_bit(v: bool) -> u8 { if v { 1u8 } else { 0u8 } }
@@ -269,12 +269,12 @@ pub fn to_bit(v: bool) -> u8 { if v { 1u8 } else { 0u8 } }
 * ~~~rust
 * rusti> !true
 * false
-* ~~~
+* ```
 *
 * ~~~rust
 * rusti> !false
 * true
-* ~~~
+* ```
 */
 #[cfg(not(test))]
 impl Not<bool> for bool {
@@ -299,25 +299,25 @@ fn cmp(&self, other: &bool) -> Ordering { to_bit(*self).cmp(&to_bit(*other)) }
 *
 * Two booleans are equal if they have the same value.
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> false.eq(&true)
 * false
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> false == false
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> false != true
 * true
-* ~~~
+* ```
 *
-* ~~~ {.rust}
+* ```rust
 * rusti> false.ne(&false)
 * false
-* ~~~
+* ```
 */
 #[cfg(not(test))]
 impl Eq for bool {
index 3c50ff35358e7ccf14238e93b1a376790e552fa8..823cc0db4b9b111b83778acf008cb3a852eb0dfd 100644 (file)
@@ -38,7 +38,7 @@
 
 An example of creating and using a C string would be:
 
-~~~{.rust}
+```rust
 use std::libc;
 externfn!(fn puts(s: *libc::c_char))
 
@@ -56,7 +56,7 @@
 do my_string.with_c_str |c_buffer| {
     unsafe { puts(c_buffer); }
 }
-~~~
+ ```
 
 */
 
@@ -204,9 +204,9 @@ pub trait ToCStr {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let s = "PATH".with_c_str(|path| libc::getenv(path))
-    /// ~~~
+    /// ```
     ///
     /// # Failure
     ///
index a4e18d98f47ea6c90209ef76183942a09f700bca..e028bbeac68a122d26ca2c226166fb946283293b 100644 (file)
@@ -67,10 +67,10 @@ pub unsafe fn unsafe_copy<T>(thing: &T) -> T {
  *
  * # Example
  *
- * ~~~ {.rust}
+ * ```rust
  * let v: &[u8] = transmute("L");
  * assert!(v == [76u8]);
- * ~~~
+ * ```
  */
 #[inline]
 pub unsafe fn transmute<L, G>(thing: L) -> G {
index c47dcfe3de69a1ce2db0bf8372303a2becb5144d..39db1df3df1c86ac74b9c0cb6a3b81427baa1f75 100644 (file)
 
 A condition is declared through the `condition!` macro provided by the compiler:
 
-~~~{.rust}
+```rust
 condition! {
     pub my_error: int -> ~str;
 }
-~~~
+ ```
 
 This macro declares an inner module called `my_error` with one static variable,
 `cond` that is a static `Condition` instance. To help understand what the other
 parameters are used for, an example usage of this condition would be:
 
-~~~{.rust}
+```rust
 do my_error::cond.trap(|raised_int| {
 
     // the condition `my_error` was raised on, and the value it raised is stored
@@ -51,7 +51,7 @@
     println(my_error::cond.raise(4)); // prints "oh well"
 
 }
-~~~
+ ```
 
 Condition handling is useful in cases where propagating errors is either to
 cumbersome or just not necessary in the first place. It should also be noted,
@@ -96,14 +96,14 @@ impl<T, U> Condition<T, U> {
     ///
     /// # Example
     ///
-    /// ~~~{.rust}
+    /// ```rust
     /// condition! { my_error: int -> int; }
     ///
     /// let trap = my_error::cond.trap(|error| error + 3);
     ///
     /// // use `trap`'s inside method to register the handler and then run a
     /// // block of code with the handler registered
-    /// ~~~
+    /// ```
     pub fn trap<'a>(&'a self, h: &'a fn(T) -> U) -> Trap<'a, T, U> {
         let h: Closure = unsafe { ::cast::transmute(h) };
         let prev = local_data::get(self.key, |k| k.map(|&x| *x));
@@ -173,14 +173,14 @@ impl<'self, T, U> Trap<'self, T, U> {
     ///
     /// # Example
     ///
-    /// ~~~{.rust}
+    /// ```rust
     /// condition! { my_error: int -> int; }
     ///
     /// let result = do my_error::cond.trap(|error| error + 3).inside {
     ///     my_error::cond.raise(4)
     /// };
     /// assert_eq!(result, 7);
-    /// ~~~
+    /// ```
     pub fn inside<V>(&self, inner: &'self fn() -> V) -> V {
         let _g = Guard { cond: self.cond };
         debug!("Trap: pushing handler to TLS");
index 99a5ed4d69812c84dfb7bdb37ca436b9ceb4f058..f35a61677e80c0edef3e410d888efac05f9af806 100644 (file)
 
 Some examples of the `format!` extension are:
 
-~~~{.rust}
+```rust
 format!("Hello")                  // => ~"Hello"
 format!("Hello, {:s}!", "world")  // => ~"Hello, world!"
 format!("The number is {:d}", 1)  // => ~"The number is 1"
 format!("{:?}", ~[3, 4])          // => ~"~[3, 4]"
 format!("{value}", value=4)       // => ~"4"
 format!("{} {}", 1, 2)            // => ~"1 2"
-~~~
+ ```
 
 From these, you can see that the first argument is a format string. It is
 required by the compiler for this to be a string literal; it cannot be a
@@ -67,9 +67,9 @@
 leverage named parameters. Named parameters are listed at the end of the
 argument list and have the syntax:
 
-~~~
+ ```
 identifier '=' expression
-~~~
+ ```
 
 It is illegal to put positional parameters (those without names) after arguments
 which have names. Like positional parameters, it is illegal to provided named
@@ -84,9 +84,9 @@
 is used (the type's rust-representation is printed). For example, this is an
 invalid format string:
 
-~~~
+ ```
 {0:d} {0:s}
-~~~
+ ```
 
 Because the first argument is both referred to as an integer as well as a
 string.
 illegal to reference an argument as such. For example, this is another invalid
 format string:
 
-~~~
+ ```
 {:.*s} {0:u}
-~~~
+ ```
 
 ### Formatting traits
 
 When implementing a format trait for your own time, you will have to implement a
 method of the signature:
 
-~~~{.rust}
+```rust
 fn fmt(value: &T, f: &mut std::fmt::Formatter);
-~~~
+ ```
 
 Your type will be passed by-reference in `value`, and then the function should
 emit output into the `f.buf` stream. It is up to each format trait
 There are a number of related macros in the `format!` family. The ones that are
 currently implemented are:
 
-~~~{.rust}
+```rust
 format!      // described above
 write!       // first argument is a &mut rt::io::Writer, the destination
 writeln!     // same as write but appends a newline
 print!       // the format string is printed to the standard output
 println!     // same as print but appends a newline
 format_args! // described below.
-~~~
+ ```
 
 
 #### `write!`
 strings and instead directly write the output. Under the hood, this function is
 actually invoking the `write` function defined in this module. Example usage is:
 
-~~~{.rust}
+```rust
 use std::rt::io;
 
 let mut w = io::mem::MemWriter::new();
 write!(&mut w as &mut io::Writer, "Hello {}!", "world");
-~~~
+ ```
 
 #### `print!`
 
 the goal of these macros is to avoid intermediate allocations when printing
 output. Example usage is:
 
-~~~{.rust}
+```rust
 print!("Hello {}!", "world");
 println!("I have a newline {}", "character at the end");
-~~~
+ ```
 
 #### `format_args!`
 This is a curious macro which is used to safely pass around
 the related macros are implemented in terms of this. First
 off, some example usage is:
 
-~~~{.rust}
+```rust
 use std::fmt;
 
 format_args!(fmt::format, "this returns {}", "~str");
 format_args!(|args| { fmt::write(my_writer, args) }, "some {}", "args");
 format_args!(my_fn, "format {}", "string");
-~~~
+ ```
 
 The first argument of the `format_args!` macro is a function (or closure) which
 takes one argument of type `&fmt::Arguments`. This structure can then be
 to reference the string value of the argument which was selected upon. As an
 example:
 
-~~~{.rust}
+```rust
 format!("{0, select, other{#}}", "hello") // => ~"hello"
-~~~
+ ```
 
 This example is the equivalent of `{0:s}` essentially.
 
 The select method is a switch over a `&str` parameter, and the parameter *must*
 be of the type `&str`. An example of the syntax is:
 
-~~~
+ ```
 {0, select, male{...} female{...} other{...}}
-~~~
+ ```
 
 Breaking this down, the `0`-th argument is selected upon with the `select`
 method, and then a number of cases follow. Each case is preceded by an
 parameter *must* be a `uint`. A plural method in its full glory can be specified
 as:
 
-~~~
+ ```
 {0, plural, offset=1 =1{...} two{...} many{...} other{...}}
-~~~
+ ```
 
 To break this down, the first `0` indicates that this method is selecting over
 the value of the first positional parameter to the format string. Next, the
 meaning that arguments are surrounded by `{}` instead of the C-like `%`. The
 actual grammar for the formatting syntax is:
 
-~~~
+ ```
 format_string := <text> [ format <text> ] *
 format := '{' [ argument ] [ ':' format_spec ] [ ',' function_spec ] '}'
 argument := integer | identifier
 selector := '=' integer | keyword
 keyword := 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
 arm := '{' format_string '}'
-~~~
+ ```
 
 ## Formatting Parameters
 
@@ -516,11 +516,11 @@ pub struct Arguments<'self> {
 ///
 /// # Example
 ///
-/// ~~~{.rust}
+/// ```rust
 /// use std::fmt;
 /// let w: &mut io::Writer = ...;
 /// format_args!(|args| { fmt::write(w, args) }, "Hello, {}!", "world");
-/// ~~~
+/// ```
 pub fn write(output: &mut io::Writer, args: &Arguments) {
     unsafe { write_unsafe(output, args.fmt, args.args) }
 }
@@ -581,11 +581,11 @@ pub unsafe fn write_unsafe(output: &mut io::Writer,
 ///
 /// # Example
 ///
-/// ~~~{.rust}
+/// ```rust
 /// use std::fmt;
 /// let s = format_args!(fmt::format, "Hello, {}!", "world");
 /// assert_eq!(s, "Hello, world!");
-/// ~~~
+/// ```
 pub fn format(args: &Arguments) -> ~str {
     unsafe { format_unsafe(args.fmt, args.args) }
 }
index ab8af22e1165ef0b0aeb8054e8e9fd7d9a28bd5a..859cf20fa4184f95f2179417b0d3b7fc14fe4ffc 100644 (file)
@@ -1047,11 +1047,11 @@ pub fn FILE_reader(f: *libc::FILE, cleanup: bool) -> @Reader {
 *
 * # Example
 *
-* ~~~ {.rust}
+* ```rust
 * let stdin = std::io::stdin();
 * let line = stdin.read_line();
 * std::io::print(line);
-* ~~~
+* ```
 */
 pub fn stdin() -> @Reader {
     #[fixed_stack_segment]; #[inline(never)];
@@ -1650,10 +1650,10 @@ pub fn buffered_file_writer(path: &Path) -> Result<@Writer, ~str> {
 *
 * # Example
 *
-* ~~~ {.rust}
+* ```rust
 * let stdout = std::io::stdout();
 * stdout.write_str("hello\n");
-* ~~~
+* ```
 */
 pub fn stdout() -> @Writer { fd_writer(libc::STDOUT_FILENO as c_int, false) }
 
@@ -1662,10 +1662,10 @@ pub fn stdout() -> @Writer { fd_writer(libc::STDOUT_FILENO as c_int, false) }
 *
 * # Example
 *
-* ~~~ {.rust}
+* ```rust
 * let stderr = std::io::stderr();
 * stderr.write_str("hello\n");
-* ~~~
+* ```
 */
 pub fn stderr() -> @Writer { fd_writer(libc::STDERR_FILENO as c_int, false) }
 
@@ -1677,10 +1677,10 @@ pub fn stderr() -> @Writer { fd_writer(libc::STDERR_FILENO as c_int, false) }
 *
 * # Example
 *
-* ~~~ {.rust}
+* ```rust
 * // print is imported into the prelude, and so is always available.
 * print("hello");
-* ~~~
+* ```
 */
 pub fn print(s: &str) {
     stdout().write_str(s);
@@ -1693,10 +1693,10 @@ pub fn print(s: &str) {
 *
 * # Example
 *
-* ~~~ {.rust}
+* ```rust
 * // println is imported into the prelude, and so is always available.
 * println("hello");
-* ~~~
+* ```
 */
 pub fn println(s: &str) {
     stdout().write_line(s);
index 87fad9aae70a97de06f0452bb5fd97b53a82d7c5..0e4cb895249a2430a631144ca4c0c5074c7e3b04 100644 (file)
@@ -34,7 +34,7 @@ trait defined in this module. For loops can be viewed as a syntactical expansion
 into a `loop`, for example, the `for` loop in this example is essentially
 translated to the `loop` below.
 
-~~~{.rust}
+```rust
 let values = ~[1, 2, 3];
 
 // "Syntactical sugar" taking advantage of an iterator
@@ -52,7 +52,7 @@ trait defined in this module. For loops can be viewed as a syntactical expansion
         None => { break }
     }
 }
-~~~
+ ```
 
 This `for` loop syntax can be applied to any iterator over any type.
 
@@ -111,14 +111,14 @@ fn size_hint(&self) -> (uint, Option<uint>) { (0, None) }
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [0];
     /// let b = [1];
     /// let mut it = a.iter().chain(b.iter());
     /// assert_eq!(it.next().get(), &0);
     /// assert_eq!(it.next().get(), &1);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn chain<U: Iterator<A>>(self, other: U) -> Chain<Self, U> {
         Chain{a: self, b: other, flag: false}
@@ -131,13 +131,13 @@ fn chain<U: Iterator<A>>(self, other: U) -> Chain<Self, U> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [0];
     /// let b = [1];
     /// let mut it = a.iter().zip(b.iter());
     /// assert_eq!(it.next().get(), (&0, &1));
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn zip<B, U: Iterator<B>>(self, other: U) -> Zip<Self, U> {
         Zip{a: self, b: other}
@@ -148,13 +148,13 @@ fn zip<B, U: Iterator<B>>(self, other: U) -> Zip<Self, U> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2];
     /// let mut it = a.iter().map(|&x| 2 * x);
     /// assert_eq!(it.next().get(), 2);
     /// assert_eq!(it.next().get(), 4);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn map<'r, B>(self, f: &'r fn(A) -> B) -> Map<'r, A, B, Self> {
         Map{iter: self, f: f}
@@ -166,12 +166,12 @@ fn map<'r, B>(self, f: &'r fn(A) -> B) -> Map<'r, A, B, Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2];
     /// let mut it = a.iter().filter(|&x| *x > 1);
     /// assert_eq!(it.next().get(), &2);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> Filter<'r, A, Self> {
         Filter{iter: self, predicate: predicate}
@@ -183,12 +183,12 @@ fn filter<'r>(self, predicate: &'r fn(&A) -> bool) -> Filter<'r, A, Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2];
     /// let mut it = a.iter().filter_map(|&x| if x > 1 {Some(2 * x)} else {None});
     /// assert_eq!(it.next().get(), 4);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn filter_map<'r, B>(self, f: &'r fn(A) -> Option<B>) -> FilterMap<'r, A, B, Self> {
         FilterMap { iter: self, f: f }
@@ -199,13 +199,13 @@ fn filter_map<'r, B>(self, f: &'r fn(A) -> Option<B>) -> FilterMap<'r, A, B, Sel
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [100, 200];
     /// let mut it = a.iter().enumerate();
     /// assert_eq!(it.next().get(), (0, &100));
     /// assert_eq!(it.next().get(), (1, &200));
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn enumerate(self) -> Enumerate<Self> {
         Enumerate{iter: self, count: 0}
@@ -217,7 +217,7 @@ fn enumerate(self) -> Enumerate<Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [100, 200, 300];
     /// let mut it = xs.iter().map(|&x|x).peekable();
     /// assert_eq!(it.peek().unwrap(), &100);
@@ -228,7 +228,7 @@ fn enumerate(self) -> Enumerate<Self> {
     /// assert_eq!(it.next().unwrap(), 300);
     /// assert!(it.peek().is_none());
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn peekable(self) -> Peekable<A, Self> {
         Peekable{iter: self, peeked: None}
@@ -240,14 +240,14 @@ fn peekable(self) -> Peekable<A, Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 2, 1];
     /// let mut it = a.iter().skip_while(|&a| *a < 3);
     /// assert_eq!(it.next().get(), &3);
     /// assert_eq!(it.next().get(), &2);
     /// assert_eq!(it.next().get(), &1);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhile<'r, A, Self> {
         SkipWhile{iter: self, flag: false, predicate: predicate}
@@ -259,13 +259,13 @@ fn skip_while<'r>(self, predicate: &'r fn(&A) -> bool) -> SkipWhile<'r, A, Self>
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 2, 1];
     /// let mut it = a.iter().take_while(|&a| *a < 3);
     /// assert_eq!(it.next().get(), &1);
     /// assert_eq!(it.next().get(), &2);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhile<'r, A, Self> {
         TakeWhile{iter: self, flag: false, predicate: predicate}
@@ -276,13 +276,13 @@ fn take_while<'r>(self, predicate: &'r fn(&A) -> bool) -> TakeWhile<'r, A, Self>
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter().skip(3);
     /// assert_eq!(it.next().get(), &4);
     /// assert_eq!(it.next().get(), &5);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn skip(self, n: uint) -> Skip<Self> {
         Skip{iter: self, n: n}
@@ -293,14 +293,14 @@ fn skip(self, n: uint) -> Skip<Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter().take(3);
     /// assert_eq!(it.next().get(), &1);
     /// assert_eq!(it.next().get(), &2);
     /// assert_eq!(it.next().get(), &3);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn take(self, n: uint) -> Take<Self> {
         Take{iter: self, n: n}
@@ -313,7 +313,7 @@ fn take(self, n: uint) -> Take<Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter().scan(1, |fac, &x| {
     ///   *fac = *fac * x;
@@ -325,7 +325,7 @@ fn take(self, n: uint) -> Take<Self> {
     /// assert_eq!(it.next().get(), 24);
     /// assert_eq!(it.next().get(), 120);
     /// assert!(it.next().is_none());
-    /// ~~~
+    /// ```
     #[inline]
     fn scan<'r, St, B>(self, initial_state: St, f: &'r fn(&mut St, A) -> Option<B>)
         -> Scan<'r, A, B, Self, St> {
@@ -337,7 +337,7 @@ fn scan<'r, St, B>(self, initial_state: St, f: &'r fn(&mut St, A) -> Option<B>)
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let xs = [2u, 3];
     /// let ys = [0u, 1, 0, 1, 2];
     /// let mut it = xs.iter().flat_map(|&x| count(0u, 1).take(x));
@@ -347,7 +347,7 @@ fn scan<'r, St, B>(self, initial_state: St, f: &'r fn(&mut St, A) -> Option<B>)
     ///     assert_eq!(x, ys[i]);
     ///     i += 1;
     /// }
-    /// ~~~
+    /// ```
     #[inline]
     fn flat_map<'r, B, U: Iterator<B>>(self, f: &'r fn(A) -> U)
         -> FlatMap<'r, A, Self, U> {
@@ -360,7 +360,7 @@ fn flat_map<'r, B, U: Iterator<B>>(self, f: &'r fn(A) -> U)
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// fn process<U: Iterator<int>>(it: U) -> int {
     ///     let mut it = it.fuse();
     ///     let mut sum = 0;
@@ -378,7 +378,7 @@ fn flat_map<'r, B, U: Iterator<B>>(self, f: &'r fn(A) -> U)
     /// }
     /// let x = ~[1,2,3,7,8,9];
     /// assert_eq!(process(x.move_iter()), 1006);
-    /// ~~~
+    /// ```
     #[inline]
     fn fuse(self) -> Fuse<Self> {
         Fuse{iter: self, done: false}
@@ -390,7 +390,7 @@ fn fuse(self) -> Fuse<Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     ///let xs = [1u, 4, 2, 3, 8, 9, 6];
     ///let sum = xs.iter()
     ///            .map(|&x| x)
@@ -399,7 +399,7 @@ fn fuse(self) -> Fuse<Self> {
     ///            .inspect(|&x| debug!("%u made it through", x))
     ///            .sum();
     ///println(sum.to_str());
-    /// ~~~
+    /// ```
     #[inline]
     fn inspect<'r>(self, f: &'r fn(&A)) -> Inspect<'r, A, Self> {
         Inspect{iter: self, f: f}
@@ -409,13 +409,13 @@ fn inspect<'r>(self, f: &'r fn(&A)) -> Inspect<'r, A, Self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::iter::count;
     ///
     /// for i in count(0, 10) {
     ///     printfln!("%d", i);
     /// }
-    /// ~~~
+    /// ```
     #[inline]
     fn advance(&mut self, f: &fn(A) -> bool) -> bool {
         loop {
@@ -433,11 +433,11 @@ fn advance(&mut self, f: &fn(A) -> bool) -> bool {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let b: ~[int] = a.iter().map(|&x| x).collect();
     /// assert!(a == b);
-    /// ~~~
+    /// ```
     #[inline]
     fn collect<B: FromIterator<A>>(&mut self) -> B {
         FromIterator::from_iterator(self)
@@ -448,11 +448,11 @@ fn collect<B: FromIterator<A>>(&mut self) -> B {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let b: ~[int] = a.iter().map(|&x| x).to_owned_vec();
     /// assert!(a == b);
-    /// ~~~
+    /// ```
     #[inline]
     fn to_owned_vec(&mut self) -> ~[A] {
         self.collect()
@@ -463,12 +463,12 @@ fn to_owned_vec(&mut self) -> ~[A] {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter();
     /// assert!(it.nth(2).get() == &3);
     /// assert!(it.nth(2) == None);
-    /// ~~~
+    /// ```
     #[inline]
     fn nth(&mut self, mut n: uint) -> Option<A> {
         loop {
@@ -485,10 +485,10 @@ fn nth(&mut self, mut n: uint) -> Option<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().last().get() == &5);
-    /// ~~~
+    /// ```
     #[inline]
     fn last(&mut self) -> Option<A> {
         let mut last = None;
@@ -501,10 +501,10 @@ fn last(&mut self) -> Option<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().fold(0, |a, &b| a + b) == 15);
-    /// ~~~
+    /// ```
     #[inline]
     fn fold<B>(&mut self, init: B, f: &fn(B, A) -> B) -> B {
         let mut accum = init;
@@ -521,12 +521,12 @@ fn fold<B>(&mut self, init: B, f: &fn(B, A) -> B) -> B {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter();
     /// assert!(it.len() == 5);
     /// assert!(it.len() == 0);
-    /// ~~~
+    /// ```
     #[inline]
     fn len(&mut self) -> uint {
         self.fold(0, |cnt, _x| cnt + 1)
@@ -536,11 +536,11 @@ fn len(&mut self) -> uint {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().all(|&x| *x > 0));
     /// assert!(!a.iter().all(|&x| *x > 2));
-    /// ~~~
+    /// ```
     #[inline]
     fn all(&mut self, f: &fn(A) -> bool) -> bool {
         for x in *self { if !f(x) { return false; } }
@@ -552,12 +552,12 @@ fn all(&mut self, f: &fn(A) -> bool) -> bool {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter();
     /// assert!(it.any(|&x| *x == 3));
     /// assert!(!it.any(|&x| *x == 3));
-    /// ~~~
+    /// ```
     #[inline]
     fn any(&mut self, f: &fn(A) -> bool) -> bool {
         for x in *self { if f(x) { return true; } }
@@ -601,10 +601,10 @@ fn count(&mut self, predicate: &fn(A) -> bool) -> uint {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let xs = [-3, 0, 1, 5, -10];
     /// assert_eq!(*xs.iter().max_by(|x| x.abs()).unwrap(), -10);
-    /// ~~~
+    /// ```
     #[inline]
     fn max_by<B: Ord>(&mut self, f: &fn(&A) -> B) -> Option<A> {
         self.fold(None, |max: Option<(A, B)>, x| {
@@ -625,10 +625,10 @@ fn max_by<B: Ord>(&mut self, f: &fn(&A) -> B) -> Option<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let xs = [-3, 0, 1, 5, -10];
     /// assert_eq!(*xs.iter().min_by(|x| x.abs()).unwrap(), 0);
-    /// ~~~
+    /// ```
     #[inline]
     fn min_by<B: Ord>(&mut self, f: &fn(&A) -> B) -> Option<A> {
         self.fold(None, |min: Option<(A, B)>, x| {
@@ -777,11 +777,11 @@ pub trait AdditiveIterator<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// let mut it = a.iter().map(|&x| x);
     /// assert!(it.sum() == 15);
-    /// ~~~
+    /// ```
     fn sum(&mut self) -> A;
 }
 
@@ -800,7 +800,7 @@ pub trait MultiplicativeIterator<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::iter::count;
     ///
     /// fn factorial(n: uint) -> uint {
@@ -809,7 +809,7 @@ pub trait MultiplicativeIterator<A> {
     /// assert!(factorial(0) == 1);
     /// assert!(factorial(1) == 1);
     /// assert!(factorial(5) == 120);
-    /// ~~~
+    /// ```
     fn product(&mut self) -> A;
 }
 
@@ -828,20 +828,20 @@ pub trait OrdIterator<A> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().max().get() == &5);
-    /// ~~~
+    /// ```
     fn max(&mut self) -> Option<A>;
 
     /// Consumes the entire iterator to return the minimum element.
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = [1, 2, 3, 4, 5];
     /// assert!(a.iter().min().get() == &1);
-    /// ~~~
+    /// ```
     fn min(&mut self) -> Option<A>;
 }
 
@@ -873,12 +873,12 @@ pub trait ClonableIterator {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let a = count(1,1).take(1);
     /// let mut cy = a.cycle();
     /// assert_eq!(cy.next(), Some(1));
     /// assert_eq!(cy.next(), Some(1));
-    /// ~~~
+    /// ```
     fn cycle(self) -> Cycle<Self>;
 }
 
index 9c4e5ba60df752c986376d7ecc0e3ba1f295500d..5058821d4568cec5bc8d7fc785291a75741af09e 100644 (file)
@@ -22,7 +22,7 @@
 named and annotated. This name is then passed to the functions in this module to
 modify/read the slot specified by the key.
 
-~~~{.rust}
+```rust
 use std::local_data;
 
 local_data_key!(key_int: int)
@@ -33,7 +33,7 @@
 
 local_data::set(key_vector, ~[4]);
 local_data::get(key_vector, |opt| assert_eq!(opt, Some(&~[4])));
-~~~
+ ```
 
 */
 
index a2a0a1ab13b5c908f8a9ac3676a768aee88754f6..afa1acd08970ae3eedee9f0ad6b6e4adf1b8be79 100644 (file)
@@ -346,9 +346,9 @@ fn trunc(&self) -> f32 { trunc(*self) }
     ///
     /// The fractional part of the number, satisfying:
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert!(x == trunc(x) + fract(x))
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn fract(&self) -> f32 { *self - self.trunc() }
index f7c31c4025049b80a075b60d20b62ad60b402895..5dbeb6c298f8f97fd42b5ffe1119bba3282f42d1 100644 (file)
@@ -364,9 +364,9 @@ fn trunc(&self) -> f64 { trunc(*self) }
     ///
     /// The fractional part of the number, satisfying:
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert!(x == trunc(x) + fract(x))
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn fract(&self) -> f64 { *self - self.trunc() }
index dc46d4fec32ff3d391de94bbb2241ce2fc69a776..7af47355c8c44b00b69db64b8982811a11d9fca5 100644 (file)
@@ -414,9 +414,9 @@ fn trunc(&self) -> float { (*self as f64).trunc() as float }
     ///
     /// The fractional part of the number, satisfying:
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert!(x == trunc(x) + fract(x))
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn fract(&self) -> float { *self - self.trunc() }
index ba8beeba4f66facf419fd28d600eec4e09ed9187..1070e8e592f0eaeb8990eb037aee58e4979bf0d9 100644 (file)
@@ -118,7 +118,7 @@ impl Div<$T,$T> for $T {
     ///
     /// # Examples
     ///
-    /// ~~~
+    /// ```
     /// assert!( 8 /  3 ==  2);
     /// assert!( 8 / -3 == -2);
     /// assert!(-8 /  3 == -2);
@@ -128,7 +128,7 @@ impl Div<$T,$T> for $T {
     /// assert!( 1 / -2 ==  0);
     /// assert!(-1 /  2 ==  0);
     /// assert!(-1 / -2 ==  0);
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn div(&self, other: &$T) -> $T { *self / *other }
@@ -139,13 +139,13 @@ impl Rem<$T,$T> for $T {
     ///
     /// Returns the integer remainder after division, satisfying:
     ///
-    /// ~~~
+    /// ```
     /// assert!((n / d) * d + (n % d) == n)
-    /// ~~~
+    /// ```
     ///
     /// # Examples
     ///
-    /// ~~~
+    /// ```
     /// assert!( 8 %  3 ==  2);
     /// assert!( 8 % -3 ==  2);
     /// assert!(-8 %  3 == -2);
@@ -155,7 +155,7 @@ impl Rem<$T,$T> for $T {
     /// assert!( 1 % -2 ==  1);
     /// assert!(-1 %  2 == -1);
     /// assert!(-1 % -2 == -1);
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn rem(&self, other: &$T) -> $T { *self % *other }
@@ -214,7 +214,7 @@ impl Integer for $T {
     ///
     /// # Examples
     ///
-    /// ~~~
+    /// ```
     /// assert!(( 8).div_floor( 3) ==  2);
     /// assert!(( 8).div_floor(-3) == -3);
     /// assert!((-8).div_floor( 3) == -3);
@@ -224,7 +224,7 @@ impl Integer for $T {
     /// assert!(( 1).div_floor(-2) == -1);
     /// assert!((-1).div_floor( 2) == -1);
     /// assert!((-1).div_floor(-2) ==  0);
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn div_floor(&self, other: &$T) -> $T {
@@ -240,13 +240,13 @@ fn div_floor(&self, other: &$T) -> $T {
     ///
     /// Integer modulo, satisfying:
     ///
-    /// ~~~
+    /// ```
     /// assert!(n.div_floor(d) * d + n.mod_floor(d) == n)
-    /// ~~~
+    /// ```
     ///
     /// # Examples
     ///
-    /// ~~~
+    /// ```
     /// assert!(( 8).mod_floor( 3) ==  2);
     /// assert!(( 8).mod_floor(-3) == -1);
     /// assert!((-8).mod_floor( 3) ==  1);
@@ -256,7 +256,7 @@ fn div_floor(&self, other: &$T) -> $T {
     /// assert!(( 1).mod_floor(-2) == -1);
     /// assert!((-1).mod_floor( 2) ==  1);
     /// assert!((-1).mod_floor(-2) == -1);
-    /// ~~~
+    /// ```
     ///
     #[inline]
     fn mod_floor(&self, other: &$T) -> $T {
index 634c86104fbabee655406cd294221715b650329d..a60bf2f33a99d9e2aa458ec230e0ca75bf578e94 100644 (file)
@@ -82,12 +82,12 @@ pub trait Unsigned: Num {}
 
 /// Times trait
 ///
-/// ~~~ {.rust}
+/// ```rust
 /// use num::Times;
 /// let ten = 10 as uint;
 /// let mut accum = 0;
 /// do ten.times { accum += 1; }
-/// ~~~
+/// ```
 ///
 pub trait Times {
     fn times(&self, it: &fn());
@@ -357,10 +357,10 @@ pub trait Float: Real
 ///
 /// # Example
 ///
-/// ~~~
+/// ```
 /// let twenty: f32 = num::cast(0x14);
 /// assert_eq!(twenty, 20f32);
-/// ~~~
+/// ```
 ///
 #[inline]
 pub fn cast<T:NumCast,U:NumCast>(n: T) -> U {
index 37c5807c70b414aae1253b7d91ba76f10ea2b840..a8d4cf541ceebcded311c9c332f94592ff6353ff 100644 (file)
@@ -23,7 +23,7 @@
 
 # Example
 
-~~~
+ ```
 let msg = Some(~"howdy");
 
 // Take a reference to the contained string
@@ -37,7 +37,7 @@
     Some(m) => m,
     None => ~"default message"
 };
-~~~
+ ```
 
 */
 
index 336284963a25668ce723779dcf36b4049e779b78..af2565ec67a36eea69fb70c138b03eca36e38f42 100644 (file)
@@ -233,6 +233,21 @@ fn get_relative_to(&self, abs2: (&Self)) -> Self {
         result
     }
 
+
+    /// Returns `true` iff `child` is a suffix of `parent`. See the test
+    /// case for examples.
+    fn is_parent_of(&self, child: &Self) -> bool {
+        if !self.is_absolute() || child.is_absolute()
+            || self.components().len() < child.components().len()
+            || self.components().is_empty() {
+            return false;
+        }
+        let child_components = child.components().len();
+        let parent_components = self.components().len();
+        let to_drop = self.components().len() - child_components;
+        self.components().slice(to_drop, parent_components) == child.components()
+    }
+
     fn components<'a>(&'a self) -> &'a [~str];
 }
 
@@ -1450,4 +1465,43 @@ fn test_relative_to8() {
 
     }
 
+
+    #[test]
+    fn test_is_parent_of() {
+        fn is_parent_of_pp(p: &PosixPath, q: &PosixPath) -> bool {
+            p.is_parent_of(q)
+        }
+
+        assert!(is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("c/d/e")));
+        assert!(!is_parent_of_pp(&PosixPath("a/b/c/d/e"), &PosixPath("c/d/e")));
+        assert!(!is_parent_of_pp(&PosixPath("/a/b/c/d/e"), &PosixPath("/c/d/e")));
+        assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("")));
+        assert!(!is_parent_of_pp(&PosixPath(""), &PosixPath("a/b/c")));
+        assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("")));
+        assert!(is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("a/b/c")));
+        assert!(!is_parent_of_pp(&PosixPath("/a/b/c"), &PosixPath("d/e/f")));
+
+        fn is_parent_of_wp(p: &WindowsPath, q: &WindowsPath) -> bool {
+            p.is_parent_of(q)
+        }
+
+        let abcde = WindowsPath("C:\\a\\b\\c\\d\\e");
+        let rel_abcde = WindowsPath("a\\b\\c\\d\\e");
+        let cde   = WindowsPath("c\\d\\e");
+        let slashcde = WindowsPath("C:\\c\\d\\e");
+        let empty = WindowsPath("");
+        let abc = WindowsPath("C:\\a\\b\\c");
+        let rel_abc = WindowsPath("a\\b\\c");
+        let def = WindowsPath("d\\e\\f");
+
+        assert!(is_parent_of_wp(&abcde, &cde));
+        assert!(!is_parent_of_wp(&rel_abcde, &cde));
+        assert!(!is_parent_of_wp(&abcde, &slashcde));
+        assert!(!is_parent_of_wp(&empty, &empty));
+        assert!(!is_parent_of_wp(&empty, &rel_abc));
+        assert!(is_parent_of_wp(&abc, &empty));
+        assert!(is_parent_of_wp(&abc, &rel_abc));
+        assert!(!is_parent_of_wp(&abc, &def));
+    }
+
 }
index 1672f0a902e71282f767c97aa6178e1237808373..96ade70f007b597d4aa075b24f3b004d7f9ab223 100644 (file)
 pub use to_bytes::IterBytes;
 pub use to_str::{ToStr, ToStrConsume};
 pub use tuple::{CopyableTuple, ImmutableTuple};
-pub use tuple::{CloneableTuple1, ImmutableTuple1};
-pub use tuple::{CloneableTuple2, CloneableTuple3, CloneableTuple4, CloneableTuple5};
-pub use tuple::{CloneableTuple6, CloneableTuple7, CloneableTuple8, CloneableTuple9};
-pub use tuple::{CloneableTuple10, CloneableTuple11, CloneableTuple12};
+pub use tuple::{Tuple1, ImmutableTuple1};
+pub use tuple::{Tuple2, Tuple3, Tuple4, Tuple5};
+pub use tuple::{Tuple6, Tuple7, Tuple8, Tuple9};
+pub use tuple::{Tuple10, Tuple11, Tuple12};
 pub use tuple::{ImmutableTuple2, ImmutableTuple3, ImmutableTuple4, ImmutableTuple5};
 pub use tuple::{ImmutableTuple6, ImmutableTuple7, ImmutableTuple8, ImmutableTuple9};
 pub use tuple::{ImmutableTuple10, ImmutableTuple11, ImmutableTuple12};
index 6d08b3c84bdedbc3bf6416050339e5758762e872..1cdf4d6da95101b1869d68fdf9e02c553ca1d8ca 100644 (file)
@@ -65,14 +65,14 @@ fn ziggurat<R:Rng>(rng: &mut R,
 ///
 /// # Example
 ///
-/// ~~~
+/// ```
 /// use std::rand::distributions::StandardNormal;
 ///
 /// fn main() {
 ///     let normal = 2.0 + (*rand::random::<StandardNormal>()) * 3.0;
 ///     printfln!("%f is from a N(2, 9) distribution", normal)
 /// }
-/// ~~~
+/// ```
 pub struct StandardNormal(f64);
 
 impl Rand for StandardNormal {
@@ -119,14 +119,14 @@ fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
 ///
 /// # Example
 ///
-/// ~~~
+/// ```
 /// use std::rand::distributions::Exp1;
 ///
 /// fn main() {
 ///     let exp2 = (*rand::random::<Exp1>()) * 0.5;
 ///     printfln!("%f is from a Exp(2) distribution", exp2);
 /// }
-/// ~~~
+/// ```
 pub struct Exp1(f64);
 
 // This could be done via `-rng.gen::<f64>().ln()` but that is slower.
index 7b753f821d71fd5d7b89394ee6afd35b40cd3a81..832978a0f1064dbe95b5357528438dbeea095f55 100644 (file)
@@ -21,7 +21,7 @@
 
 # Examples
 
-~~~ {.rust}
+```rust
 use std::rand;
 use std::rand::Rng;
 
@@ -31,16 +31,16 @@ fn main() {
         printfln!("int: %d, uint: %u", rng.gen(), rng.gen())
     }
 }
-~~~
+ ```
 
-~~~ {.rust}
+```rust
 use std::rand;
 
 fn main () {
     let tuple_ptr = rand::random::<~(f64, char)>();
     printfln!(tuple_ptr)
 }
-~~~
+ ```
 */
 
 use cast;
@@ -264,7 +264,7 @@ pub trait Rng {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
@@ -273,7 +273,7 @@ pub trait Rng {
     ///    printfln!(x);
     ///    printfln!(rng.gen::<(float, bool)>());
     /// }
-    /// ~~~
+    /// ```
     #[inline(always)]
     fn gen<T: Rand>(&mut self) -> T {
         Rand::rand(self)
@@ -283,7 +283,7 @@ fn gen<T: Rand>(&mut self) -> T {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
@@ -292,7 +292,7 @@ fn gen<T: Rand>(&mut self) -> T {
     ///    printfln!(x);
     ///    printfln!(rng.gen_vec::<(float, bool)>(5));
     /// }
-    /// ~~~
+    /// ```
     fn gen_vec<T: Rand>(&mut self, len: uint) -> ~[T] {
         vec::from_fn(len, |_| self.gen())
     }
@@ -308,7 +308,7 @@ fn gen_vec<T: Rand>(&mut self, len: uint) -> ~[T] {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
@@ -318,7 +318,7 @@ fn gen_vec<T: Rand>(&mut self, len: uint) -> ~[T] {
     ///    let m: i16 = rng.gen_integer_range(-40, 400);
     ///    printfln!(m);
     /// }
-    /// ~~~
+    /// ```
     fn gen_integer_range<T: Rand + Int>(&mut self, low: T, high: T) -> T {
         assert!(low < high, "RNG.gen_integer_range called with low >= high");
         let range = (high - low).to_u64();
@@ -335,7 +335,7 @@ fn gen_integer_range<T: Rand + Int>(&mut self, low: T, high: T) -> T {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     /// use std::rand::Rng;
     ///
@@ -343,7 +343,7 @@ fn gen_integer_range<T: Rand + Int>(&mut self, low: T, high: T) -> T {
     ///     let mut rng = rand::rng();
     ///     printfln!("%b", rng.gen_weighted_bool(3));
     /// }
-    /// ~~~
+    /// ```
     fn gen_weighted_bool(&mut self, n: uint) -> bool {
         n == 0 || self.gen_integer_range(0, n) == 0
     }
@@ -353,13 +353,13 @@ fn gen_weighted_bool(&mut self, n: uint) -> bool {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
     ///    println(rand::task_rng().gen_ascii_str(10));
     /// }
-    /// ~~~
+    /// ```
     fn gen_ascii_str(&mut self, len: uint) -> ~str {
         static GEN_ASCII_STR_CHARSET: &'static [u8] = bytes!("ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                                                              abcdefghijklmnopqrstuvwxyz\
@@ -381,14 +381,14 @@ fn choose<T: Clone>(&mut self, values: &[T]) -> T {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
     ///     printfln!(rand::task_rng().choose_option([1,2,4,8,16,32]));
     ///     printfln!(rand::task_rng().choose_option([]));
     /// }
-    /// ~~~
+    /// ```
     fn choose_option<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
         if values.is_empty() {
             None
@@ -402,7 +402,7 @@ fn choose_option<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     /// use std::rand::Rng;
     ///
@@ -413,7 +413,7 @@ fn choose_option<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
     ///              rand::Weighted {weight: 2, item: 'c'}];
     ///     printfln!("%c", rng.choose_weighted(x));
     /// }
-    /// ~~~
+    /// ```
     fn choose_weighted<T:Clone>(&mut self, v: &[Weighted<T>]) -> T {
         self.choose_weighted_option(v).expect("Rng.choose_weighted: total weight is 0")
     }
@@ -423,7 +423,7 @@ fn choose_weighted<T:Clone>(&mut self, v: &[Weighted<T>]) -> T {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     /// use std::rand::Rng;
     ///
@@ -434,7 +434,7 @@ fn choose_weighted<T:Clone>(&mut self, v: &[Weighted<T>]) -> T {
     ///              rand::Weighted {weight: 2, item: 'c'}];
     ///     printfln!(rng.choose_weighted_option(x));
     /// }
-    /// ~~~
+    /// ```
     fn choose_weighted_option<T:Clone>(&mut self, v: &[Weighted<T>])
                                        -> Option<T> {
         let mut total = 0u;
@@ -460,7 +460,7 @@ fn choose_weighted_option<T:Clone>(&mut self, v: &[Weighted<T>])
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     /// use std::rand::Rng;
     ///
@@ -471,7 +471,7 @@ fn choose_weighted_option<T:Clone>(&mut self, v: &[Weighted<T>])
     ///              rand::Weighted {weight: 2, item: 'c'}];
     ///     printfln!(rng.weighted_vec(x));
     /// }
-    /// ~~~
+    /// ```
     fn weighted_vec<T:Clone>(&mut self, v: &[Weighted<T>]) -> ~[T] {
         let mut r = ~[];
         for item in v.iter() {
@@ -486,13 +486,13 @@ fn weighted_vec<T:Clone>(&mut self, v: &[Weighted<T>]) -> ~[T] {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
     ///     printfln!(rand::task_rng().shuffle(~[1,2,3]));
     /// }
-    /// ~~~
+    /// ```
     fn shuffle<T>(&mut self, values: ~[T]) -> ~[T] {
         let mut v = values;
         self.shuffle_mut(v);
@@ -503,7 +503,7 @@ fn shuffle<T>(&mut self, values: ~[T]) -> ~[T] {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
@@ -514,7 +514,7 @@ fn shuffle<T>(&mut self, values: ~[T]) -> ~[T] {
     ///    rng.shuffle_mut(y);
     ///    printfln!(y);
     /// }
-    /// ~~~
+    /// ```
     fn shuffle_mut<T>(&mut self, values: &mut [T]) {
         let mut i = values.len();
         while i >= 2u {
@@ -529,7 +529,7 @@ fn shuffle_mut<T>(&mut self, values: &mut [T]) {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// use std::rand;
     ///
     /// fn main() {
@@ -537,7 +537,7 @@ fn shuffle_mut<T>(&mut self, values: &mut [T]) {
     ///    let sample = rng.sample(range(1, 100), 5);
     ///    printfln!(sample);
     /// }
-    /// ~~~
+    /// ```
     fn sample<A, T: Iterator<A>>(&mut self, iter: T, n: uint) -> ~[A] {
         let mut reservoir : ~[A] = vec::with_capacity(n);
         for (i, elem) in iter.enumerate() {
index 7988f640687bdd8d0f3272307a39e71b2b3f9950..3e801f28991764715aee148334ac10a57cf66486 100644 (file)
@@ -17,7 +17,7 @@
 //!
 //! # Examples
 //!
-//! ~~~
+//! ```
 //! let tcp_stream = TcpStream::connect(addr);
 //! let reader = BufferedReader::new(tcp_stream);
 //!
 //!     Some(nread) => println!("Read {} bytes", nread),
 //!     None => println!("At the end of the stream!")
 //! }
-//! ~~~
+//! ```
 //!
-//! ~~~
+//! ```
 //! let tcp_stream = TcpStream::connect(addr);
 //! let writer = BufferedWriter::new(tcp_stream);
 //!
 //! writer.write("hello, world".as_bytes());
 //! writer.flush();
-//! ~~~
+//! ```
 //!
-//! ~~~
+//! ```
 //! let tcp_stream = TcpStream::connect(addr);
 //! let stream = BufferedStream::new(tcp_stream);
 //!
@@ -48,7 +48,7 @@
 //!     Some(nread) => println!("Read {} bytes", nread),
 //!     None => println!("At the end of the stream!")
 //! }
-//! ~~~
+//! ```
 //!
 
 use prelude::*;
index 2206f8bf6ae0fb9d9ce4353c85093b0b97755a95..b11ee014af9fcc6d59c4d114db4177b42957cbfc 100644 (file)
@@ -477,7 +477,7 @@ fn exists(&self) -> bool {
 ///
 /// * Check if a file exists, reading from it if so
 ///
-/// ~~~{.rust}
+/// ```rust
 /// use std;
 /// use std::path::Path;
 /// use std::rt::io::file::{FileInfo, FileReader};
@@ -489,17 +489,17 @@ fn exists(&self) -> bool {
 ///     reader.read(mem);
 ///     // ...
 /// }
-/// ~~~
+/// ```
 ///
 /// * Is the given path a file?
 ///
-/// ~~~{.rust}
+/// ```rust
 /// let f = get_file_path_from_wherever();
 /// match f.is_file() {
 ///    true => doing_something_with_a_file(f),
 ///    _ => {}
 /// }
-/// ~~~
+/// ```
 pub trait FileInfo : FileSystemInfo {
     /// Whether the underlying implemention (be it a file path,
     /// or something else) points at a "regular file" on the FS. Will return
@@ -574,7 +574,7 @@ impl FileInfo for Path { }
 ///
 /// * Check if a directory exists, `mkdir`'ing it if not
 ///
-/// ~~~{.rust}
+/// ```rust
 /// use std;
 /// use std::path::Path;
 /// use std::rt::io::file::{DirectoryInfo};
@@ -583,11 +583,11 @@ impl FileInfo for Path { }
 /// if !dir.exists() {
 ///     dir.mkdir();
 /// }
-/// ~~~
+/// ```
 ///
 /// * Is the given path a directory? If so, iterate on its contents
 ///
-/// ~~~{.rust}
+/// ```rust
 /// fn visit_dirs(dir: &Path, cb: &fn(&Path)) {
 ///     if dir.is_dir() {
 ///         let contents = dir.readdir();
@@ -598,7 +598,7 @@ impl FileInfo for Path { }
 ///     }
 ///     else { fail!("nope"); }
 /// }
-/// ~~~
+/// ```
 trait DirectoryInfo : FileSystemInfo {
     /// Whether the underlying implemention (be it a file path,
     /// or something else) is pointing at a directory in the underlying FS.
@@ -971,4 +971,4 @@ fn file_test_directoryinfo_readdir() {
             dir.rmdir();
         }
     }
-}
\ No newline at end of file
+}
index b580b752bd985c8242eba880ed6c6d32344a6f7b..c46e1372c6414653190507e476ebcaa8cf5781a0 100644 (file)
@@ -47,4 +47,4 @@ pub fn new() -> MockWriter {
 impl Writer for MockWriter {
     fn write(&mut self, buf: &[u8]) { (self.write)(buf) }
     fn flush(&mut self) { (self.flush)() }
-}
\ No newline at end of file
+}
index 92dc62490ed1b1479bb46093cce62898c9501ea2..6563ac2e96f9e0f13962610c222b9f46225e512b 100644 (file)
 report success if it succeeded and all its children also reported success;
 otherwise, it will report failure. This is most useful for writing test cases:
 
-~~~
+ ```
 #[test]
 fn test_something_in_another_task {
     do spawn {
         assert!(collatz_conjecture_is_false());
     }
 }
-~~~
+ ```
 
 Here, as the child task will certainly outlive the parent task, we might miss
 the failure of the child when deciding whether or not the test case passed.
index 5c1bac7418e6f330ca2d92c478edcdc0cc97fa08..46d655e6379f7840132d9cd3bb15d5af13208104 100644 (file)
@@ -58,8 +58,7 @@
 #[crate_type = "lib"];
 
 #[doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
-      html_favicon_url = "http://www.rust-lang.org/favicon.ico",
-      passes = "strip-hidden")];
+      html_favicon_url = "http://www.rust-lang.org/favicon.ico")];
 
 // Don't link to std. We are std.
 #[no_std];
index c94d8f366a61fd51adc5dab68f2e424145069dcd..8dc6f783fbea76235d5daf1249c0bfcfd0793bac 100644 (file)
 
 As an example, here's a few different kinds of strings.
 
-~~~{.rust}
+```rust
 let owned_string = ~"I am an owned string";
 let managed_string = @"This string is garbage-collected";
 let borrowed_string1 = "This string is borrowed with the 'static lifetime";
 let borrowed_string2: &str = owned_string;   // owned strings can be borrowed
 let borrowed_string3: &str = managed_string; // managed strings can also be borrowed
-~~~
+ ```
 
 From the example above, you can see that rust has 3 different kinds of string
 literals. The owned/managed literals correspond to the owned/managed string
 being mutable/immutable with the same benefits of having mutable strings in
 other languages.
 
-~~~{.rust}
+```rust
 let mut buf = ~"testing";
 buf.push_char(' ');
 buf.push_str("123");
 assert_eq!(buf, ~"testing 123");
-~~~
+ ```
 
 # Representation
 
@@ -1513,10 +1513,10 @@ fn contains_char(&self, needle: char) -> bool {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v: ~[char] = "abc åäö".iter().collect();
     /// assert_eq!(v, ~['a', 'b', 'c', ' ', 'å', 'ä', 'ö']);
-    /// ~~~
+    /// ```
     #[inline]
     fn iter(&self) -> CharIterator<'self> {
         CharIterator{string: *self}
@@ -1558,13 +1558,13 @@ fn char_offset_rev_iter(&self) -> CharOffsetRevIterator<'self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v: ~[&str] = "Mary had a little lamb".split_iter(' ').collect();
     /// assert_eq!(v, ~["Mary", "had", "a", "little", "lamb"]);
     ///
     /// let v: ~[&str] = "abc1def2ghi".split_iter(|c: char| c.is_digit()).collect();
     /// assert_eq!(v, ~["abc", "def", "ghi"]);
-    /// ~~~
+    /// ```
     #[inline]
     fn split_iter<Sep: CharEq>(&self, sep: Sep) -> CharSplitIterator<'self, Sep> {
         CharSplitIterator {
@@ -1597,10 +1597,10 @@ fn splitn_iter<Sep: CharEq>(&self, sep: Sep, count: uint)
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v: ~[&str] = "A.B.".split_terminator_iter('.').collect();
     /// assert_eq!(v, ~["A", "B"]);
-    /// ~~~
+    /// ```
     #[inline]
     fn split_terminator_iter<Sep: CharEq>(&self, sep: Sep)
         -> CharSplitIterator<'self, Sep> {
@@ -1615,10 +1615,10 @@ fn split_terminator_iter<Sep: CharEq>(&self, sep: Sep)
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v: ~[&str] = "Mary had a little lamb".rsplit_iter(' ').collect();
     /// assert_eq!(v, ~["lamb", "little", "a", "had", "Mary"]);
-    /// ~~~
+    /// ```
     #[inline]
     fn rsplit_iter<Sep: CharEq>(&self, sep: Sep) -> CharRSplitIterator<'self, Sep> {
         self.split_iter(sep).invert()
@@ -1655,10 +1655,10 @@ fn matches_index_iter(&self, sep: &'self str) -> MatchesIndexIterator<'self> {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v: ~[&str] = "abcXXXabcYYYabc".split_str_iter("abc").collect()
     /// assert_eq!(v, ["", "XXX", "YYY", ""]);
-    /// ~~~
+    /// ```
     #[inline]
     fn split_str_iter(&self, sep: &'self str) -> StrSplitIterator<'self> {
         StrSplitIterator {
@@ -1853,11 +1853,11 @@ fn trim_right(&self) -> &'self str {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert_eq!("11foo1bar11".trim_chars(&'1'), "foo1bar")
     /// assert_eq!("12foo1bar12".trim_chars(& &['1', '2']), "foo1bar")
     /// assert_eq!("123foo1bar123".trim_chars(&|c: char| c.is_digit()), "foo1bar")
-    /// ~~~
+    /// ```
     #[inline]
     fn trim_chars<C: CharEq>(&self, to_trim: &C) -> &'self str {
         self.trim_left_chars(to_trim).trim_right_chars(to_trim)
@@ -1871,11 +1871,11 @@ fn trim_chars<C: CharEq>(&self, to_trim: &C) -> &'self str {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert_eq!("11foo1bar11".trim_left_chars(&'1'), "foo1bar11")
     /// assert_eq!("12foo1bar12".trim_left_chars(& &['1', '2']), "foo1bar12")
     /// assert_eq!("123foo1bar123".trim_left_chars(&|c: char| c.is_digit()), "foo1bar123")
-    /// ~~~
+    /// ```
     #[inline]
     fn trim_left_chars<C: CharEq>(&self, to_trim: &C) -> &'self str {
         match self.find(|c: char| !to_trim.matches(c)) {
@@ -1892,11 +1892,11 @@ fn trim_left_chars<C: CharEq>(&self, to_trim: &C) -> &'self str {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// assert_eq!("11foo1bar11".trim_right_chars(&'1'), "11foo1bar")
     /// assert_eq!("12foo1bar12".trim_right_chars(& &['1', '2']), "12foo1bar")
     /// assert_eq!("123foo1bar123".trim_right_chars(&|c: char| c.is_digit()), "123foo1bar")
-    /// ~~~
+    /// ```
     #[inline]
     fn trim_right_chars<C: CharEq>(&self, to_trim: &C) -> &'self str {
         match self.rfind(|c: char| !to_trim.matches(c)) {
@@ -2000,7 +2000,7 @@ fn is_char_boundary(&self, index: uint) -> bool {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let s = "中华Việt Nam";
     /// let i = 0u;
     /// while i < s.len() {
@@ -2008,11 +2008,11 @@ fn is_char_boundary(&self, index: uint) -> bool {
     ///     printfln!("%u: %c", i, ch);
     ///     i = next;
     /// }
-    /// ~~~
+    /// ```
     ///
     /// # Example output
     ///
-    /// ~~~
+    /// ```
     /// 0: 中
     /// 3: 华
     /// 6: V
@@ -2023,7 +2023,7 @@ fn is_char_boundary(&self, index: uint) -> bool {
     /// 13: N
     /// 14: a
     /// 15: m
-    /// ~~~
+    /// ```
     ///
     /// # Arguments
     ///
@@ -2228,7 +2228,7 @@ fn lev_distance(&self, t: &str) -> uint {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let string = "a\nb\nc";
     /// let mut lines = ~[];
     /// for line in string.line_iter() { lines.push(line) }
@@ -2236,7 +2236,7 @@ fn lev_distance(&self, t: &str) -> uint {
     /// assert!(string.subslice_offset(lines[0]) == 0); // &"a"
     /// assert!(string.subslice_offset(lines[1]) == 2); // &"b"
     /// assert!(string.subslice_offset(lines[2]) == 4); // &"c"
-    /// ~~~
+    /// ```
     #[inline]
     fn subslice_offset(&self, inner: &str) -> uint {
         do self.as_imm_buf |a, a_len| {
index b52dd3a906bd687811f8a829f8386d26d5dda037..1dbc644c8e5ce165b46cc05aba6d977adb49fab4 100644 (file)
  *
  * # Example
  *
- * ~~~
+ * ```
  * do spawn {
  *     log(error, "Hello, World!");
  * }
- * ~~~
+ * ```
  */
 
 #[allow(missing_doc)];
@@ -565,7 +565,7 @@ pub fn failing() -> bool {
  *
  * # Example
  *
- * ~~~
+ * ```
  * do task::unkillable {
  *     // detach / deschedule / destroy must all be called together
  *     rustrt::rust_port_detach(po);
@@ -573,7 +573,7 @@ pub fn failing() -> bool {
  *     task::deschedule();
  *     rustrt::rust_port_destroy(po);
  * }
- * ~~~
+ * ```
  */
 pub fn unkillable<U>(f: &fn() -> U) -> U {
     use rt::task::Task;
@@ -602,7 +602,7 @@ pub fn unkillable<U>(f: &fn() -> U) -> U {
  *
  * # Example
  *
- * ~~~
+ * ```
  * do task::unkillable {
  *     do task::rekillable {
  *          // Task is killable
index 2591131f21521e5020ac05aef287f63f07852bc9..623909b79759e6f962d6c04b5b1281bc355d367d 100644 (file)
@@ -80,9 +80,9 @@ fn second_ref<'a>(&'a self) -> &'a U {
 
 macro_rules! tuple_impls {
     ($(
-        ($cloneable_trait:ident, $immutable_trait:ident) {
+        ($move_trait:ident, $immutable_trait:ident) {
             $(($get_fn:ident, $get_ref_fn:ident) -> $T:ident {
-                $get_pattern:pat => $ret:expr
+                $move_pattern:pat, $ref_pattern:pat => $ret:expr
             })+
         }
     )+) => {
@@ -93,15 +93,16 @@ pub mod inner {
             #[cfg(not(test))] use num::Zero;
 
             $(
-                pub trait $cloneable_trait<$($T),+> {
-                    $(fn $get_fn(&self) -> $T;)+
+                pub trait $move_trait<$($T),+> {
+                    $(fn $get_fn(self) -> $T;)+
                 }
 
-                impl<$($T:Clone),+> $cloneable_trait<$($T),+> for ($($T,)+) {
+                impl<$($T),+> $move_trait<$($T),+> for ($($T,)+) {
                     $(
                         #[inline]
-                        fn $get_fn(&self) -> $T {
-                            self.$get_ref_fn().clone()
+                        fn $get_fn(self) -> $T {
+                            let $move_pattern = self;
+                            $ret
                         }
                     )+
                 }
@@ -114,7 +115,8 @@ impl<$($T),+> $immutable_trait<$($T),+> for ($($T,)+) {
                     $(
                         #[inline]
                         fn $get_ref_fn<'a>(&'a self) -> &'a $T {
-                            match *self { $get_pattern => $ret }
+                            let $ref_pattern = *self;
+                            $ret
                         }
                     )+
                 }
@@ -221,118 +223,118 @@ macro_rules! lexical_cmp {
 
 
 tuple_impls! {
-    (CloneableTuple1, ImmutableTuple1) {
-        (n0, n0_ref) -> A { (ref a,) => a }
+    (Tuple1, ImmutableTuple1) {
+        (n0, n0_ref) -> A { (a,), (ref a,) => a }
     }
 
-    (CloneableTuple2, ImmutableTuple2) {
-        (n0, n0_ref) -> A { (ref a,_) => a }
-        (n1, n1_ref) -> B { (_,ref b) => b }
+    (Tuple2, ImmutableTuple2) {
+        (n0, n0_ref) -> A { (a,_), (ref a,_) => a }
+        (n1, n1_ref) -> B { (_,b), (_,ref b) => b }
     }
 
-    (CloneableTuple3, ImmutableTuple3) {
-        (n0, n0_ref) -> A { (ref a,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c) => c }
+    (Tuple3, ImmutableTuple3) {
+        (n0, n0_ref) -> A { (a,_,_), (ref a,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_), (_,ref b,_) => b }
+        (n2, n2_ref) -> C { (_,_,c), (_,_,ref c) => c }
     }
 
-    (CloneableTuple4, ImmutableTuple4) {
-        (n0, n0_ref) -> A { (ref a,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d) => d }
+    (Tuple4, ImmutableTuple4) {
+        (n0, n0_ref) -> A { (a,_,_,_), (ref a,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_), (_,ref b,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_), (_,_,ref c,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d), (_,_,_,ref d) => d }
     }
 
-    (CloneableTuple5, ImmutableTuple5) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e) => e }
+    (Tuple5, ImmutableTuple5) {
+        (n0, n0_ref) -> A { (a,_,_,_,_), (ref a,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_), (_,ref b,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_), (_,_,ref c,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_), (_,_,_,ref d,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e), (_,_,_,_,ref e) => e }
     }
 
-    (CloneableTuple6, ImmutableTuple6) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e,_) => e }
-        (n5, n5_ref) -> F { (_,_,_,_,_,ref f) => f }
+    (Tuple6, ImmutableTuple6) {
+        (n0, n0_ref) -> A { (a,_,_,_,_,_), (ref a,_,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_,_), (_,ref b,_,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_,_), (_,_,ref c,_,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_,_), (_,_,_,ref d,_,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e,_), (_,_,_,_,ref e,_) => e }
+        (n5, n5_ref) -> F { (_,_,_,_,_,f), (_,_,_,_,_,ref f) => f }
     }
 
-    (CloneableTuple7, ImmutableTuple7) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_) => e }
-        (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_) => f }
-        (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g) => g }
+    (Tuple7, ImmutableTuple7) {
+        (n0, n0_ref) -> A { (a,_,_,_,_,_,_), (ref a,_,_,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_,_,_), (_,ref b,_,_,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_,_,_), (_,_,ref c,_,_,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_,_,_), (_,_,_,ref d,_,_,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e,_,_), (_,_,_,_,ref e,_,_) => e }
+        (n5, n5_ref) -> F { (_,_,_,_,_,f,_), (_,_,_,_,_,ref f,_) => f }
+        (n6, n6_ref) -> G { (_,_,_,_,_,_,g), (_,_,_,_,_,_,ref g) => g }
     }
 
-    (CloneableTuple8, ImmutableTuple8) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_) => e }
-        (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_) => f }
-        (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_) => g }
-        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h) => h }
+    (Tuple8, ImmutableTuple8) {
+        (n0, n0_ref) -> A { (a,_,_,_,_,_,_,_), (ref a,_,_,_,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_,_,_,_), (_,ref b,_,_,_,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_,_,_,_), (_,_,ref c,_,_,_,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_,_,_,_), (_,_,_,ref d,_,_,_,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e,_,_,_), (_,_,_,_,ref e,_,_,_) => e }
+        (n5, n5_ref) -> F { (_,_,_,_,_,f,_,_), (_,_,_,_,_,ref f,_,_) => f }
+        (n6, n6_ref) -> G { (_,_,_,_,_,_,g,_), (_,_,_,_,_,_,ref g,_) => g }
+        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,h), (_,_,_,_,_,_,_,ref h) => h }
     }
 
-    (CloneableTuple9, ImmutableTuple9) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_) => e }
-        (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_) => f }
-        (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_) => g }
-        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_) => h }
-        (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i) => i }
+    (Tuple9, ImmutableTuple9) {
+        (n0, n0_ref) -> A { (a,_,_,_,_,_,_,_,_), (ref a,_,_,_,_,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_,_,_,_,_), (_,ref b,_,_,_,_,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_,_,_,_,_), (_,_,ref c,_,_,_,_,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_,_,_,_,_), (_,_,_,ref d,_,_,_,_,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e,_,_,_,_), (_,_,_,_,ref e,_,_,_,_) => e }
+        (n5, n5_ref) -> F { (_,_,_,_,_,f,_,_,_), (_,_,_,_,_,ref f,_,_,_) => f }
+        (n6, n6_ref) -> G { (_,_,_,_,_,_,g,_,_), (_,_,_,_,_,_,ref g,_,_) => g }
+        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,h,_), (_,_,_,_,_,_,_,ref h,_) => h }
+        (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,i), (_,_,_,_,_,_,_,_,ref i) => i }
     }
 
-    (CloneableTuple10, ImmutableTuple10) {
-        (n0, n0_ref) -> A { (ref a,_,_,_,_,_,_,_,_,_) => a }
-        (n1, n1_ref) -> B { (_,ref b,_,_,_,_,_,_,_,_) => b }
-        (n2, n2_ref) -> C { (_,_,ref c,_,_,_,_,_,_,_) => c }
-        (n3, n3_ref) -> D { (_,_,_,ref d,_,_,_,_,_,_) => d }
-        (n4, n4_ref) -> E { (_,_,_,_,ref e,_,_,_,_,_) => e }
-        (n5, n5_ref) -> F { (_,_,_,_,_,ref f,_,_,_,_) => f }
-        (n6, n6_ref) -> G { (_,_,_,_,_,_,ref g,_,_,_) => g }
-        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,ref h,_,_) => h }
-        (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,ref i,_) => i }
-        (n9, n9_ref) -> J { (_,_,_,_,_,_,_,_,_,ref j) => j }
+    (Tuple10, ImmutableTuple10) {
+        (n0, n0_ref) -> A { (a,_,_,_,_,_,_,_,_,_), (ref a,_,_,_,_,_,_,_,_,_) => a }
+        (n1, n1_ref) -> B { (_,b,_,_,_,_,_,_,_,_), (_,ref b,_,_,_,_,_,_,_,_) => b }
+        (n2, n2_ref) -> C { (_,_,c,_,_,_,_,_,_,_), (_,_,ref c,_,_,_,_,_,_,_) => c }
+        (n3, n3_ref) -> D { (_,_,_,d,_,_,_,_,_,_), (_,_,_,ref d,_,_,_,_,_,_) => d }
+        (n4, n4_ref) -> E { (_,_,_,_,e,_,_,_,_,_), (_,_,_,_,ref e,_,_,_,_,_) => e }
+        (n5, n5_ref) -> F { (_,_,_,_,_,f,_,_,_,_), (_,_,_,_,_,ref f,_,_,_,_) => f }
+        (n6, n6_ref) -> G { (_,_,_,_,_,_,g,_,_,_), (_,_,_,_,_,_,ref g,_,_,_) => g }
+        (n7, n7_ref) -> H { (_,_,_,_,_,_,_,h,_,_), (_,_,_,_,_,_,_,ref h,_,_) => h }
+        (n8, n8_ref) -> I { (_,_,_,_,_,_,_,_,i,_), (_,_,_,_,_,_,_,_,ref i,_) => i }
+        (n9, n9_ref) -> J { (_,_,_,_,_,_,_,_,_,j), (_,_,_,_,_,_,_,_,_,ref j) => j }
     }
 
-    (CloneableTuple11, ImmutableTuple11) {
-        (n0,  n0_ref)  -> A { (ref a,_,_,_,_,_,_,_,_,_,_) => a }
-        (n1,  n1_ref)  -> B { (_,ref b,_,_,_,_,_,_,_,_,_) => b }
-        (n2,  n2_ref)  -> C { (_,_,ref c,_,_,_,_,_,_,_,_) => c }
-        (n3,  n3_ref)  -> D { (_,_,_,ref d,_,_,_,_,_,_,_) => d }
-        (n4,  n4_ref)  -> E { (_,_,_,_,ref e,_,_,_,_,_,_) => e }
-        (n5,  n5_ref)  -> F { (_,_,_,_,_,ref f,_,_,_,_,_) => f }
-        (n6,  n6_ref)  -> G { (_,_,_,_,_,_,ref g,_,_,_,_) => g }
-        (n7,  n7_ref)  -> H { (_,_,_,_,_,_,_,ref h,_,_,_) => h }
-        (n8,  n8_ref)  -> I { (_,_,_,_,_,_,_,_,ref i,_,_) => i }
-        (n9,  n9_ref)  -> J { (_,_,_,_,_,_,_,_,_,ref j,_) => j }
-        (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,ref k) => k }
+    (Tuple11, ImmutableTuple11) {
+        (n0,  n0_ref)  -> A { (a,_,_,_,_,_,_,_,_,_,_), (ref a,_,_,_,_,_,_,_,_,_,_) => a }
+        (n1,  n1_ref)  -> B { (_,b,_,_,_,_,_,_,_,_,_), (_,ref b,_,_,_,_,_,_,_,_,_) => b }
+        (n2,  n2_ref)  -> C { (_,_,c,_,_,_,_,_,_,_,_), (_,_,ref c,_,_,_,_,_,_,_,_) => c }
+        (n3,  n3_ref)  -> D { (_,_,_,d,_,_,_,_,_,_,_), (_,_,_,ref d,_,_,_,_,_,_,_) => d }
+        (n4,  n4_ref)  -> E { (_,_,_,_,e,_,_,_,_,_,_), (_,_,_,_,ref e,_,_,_,_,_,_) => e }
+        (n5,  n5_ref)  -> F { (_,_,_,_,_,f,_,_,_,_,_), (_,_,_,_,_,ref f,_,_,_,_,_) => f }
+        (n6,  n6_ref)  -> G { (_,_,_,_,_,_,g,_,_,_,_), (_,_,_,_,_,_,ref g,_,_,_,_) => g }
+        (n7,  n7_ref)  -> H { (_,_,_,_,_,_,_,h,_,_,_), (_,_,_,_,_,_,_,ref h,_,_,_) => h }
+        (n8,  n8_ref)  -> I { (_,_,_,_,_,_,_,_,i,_,_), (_,_,_,_,_,_,_,_,ref i,_,_) => i }
+        (n9,  n9_ref)  -> J { (_,_,_,_,_,_,_,_,_,j,_), (_,_,_,_,_,_,_,_,_,ref j,_) => j }
+        (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,k), (_,_,_,_,_,_,_,_,_,_,ref k) => k }
     }
 
-    (CloneableTuple12, ImmutableTuple12) {
-        (n0,  n0_ref)  -> A { (ref a,_,_,_,_,_,_,_,_,_,_,_) => a }
-        (n1,  n1_ref)  -> B { (_,ref b,_,_,_,_,_,_,_,_,_,_) => b }
-        (n2,  n2_ref)  -> C { (_,_,ref c,_,_,_,_,_,_,_,_,_) => c }
-        (n3,  n3_ref)  -> D { (_,_,_,ref d,_,_,_,_,_,_,_,_) => d }
-        (n4,  n4_ref)  -> E { (_,_,_,_,ref e,_,_,_,_,_,_,_) => e }
-        (n5,  n5_ref)  -> F { (_,_,_,_,_,ref f,_,_,_,_,_,_) => f }
-        (n6,  n6_ref)  -> G { (_,_,_,_,_,_,ref g,_,_,_,_,_) => g }
-        (n7,  n7_ref)  -> H { (_,_,_,_,_,_,_,ref h,_,_,_,_) => h }
-        (n8,  n8_ref)  -> I { (_,_,_,_,_,_,_,_,ref i,_,_,_) => i }
-        (n9,  n9_ref)  -> J { (_,_,_,_,_,_,_,_,_,ref j,_,_) => j }
-        (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,ref k,_) => k }
-        (n11, n11_ref) -> L { (_,_,_,_,_,_,_,_,_,_,_,ref l) => l }
+    (Tuple12, ImmutableTuple12) {
+        (n0,  n0_ref)  -> A { (a,_,_,_,_,_,_,_,_,_,_,_), (ref a,_,_,_,_,_,_,_,_,_,_,_) => a }
+        (n1,  n1_ref)  -> B { (_,b,_,_,_,_,_,_,_,_,_,_), (_,ref b,_,_,_,_,_,_,_,_,_,_) => b }
+        (n2,  n2_ref)  -> C { (_,_,c,_,_,_,_,_,_,_,_,_), (_,_,ref c,_,_,_,_,_,_,_,_,_) => c }
+        (n3,  n3_ref)  -> D { (_,_,_,d,_,_,_,_,_,_,_,_), (_,_,_,ref d,_,_,_,_,_,_,_,_) => d }
+        (n4,  n4_ref)  -> E { (_,_,_,_,e,_,_,_,_,_,_,_), (_,_,_,_,ref e,_,_,_,_,_,_,_) => e }
+        (n5,  n5_ref)  -> F { (_,_,_,_,_,f,_,_,_,_,_,_), (_,_,_,_,_,ref f,_,_,_,_,_,_) => f }
+        (n6,  n6_ref)  -> G { (_,_,_,_,_,_,g,_,_,_,_,_), (_,_,_,_,_,_,ref g,_,_,_,_,_) => g }
+        (n7,  n7_ref)  -> H { (_,_,_,_,_,_,_,h,_,_,_,_), (_,_,_,_,_,_,_,ref h,_,_,_,_) => h }
+        (n8,  n8_ref)  -> I { (_,_,_,_,_,_,_,_,i,_,_,_), (_,_,_,_,_,_,_,_,ref i,_,_,_) => i }
+        (n9,  n9_ref)  -> J { (_,_,_,_,_,_,_,_,_,j,_,_), (_,_,_,_,_,_,_,_,_,ref j,_,_) => j }
+        (n10, n10_ref) -> K { (_,_,_,_,_,_,_,_,_,_,k,_), (_,_,_,_,_,_,_,_,_,_,ref k,_) => k }
+        (n11, n11_ref) -> L { (_,_,_,_,_,_,_,_,_,_,_,l), (_,_,_,_,_,_,_,_,_,_,_,ref l) => l }
     }
 }
 
index ba5986aa4ab9db3fa81bf6e371879c235f32421e..c1365a44bc913c8d19a6cc8cffcd206e4bcb1a5e 100644 (file)
 
 # Example
 
-~~~
+ ```
 do || {
     ...
 }.finally {
     always_run_this();
 }
-~~~
+ ```
 */
 
 use ops::Drop;
index ae217d6af310c72d2f8ff3b43e8a674022fbe2f9..e54717053e97ba2d402dec4214bdfb8c20e52d05 100644 (file)
 Vectors are Rust's list type. Vectors contain zero or more values of
 homogeneous types:
 
-~~~ {.rust}
+```rust
 let int_vector = [1,2,3];
 let str_vector = ["one", "two", "three"];
-~~~
+ ```
 
 This is a big module, but for a high-level overview:
 
 An example is the method `.slice(a, b)` that returns an immutable "view" into
 a vector or a vector slice from the index interval `[a, b)`:
 
-~~~ {.rust}
+```rust
 let numbers = [0, 1, 2];
 let last_numbers = numbers.slice(1, 3);
 // last_numbers is now &[1, 2]
-~~~
+ ```
 
 Traits defined for the `~[T]` type, like `OwnedVector`, can only be called
 on such vectors. These methods deal with adding elements or otherwise changing
 An example is the method `.push(element)` that will add an element at the end
 of the vector:
 
-~~~ {.rust}
+```rust
 let mut numbers = ~[0, 1, 2];
 numbers.push(7);
 // numbers is now ~[0, 1, 2, 7];
-~~~
+ ```
 
 ## Implementations of other traits
 
 The iterator yields borrowed pointers to the vector's elements, so if the element
 type of the vector is `int`, the element type of the iterator is `&int`.
 
-~~~ {.rust}
+```rust
 let numbers = [0, 1, 2];
 for &x in numbers.iter() {
     println!("{} is a number!", x);
 }
-~~~
+ ```
 
 * `.rev_iter()` returns an iterator with the same values as `.iter()`,
   but going in the reverse order, starting with the back element.
@@ -1000,12 +1000,12 @@ fn rsplitn_iter(self, n: uint, pred: &'self fn(&T) -> bool) -> RSplitIterator<'s
      * Print the adjacent pairs of a vector (i.e. `[1,2]`, `[2,3]`,
      * `[3,4]`):
      *
-     * ~~~ {.rust}
+     * ```rust
      * let v = &[1,2,3,4];
      * for win in v.window_iter() {
      *     printfln!(win);
      * }
-     * ~~~
+     * ```
      *
      */
     fn window_iter(self, size: uint) -> WindowIter<'self, T> {
@@ -1029,12 +1029,12 @@ fn window_iter(self, size: uint) -> WindowIter<'self, T> {
      * Print the vector two elements at a time (i.e. `[1,2]`,
      * `[3,4]`, `[5]`):
      *
-     * ~~~ {.rust}
+     * ```rust
      * let v = &[1,2,3,4,5];
      * for win in v.chunk_iter() {
      *     printfln!(win);
      * }
-     * ~~~
+     * ```
      *
      */
     fn chunk_iter(self, size: uint) -> ChunkIter<'self, T> {
@@ -1279,13 +1279,13 @@ impl<T> OwnedVector<T> for ~[T] {
     ///
     /// # Examples
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let v = ~[~"a", ~"b"];
     /// for s in v.move_iter() {
     ///   // s has type ~str, not &~str
     ///   println(s);
     /// }
-    /// ~~~
+    /// ```
     fn move_iter(self) -> MoveIterator<T> {
         MoveIterator { v: self, idx: 0 }
     }
@@ -1449,11 +1449,11 @@ unsafe fn push_fast<T>(this: &mut ~[T], t: T) {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let mut a = ~[~1];
     /// a.push_all_move(~[~2, ~3, ~4]);
     /// assert!(a == ~[~1, ~2, ~3, ~4]);
-    /// ~~~
+    /// ```
     #[inline]
     fn push_all_move(&mut self, mut rhs: ~[T]) {
         let self_len = self.len();
@@ -1697,11 +1697,11 @@ impl<T:Clone> OwnedCopyableVector<T> for ~[T] {
     ///
     /// # Example
     ///
-    /// ~~~ {.rust}
+    /// ```rust
     /// let mut a = ~[1];
     /// a.push_all([2, 3, 4]);
     /// assert!(a == ~[1, 2, 3, 4]);
-    /// ~~~
+    /// ```
     #[inline]
     fn push_all(&mut self, rhs: &[T]) {
         let new_len = self.len() + rhs.len();
index 9222d8160ee6150450f834600e38e9dc395aa38a..646b65d080b470c327c08b64d5ea67760bd24042 100644 (file)
@@ -59,7 +59,7 @@
 an identifier in the source code. For example, the `x`s in the
 following snippet
 
-~~~
+ ```
 struct A { x : int }
 
 struct B(int);
@@ -82,7 +82,7 @@ enum C {
 
 The following simplified `Eq` is used for in-code examples:
 
-~~~
+ ```
 trait Eq {
     fn eq(&self, other: &Self);
 }
@@ -91,7 +91,7 @@ fn eq(&self, other: &int) -> bool {
         *self == *other
     }
 }
-~~~
+ ```
 
 Some examples of the values of `SubstructureFields` follow, using the
 above `Eq`, `A`, `B` and `C`.
@@ -100,50 +100,50 @@ fn eq(&self, other: &int) -> bool {
 
 When generating the `expr` for the `A` impl, the `SubstructureFields` is
 
-~~~
+ ```
 Struct(~[(Some(<ident of x>),
          <expr for &self.x>,
          ~[<expr for &other.x])])
-~~~
+ ```
 
 For the `B` impl, called with `B(a)` and `B(b)`,
 
-~~~
+ ```
 Struct(~[(None,
           <expr for &a>
           ~[<expr for &b>])])
-~~~
+ ```
 
 ## Enums
 
 When generating the `expr` for a call with `self == C0(a)` and `other
 == C0(b)`, the SubstructureFields is
 
-~~~
+ ```
 EnumMatching(0, <ast::variant for C0>,
              ~[None,
                <expr for &a>,
                ~[<expr for &b>]])
-~~~
+ ```
 
 For `C1 {x}` and `C1 {x}`,
 
-~~~
+ ```
 EnumMatching(1, <ast::variant for C1>,
              ~[Some(<ident of x>),
                <expr for &self.x>,
                ~[<expr for &other.x>]])
-~~~
+ ```
 
 For `C0(a)` and `C1 {x}` ,
 
-~~~
+ ```
 EnumNonMatching(~[(0, <ast::variant for B0>,
                    ~[(None, <expr for &a>)]),
                   (1, <ast::variant for B1>,
                    ~[(Some(<ident of x>),
                       <expr for &other.x>)])])
-~~~
+ ```
 
 (and vice versa, but with the order of the outermost list flipped.)
 
@@ -158,7 +158,7 @@ fn eq(&self, other: &int) -> bool {
 
 StaticEnum(<ast::enum_def of C>, ~[(<ident of C0>, Left(1)),
                                    (<ident of C1>, Right(~[<ident of x>]))])
-~~~
+ ```
 
 */
 
@@ -547,7 +547,7 @@ fn create_method(&self, cx: @ExtCtxt, span: Span,
     }
 
     /**
-    ~~~
+    ```
     #[deriving(Eq)]
     struct A { x: int, y: int }
 
@@ -565,7 +565,7 @@ fn eq(&self, __arg_1: &A) -> bool {
             }
         }
     }
-    ~~~
+    ```
     */
     fn expand_struct_method_body(&self,
                                  cx: @ExtCtxt,
@@ -638,7 +638,7 @@ fn expand_static_struct_method_body(&self,
     }
 
     /**
-    ~~~
+    ```
     #[deriving(Eq)]
     enum A {
         A1
@@ -661,7 +661,7 @@ fn eq(&self, __arg_1: &A) {
             }
         }
     }
-    ~~~
+    ```
     */
     fn expand_enum_method_body(&self,
                                cx: @ExtCtxt,
@@ -681,13 +681,13 @@ fn expand_enum_method_body(&self,
     /**
     Creates the nested matches for an enum definition recursively, i.e.
 
-    ~~~
+    ```
     match self {
        Variant1 => match other { Variant1 => matching, Variant2 => nonmatching, ... },
        Variant2 => match other { Variant1 => nonmatching, Variant2 => matching, ... },
        ...
     }
-    ~~~
+    ```
 
     It acts in the most naive way, so every branch (and subbranch,
     subsubbranch, etc) exists, not just the ones where all the variants in
@@ -1058,10 +1058,10 @@ pub fn cs_fold(use_foldl: bool,
 Call the method that is being derived on all the fields, and then
 process the collected results. i.e.
 
-~~~
+ ```
 f(cx, span, ~[self_1.method(__arg_1_1, __arg_2_1),
               self_2.method(__arg_1_2, __arg_2_2)])
-~~~
+ ```
 */
 #[inline]
 pub fn cs_same_method(f: &fn(@ExtCtxt, Span, ~[@Expr]) -> @Expr,
index 64f30803ca73450a4079a17715befd8e255b12c7..61c9ea7be14a27c3c7008836957d461f32895c6d 100644 (file)
@@ -944,7 +944,7 @@ macro_rules! assert_approx_eq (
 
     # Example
 
-    ~~~ {.rust}
+    ```rust
     fn choose_weighted_item(v: &[Item]) -> Item {
         assert!(!v.is_empty());
         let mut so_far = 0u;
@@ -958,7 +958,7 @@ fn choose_weighted_item(v: &[Item]) -> Item {
         // type checker that it isn't possible to get down here
         unreachable!();
     }
-    ~~~
+    ```
 
     */
     macro_rules! unreachable (() => (
index f13bd6d9123cf8361ee939473d6c3ea4b4686edf..88c9fc3e0f792917d812ae3f54cd7c63a88bef3e 100644 (file)
@@ -59,11 +59,19 @@ pub fn strip_doc_comment_decoration(comment: &str) -> ~str {
     fn vertical_trim(lines: ~[~str]) -> ~[~str] {
         let mut i = 0u;
         let mut j = lines.len();
+        // first line of all-stars should be omitted
+        if lines.len() > 0 && lines[0].iter().all(|c| c == '*') {
+            i += 1;
+        }
         while i < j && lines[i].trim().is_empty() {
-            i += 1u;
+            i += 1;
+        }
+        // like the first, a last line of all stars should be omitted
+        if j > i && lines[j - 1].iter().skip(1).all(|c| c == '*') {
+            j -= 1;
         }
-        while j > i && lines[j - 1u].trim().is_empty() {
-            j -= 1u;
+        while j > i && lines[j - 1].trim().is_empty() {
+            j -= 1;
         }
         return lines.slice(i, j).to_owned();
     }
@@ -106,8 +114,12 @@ fn horizontal_trim(lines: ~[~str]) -> ~[~str] {
         }
     }
 
-    if comment.starts_with("//") {
-        return comment.slice(3u, comment.len()).to_owned();
+    // one-line comments lose their prefix
+    static ONLINERS: &'static [&'static str] = &["///!", "///", "//!", "//"];
+    for prefix in ONLINERS.iter() {
+        if comment.starts_with(*prefix) {
+            return comment.slice_from(prefix.len()).to_owned();
+        }
     }
 
     if comment.starts_with("/*") {
@@ -384,29 +396,42 @@ mod test {
 
     #[test] fn test_block_doc_comment_1() {
         let comment = "/**\n * Test \n **  Test\n *   Test\n*/";
-        let correct_stripped = " Test \n*  Test\n   Test";
         let stripped = strip_doc_comment_decoration(comment);
-        assert_eq!(stripped.slice(0, stripped.len()), correct_stripped);
+        assert_eq!(stripped, ~" Test \n*  Test\n   Test");
     }
 
     #[test] fn test_block_doc_comment_2() {
         let comment = "/**\n * Test\n *  Test\n*/";
-        let correct_stripped = " Test\n  Test";
         let stripped = strip_doc_comment_decoration(comment);
-        assert_eq!(stripped.slice(0, stripped.len()), correct_stripped);
+        assert_eq!(stripped, ~" Test\n  Test");
     }
 
     #[test] fn test_block_doc_comment_3() {
         let comment = "/**\n let a: *int;\n *a = 5;\n*/";
-        let correct_stripped = " let a: *int;\n *a = 5;";
         let stripped = strip_doc_comment_decoration(comment);
-        assert_eq!(stripped.slice(0, stripped.len()), correct_stripped);
+        assert_eq!(stripped, ~" let a: *int;\n *a = 5;");
     }
 
-    #[test] fn test_line_doc_comment() {
-        let comment = "/// Test";
-        let correct_stripped = " Test";
+    #[test] fn test_block_doc_comment_4() {
+        let comment = "/*******************\n test\n *********************/";
         let stripped = strip_doc_comment_decoration(comment);
-        assert_eq!(stripped.slice(0, stripped.len()), correct_stripped);
+        assert_eq!(stripped, ~" test");
+    }
+
+    #[test] fn test_line_doc_comment() {
+        let stripped = strip_doc_comment_decoration("/// test");
+        assert_eq!(stripped, ~" test");
+        let stripped = strip_doc_comment_decoration("///! test");
+        assert_eq!(stripped, ~" test");
+        let stripped = strip_doc_comment_decoration("// test");
+        assert_eq!(stripped, ~" test");
+        let stripped = strip_doc_comment_decoration("// test");
+        assert_eq!(stripped, ~" test");
+        let stripped = strip_doc_comment_decoration("///test");
+        assert_eq!(stripped, ~"test");
+        let stripped = strip_doc_comment_decoration("///!test");
+        assert_eq!(stripped, ~"test");
+        let stripped = strip_doc_comment_decoration("//test");
+        assert_eq!(stripped, ~"test");
     }
 }
index 186fb387eea500c25b5a184c38cb063c0d97baaa..b87afab84eb1051832ed5e17fe08e8f234153f58 100644 (file)
@@ -200,3 +200,9 @@ rust_set_stdio_container_fd
 rust_set_stdio_container_stream
 rust_uv_process_pid
 rust_uv_pipe_init
+sdhtml_renderer
+sd_markdown_new
+sd_markdown_render
+sd_markdown_free
+bufrelease
+bufnew
diff --git a/src/rt/sundown/.gitignore b/src/rt/sundown/.gitignore
new file mode 100644 (file)
index 0000000..4415b79
--- /dev/null
@@ -0,0 +1,5 @@
+*.o
+libsundown.so*
+sundown
+smartypants
+*.exe
diff --git a/src/rt/sundown/CONTRIBUTING.md b/src/rt/sundown/CONTRIBUTING.md
new file mode 100644 (file)
index 0000000..7178d8c
--- /dev/null
@@ -0,0 +1,10 @@
+Contributing to Sundown
+=======================
+
+Do not.
+
+Unfortunately, Sundown is currently frozen as we're working with the Reddit, StackOverflow and Meteor developers to design a formal Markdown standard and parser that will supersede Sundown in all these websites (and in GitHub, of course). Our goal is to deprecate Sundown altogether before the end of the year.
+
+The new parser will be smaller, faster, safer and most importantly, more consistent.
+
+Please stay tuned.
\ No newline at end of file
diff --git a/src/rt/sundown/Makefile b/src/rt/sundown/Makefile
new file mode 100644 (file)
index 0000000..baca687
--- /dev/null
@@ -0,0 +1,83 @@
+# Makefile
+
+# Copyright (c) 2009, Natacha Porté
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+DEPDIR=depends
+
+# "Machine-dependant" options
+#MFLAGS=-fPIC
+
+CFLAGS=-c -g -O3 -fPIC -Wall -Werror -Wsign-compare -Isrc -Ihtml
+LDFLAGS=-g -O3 -Wall -Werror 
+CC=gcc
+
+
+SUNDOWN_SRC=\
+       src/markdown.o \
+       src/stack.o \
+       src/buffer.o \
+       src/autolink.o \
+       html/html.o \
+       html/html_smartypants.o \
+       html/houdini_html_e.o \
+       html/houdini_href_e.o
+
+all:           libsundown.so sundown smartypants html_blocks
+
+.PHONY:                all clean
+
+# libraries
+
+libsundown.so: libsundown.so.1
+       ln -f -s $^ $@
+
+libsundown.so.1: $(SUNDOWN_SRC)
+       $(CC) $(LDFLAGS) -shared -Wl $^ -o $@
+
+# executables
+
+sundown:       examples/sundown.o $(SUNDOWN_SRC)
+       $(CC) $(LDFLAGS) $^ -o $@
+
+smartypants: examples/smartypants.o $(SUNDOWN_SRC)
+       $(CC) $(LDFLAGS) $^ -o $@
+
+# perfect hashing
+html_blocks: src/html_blocks.h
+
+src/html_blocks.h: html_block_names.txt
+       gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case $^ > $@
+
+
+# housekeeping
+clean:
+       rm -f src/*.o html/*.o examples/*.o
+       rm -f libsundown.so libsundown.so.1 sundown smartypants
+       rm -f sundown.exe smartypants.exe
+       rm -rf $(DEPDIR)
+
+
+# dependencies
+
+include $(wildcard $(DEPDIR)/*.d)
+
+
+# generic object compilations
+
+%.o:   src/%.c examples/%.c html/%.c
+       @mkdir -p $(DEPDIR)
+       @$(CC) -MM $< > $(DEPDIR)/$*.d
+       $(CC) $(CFLAGS) -o $@ $<
+
diff --git a/src/rt/sundown/Makefile.win b/src/rt/sundown/Makefile.win
new file mode 100644 (file)
index 0000000..ea668b2
--- /dev/null
@@ -0,0 +1,33 @@
+
+CFLAGS=/O2 /EHsc /I"src/" /I"examples"/ /I"html"/
+CC=cl
+
+SUNDOWN_SRC=\
+       src\markdown.obj \
+       src\stack.obj \
+       src\buffer.obj \
+       src\autolink.obj \
+       html\html.obj \
+       html\html_smartypants.obj \
+       html\houdini_html_e.obj \
+       html\houdini_href_e.obj
+
+all: sundown.dll sundown.exe
+
+sundown.dll: $(SUNDOWN_SRC) sundown.def
+       $(CC) $(SUNDOWN_SRC) sundown.def /link /DLL $(LDFLAGS) /out:$@
+
+sundown.exe: examples\sundown.obj $(SUNDOWN_SRC)
+       $(CC) examples\sundown.obj $(SUNDOWN_SRC) /link $(LDFLAGS) /out:$@
+
+# housekeeping
+clean:
+       del $(SUNDOWN_SRC)
+       del sundown.dll sundown.exe
+       del sundown.exp sundown.lib
+
+# generic object compilations
+
+.c.obj:
+       $(CC) $(CFLAGS) /c $< /Fo$@
+
diff --git a/src/rt/sundown/README.markdown b/src/rt/sundown/README.markdown
new file mode 100644 (file)
index 0000000..6173602
--- /dev/null
@@ -0,0 +1,131 @@
+Sundown
+=======
+
+`Sundown` is a Markdown parser based on the original code of the
+[Upskirt library](http://fossil.instinctive.eu/libupskirt/index) by Natacha Porté.
+
+Features
+--------
+
+*      **Fully standards compliant**
+
+       `Sundown` passes out of the box the official Markdown v1.0.0 and v1.0.3
+       test suites, and has been extensively tested with additional corner cases
+       to make sure its output is as sane as possible at all times.
+
+*      **Massive extension support**
+
+       `Sundown` has optional support for several (unofficial) Markdown extensions,
+       such as non-strict emphasis, fenced code blocks, tables, autolinks,
+       strikethrough and more.
+
+*      **UTF-8 aware**
+
+       `Sundown` is fully UTF-8 aware, both when parsing the source document and when
+       generating the resulting (X)HTML code.
+
+*      **Tested & Ready to be used on production**
+
+       `Sundown` has been extensively security audited, and includes protection against
+       all possible DOS attacks (stack overflows, out of memory situations, malformed
+       Markdown syntax...) and against client attacks through malicious embedded HTML.
+
+       We've worked very hard to make `Sundown` never crash or run out of memory
+       under *any* input. `Sundown` renders all the Markdown content in GitHub and so
+       far hasn't crashed a single time.
+
+*      **Customizable renderers**
+
+       `Sundown` is not stuck with XHTML output: the Markdown parser of the library
+       is decoupled from the renderer, so it's trivial to extend the library with
+       custom renderers. A fully functional (X)HTML renderer is included.
+
+*      **Optimized for speed**
+
+       `Sundown` is written in C, with a special emphasis on performance. When wrapped
+       on a dynamic language such as Python or Ruby, it has shown to be up to 40
+       times faster than other native alternatives.
+
+*      **Zero-dependency**
+
+       `Sundown` is a zero-dependency library composed of 3 `.c` files and their headers.
+       No dependencies, no bullshit. Only standard C99 that builds everywhere.
+
+Credits
+-------
+
+`Sundown` is based on the original Upskirt parser by Natacha Porté, with many additions
+by Vicent Marti (@vmg) and contributions from the following authors:
+
+       Ben Noordhuis, Bruno Michel, Joseph Koshy, Krzysztof Kowalczyk, Samuel Bronson,
+       Shuhei Tanuma
+
+Bindings
+--------
+
+`Sundown` is available from other programming languages thanks to these bindings developed
+by our awesome contributors.
+
+- [Redcarpet](https://github.com/vmg/redcarpet) (Ruby)
+- [RobotSkirt](https://github.com/benmills/robotskirt) (Node.js)
+- [Misaka](https://github.com/FSX/misaka) (Python)
+- [ffi-sundown](https://github.com/postmodern/ffi-sundown) (Ruby FFI)
+- [Sundown HS](https://github.com/bitonic/sundown) (Haskell)
+- [Goskirt](https://github.com/madari/goskirt) (Go)
+- [Upskirt.go](https://github.com/buu700/upskirt.go) (Go)
+- [MoonShine](https://github.com/brandonc/moonshine) (.NET)
+- [PHP-Sundown](https://github.com/chobie/php-sundown) (PHP)
+- [Sundown.net](https://github.com/txdv/sundown.net) (.NET)
+
+Help us
+-------
+
+`Sundown` is all about security. If you find a (potential) security vulnerability in the
+library, or a way to make it crash through malicious input, please report it to us,
+either directly via email or by opening an Issue on GitHub, and help make the web safer
+for everybody.
+
+Unicode character handling
+--------------------------
+
+Given that the Markdown spec makes no provision for Unicode character handling, `Sundown`
+takes a conservative approach towards deciding which extended characters trigger Markdown
+features:
+
+*      Punctuation characters outside of the U+007F codepoint are not handled as punctuation.
+       They are considered as normal, in-word characters for word-boundary checks.
+
+*      Whitespace characters outside of the U+007F codepoint are not considered as
+       whitespace. They are considered as normal, in-word characters for word-boundary checks.
+
+Install
+-------
+
+There is nothing to install. `Sundown` is composed of 3 `.c` files (`markdown.c`,
+`buffer.c` and `array.c`), so just throw them in your project. Zero-dependency means
+zero-dependency. You might want to include `render/html.c` if you want to use the
+included XHTML renderer, or write your own renderer. Either way, it's all fun and joy.
+
+If you are hardcore, you can use the included `Makefile` to build `Sundown` into a dynamic
+library, or to build the sample `sundown` executable, which is just a commandline
+Markdown to XHTML parser. (If gcc gives you grief about `-fPIC`, e.g. with MinGW, try
+`make MFLAGS=` instead of just `make`.)
+
+License
+-------
+
+Permission to use, copy, modify, and distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+<!-- Local Variables: -->
+<!-- fill-column: 89 -->
+<!-- End: -->
diff --git a/src/rt/sundown/examples/smartypants.c b/src/rt/sundown/examples/smartypants.c
new file mode 100644 (file)
index 0000000..4840703
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "markdown.h"
+#include "html.h"
+#include "buffer.h"
+
+#include <errno.h>
+#include <getopt.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define READ_UNIT 1024
+#define OUTPUT_UNIT 64
+
+int
+main(int argc, char **argv)
+{
+       struct buf *ib, *ob;
+       size_t ret;
+       FILE *in = stdin;
+
+       /* opening the file if given from the command line */
+       if (argc > 1) {
+               in = fopen(argv[1], "r");
+               if (!in) {
+                       fprintf(stderr, "Unable to open input file \"%s\": %s\n", argv[0], strerror(errno));
+                       return 1;
+               }
+       }
+
+       /* reading everything */
+       ib = bufnew(READ_UNIT);
+       bufgrow(ib, READ_UNIT);
+       while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) {
+               ib->size += ret;
+               bufgrow(ib, ib->size + READ_UNIT);
+       }
+
+       if (in != stdin)
+               fclose(in);
+
+       /* performing markdown parsing */
+       ob = bufnew(OUTPUT_UNIT);
+
+       sdhtml_smartypants(ob, ib->data, ib->size);
+
+       /* writing the result to stdout */
+       (void)fwrite(ob->data, 1, ob->size, stdout);
+
+       /* cleanup */
+       bufrelease(ib);
+       bufrelease(ob);
+
+       return 0;
+}
+
+/* vim: set filetype=c: */
diff --git a/src/rt/sundown/examples/sundown.c b/src/rt/sundown/examples/sundown.c
new file mode 100644 (file)
index 0000000..8a475dc
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "markdown.h"
+#include "html.h"
+#include "buffer.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define READ_UNIT 1024
+#define OUTPUT_UNIT 64
+
+/* main • main function, interfacing STDIO with the parser */
+int
+main(int argc, char **argv)
+{
+       struct buf *ib, *ob;
+       int ret;
+       FILE *in = stdin;
+
+       struct sd_callbacks callbacks;
+       struct html_renderopt options;
+       struct sd_markdown *markdown;
+
+       /* opening the file if given from the command line */
+       if (argc > 1) {
+               in = fopen(argv[1], "r");
+               if (!in) {
+                       fprintf(stderr,"Unable to open input file \"%s\": %s\n", argv[1], strerror(errno));
+                       return 1;
+               }
+       }
+
+       /* reading everything */
+       ib = bufnew(READ_UNIT);
+       bufgrow(ib, READ_UNIT);
+       while ((ret = fread(ib->data + ib->size, 1, ib->asize - ib->size, in)) > 0) {
+               ib->size += ret;
+               bufgrow(ib, ib->size + READ_UNIT);
+       }
+
+       if (in != stdin)
+               fclose(in);
+
+       /* performing markdown parsing */
+       ob = bufnew(OUTPUT_UNIT);
+
+       sdhtml_renderer(&callbacks, &options, 0);
+       markdown = sd_markdown_new(0, 16, &callbacks, &options);
+
+       sd_markdown_render(ob, ib->data, ib->size, markdown);
+       sd_markdown_free(markdown);
+
+       /* writing the result to stdout */
+       ret = fwrite(ob->data, 1, ob->size, stdout);
+
+       /* cleanup */
+       bufrelease(ib);
+       bufrelease(ob);
+
+       return (ret < 0) ? -1 : 0;
+}
+
+/* vim: set filetype=c: */
diff --git a/src/rt/sundown/html/houdini.h b/src/rt/sundown/html/houdini.h
new file mode 100644 (file)
index 0000000..b4954c0
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef HOUDINI_H__
+#define HOUDINI_H__
+
+#include "buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HOUDINI_USE_LOCALE
+#      define _isxdigit(c) isxdigit(c)
+#      define _isdigit(c) isdigit(c)
+#else
+/*
+ * Helper _isdigit methods -- do not trust the current locale
+ * */
+#      define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)
+#      define _isdigit(c) ((c) >= '0' && (c) <= '9')
+#endif
+
+extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure);
+extern void houdini_unescape_html(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_xml(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_uri(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_url(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_unescape_uri(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_unescape_url(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_escape_js(struct buf *ob, const uint8_t *src, size_t size);
+extern void houdini_unescape_js(struct buf *ob, const uint8_t *src, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/rt/sundown/html/houdini_href_e.c b/src/rt/sundown/html/houdini_href_e.c
new file mode 100644 (file)
index 0000000..981b3b1
--- /dev/null
@@ -0,0 +1,108 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "houdini.h"
+
+#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10)
+
+/*
+ * The following characters will not be escaped:
+ *
+ *             -_.+!*'(),%#@?=;:/,+&$ alphanum
+ *
+ * Note that this character set is the addition of:
+ *
+ *     - The characters which are safe to be in an URL
+ *     - The characters which are *not* safe to be in
+ *     an URL because they are RESERVED characters.
+ *
+ * We asume (lazily) that any RESERVED char that
+ * appears inside an URL is actually meant to
+ * have its native function (i.e. as an URL 
+ * component/separator) and hence needs no escaping.
+ *
+ * There are two exceptions: the chacters & (amp)
+ * and ' (single quote) do not appear in the table.
+ * They are meant to appear in the URL as components,
+ * yet they require special HTML-entity escaping
+ * to generate valid HTML markup.
+ *
+ * All other characters will be escaped to %XX.
+ *
+ */
+static const char HREF_SAFE[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 
+       0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
+       1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+void
+houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)
+{
+       static const char hex_chars[] = "0123456789ABCDEF";
+       size_t  i = 0, org;
+       char hex_str[3];
+
+       bufgrow(ob, ESCAPE_GROW_FACTOR(size));
+       hex_str[0] = '%';
+
+       while (i < size) {
+               org = i;
+               while (i < size && HREF_SAFE[src[i]] != 0)
+                       i++;
+
+               if (i > org)
+                       bufput(ob, src + org, i - org);
+
+               /* escaping */
+               if (i >= size)
+                       break;
+
+               switch (src[i]) {
+               /* amp appears all the time in URLs, but needs
+                * HTML-entity escaping to be inside an href */
+               case '&': 
+                       BUFPUTSL(ob, "&amp;");
+                       break;
+
+               /* the single quote is a valid URL character
+                * according to the standard; it needs HTML
+                * entity escaping too */
+               case '\'':
+                       BUFPUTSL(ob, "&#x27;");
+                       break;
+               
+               /* the space can be escaped to %20 or a plus
+                * sign. we're going with the generic escape
+                * for now. the plus thing is more commonly seen
+                * when building GET strings */
+#if 0
+               case ' ':
+                       bufputc(ob, '+');
+                       break;
+#endif
+
+               /* every other character goes with a %XX escaping */
+               default:
+                       hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];
+                       hex_str[2] = hex_chars[src[i] & 0xF];
+                       bufput(ob, hex_str, 3);
+               }
+
+               i++;
+       }
+}
diff --git a/src/rt/sundown/html/houdini_html_e.c b/src/rt/sundown/html/houdini_html_e.c
new file mode 100644 (file)
index 0000000..d9bbf18
--- /dev/null
@@ -0,0 +1,84 @@
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "houdini.h"
+
+#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */
+
+/**
+ * According to the OWASP rules:
+ *
+ * & --> &amp;
+ * < --> &lt;
+ * > --> &gt;
+ * " --> &quot;
+ * ' --> &#x27;     &apos; is not recommended
+ * / --> &#x2F;     forward slash is included as it helps end an HTML entity
+ *
+ */
+static const char HTML_ESCAPE_TABLE[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static const char *HTML_ESCAPES[] = {
+        "",
+        "&quot;",
+        "&amp;",
+        "&#39;",
+        "&#47;",
+        "&lt;",
+        "&gt;"
+};
+
+void
+houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure)
+{
+       size_t i = 0, org, esc = 0;
+
+       bufgrow(ob, ESCAPE_GROW_FACTOR(size));
+
+       while (i < size) {
+               org = i;
+               while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)
+                       i++;
+
+               if (i > org)
+                       bufput(ob, src + org, i - org);
+
+               /* escaping */
+               if (i >= size)
+                       break;
+
+               /* The forward slash is only escaped in secure mode */
+               if (src[i] == '/' && !secure) {
+                       bufputc(ob, '/');
+               } else {
+                       bufputs(ob, HTML_ESCAPES[esc]);
+               }
+
+               i++;
+       }
+}
+
+void
+houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)
+{
+       houdini_escape_html0(ob, src, size, 1);
+}
+
diff --git a/src/rt/sundown/html/html.c b/src/rt/sundown/html/html.c
new file mode 100755 (executable)
index 0000000..7f08ee8
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * Copyright (c) 2009, Natacha Porté
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "markdown.h"
+#include "html.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "houdini.h"
+
+#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)
+
+int
+sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)
+{
+       size_t i;
+       int closed = 0;
+
+       if (tag_size < 3 || tag_data[0] != '<')
+               return HTML_TAG_NONE;
+
+       i = 1;
+
+       if (tag_data[i] == '/') {
+               closed = 1;
+               i++;
+       }
+
+       for (; i < tag_size; ++i, ++tagname) {
+               if (*tagname == 0)
+                       break;
+
+               if (tag_data[i] != *tagname)
+                       return HTML_TAG_NONE;
+       }
+
+       if (i == tag_size)
+               return HTML_TAG_NONE;
+
+       if (isspace(tag_data[i]) || tag_data[i] == '>')
+               return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;
+
+       return HTML_TAG_NONE;
+}
+
+static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length)
+{
+       houdini_escape_html0(ob, source, length, 0);
+}
+
+static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length)
+{
+       houdini_escape_href(ob, source, length);
+}
+
+/********************
+ * GENERIC RENDERER *
+ ********************/
+static int
+rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       if (!link || !link->size)
+               return 0;
+
+       if ((options->flags & HTML_SAFELINK) != 0 &&
+               !sd_autolink_issafe(link->data, link->size) &&
+               type != MKDA_EMAIL)
+               return 0;
+
+       BUFPUTSL(ob, "<a href=\"");
+       if (type == MKDA_EMAIL)
+               BUFPUTSL(ob, "mailto:");
+       escape_href(ob, link->data, link->size);
+
+       if (options->link_attributes) {
+               bufputc(ob, '\"');
+               options->link_attributes(ob, link, opaque);
+               bufputc(ob, '>');
+       } else {
+               BUFPUTSL(ob, "\">");
+       }
+
+       /*
+        * Pretty printing: if we get an email address as
+        * an actual URI, e.g. `mailto:foo@bar.com`, we don't
+        * want to print the `mailto:` prefix
+        */
+       if (bufprefix(link, "mailto:") == 0) {
+               escape_html(ob, link->data + 7, link->size - 7);
+       } else {
+               escape_html(ob, link->data, link->size);
+       }
+
+       BUFPUTSL(ob, "</a>");
+
+       return 1;
+}
+
+static void
+rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
+{
+       if (ob->size) bufputc(ob, '\n');
+
+       if (lang && lang->size) {
+               size_t i, cls;
+               BUFPUTSL(ob, "<pre><code class=\"");
+
+               for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
+                       while (i < lang->size && isspace(lang->data[i]))
+                               i++;
+
+                       if (i < lang->size) {
+                               size_t org = i;
+                               while (i < lang->size && !isspace(lang->data[i]))
+                                       i++;
+
+                               if (lang->data[org] == '.')
+                                       org++;
+
+                               if (cls) bufputc(ob, ' ');
+                               escape_html(ob, lang->data + org, i - org);
+                       }
+               }
+
+               BUFPUTSL(ob, "\">");
+       } else
+               BUFPUTSL(ob, "<pre><code>");
+
+       if (text)
+               escape_html(ob, text->data, text->size);
+
+       BUFPUTSL(ob, "</code></pre>\n");
+}
+
+static void
+rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (ob->size) bufputc(ob, '\n');
+       BUFPUTSL(ob, "<blockquote>\n");
+       if (text) bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</blockquote>\n");
+}
+
+static int
+rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)
+{
+       BUFPUTSL(ob, "<code>");
+       if (text) escape_html(ob, text->data, text->size);
+       BUFPUTSL(ob, "</code>");
+       return 1;
+}
+
+static int
+rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (!text || !text->size)
+               return 0;
+
+       BUFPUTSL(ob, "<del>");
+       bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</del>");
+       return 1;
+}
+
+static int
+rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (!text || !text->size)
+               return 0;
+
+       BUFPUTSL(ob, "<strong>");
+       bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</strong>");
+
+       return 1;
+}
+
+static int
+rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (!text || !text->size) return 0;
+       BUFPUTSL(ob, "<em>");
+       if (text) bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</em>");
+       return 1;
+}
+
+static int
+rndr_linebreak(struct buf *ob, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+       bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");
+       return 1;
+}
+
+static void
+rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       if (ob->size)
+               bufputc(ob, '\n');
+
+       if (options->flags & HTML_TOC)
+               bufprintf(ob, "<h%d id=\"toc_%d\">", level, options->toc_data.header_count++);
+       else
+               bufprintf(ob, "<h%d>", level);
+
+       if (text) bufput(ob, text->data, text->size);
+       bufprintf(ob, "</h%d>\n", level);
+}
+
+static int
+rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
+               return 0;
+
+       BUFPUTSL(ob, "<a href=\"");
+
+       if (link && link->size)
+               escape_href(ob, link->data, link->size);
+
+       if (title && title->size) {
+               BUFPUTSL(ob, "\" title=\"");
+               escape_html(ob, title->data, title->size);
+       }
+
+       if (options->link_attributes) {
+               bufputc(ob, '\"');
+               options->link_attributes(ob, link, opaque);
+               bufputc(ob, '>');
+       } else {
+               BUFPUTSL(ob, "\">");
+       }
+
+       if (content && content->size) bufput(ob, content->data, content->size);
+       BUFPUTSL(ob, "</a>");
+       return 1;
+}
+
+static void
+rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)
+{
+       if (ob->size) bufputc(ob, '\n');
+       bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
+       if (text) bufput(ob, text->data, text->size);
+       bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
+}
+
+static void
+rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)
+{
+       BUFPUTSL(ob, "<li>");
+       if (text) {
+               size_t size = text->size;
+               while (size && text->data[size - 1] == '\n')
+                       size--;
+
+               bufput(ob, text->data, size);
+       }
+       BUFPUTSL(ob, "</li>\n");
+}
+
+static void
+rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+       size_t i = 0;
+
+       if (ob->size) bufputc(ob, '\n');
+
+       if (!text || !text->size)
+               return;
+
+       while (i < text->size && isspace(text->data[i])) i++;
+
+       if (i == text->size)
+               return;
+
+       BUFPUTSL(ob, "<p>");
+       if (options->flags & HTML_HARD_WRAP) {
+               size_t org;
+               while (i < text->size) {
+                       org = i;
+                       while (i < text->size && text->data[i] != '\n')
+                               i++;
+
+                       if (i > org)
+                               bufput(ob, text->data + org, i - org);
+
+                       /*
+                        * do not insert a line break if this newline
+                        * is the last character on the paragraph
+                        */
+                       if (i >= text->size - 1)
+                               break;
+
+                       rndr_linebreak(ob, opaque);
+                       i++;
+               }
+       } else {
+               bufput(ob, &text->data[i], text->size - i);
+       }
+       BUFPUTSL(ob, "</p>\n");
+}
+
+static void
+rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
+{
+       size_t org, sz;
+       if (!text) return;
+       sz = text->size;
+       while (sz > 0 && text->data[sz - 1] == '\n') sz--;
+       org = 0;
+       while (org < sz && text->data[org] == '\n') org++;
+       if (org >= sz) return;
+       if (ob->size) bufputc(ob, '\n');
+       bufput(ob, text->data + org, sz - org);
+       bufputc(ob, '\n');
+}
+
+static int
+rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (!text || !text->size) return 0;
+       BUFPUTSL(ob, "<strong><em>");
+       bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</em></strong>");
+       return 1;
+}
+
+static void
+rndr_hrule(struct buf *ob, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+       if (ob->size) bufputc(ob, '\n');
+       bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");
+}
+
+static int
+rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+       if (!link || !link->size) return 0;
+
+       BUFPUTSL(ob, "<img src=\"");
+       escape_href(ob, link->data, link->size);
+       BUFPUTSL(ob, "\" alt=\"");
+
+       if (alt && alt->size)
+               escape_html(ob, alt->data, alt->size);
+
+       if (title && title->size) {
+               BUFPUTSL(ob, "\" title=\"");
+               escape_html(ob, title->data, title->size); }
+
+       bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
+       return 1;
+}
+
+static int
+rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
+       * It doens't see if there are any valid tags, just escape all of them. */
+       if((options->flags & HTML_ESCAPE) != 0) {
+               escape_html(ob, text->data, text->size);
+               return 1;
+       }
+
+       if ((options->flags & HTML_SKIP_HTML) != 0)
+               return 1;
+
+       if ((options->flags & HTML_SKIP_STYLE) != 0 &&
+               sdhtml_is_tag(text->data, text->size, "style"))
+               return 1;
+
+       if ((options->flags & HTML_SKIP_LINKS) != 0 &&
+               sdhtml_is_tag(text->data, text->size, "a"))
+               return 1;
+
+       if ((options->flags & HTML_SKIP_IMAGES) != 0 &&
+               sdhtml_is_tag(text->data, text->size, "img"))
+               return 1;
+
+       bufput(ob, text->data, text->size);
+       return 1;
+}
+
+static void
+rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)
+{
+       if (ob->size) bufputc(ob, '\n');
+       BUFPUTSL(ob, "<table><thead>\n");
+       if (header)
+               bufput(ob, header->data, header->size);
+       BUFPUTSL(ob, "</thead><tbody>\n");
+       if (body)
+               bufput(ob, body->data, body->size);
+       BUFPUTSL(ob, "</tbody></table>\n");
+}
+
+static void
+rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)
+{
+       BUFPUTSL(ob, "<tr>\n");
+       if (text)
+               bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</tr>\n");
+}
+
+static void
+rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque)
+{
+       if (flags & MKD_TABLE_HEADER) {
+               BUFPUTSL(ob, "<th");
+       } else {
+               BUFPUTSL(ob, "<td");
+       }
+
+       switch (flags & MKD_TABLE_ALIGNMASK) {
+       case MKD_TABLE_ALIGN_CENTER:
+               BUFPUTSL(ob, " align=\"center\">");
+               break;
+
+       case MKD_TABLE_ALIGN_L:
+               BUFPUTSL(ob, " align=\"left\">");
+               break;
+
+       case MKD_TABLE_ALIGN_R:
+               BUFPUTSL(ob, " align=\"right\">");
+               break;
+
+       default:
+               BUFPUTSL(ob, ">");
+       }
+
+       if (text)
+               bufput(ob, text->data, text->size);
+
+       if (flags & MKD_TABLE_HEADER) {
+               BUFPUTSL(ob, "</th>\n");
+       } else {
+               BUFPUTSL(ob, "</td>\n");
+       }
+}
+
+static int
+rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (!text || !text->size) return 0;
+       BUFPUTSL(ob, "<sup>");
+       bufput(ob, text->data, text->size);
+       BUFPUTSL(ob, "</sup>");
+       return 1;
+}
+
+static void
+rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)
+{
+       if (text)
+               escape_html(ob, text->data, text->size);
+}
+
+static void
+toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       /* set the level offset if this is the first header
+        * we're parsing for the document */
+       if (options->toc_data.current_level == 0) {
+               options->toc_data.level_offset = level - 1;
+       }
+       level -= options->toc_data.level_offset;
+
+       if (level > options->toc_data.current_level) {
+               while (level > options->toc_data.current_level) {
+                       BUFPUTSL(ob, "<ul>\n<li>\n");
+                       options->toc_data.current_level++;
+               }
+       } else if (level < options->toc_data.current_level) {
+               BUFPUTSL(ob, "</li>\n");
+               while (level < options->toc_data.current_level) {
+                       BUFPUTSL(ob, "</ul>\n</li>\n");
+                       options->toc_data.current_level--;
+               }
+               BUFPUTSL(ob,"<li>\n");
+       } else {
+               BUFPUTSL(ob,"</li>\n<li>\n");
+       }
+
+       bufprintf(ob, "<a href=\"#toc_%d\">", options->toc_data.header_count++);
+       if (text)
+               escape_html(ob, text->data, text->size);
+       BUFPUTSL(ob, "</a>\n");
+}
+
+static int
+toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
+{
+       if (content && content->size)
+               bufput(ob, content->data, content->size);
+       return 1;
+}
+
+static void
+toc_finalize(struct buf *ob, void *opaque)
+{
+       struct html_renderopt *options = opaque;
+
+       while (options->toc_data.current_level > 0) {
+               BUFPUTSL(ob, "</li>\n</ul>\n");
+               options->toc_data.current_level--;
+       }
+}
+
+void
+sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options)
+{
+       static const struct sd_callbacks cb_default = {
+               NULL,
+               NULL,
+               NULL,
+               toc_header,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+               NULL,
+
+               NULL,
+               rndr_codespan,
+               rndr_double_emphasis,
+               rndr_emphasis,
+               NULL,
+               NULL,
+               toc_link,
+               NULL,
+               rndr_triple_emphasis,
+               rndr_strikethrough,
+               rndr_superscript,
+
+               NULL,
+               NULL,
+
+               NULL,
+               toc_finalize,
+       };
+
+       memset(options, 0x0, sizeof(struct html_renderopt));
+       options->flags = HTML_TOC;
+
+       memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
+}
+
+void
+sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
+{
+       static const struct sd_callbacks cb_default = {
+               rndr_blockcode,
+               rndr_blockquote,
+               rndr_raw_block,
+               rndr_header,
+               rndr_hrule,
+               rndr_list,
+               rndr_listitem,
+               rndr_paragraph,
+               rndr_table,
+               rndr_tablerow,
+               rndr_tablecell,
+
+               rndr_autolink,
+               rndr_codespan,
+               rndr_double_emphasis,
+               rndr_emphasis,
+               rndr_image,
+               rndr_linebreak,
+               rndr_link,
+               rndr_raw_html,
+               rndr_triple_emphasis,
+               rndr_strikethrough,
+               rndr_superscript,
+
+               NULL,
+               rndr_normal_text,
+
+               NULL,
+               NULL,
+       };
+
+       /* Prepare the options pointer */
+       memset(options, 0x0, sizeof(struct html_renderopt));
+       options->flags = render_flags;
+
+       /* Prepare the callbacks */
+       memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
+
+       if (render_flags & HTML_SKIP_IMAGES)
+               callbacks->image = NULL;
+
+       if (render_flags & HTML_SKIP_LINKS) {
+               callbacks->link = NULL;
+               callbacks->autolink = NULL;
+       }
+
+       if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE)
+               callbacks->blockhtml = NULL;
+}
diff --git a/src/rt/sundown/html/html.h b/src/rt/sundown/html/html.h
new file mode 100644 (file)
index 0000000..4c8810d
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UPSKIRT_HTML_H
+#define UPSKIRT_HTML_H
+
+#include "markdown.h"
+#include "buffer.h"
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct html_renderopt {
+       struct {
+               int header_count;
+               int current_level;
+               int level_offset;
+       } toc_data;
+
+       unsigned int flags;
+
+       /* extra callbacks */
+       void (*link_attributes)(struct buf *ob, const struct buf *url, void *self);
+};
+
+typedef enum {
+       HTML_SKIP_HTML = (1 << 0),
+       HTML_SKIP_STYLE = (1 << 1),
+       HTML_SKIP_IMAGES = (1 << 2),
+       HTML_SKIP_LINKS = (1 << 3),
+       HTML_EXPAND_TABS = (1 << 4),
+       HTML_SAFELINK = (1 << 5),
+       HTML_TOC = (1 << 6),
+       HTML_HARD_WRAP = (1 << 7),
+       HTML_USE_XHTML = (1 << 8),
+       HTML_ESCAPE = (1 << 9),
+} html_render_mode;
+
+typedef enum {
+       HTML_TAG_NONE = 0,
+       HTML_TAG_OPEN,
+       HTML_TAG_CLOSE,
+} html_tag;
+
+int
+sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname);
+
+extern void
+sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);
+
+extern void
+sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr);
+
+extern void
+sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/rt/sundown/html/html_smartypants.c b/src/rt/sundown/html/html_smartypants.c
new file mode 100644 (file)
index 0000000..367c26a
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "buffer.h"
+#include "html.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(_WIN32)
+#define snprintf       _snprintf               
+#endif
+
+struct smartypants_data {
+       int in_squote;
+       int in_dquote;
+};
+
+static size_t smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+static size_t smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);
+
+static size_t (*smartypants_cb_ptrs[])
+       (struct buf *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) =
+{
+       NULL,                                   /* 0 */
+       smartypants_cb__dash,   /* 1 */
+       smartypants_cb__parens, /* 2 */
+       smartypants_cb__squote, /* 3 */
+       smartypants_cb__dquote, /* 4 */
+       smartypants_cb__amp,    /* 5 */
+       smartypants_cb__period, /* 6 */
+       smartypants_cb__number, /* 7 */
+       smartypants_cb__ltag,   /* 8 */
+       smartypants_cb__backtick, /* 9 */
+       smartypants_cb__escape, /* 10 */
+};
+
+static const uint8_t smartypants_cb_chars[] = {
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0,
+       0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,
+       9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+static inline int
+word_boundary(uint8_t c)
+{
+       return c == 0 || isspace(c) || ispunct(c);
+}
+
+static int
+smartypants_quotes(struct buf *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open)
+{
+       char ent[8];
+
+       if (*is_open && !word_boundary(next_char))
+               return 0;
+
+       if (!(*is_open) && !word_boundary(previous_char))
+               return 0;
+
+       snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote);
+       *is_open = !(*is_open);
+       bufputs(ob, ent);
+       return 1;
+}
+
+static size_t
+smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 2) {
+               uint8_t t1 = tolower(text[1]);
+
+               if (t1 == '\'') {
+                       if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))
+                               return 1;
+               }
+
+               if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&
+                       (size == 3 || word_boundary(text[2]))) {
+                       BUFPUTSL(ob, "&rsquo;");
+                       return 0;
+               }
+
+               if (size >= 3) {
+                       uint8_t t2 = tolower(text[2]);
+
+                       if (((t1 == 'r' && t2 == 'e') ||
+                               (t1 == 'l' && t2 == 'l') ||
+                               (t1 == 'v' && t2 == 'e')) &&
+                               (size == 4 || word_boundary(text[3]))) {
+                               BUFPUTSL(ob, "&rsquo;");
+                               return 0;
+                       }
+               }
+       }
+
+       if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote))
+               return 0;
+
+       bufputc(ob, text[0]);
+       return 0;
+}
+
+static size_t
+smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 3) {
+               uint8_t t1 = tolower(text[1]);
+               uint8_t t2 = tolower(text[2]);
+
+               if (t1 == 'c' && t2 == ')') {
+                       BUFPUTSL(ob, "&copy;");
+                       return 2;
+               }
+
+               if (t1 == 'r' && t2 == ')') {
+                       BUFPUTSL(ob, "&reg;");
+                       return 2;
+               }
+
+               if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') {
+                       BUFPUTSL(ob, "&trade;");
+                       return 3;
+               }
+       }
+
+       bufputc(ob, text[0]);
+       return 0;
+}
+
+static size_t
+smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 3 && text[1] == '-' && text[2] == '-') {
+               BUFPUTSL(ob, "&mdash;");
+               return 2;
+       }
+
+       if (size >= 2 && text[1] == '-') {
+               BUFPUTSL(ob, "&ndash;");
+               return 1;
+       }
+
+       bufputc(ob, text[0]);
+       return 0;
+}
+
+static size_t
+smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 6 && memcmp(text, "&quot;", 6) == 0) {
+               if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote))
+                       return 5;
+       }
+
+       if (size >= 4 && memcmp(text, "&#0;", 4) == 0)
+               return 3;
+
+       bufputc(ob, '&');
+       return 0;
+}
+
+static size_t
+smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 3 && text[1] == '.' && text[2] == '.') {
+               BUFPUTSL(ob, "&hellip;");
+               return 2;
+       }
+
+       if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') {
+               BUFPUTSL(ob, "&hellip;");
+               return 4;
+       }
+
+       bufputc(ob, text[0]);
+       return 0;
+}
+
+static size_t
+smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size >= 2 && text[1] == '`') {
+               if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static size_t
+smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (word_boundary(previous_char) && size >= 3) {
+               if (text[0] == '1' && text[1] == '/' && text[2] == '2') {
+                       if (size == 3 || word_boundary(text[3])) {
+                               BUFPUTSL(ob, "&frac12;");
+                               return 2;
+                       }
+               }
+
+               if (text[0] == '1' && text[1] == '/' && text[2] == '4') {
+                       if (size == 3 || word_boundary(text[3]) ||
+                               (size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) {
+                               BUFPUTSL(ob, "&frac14;");
+                               return 2;
+                       }
+               }
+
+               if (text[0] == '3' && text[1] == '/' && text[2] == '4') {
+                       if (size == 3 || word_boundary(text[3]) ||
+                               (size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) {
+                               BUFPUTSL(ob, "&frac34;");
+                               return 2;
+                       }
+               }
+       }
+
+       bufputc(ob, text[0]);
+       return 0;
+}
+
+static size_t
+smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote))
+               BUFPUTSL(ob, "&quot;");
+
+       return 0;
+}
+
+static size_t
+smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       static const char *skip_tags[] = {
+         "pre", "code", "var", "samp", "kbd", "math", "script", "style"
+       };
+       static const size_t skip_tags_count = 8;
+
+       size_t tag, i = 0;
+
+       while (i < size && text[i] != '>')
+               i++;
+
+       for (tag = 0; tag < skip_tags_count; ++tag) {
+               if (sdhtml_is_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN)
+                       break;
+       }
+
+       if (tag < skip_tags_count) {
+               for (;;) {
+                       while (i < size && text[i] != '<')
+                               i++;
+
+                       if (i == size)
+                               break;
+
+                       if (sdhtml_is_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE)
+                               break;
+
+                       i++;
+               }
+
+               while (i < size && text[i] != '>')
+                       i++;
+       }
+
+       bufput(ob, text, i + 1);
+       return i;
+}
+
+static size_t
+smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)
+{
+       if (size < 2)
+               return 0;
+
+       switch (text[1]) {
+       case '\\':
+       case '"':
+       case '\'':
+       case '.':
+       case '-':
+       case '`':
+               bufputc(ob, text[1]);
+               return 1;
+
+       default:
+               bufputc(ob, '\\');
+               return 0;
+       }
+}
+
+#if 0
+static struct {
+    uint8_t c0;
+    const uint8_t *pattern;
+    const uint8_t *entity;
+    int skip;
+} smartypants_subs[] = {
+    { '\'', "'s>",      "&rsquo;",  0 },
+    { '\'', "'t>",      "&rsquo;",  0 },
+    { '\'', "'re>",     "&rsquo;",  0 },
+    { '\'', "'ll>",     "&rsquo;",  0 },
+    { '\'', "'ve>",     "&rsquo;",  0 },
+    { '\'', "'m>",      "&rsquo;",  0 },
+    { '\'', "'d>",      "&rsquo;",  0 },
+    { '-',  "--",       "&mdash;",  1 },
+    { '-',  "<->",      "&ndash;",  0 },
+    { '.',  "...",      "&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;",      0,       3 },
+};
+#endif
+
+void
+sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size)
+{
+       size_t i;
+       struct smartypants_data smrt = {0, 0};
+
+       if (!text)
+               return;
+
+       bufgrow(ob, size);
+
+       for (i = 0; i < size; ++i) {
+               size_t org;
+               uint8_t action = 0;
+
+               org = i;
+               while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)
+                       i++;
+
+               if (i > org)
+                       bufput(ob, text + org, i - org);
+
+               if (i < size) {
+                       i += smartypants_cb_ptrs[(int)action]
+                               (ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);
+               }
+       }
+}
+
+
diff --git a/src/rt/sundown/html_block_names.txt b/src/rt/sundown/html_block_names.txt
new file mode 100644 (file)
index 0000000..a41d7d1
--- /dev/null
@@ -0,0 +1,25 @@
+##
+p
+dl
+h1
+h2
+h3
+h4
+h5
+h6
+ol
+ul
+del
+div
+ins
+pre
+form
+math
+table
+figure
+iframe
+script
+style
+fieldset
+noscript
+blockquote
diff --git a/src/rt/sundown/src/autolink.c b/src/rt/sundown/src/autolink.c
new file mode 100644 (file)
index 0000000..6f8d6ab
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "buffer.h"
+#include "autolink.h"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if defined(_WIN32)
+#define strncasecmp    _strnicmp
+#endif
+
+int
+sd_autolink_issafe(const uint8_t *link, size_t link_len)
+{
+       static const size_t valid_uris_count = 5;
+       static const char *valid_uris[] = {
+               "/", "http://", "https://", "ftp://", "mailto:"
+       };
+
+       size_t i;
+
+       for (i = 0; i < valid_uris_count; ++i) {
+               size_t len = strlen(valid_uris[i]);
+
+               if (link_len > len &&
+                       strncasecmp((char *)link, valid_uris[i], len) == 0 &&
+                       isalnum(link[len]))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static size_t
+autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
+{
+       uint8_t cclose, copen = 0;
+       size_t i;
+
+       for (i = 0; i < link_end; ++i)
+               if (data[i] == '<') {
+                       link_end = i;
+                       break;
+               }
+
+       while (link_end > 0) {
+               if (strchr("?!.,", data[link_end - 1]) != NULL)
+                       link_end--;
+
+               else if (data[link_end - 1] == ';') {
+                       size_t new_end = link_end - 2;
+
+                       while (new_end > 0 && isalpha(data[new_end]))
+                               new_end--;
+
+                       if (new_end < link_end - 2 && data[new_end] == '&')
+                               link_end = new_end;
+                       else
+                               link_end--;
+               }
+               else break;
+       }
+
+       if (link_end == 0)
+               return 0;
+
+       cclose = data[link_end - 1];
+
+       switch (cclose) {
+       case '"':       copen = '"'; break;
+       case '\'':      copen = '\''; break;
+       case ')':       copen = '('; break;
+       case ']':       copen = '['; break;
+       case '}':       copen = '{'; break;
+       }
+
+       if (copen != 0) {
+               size_t closing = 0;
+               size_t opening = 0;
+               size_t i = 0;
+
+               /* Try to close the final punctuation sign in this same line;
+                * if we managed to close it outside of the URL, that means that it's
+                * not part of the URL. If it closes inside the URL, that means it
+                * is part of the URL.
+                *
+                * Examples:
+                *
+                *      foo http://www.pokemon.com/Pikachu_(Electric) bar
+                *              => http://www.pokemon.com/Pikachu_(Electric)
+                *
+                *      foo (http://www.pokemon.com/Pikachu_(Electric)) bar
+                *              => http://www.pokemon.com/Pikachu_(Electric)
+                *
+                *      foo http://www.pokemon.com/Pikachu_(Electric)) bar
+                *              => http://www.pokemon.com/Pikachu_(Electric))
+                *
+                *      (foo http://www.pokemon.com/Pikachu_(Electric)) bar
+                *              => foo http://www.pokemon.com/Pikachu_(Electric)
+                */
+
+               while (i < link_end) {
+                       if (data[i] == copen)
+                               opening++;
+                       else if (data[i] == cclose)
+                               closing++;
+
+                       i++;
+               }
+
+               if (closing != opening)
+                       link_end--;
+       }
+
+       return link_end;
+}
+
+static size_t
+check_domain(uint8_t *data, size_t size, int allow_short)
+{
+       size_t i, np = 0;
+
+       if (!isalnum(data[0]))
+               return 0;
+
+       for (i = 1; i < size - 1; ++i) {
+               if (data[i] == '.') np++;
+               else if (!isalnum(data[i]) && data[i] != '-') break;
+       }
+
+       if (allow_short) {
+               /* We don't need a valid domain in the strict sense (with
+                * least one dot; so just make sure it's composed of valid
+                * domain characters and return the length of the the valid
+                * sequence. */
+               return i;
+       } else {
+               /* a valid domain needs to have at least a dot.
+                * that's as far as we get */
+               return np ? i : 0;
+       }
+}
+
+size_t
+sd_autolink__www(
+       size_t *rewind_p,
+       struct buf *link,
+       uint8_t *data,
+       size_t max_rewind,
+       size_t size,
+       unsigned int flags)
+{
+       size_t link_end;
+
+       if (max_rewind > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
+               return 0;
+
+       if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
+               return 0;
+
+       link_end = check_domain(data, size, 0);
+
+       if (link_end == 0)
+               return 0;
+
+       while (link_end < size && !isspace(data[link_end]))
+               link_end++;
+
+       link_end = autolink_delim(data, link_end, max_rewind, size);
+
+       if (link_end == 0)
+               return 0;
+
+       bufput(link, data, link_end);
+       *rewind_p = 0;
+
+       return (int)link_end;
+}
+
+size_t
+sd_autolink__email(
+       size_t *rewind_p,
+       struct buf *link,
+       uint8_t *data,
+       size_t max_rewind,
+       size_t size,
+       unsigned int flags)
+{
+       size_t link_end, rewind;
+       int nb = 0, np = 0;
+
+       for (rewind = 0; rewind < max_rewind; ++rewind) {
+               uint8_t c = data[-rewind - 1];
+
+               if (isalnum(c))
+                       continue;
+
+               if (strchr(".+-_", c) != NULL)
+                       continue;
+
+               break;
+       }
+
+       if (rewind == 0)
+               return 0;
+
+       for (link_end = 0; link_end < size; ++link_end) {
+               uint8_t c = data[link_end];
+
+               if (isalnum(c))
+                       continue;
+
+               if (c == '@')
+                       nb++;
+               else if (c == '.' && link_end < size - 1)
+                       np++;
+               else if (c != '-' && c != '_')
+                       break;
+       }
+
+       if (link_end < 2 || nb != 1 || np == 0 ||
+               !isalpha(data[link_end - 1]))
+               return 0;
+
+       link_end = autolink_delim(data, link_end, max_rewind, size);
+
+       if (link_end == 0)
+               return 0;
+
+       bufput(link, data - rewind, link_end + rewind);
+       *rewind_p = rewind;
+
+       return link_end;
+}
+
+size_t
+sd_autolink__url(
+       size_t *rewind_p,
+       struct buf *link,
+       uint8_t *data,
+       size_t max_rewind,
+       size_t size,
+       unsigned int flags)
+{
+       size_t link_end, rewind = 0, domain_len;
+
+       if (size < 4 || data[1] != '/' || data[2] != '/')
+               return 0;
+
+       while (rewind < max_rewind && isalpha(data[-rewind - 1]))
+               rewind++;
+
+       if (!sd_autolink_issafe(data - rewind, size + rewind))
+               return 0;
+
+       link_end = strlen("://");
+
+       domain_len = check_domain(
+               data + link_end,
+               size - link_end,
+               flags & SD_AUTOLINK_SHORT_DOMAINS);
+
+       if (domain_len == 0)
+               return 0;
+
+       link_end += domain_len;
+       while (link_end < size && !isspace(data[link_end]))
+               link_end++;
+
+       link_end = autolink_delim(data, link_end, max_rewind, size);
+
+       if (link_end == 0)
+               return 0;
+
+       bufput(link, data - rewind, link_end + rewind);
+       *rewind_p = rewind;
+
+       return link_end;
+}
+
diff --git a/src/rt/sundown/src/autolink.h b/src/rt/sundown/src/autolink.h
new file mode 100644 (file)
index 0000000..65e0fe6
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UPSKIRT_AUTOLINK_H
+#define UPSKIRT_AUTOLINK_H
+
+#include "buffer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+       SD_AUTOLINK_SHORT_DOMAINS = (1 << 0),
+};
+
+int
+sd_autolink_issafe(const uint8_t *link, size_t link_len);
+
+size_t
+sd_autolink__www(size_t *rewind_p, struct buf *link,
+       uint8_t *data, size_t offset, size_t size, unsigned int flags);
+
+size_t
+sd_autolink__email(size_t *rewind_p, struct buf *link,
+       uint8_t *data, size_t offset, size_t size, unsigned int flags);
+
+size_t
+sd_autolink__url(size_t *rewind_p, struct buf *link,
+       uint8_t *data, size_t offset, size_t size, unsigned int flags);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* vim: set filetype=c: */
diff --git a/src/rt/sundown/src/buffer.c b/src/rt/sundown/src/buffer.c
new file mode 100644 (file)
index 0000000..47b40ce
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
+
+#include "buffer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* MSVC compat */
+#if defined(_MSC_VER)
+#      define _buf_vsnprintf _vsnprintf
+#else
+#      define _buf_vsnprintf vsnprintf
+#endif
+
+int
+bufprefix(const struct buf *buf, const char *prefix)
+{
+       size_t i;
+       assert(buf && buf->unit);
+
+       for (i = 0; i < buf->size; ++i) {
+               if (prefix[i] == 0)
+                       return 0;
+
+               if (buf->data[i] != prefix[i])
+                       return buf->data[i] - prefix[i];
+       }
+
+       return 0;
+}
+
+/* bufgrow: increasing the allocated size to the given value */
+int
+bufgrow(struct buf *buf, size_t neosz)
+{
+       size_t neoasz;
+       void *neodata;
+
+       assert(buf && buf->unit);
+
+       if (neosz > BUFFER_MAX_ALLOC_SIZE)
+               return BUF_ENOMEM;
+
+       if (buf->asize >= neosz)
+               return BUF_OK;
+
+       neoasz = buf->asize + buf->unit;
+       while (neoasz < neosz)
+               neoasz += buf->unit;
+
+       neodata = realloc(buf->data, neoasz);
+       if (!neodata)
+               return BUF_ENOMEM;
+
+       buf->data = neodata;
+       buf->asize = neoasz;
+       return BUF_OK;
+}
+
+
+/* bufnew: allocation of a new buffer */
+struct buf *
+bufnew(size_t unit)
+{
+       struct buf *ret;
+       ret = malloc(sizeof (struct buf));
+
+       if (ret) {
+               ret->data = 0;
+               ret->size = ret->asize = 0;
+               ret->unit = unit;
+       }
+       return ret;
+}
+
+/* bufnullterm: NULL-termination of the string array */
+const char *
+bufcstr(struct buf *buf)
+{
+       assert(buf && buf->unit);
+
+       if (buf->size < buf->asize && buf->data[buf->size] == 0)
+               return (char *)buf->data;
+
+       if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {
+               buf->data[buf->size] = 0;
+               return (char *)buf->data;
+       }
+
+       return NULL;
+}
+
+/* bufprintf: formatted printing to a buffer */
+void
+bufprintf(struct buf *buf, const char *fmt, ...)
+{
+       va_list ap;
+       int n;
+
+       assert(buf && buf->unit);
+
+       if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0)
+               return;
+       
+       va_start(ap, fmt);
+       n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
+       va_end(ap);
+
+       if (n < 0) {
+#ifdef _MSC_VER
+               va_start(ap, fmt);
+               n = _vscprintf(fmt, ap);
+               va_end(ap);
+#else
+               return;
+#endif
+       }
+
+       if ((size_t)n >= buf->asize - buf->size) {
+               if (bufgrow(buf, buf->size + n + 1) < 0)
+                       return;
+
+               va_start(ap, fmt);
+               n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);
+               va_end(ap);
+       }
+
+       if (n < 0)
+               return;
+
+       buf->size += n;
+}
+
+/* bufput: appends raw data to a buffer */
+void
+bufput(struct buf *buf, const void *data, size_t len)
+{
+       assert(buf && buf->unit);
+
+       if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)
+               return;
+
+       memcpy(buf->data + buf->size, data, len);
+       buf->size += len;
+}
+
+/* bufputs: appends a NUL-terminated string to a buffer */
+void
+bufputs(struct buf *buf, const char *str)
+{
+       bufput(buf, str, strlen(str));
+}
+
+
+/* bufputc: appends a single uint8_t to a buffer */
+void
+bufputc(struct buf *buf, int c)
+{
+       assert(buf && buf->unit);
+
+       if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)
+               return;
+
+       buf->data[buf->size] = c;
+       buf->size += 1;
+}
+
+/* bufrelease: decrease the reference count and free the buffer if needed */
+void
+bufrelease(struct buf *buf)
+{
+       if (!buf)
+               return;
+
+       free(buf->data);
+       free(buf);
+}
+
+
+/* bufreset: frees internal data of the buffer */
+void
+bufreset(struct buf *buf)
+{
+       if (!buf)
+               return;
+
+       free(buf->data);
+       buf->data = NULL;
+       buf->size = buf->asize = 0;
+}
+
+/* bufslurp: removes a given number of bytes from the head of the array */
+void
+bufslurp(struct buf *buf, size_t len)
+{
+       assert(buf && buf->unit);
+
+       if (len >= buf->size) {
+               buf->size = 0;
+               return;
+       }
+
+       buf->size -= len;
+       memmove(buf->data, buf->data + len, buf->size);
+}
+
diff --git a/src/rt/sundown/src/buffer.h b/src/rt/sundown/src/buffer.h
new file mode 100644 (file)
index 0000000..221d142
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef BUFFER_H__
+#define BUFFER_H__
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(_MSC_VER)
+#define __attribute__(x)
+#define inline
+#endif
+
+typedef enum {
+       BUF_OK = 0,
+       BUF_ENOMEM = -1,
+} buferror_t;
+
+/* struct buf: character array buffer */
+struct buf {
+       uint8_t *data;          /* actual character data */
+       size_t size;    /* size of the string */
+       size_t asize;   /* allocated size (0 = volatile buffer) */
+       size_t unit;    /* reallocation unit size (0 = read-only buffer) */
+};
+
+/* CONST_BUF: global buffer from a string litteral */
+#define BUF_STATIC(string) \
+       { (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 }
+
+/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */
+#define BUF_VOLATILE(strname) \
+       { (uint8_t *)strname, strlen(strname), 0, 0, 0 }
+
+/* BUFPUTSL: optimized bufputs of a string litteral */
+#define BUFPUTSL(output, literal) \
+       bufput(output, literal, sizeof literal - 1)
+
+/* bufgrow: increasing the allocated size to the given value */
+int bufgrow(struct buf *, size_t);
+
+/* bufnew: allocation of a new buffer */
+struct buf *bufnew(size_t) __attribute__ ((malloc));
+
+/* bufnullterm: NUL-termination of the string array (making a C-string) */
+const char *bufcstr(struct buf *);
+
+/* bufprefix: compare the beginning of a buffer with a string */
+int bufprefix(const struct buf *buf, const char *prefix);
+
+/* bufput: appends raw data to a buffer */
+void bufput(struct buf *, const void *, size_t);
+
+/* bufputs: appends a NUL-terminated string to a buffer */
+void bufputs(struct buf *, const char *);
+
+/* bufputc: appends a single char to a buffer */
+void bufputc(struct buf *, int);
+
+/* bufrelease: decrease the reference count and free the buffer if needed */
+void bufrelease(struct buf *);
+
+/* bufreset: frees internal data of the buffer */
+void bufreset(struct buf *);
+
+/* bufslurp: removes a given number of bytes from the head of the array */
+void bufslurp(struct buf *, size_t);
+
+/* bufprintf: formatted printing to a buffer */
+void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/rt/sundown/src/html_blocks.h b/src/rt/sundown/src/html_blocks.h
new file mode 100644 (file)
index 0000000..09a758f
--- /dev/null
@@ -0,0 +1,206 @@
+/* C code produced by gperf version 3.0.3 */
+/* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt  */
+/* Computed positions: -k'1-2' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+/* maximum key range = 37, duplicates = 0 */
+
+#ifndef GPERF_DOWNCASE
+#define GPERF_DOWNCASE 1
+static unsigned char gperf_downcase[256] =
+  {
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
+     45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+     60,  61,  62,  63,  64,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106,
+    107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+    122,  91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103, 104,
+    105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+    120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+    135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+    150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+    165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+    180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+    210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+    225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+    255
+  };
+#endif
+
+#ifndef GPERF_CASE_STRNCMP
+#define GPERF_CASE_STRNCMP 1
+static int
+gperf_case_strncmp (s1, s2, n)
+     register const char *s1;
+     register const char *s2;
+     register unsigned int n;
+{
+  for (; n > 0;)
+    {
+      unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
+      unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
+      if (c1 != 0 && c1 == c2)
+        {
+          n--;
+          continue;
+        }
+      return (int)c1 - (int)c2;
+    }
+  return 0;
+}
+#endif
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash_block_tag (str, len)
+     register const char *str;
+     register unsigned int len;
+{
+  static const unsigned char asso_values[] =
+    {
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+       8, 30, 25, 20, 15, 10, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38,  0, 38,  0, 38,
+       5,  5,  5, 15,  0, 38, 38,  0, 15, 10,
+       0, 38, 38, 15,  0,  5, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38,  0, 38,
+       0, 38,  5,  5,  5, 15,  0, 38, 38,  0,
+      15, 10,  0, 38, 38, 15,  0,  5, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38
+    };
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+        hval += asso_values[(unsigned char)str[1]+1];
+      /*FALLTHROUGH*/
+      case 1:
+        hval += asso_values[(unsigned char)str[0]];
+        break;
+    }
+  return hval;
+}
+
+#ifdef __GNUC__
+__inline
+#ifdef __GNUC_STDC_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
+#endif
+const char *
+find_block_tag (str, len)
+     register const char *str;
+     register unsigned int len;
+{
+  enum
+    {
+      TOTAL_KEYWORDS = 24,
+      MIN_WORD_LENGTH = 1,
+      MAX_WORD_LENGTH = 10,
+      MIN_HASH_VALUE = 1,
+      MAX_HASH_VALUE = 37
+    };
+
+  static const char * const wordlist[] =
+    {
+      "",
+      "p",
+      "dl",
+      "div",
+      "math",
+      "table",
+      "",
+      "ul",
+      "del",
+      "form",
+      "blockquote",
+      "figure",
+      "ol",
+      "fieldset",
+      "",
+      "h1",
+      "",
+      "h6",
+      "pre",
+      "", "",
+      "script",
+      "h5",
+      "noscript",
+      "",
+      "style",
+      "iframe",
+      "h4",
+      "ins",
+      "", "", "",
+      "h3",
+      "", "", "", "",
+      "h2"
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = hash_block_tag (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register const char *s = wordlist[key];
+
+          if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, len) && s[len] == '\0')
+            return s;
+        }
+    }
+  return 0;
+}
diff --git a/src/rt/sundown/src/markdown.c b/src/rt/sundown/src/markdown.c
new file mode 100644 (file)
index 0000000..ea3cf23
--- /dev/null
@@ -0,0 +1,2556 @@
+/* markdown.c - generic markdown parser */
+
+/*
+ * Copyright (c) 2009, Natacha Porté
+ * Copyright (c) 2011, Vicent Marti
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "markdown.h"
+#include "stack.h"
+
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#if defined(_WIN32)
+#define strncasecmp    _strnicmp
+#endif
+
+#define REF_TABLE_SIZE 8
+
+#define BUFFER_BLOCK 0
+#define BUFFER_SPAN 1
+
+#define MKD_LI_END 8   /* internal list flag */
+
+#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n)
+#define GPERF_DOWNCASE 1
+#define GPERF_CASE_STRNCMP 1
+#include "html_blocks.h"
+
+/***************
+ * LOCAL TYPES *
+ ***************/
+
+/* link_ref: reference to a link */
+struct link_ref {
+       unsigned int id;
+
+       struct buf *link;
+       struct buf *title;
+
+       struct link_ref *next;
+};
+
+/* char_trigger: function pointer to render active chars */
+/*   returns the number of chars taken care of */
+/*   data is the pointer of the beginning of the span */
+/*   offset is the number of valid chars before data */
+struct sd_markdown;
+typedef size_t
+(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+
+static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);
+
+enum markdown_char_t {
+       MD_CHAR_NONE = 0,
+       MD_CHAR_EMPHASIS,
+       MD_CHAR_CODESPAN,
+       MD_CHAR_LINEBREAK,
+       MD_CHAR_LINK,
+       MD_CHAR_LANGLE,
+       MD_CHAR_ESCAPE,
+       MD_CHAR_ENTITITY,
+       MD_CHAR_AUTOLINK_URL,
+       MD_CHAR_AUTOLINK_EMAIL,
+       MD_CHAR_AUTOLINK_WWW,
+       MD_CHAR_SUPERSCRIPT,
+};
+
+static char_trigger markdown_char_ptrs[] = {
+       NULL,
+       &char_emphasis,
+       &char_codespan,
+       &char_linebreak,
+       &char_link,
+       &char_langle_tag,
+       &char_escape,
+       &char_entity,
+       &char_autolink_url,
+       &char_autolink_email,
+       &char_autolink_www,
+       &char_superscript,
+};
+
+/* render • structure containing one particular render */
+struct sd_markdown {
+       struct sd_callbacks     cb;
+       void *opaque;
+
+       struct link_ref *refs[REF_TABLE_SIZE];
+       uint8_t active_char[256];
+       struct stack work_bufs[2];
+       unsigned int ext_flags;
+       size_t max_nesting;
+       int in_link_body;
+};
+
+/***************************
+ * HELPER FUNCTIONS *
+ ***************************/
+
+static inline struct buf *
+rndr_newbuf(struct sd_markdown *rndr, int type)
+{
+       static const size_t buf_size[2] = {256, 64};
+       struct buf *work = NULL;
+       struct stack *pool = &rndr->work_bufs[type];
+
+       if (pool->size < pool->asize &&
+               pool->item[pool->size] != NULL) {
+               work = pool->item[pool->size++];
+               work->size = 0;
+       } else {
+               work = bufnew(buf_size[type]);
+               stack_push(pool, work);
+       }
+
+       return work;
+}
+
+static inline void
+rndr_popbuf(struct sd_markdown *rndr, int type)
+{
+       rndr->work_bufs[type].size--;
+}
+
+static void
+unscape_text(struct buf *ob, struct buf *src)
+{
+       size_t i = 0, org;
+       while (i < src->size) {
+               org = i;
+               while (i < src->size && src->data[i] != '\\')
+                       i++;
+
+               if (i > org)
+                       bufput(ob, src->data + org, i - org);
+
+               if (i + 1 >= src->size)
+                       break;
+
+               bufputc(ob, src->data[i + 1]);
+               i += 2;
+       }
+}
+
+static unsigned int
+hash_link_ref(const uint8_t *link_ref, size_t length)
+{
+       size_t i;
+       unsigned int hash = 0;
+
+       for (i = 0; i < length; ++i)
+               hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
+
+       return hash;
+}
+
+static struct link_ref *
+add_link_ref(
+       struct link_ref **references,
+       const uint8_t *name, size_t name_size)
+{
+       struct link_ref *ref = calloc(1, sizeof(struct link_ref));
+
+       if (!ref)
+               return NULL;
+
+       ref->id = hash_link_ref(name, name_size);
+       ref->next = references[ref->id % REF_TABLE_SIZE];
+
+       references[ref->id % REF_TABLE_SIZE] = ref;
+       return ref;
+}
+
+static struct link_ref *
+find_link_ref(struct link_ref **references, uint8_t *name, size_t length)
+{
+       unsigned int hash = hash_link_ref(name, length);
+       struct link_ref *ref = NULL;
+
+       ref = references[hash % REF_TABLE_SIZE];
+
+       while (ref != NULL) {
+               if (ref->id == hash)
+                       return ref;
+
+               ref = ref->next;
+       }
+
+       return NULL;
+}
+
+static void
+free_link_refs(struct link_ref **references)
+{
+       size_t i;
+
+       for (i = 0; i < REF_TABLE_SIZE; ++i) {
+               struct link_ref *r = references[i];
+               struct link_ref *next;
+
+               while (r) {
+                       next = r->next;
+                       bufrelease(r->link);
+                       bufrelease(r->title);
+                       free(r);
+                       r = next;
+               }
+       }
+}
+
+/*
+ * Check whether a char is a Markdown space.
+
+ * Right now we only consider spaces the actual
+ * space and a newline: tabs and carriage returns
+ * are filtered out during the preprocessing phase.
+ *
+ * If we wanted to actually be UTF-8 compliant, we
+ * should instead extract an Unicode codepoint from
+ * this character and check for space properties.
+ */
+static inline int
+_isspace(int c)
+{
+       return c == ' ' || c == '\n';
+}
+
+/****************************
+ * INLINE PARSING FUNCTIONS *
+ ****************************/
+
+/* is_mail_autolink • looks for the address part of a mail autolink and '>' */
+/* this is less strict than the original markdown e-mail address matching */
+static size_t
+is_mail_autolink(uint8_t *data, size_t size)
+{
+       size_t i = 0, nb = 0;
+
+       /* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */
+       for (i = 0; i < size; ++i) {
+               if (isalnum(data[i]))
+                       continue;
+
+               switch (data[i]) {
+                       case '@':
+                               nb++;
+
+                       case '-':
+                       case '.':
+                       case '_':
+                               break;
+
+                       case '>':
+                               return (nb == 1) ? i + 1 : 0;
+
+                       default:
+                               return 0;
+               }
+       }
+
+       return 0;
+}
+
+/* tag_length • returns the length of the given tag, or 0 is it's not valid */
+static size_t
+tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
+{
+       size_t i, j;
+
+       /* a valid tag can't be shorter than 3 chars */
+       if (size < 3) return 0;
+
+       /* begins with a '<' optionally followed by '/', followed by letter or number */
+       if (data[0] != '<') return 0;
+       i = (data[1] == '/') ? 2 : 1;
+
+       if (!isalnum(data[i]))
+               return 0;
+
+       /* scheme test */
+       *autolink = MKDA_NOT_AUTOLINK;
+
+       /* try to find the beginning of an URI */
+       while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))
+               i++;
+
+       if (i > 1 && data[i] == '@') {
+               if ((j = is_mail_autolink(data + i, size - i)) != 0) {
+                       *autolink = MKDA_EMAIL;
+                       return i + j;
+               }
+       }
+
+       if (i > 2 && data[i] == ':') {
+               *autolink = MKDA_NORMAL;
+               i++;
+       }
+
+       /* completing autolink test: no whitespace or ' or " */
+       if (i >= size)
+               *autolink = MKDA_NOT_AUTOLINK;
+
+       else if (*autolink) {
+               j = i;
+
+               while (i < size) {
+                       if (data[i] == '\\') i += 2;
+                       else if (data[i] == '>' || data[i] == '\'' ||
+                                       data[i] == '"' || data[i] == ' ' || data[i] == '\n')
+                                       break;
+                       else i++;
+               }
+
+               if (i >= size) return 0;
+               if (i > j && data[i] == '>') return i + 1;
+               /* one of the forbidden chars has been found */
+               *autolink = MKDA_NOT_AUTOLINK;
+       }
+
+       /* looking for sometinhg looking like a tag end */
+       while (i < size && data[i] != '>') i++;
+       if (i >= size) return 0;
+       return i + 1;
+}
+
+/* parse_inline • parses inline markdown elements */
+static void
+parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t i = 0, end = 0;
+       uint8_t action = 0;
+       struct buf work = { 0, 0, 0, 0 };
+
+       if (rndr->work_bufs[BUFFER_SPAN].size +
+               rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
+               return;
+
+       while (i < size) {
+               /* copying inactive chars into the output */
+               while (end < size && (action = rndr->active_char[data[end]]) == 0) {
+                       end++;
+               }
+
+               if (rndr->cb.normal_text) {
+                       work.data = data + i;
+                       work.size = end - i;
+                       rndr->cb.normal_text(ob, &work, rndr->opaque);
+               }
+               else
+                       bufput(ob, data + i, end - i);
+
+               if (end >= size) break;
+               i = end;
+
+               end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
+               if (!end) /* no action from the callback */
+                       end = i + 1;
+               else {
+                       i += end;
+                       end = i;
+               }
+       }
+}
+
+/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */
+static size_t
+find_emph_char(uint8_t *data, size_t size, uint8_t c)
+{
+       size_t i = 1;
+
+       while (i < size) {
+               while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')
+                       i++;
+
+               if (i == size)
+                       return 0;
+
+               if (data[i] == c)
+                       return i;
+
+               /* not counting escaped chars */
+               if (i && data[i - 1] == '\\') {
+                       i++; continue;
+               }
+
+               if (data[i] == '`') {
+                       size_t span_nb = 0, bt;
+                       size_t tmp_i = 0;
+
+                       /* counting the number of opening backticks */
+                       while (i < size && data[i] == '`') {
+                               i++; span_nb++;
+                       }
+
+                       if (i >= size) return 0;
+
+                       /* finding the matching closing sequence */
+                       bt = 0;
+                       while (i < size && bt < span_nb) {
+                               if (!tmp_i && data[i] == c) tmp_i = i;
+                               if (data[i] == '`') bt++;
+                               else bt = 0;
+                               i++;
+                       }
+
+                       if (i >= size) return tmp_i;
+               }
+               /* skipping a link */
+               else if (data[i] == '[') {
+                       size_t tmp_i = 0;
+                       uint8_t cc;
+
+                       i++;
+                       while (i < size && data[i] != ']') {
+                               if (!tmp_i && data[i] == c) tmp_i = i;
+                               i++;
+                       }
+
+                       i++;
+                       while (i < size && (data[i] == ' ' || data[i] == '\n'))
+                               i++;
+
+                       if (i >= size)
+                               return tmp_i;
+
+                       switch (data[i]) {
+                       case '[':
+                               cc = ']'; break;
+
+                       case '(':
+                               cc = ')'; break;
+
+                       default:
+                               if (tmp_i)
+                                       return tmp_i;
+                               else
+                                       continue;
+                       }
+
+                       i++;
+                       while (i < size && data[i] != cc) {
+                               if (!tmp_i && data[i] == c) tmp_i = i;
+                               i++;
+                       }
+
+                       if (i >= size)
+                               return tmp_i;
+
+                       i++;
+               }
+       }
+
+       return 0;
+}
+
+/* parse_emph1 • parsing single emphase */
+/* closed by a symbol not preceded by whitespace and not followed by symbol */
+static size_t
+parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+       size_t i = 0, len;
+       struct buf *work = 0;
+       int r;
+
+       if (!rndr->cb.emphasis) return 0;
+
+       /* skipping one symbol if coming from emph3 */
+       if (size > 1 && data[0] == c && data[1] == c) i = 1;
+
+       while (i < size) {
+               len = find_emph_char(data + i, size - i, c);
+               if (!len) return 0;
+               i += len;
+               if (i >= size) return 0;
+
+               if (data[i] == c && !_isspace(data[i - 1])) {
+
+                       if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
+                               if (i + 1 < size && isalnum(data[i + 1]))
+                                       continue;
+                       }
+
+                       work = rndr_newbuf(rndr, BUFFER_SPAN);
+                       parse_inline(work, rndr, data, i);
+                       r = rndr->cb.emphasis(ob, work, rndr->opaque);
+                       rndr_popbuf(rndr, BUFFER_SPAN);
+                       return r ? i + 1 : 0;
+               }
+       }
+
+       return 0;
+}
+
+/* parse_emph2 • parsing single emphase */
+static size_t
+parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+       int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);
+       size_t i = 0, len;
+       struct buf *work = 0;
+       int r;
+
+       render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;
+
+       if (!render_method)
+               return 0;
+
+       while (i < size) {
+               len = find_emph_char(data + i, size - i, c);
+               if (!len) return 0;
+               i += len;
+
+               if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
+                       work = rndr_newbuf(rndr, BUFFER_SPAN);
+                       parse_inline(work, rndr, data, i);
+                       r = render_method(ob, work, rndr->opaque);
+                       rndr_popbuf(rndr, BUFFER_SPAN);
+                       return r ? i + 2 : 0;
+               }
+               i++;
+       }
+       return 0;
+}
+
+/* parse_emph3 • parsing single emphase */
+/* finds the first closing tag, and delegates to the other emph */
+static size_t
+parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)
+{
+       size_t i = 0, len;
+       int r;
+
+       while (i < size) {
+               len = find_emph_char(data + i, size - i, c);
+               if (!len) return 0;
+               i += len;
+
+               /* skip whitespace preceded symbols */
+               if (data[i] != c || _isspace(data[i - 1]))
+                       continue;
+
+               if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {
+                       /* triple symbol found */
+                       struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+                       parse_inline(work, rndr, data, i);
+                       r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);
+                       rndr_popbuf(rndr, BUFFER_SPAN);
+                       return r ? i + 3 : 0;
+
+               } else if (i + 1 < size && data[i + 1] == c) {
+                       /* double symbol found, handing over to emph1 */
+                       len = parse_emph1(ob, rndr, data - 2, size + 2, c);
+                       if (!len) return 0;
+                       else return len - 2;
+
+               } else {
+                       /* single symbol found, handing over to emph2 */
+                       len = parse_emph2(ob, rndr, data - 1, size + 1, c);
+                       if (!len) return 0;
+                       else return len - 1;
+               }
+       }
+       return 0;
+}
+
+/* char_emphasis • single and double emphasis parsing */
+static size_t
+char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       uint8_t c = data[0];
+       size_t ret;
+
+       if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
+               if (offset > 0 && !_isspace(data[-1]) && data[-1] != '>')
+                       return 0;
+       }
+
+       if (size > 2 && data[1] != c) {
+               /* whitespace cannot follow an opening emphasis;
+                * strikethrough only takes two characters '~~' */
+               if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)
+                       return 0;
+
+               return ret + 1;
+       }
+
+       if (size > 3 && data[1] == c && data[2] != c) {
+               if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)
+                       return 0;
+
+               return ret + 2;
+       }
+
+       if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {
+               if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)
+                       return 0;
+
+               return ret + 3;
+       }
+
+       return 0;
+}
+
+
+/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */
+static size_t
+char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
+               return 0;
+
+       /* removing the last space from ob and rendering */
+       while (ob->size && ob->data[ob->size - 1] == ' ')
+               ob->size--;
+
+       return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;
+}
+
+
+/* char_codespan • '`' parsing a code span (assuming codespan != 0) */
+static size_t
+char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       size_t end, nb = 0, i, f_begin, f_end;
+
+       /* counting the number of backticks in the delimiter */
+       while (nb < size && data[nb] == '`')
+               nb++;
+
+       /* finding the next delimiter */
+       i = 0;
+       for (end = nb; end < size && i < nb; end++) {
+               if (data[end] == '`') i++;
+               else i = 0;
+       }
+
+       if (i < nb && end >= size)
+               return 0; /* no matching delimiter */
+
+       /* trimming outside whitespaces */
+       f_begin = nb;
+       while (f_begin < end && data[f_begin] == ' ')
+               f_begin++;
+
+       f_end = end - nb;
+       while (f_end > nb && data[f_end-1] == ' ')
+               f_end--;
+
+       /* real code span */
+       if (f_begin < f_end) {
+               struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };
+               if (!rndr->cb.codespan(ob, &work, rndr->opaque))
+                       end = 0;
+       } else {
+               if (!rndr->cb.codespan(ob, 0, rndr->opaque))
+                       end = 0;
+       }
+
+       return end;
+}
+
+
+/* char_escape • '\\' backslash escape */
+static size_t
+char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";
+       struct buf work = { 0, 0, 0, 0 };
+
+       if (size > 1) {
+               if (strchr(escape_chars, data[1]) == NULL)
+                       return 0;
+
+               if (rndr->cb.normal_text) {
+                       work.data = data + 1;
+                       work.size = 1;
+                       rndr->cb.normal_text(ob, &work, rndr->opaque);
+               }
+               else bufputc(ob, data[1]);
+       } else if (size == 1) {
+               bufputc(ob, data[0]);
+       }
+
+       return 2;
+}
+
+/* char_entity • '&' escaped when it doesn't belong to an entity */
+/* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
+static size_t
+char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       size_t end = 1;
+       struct buf work = { 0, 0, 0, 0 };
+
+       if (end < size && data[end] == '#')
+               end++;
+
+       while (end < size && isalnum(data[end]))
+               end++;
+
+       if (end < size && data[end] == ';')
+               end++; /* real entity */
+       else
+               return 0; /* lone '&' */
+
+       if (rndr->cb.entity) {
+               work.data = data;
+               work.size = end;
+               rndr->cb.entity(ob, &work, rndr->opaque);
+       }
+       else bufput(ob, data, end);
+
+       return end;
+}
+
+/* char_langle_tag • '<' when tags or autolinks are allowed */
+static size_t
+char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
+       size_t end = tag_length(data, size, &altype);
+       struct buf work = { data, end, 0, 0 };
+       int ret = 0;
+
+       if (end > 2) {
+               if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {
+                       struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);
+                       work.data = data + 1;
+                       work.size = end - 2;
+                       unscape_text(u_link, &work);
+                       ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);
+                       rndr_popbuf(rndr, BUFFER_SPAN);
+               }
+               else if (rndr->cb.raw_html_tag)
+                       ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);
+       }
+
+       if (!ret) return 0;
+       else return end;
+}
+
+static size_t
+char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       struct buf *link, *link_url, *link_text;
+       size_t link_len, rewind;
+
+       if (!rndr->cb.link || rndr->in_link_body)
+               return 0;
+
+       link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+       if ((link_len = sd_autolink__www(&rewind, link, data, offset, size, 0)) > 0) {
+               link_url = rndr_newbuf(rndr, BUFFER_SPAN);
+               BUFPUTSL(link_url, "http://");
+               bufput(link_url, link->data, link->size);
+
+               ob->size -= rewind;
+               if (rndr->cb.normal_text) {
+                       link_text = rndr_newbuf(rndr, BUFFER_SPAN);
+                       rndr->cb.normal_text(link_text, link, rndr->opaque);
+                       rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);
+                       rndr_popbuf(rndr, BUFFER_SPAN);
+               } else {
+                       rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);
+               }
+               rndr_popbuf(rndr, BUFFER_SPAN);
+       }
+
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       return link_len;
+}
+
+static size_t
+char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       struct buf *link;
+       size_t link_len, rewind;
+
+       if (!rndr->cb.autolink || rndr->in_link_body)
+               return 0;
+
+       link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+       if ((link_len = sd_autolink__email(&rewind, link, data, offset, size, 0)) > 0) {
+               ob->size -= rewind;
+               rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);
+       }
+
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       return link_len;
+}
+
+static size_t
+char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       struct buf *link;
+       size_t link_len, rewind;
+
+       if (!rndr->cb.autolink || rndr->in_link_body)
+               return 0;
+
+       link = rndr_newbuf(rndr, BUFFER_SPAN);
+
+       if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, 0)) > 0) {
+               ob->size -= rewind;
+               rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);
+       }
+
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       return link_len;
+}
+
+/* char_link • '[': parsing a link or an image */
+static size_t
+char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       int is_img = (offset && data[-1] == '!'), level;
+       size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
+       struct buf *content = 0;
+       struct buf *link = 0;
+       struct buf *title = 0;
+       struct buf *u_link = 0;
+       size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;
+       int text_has_nl = 0, ret = 0;
+       int in_title = 0, qtype = 0;
+
+       /* checking whether the correct renderer exists */
+       if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))
+               goto cleanup;
+
+       /* looking for the matching closing bracket */
+       for (level = 1; i < size; i++) {
+               if (data[i] == '\n')
+                       text_has_nl = 1;
+
+               else if (data[i - 1] == '\\')
+                       continue;
+
+               else if (data[i] == '[')
+                       level++;
+
+               else if (data[i] == ']') {
+                       level--;
+                       if (level <= 0)
+                               break;
+               }
+       }
+
+       if (i >= size)
+               goto cleanup;
+
+       txt_e = i;
+       i++;
+
+       /* skip any amount of whitespace or newline */
+       /* (this is much more laxist than original markdown syntax) */
+       while (i < size && _isspace(data[i]))
+               i++;
+
+       /* inline style link */
+       if (i < size && data[i] == '(') {
+               /* skipping initial whitespace */
+               i++;
+
+               while (i < size && _isspace(data[i]))
+                       i++;
+
+               link_b = i;
+
+               /* looking for link end: ' " ) */
+               while (i < size) {
+                       if (data[i] == '\\') i += 2;
+                       else if (data[i] == ')') break;
+                       else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
+                       else i++;
+               }
+
+               if (i >= size) goto cleanup;
+               link_e = i;
+
+               /* looking for title end if present */
+               if (data[i] == '\'' || data[i] == '"') {
+                       qtype = data[i];
+                       in_title = 1;
+                       i++;
+                       title_b = i;
+
+                       while (i < size) {
+                               if (data[i] == '\\') i += 2;
+                               else if (data[i] == qtype) {in_title = 0; i++;}
+                               else if ((data[i] == ')') && !in_title) break;
+                               else i++;
+                       }
+
+                       if (i >= size) goto cleanup;
+
+                       /* skipping whitespaces after title */
+                       title_e = i - 1;
+                       while (title_e > title_b && _isspace(data[title_e]))
+                               title_e--;
+
+                       /* checking for closing quote presence */
+                       if (data[title_e] != '\'' &&  data[title_e] != '"') {
+                               title_b = title_e = 0;
+                               link_e = i;
+                       }
+               }
+
+               /* remove whitespace at the end of the link */
+               while (link_e > link_b && _isspace(data[link_e - 1]))
+                       link_e--;
+
+               /* remove optional angle brackets around the link */
+               if (data[link_b] == '<') link_b++;
+               if (data[link_e - 1] == '>') link_e--;
+
+               /* building escaped link and title */
+               if (link_e > link_b) {
+                       link = rndr_newbuf(rndr, BUFFER_SPAN);
+                       bufput(link, data + link_b, link_e - link_b);
+               }
+
+               if (title_e > title_b) {
+                       title = rndr_newbuf(rndr, BUFFER_SPAN);
+                       bufput(title, data + title_b, title_e - title_b);
+               }
+
+               i++;
+       }
+
+       /* reference style link */
+       else if (i < size && data[i] == '[') {
+               struct buf id = { 0, 0, 0, 0 };
+               struct link_ref *lr;
+
+               /* looking for the id */
+               i++;
+               link_b = i;
+               while (i < size && data[i] != ']') i++;
+               if (i >= size) goto cleanup;
+               link_e = i;
+
+               /* finding the link_ref */
+               if (link_b == link_e) {
+                       if (text_has_nl) {
+                               struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);
+                               size_t j;
+
+                               for (j = 1; j < txt_e; j++) {
+                                       if (data[j] != '\n')
+                                               bufputc(b, data[j]);
+                                       else if (data[j - 1] != ' ')
+                                               bufputc(b, ' ');
+                               }
+
+                               id.data = b->data;
+                               id.size = b->size;
+                       } else {
+                               id.data = data + 1;
+                               id.size = txt_e - 1;
+                       }
+               } else {
+                       id.data = data + link_b;
+                       id.size = link_e - link_b;
+               }
+
+               lr = find_link_ref(rndr->refs, id.data, id.size);
+               if (!lr)
+                       goto cleanup;
+
+               /* keeping link and title from link_ref */
+               link = lr->link;
+               title = lr->title;
+               i++;
+       }
+
+       /* shortcut reference style link */
+       else {
+               struct buf id = { 0, 0, 0, 0 };
+               struct link_ref *lr;
+
+               /* crafting the id */
+               if (text_has_nl) {
+                       struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);
+                       size_t j;
+
+                       for (j = 1; j < txt_e; j++) {
+                               if (data[j] != '\n')
+                                       bufputc(b, data[j]);
+                               else if (data[j - 1] != ' ')
+                                       bufputc(b, ' ');
+                       }
+
+                       id.data = b->data;
+                       id.size = b->size;
+               } else {
+                       id.data = data + 1;
+                       id.size = txt_e - 1;
+               }
+
+               /* finding the link_ref */
+               lr = find_link_ref(rndr->refs, id.data, id.size);
+               if (!lr)
+                       goto cleanup;
+
+               /* keeping link and title from link_ref */
+               link = lr->link;
+               title = lr->title;
+
+               /* rewinding the whitespace */
+               i = txt_e + 1;
+       }
+
+       /* building content: img alt is escaped, link content is parsed */
+       if (txt_e > 1) {
+               content = rndr_newbuf(rndr, BUFFER_SPAN);
+               if (is_img) {
+                       bufput(content, data + 1, txt_e - 1);
+               } else {
+                       /* disable autolinking when parsing inline the
+                        * content of a link */
+                       rndr->in_link_body = 1;
+                       parse_inline(content, rndr, data + 1, txt_e - 1);
+                       rndr->in_link_body = 0;
+               }
+       }
+
+       if (link) {
+               u_link = rndr_newbuf(rndr, BUFFER_SPAN);
+               unscape_text(u_link, link);
+       }
+
+       /* calling the relevant rendering function */
+       if (is_img) {
+               if (ob->size && ob->data[ob->size - 1] == '!')
+                       ob->size -= 1;
+
+               ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);
+       } else {
+               ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);
+       }
+
+       /* cleanup */
+cleanup:
+       rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size;
+       return ret ? i : 0;
+}
+
+static size_t
+char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)
+{
+       size_t sup_start, sup_len;
+       struct buf *sup;
+
+       if (!rndr->cb.superscript)
+               return 0;
+
+       if (size < 2)
+               return 0;
+
+       if (data[1] == '(') {
+               sup_start = sup_len = 2;
+
+               while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')
+                       sup_len++;
+
+               if (sup_len == size)
+                       return 0;
+       } else {
+               sup_start = sup_len = 1;
+
+               while (sup_len < size && !_isspace(data[sup_len]))
+                       sup_len++;
+       }
+
+       if (sup_len - sup_start == 0)
+               return (sup_start == 2) ? 3 : 0;
+
+       sup = rndr_newbuf(rndr, BUFFER_SPAN);
+       parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);
+       rndr->cb.superscript(ob, sup, rndr->opaque);
+       rndr_popbuf(rndr, BUFFER_SPAN);
+
+       return (sup_start == 2) ? sup_len + 1 : sup_len;
+}
+
+/*********************************
+ * BLOCK-LEVEL PARSING FUNCTIONS *
+ *********************************/
+
+/* is_empty • returns the line length when it is empty, 0 otherwise */
+static size_t
+is_empty(uint8_t *data, size_t size)
+{
+       size_t i;
+
+       for (i = 0; i < size && data[i] != '\n'; i++)
+               if (data[i] != ' ')
+                       return 0;
+
+       return i + 1;
+}
+
+/* is_hrule • returns whether a line is a horizontal rule */
+static int
+is_hrule(uint8_t *data, size_t size)
+{
+       size_t i = 0, n = 0;
+       uint8_t c;
+
+       /* skipping initial spaces */
+       if (size < 3) return 0;
+       if (data[0] == ' ') { i++;
+       if (data[1] == ' ') { i++;
+       if (data[2] == ' ') { i++; } } }
+
+       /* looking at the hrule uint8_t */
+       if (i + 2 >= size
+       || (data[i] != '*' && data[i] != '-' && data[i] != '_'))
+               return 0;
+       c = data[i];
+
+       /* the whole line must be the char or whitespace */
+       while (i < size && data[i] != '\n') {
+               if (data[i] == c) n++;
+               else if (data[i] != ' ')
+                       return 0;
+
+               i++;
+       }
+
+       return n >= 3;
+}
+
+/* check if a line begins with a code fence; return the
+ * width of the code fence */
+static size_t
+prefix_codefence(uint8_t *data, size_t size)
+{
+       size_t i = 0, n = 0;
+       uint8_t c;
+
+       /* skipping initial spaces */
+       if (size < 3) return 0;
+       if (data[0] == ' ') { i++;
+       if (data[1] == ' ') { i++;
+       if (data[2] == ' ') { i++; } } }
+
+       /* looking at the hrule uint8_t */
+       if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))
+               return 0;
+
+       c = data[i];
+
+       /* the whole line must be the uint8_t or whitespace */
+       while (i < size && data[i] == c) {
+               n++; i++;
+       }
+
+       if (n < 3)
+               return 0;
+
+       return i;
+}
+
+/* check if a line is a code fence; return its size if it is */
+static size_t
+is_codefence(uint8_t *data, size_t size, struct buf *syntax)
+{
+       size_t i = 0, syn_len = 0;
+       uint8_t *syn_start;
+
+       i = prefix_codefence(data, size);
+       if (i == 0)
+               return 0;
+
+       while (i < size && data[i] == ' ')
+               i++;
+
+       syn_start = data + i;
+
+       if (i < size && data[i] == '{') {
+               i++; syn_start++;
+
+               while (i < size && data[i] != '}' && data[i] != '\n') {
+                       syn_len++; i++;
+               }
+
+               if (i == size || data[i] != '}')
+                       return 0;
+
+               /* strip all whitespace at the beginning and the end
+                * of the {} block */
+               while (syn_len > 0 && _isspace(syn_start[0])) {
+                       syn_start++; syn_len--;
+               }
+
+               while (syn_len > 0 && _isspace(syn_start[syn_len - 1]))
+                       syn_len--;
+
+               i++;
+       } else {
+               while (i < size && !_isspace(data[i])) {
+                       syn_len++; i++;
+               }
+       }
+
+       if (syntax) {
+               syntax->data = syn_start;
+               syntax->size = syn_len;
+       }
+
+       while (i < size && data[i] != '\n') {
+               if (!_isspace(data[i]))
+                       return 0;
+
+               i++;
+       }
+
+       return i + 1;
+}
+
+/* is_atxheader • returns whether the line is a hash-prefixed header */
+static int
+is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       if (data[0] != '#')
+               return 0;
+
+       if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) {
+               size_t level = 0;
+
+               while (level < size && level < 6 && data[level] == '#')
+                       level++;
+
+               if (level < size && data[level] != ' ')
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* is_headerline • returns whether the line is a setext-style hdr underline */
+static int
+is_headerline(uint8_t *data, size_t size)
+{
+       size_t i = 0;
+
+       /* test of level 1 header */
+       if (data[i] == '=') {
+               for (i = 1; i < size && data[i] == '='; i++);
+               while (i < size && data[i] == ' ') i++;
+               return (i >= size || data[i] == '\n') ? 1 : 0; }
+
+       /* test of level 2 header */
+       if (data[i] == '-') {
+               for (i = 1; i < size && data[i] == '-'; i++);
+               while (i < size && data[i] == ' ') i++;
+               return (i >= size || data[i] == '\n') ? 2 : 0; }
+
+       return 0;
+}
+
+static int
+is_next_headerline(uint8_t *data, size_t size)
+{
+       size_t i = 0;
+
+       while (i < size && data[i] != '\n')
+               i++;
+
+       if (++i >= size)
+               return 0;
+
+       return is_headerline(data + i, size - i);
+}
+
+/* prefix_quote • returns blockquote prefix length */
+static size_t
+prefix_quote(uint8_t *data, size_t size)
+{
+       size_t i = 0;
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+
+       if (i < size && data[i] == '>') {
+               if (i + 1 < size && data[i + 1] == ' ')
+                       return i + 2;
+
+               return i + 1;
+       }
+
+       return 0;
+}
+
+/* prefix_code • returns prefix length for block code*/
+static size_t
+prefix_code(uint8_t *data, size_t size)
+{
+       if (size > 3 && data[0] == ' ' && data[1] == ' '
+               && data[2] == ' ' && data[3] == ' ') return 4;
+
+       return 0;
+}
+
+/* prefix_oli • returns ordered list item prefix */
+static size_t
+prefix_oli(uint8_t *data, size_t size)
+{
+       size_t i = 0;
+
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+
+       if (i >= size || data[i] < '0' || data[i] > '9')
+               return 0;
+
+       while (i < size && data[i] >= '0' && data[i] <= '9')
+               i++;
+
+       if (i + 1 >= size || data[i] != '.' || data[i + 1] != ' ')
+               return 0;
+
+       if (is_next_headerline(data + i, size - i))
+               return 0;
+
+       return i + 2;
+}
+
+/* prefix_uli • returns ordered list item prefix */
+static size_t
+prefix_uli(uint8_t *data, size_t size)
+{
+       size_t i = 0;
+
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+       if (i < size && data[i] == ' ') i++;
+
+       if (i + 1 >= size ||
+               (data[i] != '*' && data[i] != '+' && data[i] != '-') ||
+               data[i + 1] != ' ')
+               return 0;
+
+       if (is_next_headerline(data + i, size - i))
+               return 0;
+
+       return i + 2;
+}
+
+
+/* parse_block • parsing of one block, returning next uint8_t to parse */
+static void parse_block(struct buf *ob, struct sd_markdown *rndr,
+                       uint8_t *data, size_t size);
+
+
+/* parse_blockquote • handles parsing of a blockquote fragment */
+static size_t
+parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t beg, end = 0, pre, work_size = 0;
+       uint8_t *work_data = 0;
+       struct buf *out = 0;
+
+       out = rndr_newbuf(rndr, BUFFER_BLOCK);
+       beg = 0;
+       while (beg < size) {
+               for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
+
+               pre = prefix_quote(data + beg, end - beg);
+
+               if (pre)
+                       beg += pre; /* skipping prefix */
+
+               /* empty line followed by non-quote line */
+               else if (is_empty(data + beg, end - beg) &&
+                               (end >= size || (prefix_quote(data + end, size - end) == 0 &&
+                               !is_empty(data + end, size - end))))
+                       break;
+
+               if (beg < end) { /* copy into the in-place working buffer */
+                       /* bufput(work, data + beg, end - beg); */
+                       if (!work_data)
+                               work_data = data + beg;
+                       else if (data + beg != work_data + work_size)
+                               memmove(work_data + work_size, data + beg, end - beg);
+                       work_size += end - beg;
+               }
+               beg = end;
+       }
+
+       parse_block(out, rndr, work_data, work_size);
+       if (rndr->cb.blockquote)
+               rndr->cb.blockquote(ob, out, rndr->opaque);
+       rndr_popbuf(rndr, BUFFER_BLOCK);
+       return end;
+}
+
+static size_t
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);
+
+/* parse_blockquote • handles parsing of a regular paragraph */
+static size_t
+parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t i = 0, end = 0;
+       int level = 0;
+       struct buf work = { data, 0, 0, 0 };
+
+       while (i < size) {
+               for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;
+
+               if (is_empty(data + i, size - i))
+                       break;
+
+               if ((level = is_headerline(data + i, size - i)) != 0)
+                       break;
+
+               if (is_atxheader(rndr, data + i, size - i) ||
+                       is_hrule(data + i, size - i) ||
+                       prefix_quote(data + i, size - i)) {
+                       end = i;
+                       break;
+               }
+
+               /*
+                * Early termination of a paragraph with the same logic
+                * as Markdown 1.0.0. If this logic is applied, the
+                * Markdown 1.0.3 test suite won't pass cleanly
+                *
+                * :: If the first character in a new line is not a letter,
+                * let's check to see if there's some kind of block starting
+                * here
+                */
+               if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) {
+                       if (prefix_oli(data + i, size - i) ||
+                               prefix_uli(data + i, size - i)) {
+                               end = i;
+                               break;
+                       }
+
+                       /* see if an html block starts here */
+                       if (data[i] == '<' && rndr->cb.blockhtml &&
+                               parse_htmlblock(ob, rndr, data + i, size - i, 0)) {
+                               end = i;
+                               break;
+                       }
+
+                       /* see if a code fence starts here */
+                       if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&
+                               is_codefence(data + i, size - i, NULL) != 0) {
+                               end = i;
+                               break;
+                       }
+               }
+
+               i = end;
+       }
+
+       work.size = i;
+       while (work.size && data[work.size - 1] == '\n')
+               work.size--;
+
+       if (!level) {
+               struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
+               parse_inline(tmp, rndr, work.data, work.size);
+               if (rndr->cb.paragraph)
+                       rndr->cb.paragraph(ob, tmp, rndr->opaque);
+               rndr_popbuf(rndr, BUFFER_BLOCK);
+       } else {
+               struct buf *header_work;
+
+               if (work.size) {
+                       size_t beg;
+                       i = work.size;
+                       work.size -= 1;
+
+                       while (work.size && data[work.size] != '\n')
+                               work.size -= 1;
+
+                       beg = work.size + 1;
+                       while (work.size && data[work.size - 1] == '\n')
+                               work.size -= 1;
+
+                       if (work.size > 0) {
+                               struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);
+                               parse_inline(tmp, rndr, work.data, work.size);
+
+                               if (rndr->cb.paragraph)
+                                       rndr->cb.paragraph(ob, tmp, rndr->opaque);
+
+                               rndr_popbuf(rndr, BUFFER_BLOCK);
+                               work.data += beg;
+                               work.size = i - beg;
+                       }
+                       else work.size = i;
+               }
+
+               header_work = rndr_newbuf(rndr, BUFFER_SPAN);
+               parse_inline(header_work, rndr, work.data, work.size);
+
+               if (rndr->cb.header)
+                       rndr->cb.header(ob, header_work, (int)level, rndr->opaque);
+
+               rndr_popbuf(rndr, BUFFER_SPAN);
+       }
+
+       return end;
+}
+
+/* parse_fencedcode • handles parsing of a block-level code fragment */
+static size_t
+parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t beg, end;
+       struct buf *work = 0;
+       struct buf lang = { 0, 0, 0, 0 };
+
+       beg = is_codefence(data, size, &lang);
+       if (beg == 0) return 0;
+
+       work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+       while (beg < size) {
+               size_t fence_end;
+               struct buf fence_trail = { 0, 0, 0, 0 };
+
+               fence_end = is_codefence(data + beg, size - beg, &fence_trail);
+               if (fence_end != 0 && fence_trail.size == 0) {
+                       beg += fence_end;
+                       break;
+               }
+
+               for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);
+
+               if (beg < end) {
+                       /* verbatim copy to the working buffer,
+                               escaping entities */
+                       if (is_empty(data + beg, end - beg))
+                               bufputc(work, '\n');
+                       else bufput(work, data + beg, end - beg);
+               }
+               beg = end;
+       }
+
+       if (work->size && work->data[work->size - 1] != '\n')
+               bufputc(work, '\n');
+
+       if (rndr->cb.blockcode)
+               rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);
+
+       rndr_popbuf(rndr, BUFFER_BLOCK);
+       return beg;
+}
+
+static size_t
+parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t beg, end, pre;
+       struct buf *work = 0;
+
+       work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+       beg = 0;
+       while (beg < size) {
+               for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};
+               pre = prefix_code(data + beg, end - beg);
+
+               if (pre)
+                       beg += pre; /* skipping prefix */
+               else if (!is_empty(data + beg, end - beg))
+                       /* non-empty non-prefixed line breaks the pre */
+                       break;
+
+               if (beg < end) {
+                       /* verbatim copy to the working buffer,
+                               escaping entities */
+                       if (is_empty(data + beg, end - beg))
+                               bufputc(work, '\n');
+                       else bufput(work, data + beg, end - beg);
+               }
+               beg = end;
+       }
+
+       while (work->size && work->data[work->size - 1] == '\n')
+               work->size -= 1;
+
+       bufputc(work, '\n');
+
+       if (rndr->cb.blockcode)
+               rndr->cb.blockcode(ob, work, NULL, rndr->opaque);
+
+       rndr_popbuf(rndr, BUFFER_BLOCK);
+       return beg;
+}
+
+/* parse_listitem • parsing of a single list item */
+/*     assuming initial prefix is already removed */
+static size_t
+parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)
+{
+       struct buf *work = 0, *inter = 0;
+       size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
+       int in_empty = 0, has_inside_empty = 0, in_fence = 0;
+
+       /* keeping track of the first indentation prefix */
+       while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
+               orgpre++;
+
+       beg = prefix_uli(data, size);
+       if (!beg)
+               beg = prefix_oli(data, size);
+
+       if (!beg)
+               return 0;
+
+       /* skipping to the beginning of the following line */
+       end = beg;
+       while (end < size && data[end - 1] != '\n')
+               end++;
+
+       /* getting working buffers */
+       work = rndr_newbuf(rndr, BUFFER_SPAN);
+       inter = rndr_newbuf(rndr, BUFFER_SPAN);
+
+       /* putting the first line into the working buffer */
+       bufput(work, data + beg, end - beg);
+       beg = end;
+
+       /* process the following lines */
+       while (beg < size) {
+               size_t has_next_uli = 0, has_next_oli = 0;
+
+               end++;
+
+               while (end < size && data[end - 1] != '\n')
+                       end++;
+
+               /* process an empty line */
+               if (is_empty(data + beg, end - beg)) {
+                       in_empty = 1;
+                       beg = end;
+                       continue;
+               }
+
+               /* calculating the indentation */
+               i = 0;
+               while (i < 4 && beg + i < end && data[beg + i] == ' ')
+                       i++;
+
+               pre = i;
+
+               if (rndr->ext_flags & MKDEXT_FENCED_CODE) {
+                       if (is_codefence(data + beg + i, end - beg - i, NULL) != 0)
+                               in_fence = !in_fence;
+               }
+
+               /* Only check for new list items if we are **not** inside
+                * a fenced code block */
+               if (!in_fence) {
+                       has_next_uli = prefix_uli(data + beg + i, end - beg - i);
+                       has_next_oli = prefix_oli(data + beg + i, end - beg - i);
+               }
+
+               /* checking for ul/ol switch */
+               if (in_empty && (
+                       ((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
+                       (!(*flags & MKD_LIST_ORDERED) && has_next_oli))){
+                       *flags |= MKD_LI_END;
+                       break; /* the following item must have same list type */
+               }
+
+               /* checking for a new item */
+               if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
+                       if (in_empty)
+                               has_inside_empty = 1;
+
+                       if (pre == orgpre) /* the following item must have */
+                               break;             /* the same indentation */
+
+                       if (!sublist)
+                               sublist = work->size;
+               }
+               /* joining only indented stuff after empty lines;
+                * note that now we only require 1 space of indentation
+                * to continue a list */
+               else if (in_empty && pre == 0) {
+                       *flags |= MKD_LI_END;
+                       break;
+               }
+               else if (in_empty) {
+                       bufputc(work, '\n');
+                       has_inside_empty = 1;
+               }
+
+               in_empty = 0;
+
+               /* adding the line without prefix into the working buffer */
+               bufput(work, data + beg + i, end - beg - i);
+               beg = end;
+       }
+
+       /* render of li contents */
+       if (has_inside_empty)
+               *flags |= MKD_LI_BLOCK;
+
+       if (*flags & MKD_LI_BLOCK) {
+               /* intermediate render of block li */
+               if (sublist && sublist < work->size) {
+                       parse_block(inter, rndr, work->data, sublist);
+                       parse_block(inter, rndr, work->data + sublist, work->size - sublist);
+               }
+               else
+                       parse_block(inter, rndr, work->data, work->size);
+       } else {
+               /* intermediate render of inline li */
+               if (sublist && sublist < work->size) {
+                       parse_inline(inter, rndr, work->data, sublist);
+                       parse_block(inter, rndr, work->data + sublist, work->size - sublist);
+               }
+               else
+                       parse_inline(inter, rndr, work->data, work->size);
+       }
+
+       /* render of li itself */
+       if (rndr->cb.listitem)
+               rndr->cb.listitem(ob, inter, *flags, rndr->opaque);
+
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       return beg;
+}
+
+
+/* parse_list • parsing ordered or unordered list block */
+static size_t
+parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)
+{
+       struct buf *work = 0;
+       size_t i = 0, j;
+
+       work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+       while (i < size) {
+               j = parse_listitem(work, rndr, data + i, size - i, &flags);
+               i += j;
+
+               if (!j || (flags & MKD_LI_END))
+                       break;
+       }
+
+       if (rndr->cb.list)
+               rndr->cb.list(ob, work, flags, rndr->opaque);
+       rndr_popbuf(rndr, BUFFER_BLOCK);
+       return i;
+}
+
+/* parse_atxheader • parsing of atx-style headers */
+static size_t
+parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t level = 0;
+       size_t i, end, skip;
+
+       while (level < size && level < 6 && data[level] == '#')
+               level++;
+
+       for (i = level; i < size && data[i] == ' '; i++);
+
+       for (end = i; end < size && data[end] != '\n'; end++);
+       skip = end;
+
+       while (end && data[end - 1] == '#')
+               end--;
+
+       while (end && data[end - 1] == ' ')
+               end--;
+
+       if (end > i) {
+               struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+               parse_inline(work, rndr, data + i, end - i);
+
+               if (rndr->cb.header)
+                       rndr->cb.header(ob, work, (int)level, rndr->opaque);
+
+               rndr_popbuf(rndr, BUFFER_SPAN);
+       }
+
+       return skip;
+}
+
+
+/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
+/*     returns the length on match, 0 otherwise */
+static size_t
+htmlblock_end_tag(
+       const char *tag,
+       size_t tag_len,
+       struct sd_markdown *rndr,
+       uint8_t *data,
+       size_t size)
+{
+       size_t i, w;
+
+       /* checking if tag is a match */
+       if (tag_len + 3 >= size ||
+               strncasecmp((char *)data + 2, tag, tag_len) != 0 ||
+               data[tag_len + 2] != '>')
+               return 0;
+
+       /* checking white lines */
+       i = tag_len + 3;
+       w = 0;
+       if (i < size && (w = is_empty(data + i, size - i)) == 0)
+               return 0; /* non-blank after tag */
+       i += w;
+       w = 0;
+
+       if (i < size)
+               w = is_empty(data + i, size - i);
+
+       return i + w;
+}
+
+static size_t
+htmlblock_end(const char *curtag,
+       struct sd_markdown *rndr,
+       uint8_t *data,
+       size_t size,
+       int start_of_line)
+{
+       size_t tag_size = strlen(curtag);
+       size_t i = 1, end_tag;
+       int block_lines = 0;
+
+       while (i < size) {
+               i++;
+               while (i < size && !(data[i - 1] == '<' && data[i] == '/')) {
+                       if (data[i] == '\n')
+                               block_lines++;
+
+                       i++;
+               }
+
+               /* If we are only looking for unindented tags, skip the tag
+                * if it doesn't follow a newline.
+                *
+                * The only exception to this is if the tag is still on the
+                * initial line; in that case it still counts as a closing
+                * tag
+                */
+               if (start_of_line && block_lines > 0 && data[i - 2] != '\n')
+                       continue;
+
+               if (i + 2 + tag_size >= size)
+                       break;
+
+               end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1);
+               if (end_tag)
+                       return i + end_tag - 1;
+       }
+
+       return 0;
+}
+
+
+/* parse_htmlblock • parsing of inline HTML block */
+static size_t
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)
+{
+       size_t i, j = 0, tag_end;
+       const char *curtag = NULL;
+       struct buf work = { data, 0, 0, 0 };
+
+       /* identification of the opening tag */
+       if (size < 2 || data[0] != '<')
+               return 0;
+
+       i = 1;
+       while (i < size && data[i] != '>' && data[i] != ' ')
+               i++;
+
+       if (i < size)
+               curtag = find_block_tag((char *)data + 1, (int)i - 1);
+
+       /* handling of special cases */
+       if (!curtag) {
+
+               /* HTML comment, laxist form */
+               if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {
+                       i = 5;
+
+                       while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>'))
+                               i++;
+
+                       i++;
+
+                       if (i < size)
+                               j = is_empty(data + i, size - i);
+
+                       if (j) {
+                               work.size = i + j;
+                               if (do_render && rndr->cb.blockhtml)
+                                       rndr->cb.blockhtml(ob, &work, rndr->opaque);
+                               return work.size;
+                       }
+               }
+
+               /* HR, which is the only self-closing block tag considered */
+               if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {
+                       i = 3;
+                       while (i < size && data[i] != '>')
+                               i++;
+
+                       if (i + 1 < size) {
+                               i++;
+                               j = is_empty(data + i, size - i);
+                               if (j) {
+                                       work.size = i + j;
+                                       if (do_render && rndr->cb.blockhtml)
+                                               rndr->cb.blockhtml(ob, &work, rndr->opaque);
+                                       return work.size;
+                               }
+                       }
+               }
+
+               /* no special case recognised */
+               return 0;
+       }
+
+       /* looking for an unindented matching closing tag */
+       /*      followed by a blank line */
+       tag_end = htmlblock_end(curtag, rndr, data, size, 1);
+
+       /* if not found, trying a second pass looking for indented match */
+       /* but not if tag is "ins" or "del" (following original Markdown.pl) */
+       if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {
+               tag_end = htmlblock_end(curtag, rndr, data, size, 0);
+       }
+
+       if (!tag_end)
+               return 0;
+
+       /* the end of the block has been found */
+       work.size = tag_end;
+       if (do_render && rndr->cb.blockhtml)
+               rndr->cb.blockhtml(ob, &work, rndr->opaque);
+
+       return tag_end;
+}
+
+static void
+parse_table_row(
+       struct buf *ob,
+       struct sd_markdown *rndr,
+       uint8_t *data,
+       size_t size,
+       size_t columns,
+       int *col_data,
+       int header_flag)
+{
+       size_t i = 0, col;
+       struct buf *row_work = 0;
+
+       if (!rndr->cb.table_cell || !rndr->cb.table_row)
+               return;
+
+       row_work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+       if (i < size && data[i] == '|')
+               i++;
+
+       for (col = 0; col < columns && i < size; ++col) {
+               size_t cell_start, cell_end;
+               struct buf *cell_work;
+
+               cell_work = rndr_newbuf(rndr, BUFFER_SPAN);
+
+               while (i < size && _isspace(data[i]))
+                       i++;
+
+               cell_start = i;
+
+               while (i < size && data[i] != '|')
+                       i++;
+
+               cell_end = i - 1;
+
+               while (cell_end > cell_start && _isspace(data[cell_end]))
+                       cell_end--;
+
+               parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);
+               rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);
+
+               rndr_popbuf(rndr, BUFFER_SPAN);
+               i++;
+       }
+
+       for (; col < columns; ++col) {
+               struct buf empty_cell = { 0, 0, 0, 0 };
+               rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);
+       }
+
+       rndr->cb.table_row(ob, row_work, rndr->opaque);
+
+       rndr_popbuf(rndr, BUFFER_SPAN);
+}
+
+static size_t
+parse_table_header(
+       struct buf *ob,
+       struct sd_markdown *rndr,
+       uint8_t *data,
+       size_t size,
+       size_t *columns,
+       int **column_data)
+{
+       int pipes;
+       size_t i = 0, col, header_end, under_end;
+
+       pipes = 0;
+       while (i < size && data[i] != '\n')
+               if (data[i++] == '|')
+                       pipes++;
+
+       if (i == size || pipes == 0)
+               return 0;
+
+       header_end = i;
+
+       while (header_end > 0 && _isspace(data[header_end - 1]))
+               header_end--;
+
+       if (data[0] == '|')
+               pipes--;
+
+       if (header_end && data[header_end - 1] == '|')
+               pipes--;
+
+       *columns = pipes + 1;
+       *column_data = calloc(*columns, sizeof(int));
+
+       /* Parse the header underline */
+       i++;
+       if (i < size && data[i] == '|')
+               i++;
+
+       under_end = i;
+       while (under_end < size && data[under_end] != '\n')
+               under_end++;
+
+       for (col = 0; col < *columns && i < under_end; ++col) {
+               size_t dashes = 0;
+
+               while (i < under_end && data[i] == ' ')
+                       i++;
+
+               if (data[i] == ':') {
+                       i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L;
+                       dashes++;
+               }
+
+               while (i < under_end && data[i] == '-') {
+                       i++; dashes++;
+               }
+
+               if (i < under_end && data[i] == ':') {
+                       i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R;
+                       dashes++;
+               }
+
+               while (i < under_end && data[i] == ' ')
+                       i++;
+
+               if (i < under_end && data[i] != '|')
+                       break;
+
+               if (dashes < 3)
+                       break;
+
+               i++;
+       }
+
+       if (col < *columns)
+               return 0;
+
+       parse_table_row(
+               ob, rndr, data,
+               header_end,
+               *columns,
+               *column_data,
+               MKD_TABLE_HEADER
+       );
+
+       return under_end + 1;
+}
+
+static size_t
+parse_table(
+       struct buf *ob,
+       struct sd_markdown *rndr,
+       uint8_t *data,
+       size_t size)
+{
+       size_t i;
+
+       struct buf *header_work = 0;
+       struct buf *body_work = 0;
+
+       size_t columns;
+       int *col_data = NULL;
+
+       header_work = rndr_newbuf(rndr, BUFFER_SPAN);
+       body_work = rndr_newbuf(rndr, BUFFER_BLOCK);
+
+       i = parse_table_header(header_work, rndr, data, size, &columns, &col_data);
+       if (i > 0) {
+
+               while (i < size) {
+                       size_t row_start;
+                       int pipes = 0;
+
+                       row_start = i;
+
+                       while (i < size && data[i] != '\n')
+                               if (data[i++] == '|')
+                                       pipes++;
+
+                       if (pipes == 0 || i == size) {
+                               i = row_start;
+                               break;
+                       }
+
+                       parse_table_row(
+                               body_work,
+                               rndr,
+                               data + row_start,
+                               i - row_start,
+                               columns,
+                               col_data, 0
+                       );
+
+                       i++;
+               }
+
+               if (rndr->cb.table)
+                       rndr->cb.table(ob, header_work, body_work, rndr->opaque);
+       }
+
+       free(col_data);
+       rndr_popbuf(rndr, BUFFER_SPAN);
+       rndr_popbuf(rndr, BUFFER_BLOCK);
+       return i;
+}
+
+/* parse_block • parsing of one block, returning next uint8_t to parse */
+static void
+parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
+{
+       size_t beg, end, i;
+       uint8_t *txt_data;
+       beg = 0;
+
+       if (rndr->work_bufs[BUFFER_SPAN].size +
+               rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)
+               return;
+
+       while (beg < size) {
+               txt_data = data + beg;
+               end = size - beg;
+
+               if (is_atxheader(rndr, txt_data, end))
+                       beg += parse_atxheader(ob, rndr, txt_data, end);
+
+               else if (data[beg] == '<' && rndr->cb.blockhtml &&
+                               (i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)
+                       beg += i;
+
+               else if ((i = is_empty(txt_data, end)) != 0)
+                       beg += i;
+
+               else if (is_hrule(txt_data, end)) {
+                       if (rndr->cb.hrule)
+                               rndr->cb.hrule(ob, rndr->opaque);
+
+                       while (beg < size && data[beg] != '\n')
+                               beg++;
+
+                       beg++;
+               }
+
+               else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&
+                       (i = parse_fencedcode(ob, rndr, txt_data, end)) != 0)
+                       beg += i;
+
+               else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 &&
+                       (i = parse_table(ob, rndr, txt_data, end)) != 0)
+                       beg += i;
+
+               else if (prefix_quote(txt_data, end))
+                       beg += parse_blockquote(ob, rndr, txt_data, end);
+
+               else if (prefix_code(txt_data, end))
+                       beg += parse_blockcode(ob, rndr, txt_data, end);
+
+               else if (prefix_uli(txt_data, end))
+                       beg += parse_list(ob, rndr, txt_data, end, 0);
+
+               else if (prefix_oli(txt_data, end))
+                       beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
+
+               else
+                       beg += parse_paragraph(ob, rndr, txt_data, end);
+       }
+}
+
+
+
+/*********************
+ * REFERENCE PARSING *
+ *********************/
+
+/* is_ref • returns whether a line is a reference or not */
+static int
+is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
+{
+/*     int n; */
+       size_t i = 0;
+       size_t id_offset, id_end;
+       size_t link_offset, link_end;
+       size_t title_offset, title_end;
+       size_t line_end;
+
+       /* up to 3 optional leading spaces */
+       if (beg + 3 >= end) return 0;
+       if (data[beg] == ' ') { i = 1;
+       if (data[beg + 1] == ' ') { i = 2;
+       if (data[beg + 2] == ' ') { i = 3;
+       if (data[beg + 3] == ' ') return 0; } } }
+       i += beg;
+
+       /* id part: anything but a newline between brackets */
+       if (data[i] != '[') return 0;
+       i++;
+       id_offset = i;
+       while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')
+               i++;
+       if (i >= end || data[i] != ']') return 0;
+       id_end = i;
+
+       /* spacer: colon (space | tab)* newline? (space | tab)* */
+       i++;
+       if (i >= end || data[i] != ':') return 0;
+       i++;
+       while (i < end && data[i] == ' ') i++;
+       if (i < end && (data[i] == '\n' || data[i] == '\r')) {
+               i++;
+               if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
+       while (i < end && data[i] == ' ') i++;
+       if (i >= end) return 0;
+
+       /* link: whitespace-free sequence, optionally between angle brackets */
+       if (data[i] == '<')
+               i++;
+
+       link_offset = i;
+
+       while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
+               i++;
+
+       if (data[i - 1] == '>') link_end = i - 1;
+       else link_end = i;
+
+       /* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
+       while (i < end && data[i] == ' ') i++;
+       if (i < end && data[i] != '\n' && data[i] != '\r'
+                       && data[i] != '\'' && data[i] != '"' && data[i] != '(')
+               return 0;
+       line_end = 0;
+       /* computing end-of-line */
+       if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i;
+       if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+               line_end = i + 1;
+
+       /* optional (space|tab)* spacer after a newline */
+       if (line_end) {
+               i = line_end + 1;
+               while (i < end && data[i] == ' ') i++; }
+
+       /* optional title: any non-newline sequence enclosed in '"()
+                                       alone on its line */
+       title_offset = title_end = 0;
+       if (i + 1 < end
+       && (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {
+               i++;
+               title_offset = i;
+               /* looking for EOL */
+               while (i < end && data[i] != '\n' && data[i] != '\r') i++;
+               if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')
+                       title_end = i + 1;
+               else    title_end = i;
+               /* stepping back */
+               i -= 1;
+               while (i > title_offset && data[i] == ' ')
+                       i -= 1;
+               if (i > title_offset
+               && (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
+                       line_end = title_end;
+                       title_end = i; } }
+
+       if (!line_end || link_end == link_offset)
+               return 0; /* garbage after the link empty link */
+
+       /* a valid ref has been found, filling-in return structures */
+       if (last)
+               *last = line_end;
+
+       if (refs) {
+               struct link_ref *ref;
+
+               ref = add_link_ref(refs, data + id_offset, id_end - id_offset);
+               if (!ref)
+                       return 0;
+
+               ref->link = bufnew(link_end - link_offset);
+               bufput(ref->link, data + link_offset, link_end - link_offset);
+
+               if (title_end > title_offset) {
+                       ref->title = bufnew(title_end - title_offset);
+                       bufput(ref->title, data + title_offset, title_end - title_offset);
+               }
+       }
+
+       return 1;
+}
+
+static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)
+{
+       size_t  i = 0, tab = 0;
+
+       while (i < size) {
+               size_t org = i;
+
+               while (i < size && line[i] != '\t') {
+                       i++; tab++;
+               }
+
+               if (i > org)
+                       bufput(ob, line + org, i - org);
+
+               if (i >= size)
+                       break;
+
+               do {
+                       bufputc(ob, ' '); tab++;
+               } while (tab % 4);
+
+               i++;
+       }
+}
+
+/**********************
+ * EXPORTED FUNCTIONS *
+ **********************/
+
+struct sd_markdown *
+sd_markdown_new(
+       unsigned int extensions,
+       size_t max_nesting,
+       const struct sd_callbacks *callbacks,
+       void *opaque)
+{
+       struct sd_markdown *md = NULL;
+
+       assert(max_nesting > 0 && callbacks);
+
+       md = malloc(sizeof(struct sd_markdown));
+       if (!md)
+               return NULL;
+
+       memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
+
+       stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
+       stack_init(&md->work_bufs[BUFFER_SPAN], 8);
+
+       memset(md->active_char, 0x0, 256);
+
+       if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) {
+               md->active_char['*'] = MD_CHAR_EMPHASIS;
+               md->active_char['_'] = MD_CHAR_EMPHASIS;
+               if (extensions & MKDEXT_STRIKETHROUGH)
+                       md->active_char['~'] = MD_CHAR_EMPHASIS;
+       }
+
+       if (md->cb.codespan)
+               md->active_char['`'] = MD_CHAR_CODESPAN;
+
+       if (md->cb.linebreak)
+               md->active_char['\n'] = MD_CHAR_LINEBREAK;
+
+       if (md->cb.image || md->cb.link)
+               md->active_char['['] = MD_CHAR_LINK;
+
+       md->active_char['<'] = MD_CHAR_LANGLE;
+       md->active_char['\\'] = MD_CHAR_ESCAPE;
+       md->active_char['&'] = MD_CHAR_ENTITITY;
+
+       if (extensions & MKDEXT_AUTOLINK) {
+               md->active_char[':'] = MD_CHAR_AUTOLINK_URL;
+               md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
+               md->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
+       }
+
+       if (extensions & MKDEXT_SUPERSCRIPT)
+               md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
+
+       /* Extension data */
+       md->ext_flags = extensions;
+       md->opaque = opaque;
+       md->max_nesting = max_nesting;
+       md->in_link_body = 0;
+
+       return md;
+}
+
+void
+sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)
+{
+#define MARKDOWN_GROW(x) ((x) + ((x) >> 1))
+       static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
+
+       struct buf *text;
+       size_t beg, end;
+
+       text = bufnew(64);
+       if (!text)
+               return;
+
+       /* Preallocate enough space for our buffer to avoid expanding while copying */
+       bufgrow(text, doc_size);
+
+       /* reset the references table */
+       memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
+
+       /* first pass: looking for references, copying everything else */
+       beg = 0;
+
+       /* Skip a possible UTF-8 BOM, even though the Unicode standard
+        * discourages having these in UTF-8 documents */
+       if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)
+               beg += 3;
+
+       while (beg < doc_size) /* iterating over lines */
+               if (is_ref(document, beg, doc_size, &end, md->refs))
+                       beg = end;
+               else { /* skipping to the next line */
+                       end = beg;
+                       while (end < doc_size && document[end] != '\n' && document[end] != '\r')
+                               end++;
+
+                       /* adding the line body if present */
+                       if (end > beg)
+                               expand_tabs(text, document + beg, end - beg);
+
+                       while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
+                               /* add one \n per newline */
+                               if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
+                                       bufputc(text, '\n');
+                               end++;
+                       }
+
+                       beg = end;
+               }
+
+       /* pre-grow the output buffer to minimize allocations */
+       bufgrow(ob, MARKDOWN_GROW(text->size));
+
+       /* second pass: actual rendering */
+       if (md->cb.doc_header)
+               md->cb.doc_header(ob, md->opaque);
+
+       if (text->size) {
+               /* adding a final newline if not already present */
+               if (text->data[text->size - 1] != '\n' &&  text->data[text->size - 1] != '\r')
+                       bufputc(text, '\n');
+
+               parse_block(ob, md, text->data, text->size);
+       }
+
+       if (md->cb.doc_footer)
+               md->cb.doc_footer(ob, md->opaque);
+
+       /* clean-up */
+       bufrelease(text);
+       free_link_refs(md->refs);
+
+       assert(md->work_bufs[BUFFER_SPAN].size == 0);
+       assert(md->work_bufs[BUFFER_BLOCK].size == 0);
+}
+
+void
+sd_markdown_free(struct sd_markdown *md)
+{
+       size_t i;
+
+       for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i)
+               bufrelease(md->work_bufs[BUFFER_SPAN].item[i]);
+
+       for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i)
+               bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);
+
+       stack_free(&md->work_bufs[BUFFER_SPAN]);
+       stack_free(&md->work_bufs[BUFFER_BLOCK]);
+
+       free(md);
+}
+
+void
+sd_version(int *ver_major, int *ver_minor, int *ver_revision)
+{
+       *ver_major = SUNDOWN_VER_MAJOR;
+       *ver_minor = SUNDOWN_VER_MINOR;
+       *ver_revision = SUNDOWN_VER_REVISION;
+}
+
+/* vim: set filetype=c: */
diff --git a/src/rt/sundown/src/markdown.h b/src/rt/sundown/src/markdown.h
new file mode 100644 (file)
index 0000000..6f6553e
--- /dev/null
@@ -0,0 +1,138 @@
+/* markdown.h - generic markdown parser */
+
+/*
+ * Copyright (c) 2009, Natacha Porté
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef UPSKIRT_MARKDOWN_H
+#define UPSKIRT_MARKDOWN_H
+
+#include "buffer.h"
+#include "autolink.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SUNDOWN_VERSION "1.16.0"
+#define SUNDOWN_VER_MAJOR 1
+#define SUNDOWN_VER_MINOR 16
+#define SUNDOWN_VER_REVISION 0
+
+/********************
+ * TYPE DEFINITIONS *
+ ********************/
+
+/* mkd_autolink - type of autolink */
+enum mkd_autolink {
+       MKDA_NOT_AUTOLINK,      /* used internally when it is not an autolink*/
+       MKDA_NORMAL,            /* normal http/http/ftp/mailto/etc link */
+       MKDA_EMAIL,                     /* e-mail link without explit mailto: */
+};
+
+enum mkd_tableflags {
+       MKD_TABLE_ALIGN_L = 1,
+       MKD_TABLE_ALIGN_R = 2,
+       MKD_TABLE_ALIGN_CENTER = 3,
+       MKD_TABLE_ALIGNMASK = 3,
+       MKD_TABLE_HEADER = 4
+};
+
+enum mkd_extensions {
+       MKDEXT_NO_INTRA_EMPHASIS = (1 << 0),
+       MKDEXT_TABLES = (1 << 1),
+       MKDEXT_FENCED_CODE = (1 << 2),
+       MKDEXT_AUTOLINK = (1 << 3),
+       MKDEXT_STRIKETHROUGH = (1 << 4),
+       MKDEXT_SPACE_HEADERS = (1 << 6),
+       MKDEXT_SUPERSCRIPT = (1 << 7),
+       MKDEXT_LAX_SPACING = (1 << 8),
+};
+
+/* sd_callbacks - functions for rendering parsed data */
+struct sd_callbacks {
+       /* block level callbacks - NULL skips the block */
+       void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque);
+       void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque);
+       void (*blockhtml)(struct buf *ob,const  struct buf *text, void *opaque);
+       void (*header)(struct buf *ob, const struct buf *text, int level, void *opaque);
+       void (*hrule)(struct buf *ob, void *opaque);
+       void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+       void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+       void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque);
+       void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque);
+       void (*table_row)(struct buf *ob, const struct buf *text, void *opaque);
+       void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque);
+
+
+       /* span level callbacks - NULL or return 0 prints the span verbatim */
+       int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque);
+       int (*codespan)(struct buf *ob, const struct buf *text, void *opaque);
+       int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+       int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+       int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque);
+       int (*linebreak)(struct buf *ob, void *opaque);
+       int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque);
+       int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque);
+       int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque);
+       int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque);
+       int (*superscript)(struct buf *ob, const struct buf *text, void *opaque);
+
+       /* low level callbacks - NULL copies input directly into the output */
+       void (*entity)(struct buf *ob, const struct buf *entity, void *opaque);
+       void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque);
+
+       /* header and footer */
+       void (*doc_header)(struct buf *ob, void *opaque);
+       void (*doc_footer)(struct buf *ob, void *opaque);
+};
+
+struct sd_markdown;
+
+/*********
+ * FLAGS *
+ *********/
+
+/* list/listitem flags */
+#define MKD_LIST_ORDERED       1
+#define MKD_LI_BLOCK           2  /* <li> containing block data */
+
+/**********************
+ * EXPORTED FUNCTIONS *
+ **********************/
+
+extern struct sd_markdown *
+sd_markdown_new(
+       unsigned int extensions,
+       size_t max_nesting,
+       const struct sd_callbacks *callbacks,
+       void *opaque);
+
+extern void
+sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md);
+
+extern void
+sd_markdown_free(struct sd_markdown *md);
+
+extern void
+sd_version(int *major, int *minor, int *revision);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/* vim: set filetype=c: */
diff --git a/src/rt/sundown/src/stack.c b/src/rt/sundown/src/stack.c
new file mode 100644 (file)
index 0000000..ce069ff
--- /dev/null
@@ -0,0 +1,81 @@
+#include "stack.h"
+#include <string.h>
+
+int
+stack_grow(struct stack *st, size_t new_size)
+{
+       void **new_st;
+
+       if (st->asize >= new_size)
+               return 0;
+
+       new_st = realloc(st->item, new_size * sizeof(void *));
+       if (new_st == NULL)
+               return -1;
+
+       memset(new_st + st->asize, 0x0,
+               (new_size - st->asize) * sizeof(void *));
+
+       st->item = new_st;
+       st->asize = new_size;
+
+       if (st->size > new_size)
+               st->size = new_size;
+
+       return 0;
+}
+
+void
+stack_free(struct stack *st)
+{
+       if (!st)
+               return;
+
+       free(st->item);
+
+       st->item = NULL;
+       st->size = 0;
+       st->asize = 0;
+}
+
+int
+stack_init(struct stack *st, size_t initial_size)
+{
+       st->item = NULL;
+       st->size = 0;
+       st->asize = 0;
+
+       if (!initial_size)
+               initial_size = 8;
+
+       return stack_grow(st, initial_size);
+}
+
+void *
+stack_pop(struct stack *st)
+{
+       if (!st->size)
+               return NULL;
+
+       return st->item[--st->size];
+}
+
+int
+stack_push(struct stack *st, void *item)
+{
+       if (stack_grow(st, st->size * 2) < 0)
+               return -1;
+
+       st->item[st->size++] = item;
+       return 0;
+}
+
+void *
+stack_top(struct stack *st)
+{
+       if (!st->size)
+               return NULL;
+
+       return st->item[st->size - 1];
+}
+
diff --git a/src/rt/sundown/src/stack.h b/src/rt/sundown/src/stack.h
new file mode 100644 (file)
index 0000000..08ff030
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef STACK_H__
+#define STACK_H__
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct stack {
+       void **item;
+       size_t size;
+       size_t asize;
+};
+
+void stack_free(struct stack *);
+int stack_grow(struct stack *, size_t);
+int stack_init(struct stack *, size_t);
+
+int stack_push(struct stack *, void *);
+
+void *stack_pop(struct stack *);
+void *stack_top(struct stack *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 45a26068d8285db4da968f0c96a3d79c637b2ae8..bcaa3b9086cf3e5deda357aa10f519cfe157bef2 100644 (file)
@@ -34,4 +34,4 @@ fn same_variant() {
 }
 
 fn main() {
-}
\ No newline at end of file
+}
index ae02245c97f52629d6a4d688a088cd0bd542fe9d..de2a8d83268089cc692fed36d3d4626821bfd798 100644 (file)
@@ -32,4 +32,4 @@ fn same_variant() {
 }
 
 fn main() {
-}
\ No newline at end of file
+}
index 3d9738df059ca6c9bbf3488bb83ce4c64846944d..da0a9323d2c8178da4b8cff2cf4db54e790596c8 100644 (file)
@@ -40,4 +40,4 @@ fn same_variant() {
 }
 
 fn main() {
-}
\ No newline at end of file
+}
index ff1ec38ad6406b992e61475073a98ded1eb6f548..c142876c5c2c5c60bb10cf9ba45b9eb020ff4a8e 100644 (file)
@@ -40,4 +40,4 @@ fn explicit() {
         rewrite(&mut a)); //~ ERROR cannot borrow
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 0adf486b8b3ab30b011b45c82e3e3e089f19cf91..622d2e78ee7946666df589d63e01f272e354012c 100644 (file)
@@ -40,4 +40,4 @@ fn explicit() {
         a); //~ ERROR cannot move
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index c99a1ee60d7fd39bc496866b6c90cb7e54c9df10..628ccd1a5d782e537c4de34007e732d4b38f7290 100644 (file)
@@ -13,4 +13,4 @@ fn let_pat() {
     //~^ ERROR cannot move out of dereference of & pointer
 }
 
-pub fn main() {}
\ No newline at end of file
+pub fn main() {}
index 6a3832d2304cf8d7a1879200b2e85ffdfc3d5ebc..565629b1c306df59cae114fcbe10da62c6e6a9b4 100644 (file)
@@ -12,4 +12,4 @@ fn foo(t0: &mut int) {
 }
 
 fn main() {
-}
\ No newline at end of file
+}
index bea5f1f6ea765c08c880382646bda9964a43d31c..ab6f70945be6cab8a66db1bd2da1ccb92ee24273 100644 (file)
@@ -13,4 +13,4 @@ fn foo<'a>(mut t0: &'a mut int,
 }
 
 fn main() {
-}
\ No newline at end of file
+}
index 1047a99577143d90c33967826793f224f021f2ca..0a94d6c456081d9cb794103164d35e33c5731d2d 100644 (file)
@@ -25,4 +25,4 @@ fn main() {
     let s = @S { unused: 0 };
     let _s2 = s as @mut T; //~ error: types differ in mutability
     let _s3 = &s as &mut T; //~ error: types differ in mutability
-}
\ No newline at end of file
+}
index a083757a0eb9a7fc732adf20bb096ba5f9badc90..ce58b260f61860ef6dd2107cd17770a2964ce90a 100644 (file)
@@ -11,4 +11,4 @@
 fn main() {
     let foo = ['h' as u8, 'i' as u8, 0 as u8];
     let bar = &foo as *u8; //~ ERROR mismatched types
-}
\ No newline at end of file
+}
index a3484cb33dcaecb81a3260e72281eb9e7be5a3e8..fbf1acb60665b307c9b5d6fc4c5fa6ea779fb049 100644 (file)
@@ -5,4 +5,4 @@ fn take(f: &fn:Foo()) {
     //~^ ERROR only the builtin traits can be used as closure or object bounds
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 590c12826e4fe834698c3604de827859d88754f5..2c3fbc827aad689e390fd9a517151a9d57a02a97 100644 (file)
@@ -42,4 +42,4 @@ fn call_the_fn(s: &TheStruct) {
     }
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 72c6df57c4ff580946ed9eb099c65565a6be6360..40d733f8bab5c101d2826adaf73ef53e98412c99 100644 (file)
@@ -35,4 +35,4 @@ fn call_the_fn(s: &TheStruct) {
     }
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 1f7cbfe98078288953fbfe7ebdd10e4b09892dd5..e4576e0f57c54122c5c7e87cf4bd0b864a6a36ff 100644 (file)
@@ -12,4 +12,4 @@
 #[auto_decode] //~ ERROR: `#[auto_decode]` is deprecated
 struct A;
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 3e771eef970f7e1e364a1ce7b15a3acb835d7c72..ea8ee8699e4821742811fb39dd7a20efcc1d2acc 100644 (file)
@@ -12,4 +12,4 @@ fn bad (p: *int) {
     let _q: &int = p as &int; //~ ERROR non-scalar cast
 }
 
-fn main() { }
\ No newline at end of file
+fn main() { }
index f7144b4c8fa91fa8c5356081ae63135c49bbc365..6f410ea3c3739f5576cc8a9aef338745ea332e2c 100644 (file)
@@ -12,4 +12,4 @@
 
 fn main() {
     let z = NonCopyable{ p: () }; //~ ERROR structure has no field named `p`
-}
\ No newline at end of file
+}
index 391c1019a946824dfd239a175eedacf773fcbc26..14dcc4ea8a3a89d2ce2a52ee7bd975eb833af4b0 100644 (file)
@@ -21,4 +21,4 @@ fn main()
 
       twice(x);
       invoke(sq);
-}
\ No newline at end of file
+}
index 358c61921470fcb917925ca81451a13b31d35073..6b5055cb1a2cdd11bb370a0bf4ee33078c644767 100644 (file)
@@ -49,4 +49,4 @@ pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
 }
 
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 2a61b72997d1f9cc855faceb3f871e1fed152c3f..37b9cc55dc8226eaeea8e00d6d72f12e2e35f191 100644 (file)
@@ -52,4 +52,4 @@ pub fn opt_str3<'a>(maybestr: &'a Option<~str>) -> &'static str {
     }
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 90ef7843d4bf921fad612d04673e4d7947e8e601..ef3f8140c68a043e9c21faa09702fed28c45c532 100644 (file)
@@ -12,4 +12,4 @@ mod m {
     // An inferred main entry point (that doesn't use #[main])
     // must appear at the top of the crate
     fn main() { } //~ NOTE here is a function named 'main'
-}
\ No newline at end of file
+}
index e5399fc7fa3b9332e1b91723b494d6382e5b7893..66ab4b7705433851896c2f167a315e05bac63d9e 100644 (file)
@@ -34,4 +34,4 @@ fn ordering4<'a, 'b>(a: &'a uint, b: &'b uint, x: &fn(&'a &'b uint)) {
     let z: Option<&'a &'b uint> = None;
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index d06dcd8aa86b81d4dab0a01ea03f629f66f7fb34..c9859899ea4f1132349beea335a804839711b4bb 100644 (file)
@@ -37,4 +37,4 @@ fn call4<'a, 'b>(a: &'a uint, b: &'b uint) {
 }
 
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index f90fe924587dfe3b519acc34f6e51b058610a37c..4848262750720fc61766f40f9f48786ab25e2e1b 100644 (file)
@@ -8,4 +8,4 @@ fn arg_closure() -> &'static int {
     with(|~ref x| x) //~ ERROR borrowed value does not live long enough
 }
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index b4a72548cfc0ddea9176d4a0267afdc7c525af70..e40bca58bad75f0f25ae5734febb80de564e5c90 100644 (file)
@@ -11,4 +11,4 @@
 fn main() {
     let (x, y) = (); //~ ERROR expected `()` but found tuple (types differ)
     return x;
-}
\ No newline at end of file
+}
index 216779fac7c467d38bc6041680c397982480ce97..a5f85a685e6958ce63b639bdd39956db3b3730b0 100644 (file)
@@ -20,4 +20,4 @@ enum color {
     white = 0x000000,
 }
 
-fn main() { }
\ No newline at end of file
+fn main() { }
index 6af3166a2ff4e24c2dec3c326f47a1a40046568d..7c41c5d12e6000a52d537e52d1689817a7d3d528 100644 (file)
@@ -3,4 +3,4 @@
 
 fn foo<Foo, Bar>(x: Foo) -> Bar { x } //~ ERROR expected `Bar` but found `Foo`
 
-fn main() {}
\ No newline at end of file
+fn main() {}
index 59e82a038bc79dfe3219c02df559657956424501..0e7572220a8b45ceca4181361516f3d5b75955b6 100644 (file)
@@ -47,4 +47,4 @@ fn fun2() {
 pub fn main() {
     fun1();
     fun2();
-}
\ No newline at end of file
+}
index 8f6a319798626334dfbd514871202fd727bb9a9a..74698b9db28bc79defe796036a9b646f9cc6fcad 100644 (file)
@@ -14,4 +14,4 @@
 pub fn main() {
   assert_eq!(Foo, Foo);
   assert!(!(Foo != Foo));
-}
\ No newline at end of file
+}
index 121757fb5904655bac08600cbb7f36c1f4982f3e..c7ba345517cd6193c6040e5a88d757816e6974f6 100644 (file)
@@ -34,4 +34,4 @@ fn drop(&mut self) {
 fn main() {
     let y = Y;
     let _z = Z{x: y};
-}
\ No newline at end of file
+}
index ffef5369f237e6c8c505df3d062870ffd7ce0e58..730ed18fbbd4540397cee745af724703daa56467 100644 (file)
@@ -37,4 +37,4 @@ pub fn main() {
         let x = trouble();
         assert_eq!(x,12345);
     }
-}
\ No newline at end of file
+}
index 9a4a868374235fa3768f9bee4f8c27a3ba90c08b..69ddb3701b09951ae95100a0834d54f3dcebee9f 100644 (file)
@@ -29,4 +29,4 @@ pub fn main() {
         let t = SThunk { x : 10 };
         assert_eq!(xcc::callback(t), xcc::Red)
     }
-}
\ No newline at end of file
+}