Permalink
Browse files

initial import

  • Loading branch information...
0 parents commit 06fa2f01aedea4a9a422339c849d35f7d5386815 Mark Murphy committed Nov 13, 2011
8 .classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
33 .project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>AndDown</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
8 AndroidManifest.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.commonsware.cwac.anddown"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="7"/>
+</manifest>
100 README.markdown
@@ -0,0 +1,100 @@
+CWAC AndDown: Markdown. Android. Perfect Together.
+==================================================
+
+Markdown is a popular markup language for wikitext and the like.
+While there are Java-based Markdown processors available, these are
+likely to be slow on Android, where they run at all.
+
+This project offers an Android-compliant wrapper around [sundown](https://github.com/tanoku/sundown),
+a C-based Markdown parser and HTML generator. By using the NDK with this
+C library, parsing speeds are nice and fast -- 5K of input can be
+converted into HTML in about 1 millisecond. Also, sundown is a fairly
+popular Markdown processor and is the brains behind GitHub-flavored Markdown.
+
+Usage
+-----
+Clone the repository and add it as an Android library project to your
+application. You will also need to install the NDK and run `ndk-build`
+from the project root directory, in order to build the `.so` file. A future
+version of this project will (hopefully) have better packaging &mdash;
+in the interim, your patience is appreciated.
+
+Create an instance of `com.commonsware.cwac.anddown.AndDown`, then call
+`markdownToHtml()` on it, supplying
+a `String` containing your Markdown source. This method returns a `String`
+containing HTML generated from that source.
+
+And, that's pretty much it, at least at this time.
+
+There is no statefulness in the `AndDown` object. It should be reusable
+without having to create a fresh instance each time. It might even
+be thread-safe, though this has not been tested.
+
+Limitations
+-----------
+The Markdown spec says that Markdown converted to HTML should use
+`<em>` and `<strong>` tags. On most browsers, those map to italics and
+boldface, respectively. However, the `Html.fromHtml()` method in Android
+that creates a `SpannedString` from HTML source flips those, so what you
+might be used to seeing in boldface turns into italics and vice-versa.
+This should only be an issue if you are displaing the Markdown-generated
+HTML in a `TextView` &mdash; `WebView` in particular should behave more
+normally.
+
+Also, while sundown is very fast, using the resulting HTML will inevitably
+be slower. The same 5K sample file that sundown processes in about 1
+millisecond takes a few hundred milliseconds to convert into a `SpannedString`
+via `Html.fromHtml()`.
+
+The author of this project is a complete klutz when it comes to C/JNI/NDK
+development. You have been warned.
+
+Dependencies
+------------
+This project has no dependencies. This repository includes a copy of the
+relevant files from the sundown project.
+
+Version
+-------
+This is version v0.1 of this module, which means it is really new. Moreover,
+it is the first CWAC project incorporating the NDK, so there may be new
+and exiciting issues as a result.
+
+Demo
+----
+In the `demo/` sub-project you will find
+a sample activity that demonstrates the use of `AndDown`, loading the
+sundown README out of a raw resource and displaying it in a `TextView`
+wrapped in a `ScrollView`.
+
+Future
+------
+Future editions of this project will add things like:
+
+ - a custom sundown renderer that generates a `SpannedString` directly, bypassing HTML
+ - code to convert a `SpannedString` back into Markdown source
+
+License
+-------
+The code in this project is licensed under the Apache
+Software License 2.0, per the terms of the included LICENSE
+file.
+
+The sundown source code is available under [its own license](https://github.com/tanoku/sundown),
+which appears to be a modified BSD license.
+
+Questions
+---------
+If you have questions regarding the use of this code, please post a question
+on [StackOverflow](http://stackoverflow.com/questions/ask) tagged with `commonsware` and `android`. Be sure to indicate
+what CWAC module you are having issues with, and be sure to include source code
+and stack traces if you are encountering crashes.
+
+Release Notes
+-------------
+v0.1.0: initial release
+
+Who Made This?
+--------------
+<a href="http://commonsware.com">![CommonsWare](http://commonsware.com/images/logo.png)</a>
+
85 build.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="AndDown" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
8 demo/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="src" path="gen"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+ <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+ <classpathentry kind="output" path="bin/classes"/>
+</classpath>
33 demo/.project
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>AndDownDemo</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>com.android.ide.eclipse.adt.ApkBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
23 demo/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.commonsware.cwac.anddown.demo"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-sdk android:minSdkVersion="7"/>
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name">
+ <activity
+ android:label="@string/app_name"
+ android:name=".AndDownDemoActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
85 demo/build.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="AndDownDemo" default="help">
+
+ <!-- The local.properties file is created and updated by the 'android' tool.
+ It contains the path to the SDK. It should *NOT* be checked into
+ Version Control Systems. -->
+ <loadproperties srcFile="local.properties" />
+
+ <!-- The ant.properties file can be created by you. It is only edited by the
+ 'android' tool to add properties to it.
+ This is the place to change some Ant specific build properties.
+ Here are some properties you may want to change/update:
+
+ source.dir
+ The name of the source directory. Default is 'src'.
+ out.dir
+ The name of the output directory. Default is 'bin'.
+
+ For other overridable properties, look at the beginning of the rules
+ files in the SDK, at tools/ant/build.xml
+
+ Properties related to the SDK location or the project target should
+ be updated using the 'android' tool with the 'update' action.
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems.
+
+ -->
+ <property file="ant.properties" />
+
+ <!-- The project.properties file is created and updated by the 'android'
+ tool, as well as ADT.
+
+ This contains project specific properties such as project target, and library
+ dependencies. Lower level build properties are stored in ant.properties
+ (or in .classpath for Eclipse projects).
+
+ This file is an integral part of the build system for your
+ application and should be checked into Version Control Systems. -->
+ <loadproperties srcFile="project.properties" />
+
+ <!-- quick check on sdk.dir -->
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+ unless="sdk.dir"
+ />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+ in between standard targets -->
+<!--
+ <target name="-pre-build">
+ </target>
+ <target name="-pre-compile">
+ </target>
+
+ /* This is typically used for code obfuscation.
+ Compiled code location: ${out.classes.absolute.dir}
+ If this is not done in place, override ${out.dex.input.absolute.dir} */
+ <target name="-post-compile">
+ </target>
+-->
+
+ <!-- Import the actual build file.
+
+ To customize existing targets, there are two options:
+ - Customize only one target:
+ - copy/paste the target into this file, *before* the
+ <import> task.
+ - customize it to your needs.
+ - Customize the whole content of build.xml
+ - copy/paste the content of the rules files (minus the top node)
+ into this file, replacing the <import> task.
+ - customize to your needs.
+
+ ***********************
+ ****** IMPORTANT ******
+ ***********************
+ In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+ in order to avoid having your file be overridden by tools such as "android update project"
+ -->
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>
40 demo/proguard.cfg
@@ -0,0 +1,40 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class com.android.vending.licensing.ILicensingService
+
+-keepclasseswithmembernames class * {
+ native <methods>;
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+ public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+ public void *(android.view.View);
+}
+
+-keepclassmembers enum * {
+ public static **[] values();
+ public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+ public static final android.os.Parcelable$Creator *;
+}
12 demo/project.properties
@@ -0,0 +1,12 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+
+# Project target.
+target=android-7
+android.library.reference.1=..
BIN demo/res/drawable-hdpi/ic_launcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN demo/res/drawable-ldpi/ic_launcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN demo/res/drawable-mdpi/ic_launcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 demo/res/layout/main.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+
+ <TextView
+ android:id="@+id/text"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"/>
+
+</ScrollView>
131 demo/res/raw/readme.txt
@@ -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 (@tanoku) 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/tanoku/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/rostayob/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: -->
7 demo/res/values/strings.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="hello">Hello World, AndDownDemoActivity!</string>
+ <string name="app_name">CWAC: AndDown</string>
+
+</resources>
63 demo/src/com/commonsware/cwac/anddown/demo/AndDownDemoActivity.java
@@ -0,0 +1,63 @@
+package com.commonsware.cwac.anddown.demo;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Html;
+import android.util.TimingLogger;
+import android.widget.TextView;
+import com.commonsware.cwac.anddown.AndDown;
+
+public class AndDownDemoActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+
+ TextView tv=(TextView)findViewById(R.id.text);
+
+ TimingLogger timings = new TimingLogger("AndDown", "onCreate");
+
+ String raw=readRawTextFile(this, R.raw.readme);
+
+ timings.addSplit("read raw");
+
+ AndDown converter=new AndDown();
+
+ timings.addSplit("instantiated AndDown");
+
+ String cooked=converter.markdownToHtml(raw);
+
+ timings.addSplit("convert to HTML");
+
+ CharSequence cs=Html.fromHtml(cooked);
+
+ timings.addSplit("convert to Spanned");
+ timings.dumpToLog();
+
+ tv.setText(cs);
+ }
+
+ public static String readRawTextFile(Context ctx, int resId) {
+ InputStream inputStream=ctx.getResources().openRawResource(resId);
+ InputStreamReader inputreader=new InputStreamReader(inputStream);
+ BufferedReader buffreader=new BufferedReader(inputreader);
+ String line;
+ StringBuilder text=new StringBuilder();
+
+ try {
+ while ((line=buffreader.readLine())!=null) {
+ text.append(line);
+ text.append('\n');
+ }
+ }
+ catch (IOException e) {
+ return null;
+ }
+ return text.toString();
+ }
+}
27 jni/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := anddown
+LOCAL_SRC_FILES := anddown.c src/autolink.c src/buffer.c src/markdown.c src/stack.c html/houdini_href_e.c html/houdini_html_e.c html/html_smartypants.c html/html.c
+LOCAL_C_INCLUDES := jni/src jni/html
+
+include $(BUILD_SHARED_LIBRARY)
+
+#src/html_blocks.h: html_block_names.txt
+# gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case $^ > $@
+
57 jni/anddown.c
@@ -0,0 +1,57 @@
+/***
+ Copyright (c) 2010 CommonsWare, LLC
+ Portions (c) somebody else who didn't bother to indicate who they were
+
+ Licensed under the Apache License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may obtain
+ a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+
+#include "com_commonsware_cwac_anddown_AndDown.h"
+#include "markdown.h"
+#include "html.h"
+#include "buffer.h"
+
+#define INPUT_UNIT 64
+#define OUTPUT_UNIT 64
+
+JNIEXPORT jstring JNICALL Java_com_commonsware_cwac_anddown_AndDown_markdownToHtml
+ (JNIEnv *env, jobject o, jstring raw) {
+ struct buf *ib, *ob;
+ int ret;
+ jstring result;
+
+ struct sd_callbacks callbacks;
+ struct html_renderopt options;
+ struct sd_markdown *markdown;
+
+ const char* str;
+ str = (*env)->GetStringUTFChars(env, raw, NULL);
+
+ ib = bufnew(INPUT_UNIT);
+ bufputs(ib, str);
+ ob = bufnew(OUTPUT_UNIT);
+
+ (*env)->ReleaseStringUTFChars(env, raw, str);
+
+ 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);
+
+ result=(*env)->NewStringUTF(env, bufcstr(ob));
+
+ /* cleanup */
+ bufrelease(ib);
+ bufrelease(ob);
+
+ return(result);
+}
+
21 jni/com_commonsware_cwac_anddown_AndDown.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_commonsware_cwac_anddown_AndDown */
+
+#ifndef _Included_com_commonsware_cwac_anddown_AndDown
+#define _Included_com_commonsware_cwac_anddown_AndDown
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_commonsware_cwac_anddown_AndDown
+ * Method: markdown
+ * Signature: (Ljava/lang/String;)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_com_commonsware_cwac_anddown_AndDown_markdown
+ (JNIEnv *, jobject, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
72 jni/examples/smartypants.c
@@ -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 */
+ fwrite(ob->data, 1, ob->size, stdout);
+
+ /* cleanup */
+ bufrelease(ib);
+ bufrelease(ob);
+
+ return 0;
+}
+
+/* vim: set filetype=c: */
80 jni/examples/sundown.c
@@ -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: */
28 jni/html/houdini.h
@@ -0,0 +1,28 @@
+#ifndef __HOUDINI_H__
+#define __HOUDINI_H__
+
+#include "buffer.h"
+
+#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_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);
+
+#endif
108 jni/html/houdini_href_e.c
@@ -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++;
+ }
+}
84 jni/html/houdini_html_e.c
@@ -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);
+}
+
603 jni/html/html.c
@@ -0,0 +1,603 @@
+/*
+ * 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;
+}
+
+/********************
+ * 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:");
+ houdini_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) {
+ houdini_escape_html(ob, link->data + 7, link->size - 7);
+ } else {
+ houdini_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, ' ');
+ houdini_escape_html(ob, lang->data + org, i - org);
+ }
+ }
+
+ BUFPUTSL(ob, "\">");
+ } else
+ BUFPUTSL(ob, "<pre><code>");
+
+ if (text)
+ houdini_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) houdini_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)
+ houdini_escape_href(ob, link->data, link->size);
+
+ if (title && title->size) {
+ BUFPUTSL(ob, "\" title=\"");
+ houdini_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 -= 1;
+ org = 0;
+ while (org < sz && text->data[org] == '\n') org += 1;
+ 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=\"");
+ houdini_escape_href(ob, link->data, link->size);
+ BUFPUTSL(ob, "\" alt=\"");
+
+ if (alt && alt->size)
+ houdini_escape_html(ob, alt->data, alt->size);
+
+ if (title && title->size) {
+ BUFPUTSL(ob, "\" title=\"");
+ houdini_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;
+
+ 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)
+ houdini_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;
+
+ 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)
+ bufput(ob, text->data, text->size);
+ BUFPUTSL(ob, "</a>\n");
+}
+
+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,
+ NULL,
+ 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)
+ callbacks->blockhtml = NULL;
+}
67 jni/html/html.h
@@ -0,0 +1,67 @@
+/*
+ * 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>
+
+struct html_renderopt {
+ struct {
+ int header_count;
+ int current_level;
+ } 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_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);
+
+#endif
+
383 jni/html/html_smartypants.c
@@ -0,0 +1,383 @@
+/*
+ * 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>
+
+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", "kbd", "script"};
+ static const size_t skip_tags_count = 4;
+
+ 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);
+ }
+ }
+}
+
+
24 jni/html_block_names.txt
@@ -0,0 +1,24 @@
+##
+p
+dl
+h1
+h2
+h3
+h4
+h5
+h6
+ol
+ul
+del
+div
+ins
+pre
+form
+math
+table
+figure
+iframe
+script
+fieldset
+noscript
+blockquote
261 jni/src/autolink.c
@@ -0,0 +1,261 @@
+/*
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+int
+sd_autolink_issafe(const uint8_t *link, size_t link_len)
+{
+ static const size_t valid_uris_count = 4;
+ 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 offset, 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