From d38f82f6462af4e5aad6a2c776f5c00ce5b13c87 Mon Sep 17 00:00:00 2001
From: Linnnus
" },
+ { "\t", 0, "", "\n
" },
+ { ">", 2, "", "\n
", "
" },
+ { "###### ", 1, "", "
" },
+ { "##### ", 1, "", "
" },
+ { "#### ", 1, "", "
" },
+ { "### ", 1, "", "
" },
+ { "## ", 1, "", "
" },
+ { "# ", 1, "", "
" },
+ { "- - -\n", 1, "
", ""},
+ { "---\n", 1, "
", ""},
+};
+
+static Tag underline[] = {
+ { "=", 1, "", "
\n" },
+ { "-", 1, "", "
\n" },
+};
+
+static Tag surround[] = {
+ { "```", 0, "", "
" },
+ { "``", 0, "", "
" },
+ { "`", 0, "", "
" },
+ { "___", 1, "", "" },
+ { "***", 1, "", "" },
+ { "__", 1, "", "" },
+ { "**", 1, "", "" },
+ { "_", 1, "", "" },
+ { "*", 1, "", "" },
+};
+
+static const char *replace[][2] = {
+ /* Backslash escapes */
+ { "\\\\", "\\" },
+ { "\\`", "`" },
+ { "\\*", "*" },
+ { "\\_", "_" },
+ { "\\{", "{" },
+ { "\\}", "}" },
+ { "\\[", "[" },
+ { "\\]", "]" },
+ { "\\(", "(" },
+ { "\\)", ")" },
+ { "\\#", "#" },
+ { "\\+", "+" },
+ { "\\-", "-" },
+ { "\\.", "." },
+ { "\\!", "!" },
+ { "\\\"", """ },
+ { "\\$", "$" },
+ { "\\%", "%" },
+ { "\\&", "&" },
+ { "\\'", "'" },
+ { "\\,", "," },
+ { "\\-", "-" },
+ { "\\.", "." },
+ { "\\/", "/" },
+ { "\\:", ":" },
+ { "\\;", ";" },
+ { "\\<", "<" },
+ { "\\>", ">" },
+ { "\\=", "=" },
+ { "\\?", "?" },
+ { "\\@", "@" },
+ { "\\^", "^" },
+ { "\\|", "|" },
+ { "\\~", "~" },
+ /* HTML syntax symbols that need to be turned into entities */
+ { "<", "<" },
+ { ">", ">" },
+ { "&", "&" }, /* Avoid replacing the & in & */
+ { "&", "&" },
+ /* Preserve newlines with two spaces before linebreak */
+ { " \n", "
\n" },
+};
+
+static const char *code_fence = "```";
+
+void
+eprint(const char *format, ...) {
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ exit(EXIT_FAILURE);
+}
+
+void end_paragraph(void) {
+ if (in_paragraph) {
+ fputs("
", stdout);
+ } else {
+ fputs("", stdout);
+ }
+ hprint(start, stop);
+ fputs("
\n", stdout);
+ return -(stop - begin + l);
+}
+
+int
+dohtml(const char *begin, const char *end, int newblock) {
+ const char *p, *tag, *tagend;
+
+ if (nohtml || begin + 2 >= end)
+ return 0;
+ p = begin;
+ if (p[0] != '<' || !isalpha(p[1]))
+ return 0;
+ p++;
+ tag = p;
+ for (; isalnum(*p) && p < end; p++);
+ tagend = p;
+ if (p > end || tag == tagend)
+ return 0;
+ while ((p = strstr(p, "")) && p < end) {
+ p += 2;
+ if (strncmp(p, tag, tagend - tag) == 0 && p[tagend - tag] == '>') {
+ p++;
+ fwrite(begin, sizeof(char), p - begin + tagend - tag, stdout);
+ return p - begin + tagend - tag;
+ }
+ }
+ p = strchr(tagend, '>');
+ if (p) {
+ fwrite(begin, sizeof(char), p - begin + 2, stdout);
+ return p - begin + 2;
+ }
+ else
+ return 0;
+}
+
+int
+dolineprefix(const char *begin, const char *end, int newblock) {
+ unsigned int i, j, l;
+ char *buffer;
+ const char *p;
+ int consumed_input = 0;
+
+ if (newblock)
+ p = begin;
+ else if (*begin == '\n') {
+ p = begin + 1;
+ consumed_input += 1;
+ } else
+ return 0;
+ for (i = 0; i < LENGTH(lineprefix); i++) {
+ l = strlen(lineprefix[i].search);
+ if (end - p + 1 < l)
+ continue;
+ if (strncmp(lineprefix[i].search, p, l))
+ continue;
+ if (*begin == '\n')
+ fputc('\n', stdout);
+
+ /* All line prefixes add a block element. These are not allowed
+ * inside paragraphs, so we must end the paragraph first. */
+ end_paragraph();
+
+ fputs(lineprefix[i].before, stdout);
+ if (lineprefix[i].search[l-1] == '\n') {
+ fputc('\n', stdout);
+ return l - 1 + consumed_input;
+ }
+ if (!(buffer = malloc(BUFSIZ)))
+ eprint("Malloc failed.");
+ buffer[0] = '\0';
+
+ /* Collect lines into buffer while they start with the prefix */
+ j = 0;
+ while ((strncmp(lineprefix[i].search, p, l) == 0) && p + l < end) {
+ p += l;
+
+ /* Special case for blockquotes: optional space after > */
+ if (lineprefix[i].search[0] == '>' && *p == ' ') {
+ p++;
+ }
+
+ while (p < end) {
+ ADDC(buffer, j) = *p;
+ j++;
+ if (*(p++) == '\n')
+ break;
+ }
+ }
+
+ /* Skip empty lines in block */
+ while (*(buffer + j - 1) == '\n') {
+ j--;
+ }
+
+ ADDC(buffer, j) = '\0';
+ if (lineprefix[i].process)
+ process(buffer, buffer + strlen(buffer), lineprefix[i].process >= 2);
+ else
+ hprint(buffer, buffer + strlen(buffer));
+ puts(lineprefix[i].after);
+ free(buffer);
+ return -(p - begin);
+ }
+ return 0;
+}
+
+int
+dolink(const char *begin, const char *end, int newblock) {
+ int img, len, sep, parens_depth = 1;
+ const char *desc, *link, *p, *q, *descend, *linkend;
+ const char *title = NULL, *titleend = NULL;
+
+ if (*begin == '[')
+ img = 0;
+ else if (strncmp(begin, ") || p > end)
+ return 0;
+ for (q = strstr(desc, ") || p > end)
+ return 0;
+ descend = p;
+ link = p + 2;
+
+ /* find end of link while handling nested parens */
+ q = link;
+ while (parens_depth) {
+ if (!(q = strpbrk(q, "()")) || q > end)
+ return 0;
+ if (*q == '(')
+ parens_depth++;
+ else
+ parens_depth--;
+ if (parens_depth && q < end)
+ q++;
+ }
+
+ if ((p = strpbrk(link, "\"'")) && p < end && q > p) {
+ sep = p[0]; /* separator: can be " or ' */
+ title = p + 1;
+ /* strip trailing whitespace */
+ for (linkend = p; linkend > link && isspace(*(linkend - 1)); linkend--);
+ for (titleend = q - 1; titleend > link && isspace(*(titleend)); titleend--);
+ if (titleend < title || *titleend != sep) {
+ return 0;
+ }
+ }
+ else {
+ linkend = q;
+ }
+
+ /* Links can be given in angular brackets */
+ if (*link == '<' && *(linkend - 1) == '>') {
+ link++;
+ linkend--;
+ }
+
+ len = q + 1 - begin;
+ if (img) {
+ fputs("
", stdout);
+ }
+ else {
+ fputs("", stdout);
+ process(desc, descend, 0);
+ fputs("", stdout);
+ }
+ return len;
+}
+
+int
+dolist(const char *begin, const char *end, int newblock) {
+ unsigned int i, j, indent, run, isblock, start_number;
+ const char *p, *q, *num_start;
+ char *buffer = NULL;
+ char marker = '\0'; /* Bullet symbol or \0 for unordered lists */
+
+ isblock = 0;
+ if (newblock)
+ p = begin;
+ else if (*begin == '\n')
+ p = begin + 1;
+ else
+ return 0;
+ q = p;
+ if (*p == '-' || *p == '*' || *p == '+') {
+ marker = *p;
+ } else {
+ num_start = p;
+ for (; p < end && *p >= '0' && *p <= '9'; p++);
+ if (p >= end || *p != '.')
+ return 0;
+ start_number = atoi(num_start);
+ }
+ p++;
+ if (p >= end || !(*p == ' ' || *p == '\t'))
+ return 0;
+
+ end_paragraph();
+
+ for (p++; p != end && (*p == ' ' || *p == '\t'); p++);
+ indent = p - q;
+ buffer = ereallocz(buffer, BUFSIZ);
+ if (!newblock)
+ fputc('\n', stdout);
+
+ if (marker) {
+ fputs("\n", stdout);
+ } else if (start_number == 1) {
+ fputs("\n", stdout);
+ } else {
+ printf("\n", start_number);
+ }
+ run = 1;
+ for (; p < end && run; p++) {
+ for (i = 0; p < end && run; p++, i++) {
+ if (*p == '\n') {
+ if (p + 1 == end)
+ break;
+ else {
+ /* Handle empty lines */
+ for (q = p + 1; (*q == ' ' || *q == '\t') && q < end; q++);
+ if (*q == '\n') {
+ ADDC(buffer, i) = '\n';
+ i++;
+ run = 0;
+ isblock++;
+ p = q;
+ }
+ }
+ q = p + 1;
+ j = 0;
+ if (marker && *q == marker)
+ j = 1;
+ else {
+ for (; q + j != end && q[j] >= '0' && q[j] <= '9' && j < indent; j++);
+ if (q + j == end)
+ break;
+ if (j > 0 && q[j] == '.')
+ j++;
+ else
+ j = 0;
+ }
+ if (q + indent < end)
+ for (; (q[j] == ' ' || q[j] == '\t') && j < indent; j++);
+ if (j == indent) {
+ ADDC(buffer, i) = '\n';
+ i++;
+ p += indent;
+ run = 1;
+ if (*q == ' ' || *q == '\t')
+ p++;
+ else
+ break;
+ }
+ else if (j < indent)
+ run = 0;
+ }
+ ADDC(buffer, i) = *p;
+ }
+ ADDC(buffer, i) = '\0';
+ fputs("- ", stdout);
+ process(buffer, buffer + i, isblock > 1 || (isblock == 1 && run));
+ fputs("
\n", stdout);
+ }
+ fputs(marker ? "
\n" : "\n", stdout);
+ free(buffer);
+ p--;
+ while (*(--p) == '\n');
+ return -(p - begin + 1);
+}
+
+int
+dotable(const char *begin, const char *end, int newblock) {
+ /* table state */
+ static signed char intable, inrow, incell;
+ static unsigned long int calign;
+ static const char *align_table[] = {
+ "",
+ " style=\"text-align: left\"",
+ " style=\"text-align: right\"",
+ " style=\"text-align: center\"",
+ };
+
+ const char *p;
+ int i, l = (int)sizeof(calign) * 4;
+
+ if(*begin != '|')
+ return 0;
+ if (intable == 2) { /* in alignment row, skip it. */
+ ++intable;
+ for (p = begin; p < end && *p != '\n'; ++p);
+ return p - begin + 1;
+ }
+ if(inrow && (begin + 1 >= end || begin[1] == '\n')) { /* close cell and row and if ends, table too */
+ fprintf(stdout, "", inrow == -1 ? 'h' : 'd');
+ if (inrow == -1)
+ intable = 2;
+ inrow = 0;
+ if(end - begin <= 2 || begin[2] == '\n') {
+ intable = 0;
+ fputs("\n\n", stdout);
+ }
+ return 1;
+ }
+
+ if(!intable) { /* open table */
+ intable = 1; inrow = -1; incell = 0; calign = 0;
+ for (p = begin; p < end && *p != '\n'; ++p);
+ if(*p == '\n') { /* load alignment from 2nd line */
+ for(i = -1, ++p; p < end && *p != '\n'; p++) {
+ if(*p == '|') {
+ i++;
+ do { p++; } while(p < end && (*p == ' ' || *p == '\t'));
+ if(i < l && *p == ':')
+ calign |= 1ul << (i * 2);
+ if (*p == '\n')
+ break;
+ } else if(i < l && *p == ':') {
+ calign |= 1ul << (i * 2 + 1);
+ }
+ }
+ }
+ fputs("\n", stdout);
+ }
+ if(!inrow) { /* open row */
+ inrow = 1; incell = 0;
+ fputs(" ", stdout);
+ }
+ if(incell) /* close cell */
+ fprintf(stdout, "", inrow == -1 ? 'h' : 'd');
+ l = incell < l ? (calign >> (incell * 2)) & 3 : 0; /* open cell */
+ fprintf(stdout, "", inrow == -1 ? 'h' : 'd', align_table[l]);
+ incell++;
+ for(p = begin + 1; p < end && *p == ' '; p++);
+ return p - begin;
+}
+
+int
+doparagraph(const char *begin, const char *end, int newblock) {
+ const char *p;
+ regmatch_t match;
+
+ if (!newblock)
+ return 0;
+ if (regexec(&p_end_regex, begin + 1, 1, &match, 0)) {
+ p = end;
+ } else {
+ p = begin + 1 + match.rm_so;
+ }
+
+ fputs("", stdout);
+ in_paragraph = 1;
+ process(begin, p, 0);
+ end_paragraph();
+
+ return -(p - begin);
+}
+
+int
+doreplace(const char *begin, const char *end, int newblock) {
+ unsigned int i, l;
+
+ for (i = 0; i < LENGTH(replace); i++) {
+ l = strlen(replace[i][0]);
+ if (end - begin < l)
+ continue;
+ if (strncmp(replace[i][0], begin, l) == 0) {
+ fputs(replace[i][1], stdout);
+ return l;
+ }
+ }
+ return 0;
+}
+
+int
+doshortlink(const char *begin, const char *end, int newblock) {
+ const char *p, *c;
+ int ismail = 0;
+
+ if (*begin != '<')
+ return 0;
+ for (p = begin + 1; p != end; p++) {
+ switch (*p) {
+ case ' ':
+ case '\t':
+ case '\n':
+ return 0;
+ case '#':
+ case ':':
+ ismail = -1;
+ break;
+ case '@':
+ if (ismail == 0)
+ ismail = 1;
+ break;
+ case '>':
+ if (ismail == 0)
+ return 0;
+ fputs("'; c++)
+ fprintf(stdout, "%u;", *c);
+ fputs("\">", stdout);
+ for (c = begin + 1; *c != '>'; c++)
+ fprintf(stdout, "%u;", *c);
+ }
+ else {
+ hprint(begin + 1, p);
+ fputs("\">", stdout);
+ hprint(begin + 1, p);
+ }
+ fputs("", stdout);
+ return p - begin + 1;
+ }
+ }
+ return 0;
+}
+
+int
+dosurround(const char *begin, const char *end, int newblock) {
+ unsigned int i, l;
+ const char *p, *start, *stop;
+
+ for (i = 0; i < LENGTH(surround); i++) {
+ l = strlen(surround[i].search);
+ if (end - begin < 2*l || strncmp(begin, surround[i].search, l) != 0)
+ continue;
+ start = begin + l;
+ p = start;
+ do {
+ stop = p;
+ p = strstr(p + 1, surround[i].search);
+ } while (p && p[-1] == '\\');
+ if (p && p[-1] != '\\')
+ stop = p;
+ if (!stop || stop < start || stop >= end)
+ continue;
+ fputs(surround[i].before, stdout);
+
+ /* Single space at start and end are ignored */
+ if (start[0] == ' ' && stop[-1] == ' ' && start < stop - 1) {
+ start++;
+ stop--;
+ l++;
+ }
+
+ if (surround[i].process)
+ process(start, stop, 0);
+ else
+ hprint(start, stop);
+ fputs(surround[i].after, stdout);
+ return stop - start + 2 * l;
+ }
+ return 0;
+}
+
+int
+dounderline(const char *begin, const char *end, int newblock) {
+ unsigned int i, j, l;
+ const char *p;
+
+ if (!newblock)
+ return 0;
+ p = begin;
+ for (l = 0; p + l != end && p[l] != '\n'; l++);
+ p += l + 1;
+ if (l == 0)
+ return 0;
+ for (i = 0; i < LENGTH(underline); i++) {
+ for (j = 0; p + j < end && p[j] != '\n' && p[j] == underline[i].search[0]; j++);
+ if (j >= 3) {
+ fputs(underline[i].before, stdout);
+ if (underline[i].process)
+ process(begin, begin + l, 0);
+ else
+ hprint(begin, begin + l);
+ fputs(underline[i].after, stdout);
+ return -(j + p - begin);
+ }
+ }
+ return 0;
+}
+
+void *
+ereallocz(void *p, size_t size) {
+ void *res;
+ res = realloc(p, size);
+ if (!res)
+ eprint("realloc: %zu bytes\n", size);
+ return res;
+}
+
+void
+hprint(const char *begin, const char *end) {
+ const char *p;
+
+ for (p = begin; p != end; p++) {
+ if (*p == '&')
+ fputs("&", stdout);
+ else if (*p == '"')
+ fputs(""", stdout);
+ else if (*p == '>')
+ fputs(">", stdout);
+ else if (*p == '<')
+ fputs("<", stdout);
+ else
+ fputc(*p, stdout);
+ }
+}
+
+void
+process(const char *begin, const char *end, int newblock) {
+ const char *p;
+ int affected;
+ unsigned int i;
+
+ for (p = begin; p < end;) {
+ if (newblock)
+ while (*p == '\n')
+ if (++p == end)
+ return;
+
+ for (i = 0; i < LENGTH(parsers); i++)
+ if ((affected = parsers[i](p, end, newblock)))
+ break;
+ if (affected)
+ p += abs(affected);
+ else
+ fputc(*p++, stdout);
+
+ /* Don't print single newline at end */
+ if (p + 1 == end && *p == '\n')
+ return;
+
+ if (p[0] == '\n' && p + 1 != end && p[1] == '\n')
+ newblock = 1;
+ else
+ newblock = affected < 0;
+ }
+}
+
+int
+main(int argc, char *argv[]) {
+ char *buffer = NULL;
+ int s, i;
+ unsigned long len, bsize;
+ FILE *source = stdin;
+
+ regcomp(&p_end_regex, "(\n\n|(^|\n)```)", REG_EXTENDED);
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-v", argv[i]))
+ eprint("simple markup %s (C) Enno Boland\n",VERSION);
+ else if (!strcmp("-n", argv[i]))
+ nohtml = 1;
+ else if (argv[i][0] != '-')
+ break;
+ else if (!strcmp("--", argv[i])) {
+ i++;
+ break;
+ }
+ else
+ eprint("Usage %s [-n] [file]\n -n escape html strictly\n", argv[0]);
+ }
+ if (i < argc && !(source = fopen(argv[i], "r")))
+ eprint("Cannot open file `%s`\n",argv[i]);
+ bsize = 2 * BUFSIZ;
+ buffer = ereallocz(buffer, bsize);
+ len = 0;
+ while ((s = fread(buffer + len, 1, BUFSIZ, source))) {
+ len += s;
+ if (BUFSIZ + len + 1 > bsize) {
+ bsize += BUFSIZ;
+ if (!(buffer = realloc(buffer, bsize)))
+ eprint("realloc failed.");
+ }
+ }
+ buffer[len] = '\0';
+ process(buffer, buffer + len, 1);
+ fclose(source);
+ free(buffer);
+ return EXIT_SUCCESS;
+}
diff --git a/src/arena.c b/src/arena.c
new file mode 100644
index 0000000..3782b79
--- /dev/null
+++ b/src/arena.c
@@ -0,0 +1,56 @@
+#include "arena.h"
+
+#include // assert
+#include // uintptr_t
+#include // fprintf
+#include // abort, malloc
+#include // noreturn
+#include // memset
+
+static noreturn void arena_panic(const char *reason) {
+ fprintf(stderr, "Memory allocation failed: %s", reason);
+ abort();
+}
+
+struct arena arena_create(size_t capacity) {
+ struct arena arena = {
+ .root = malloc(capacity),
+ .capacity = capacity,
+ .used = 0,
+ };
+ if (arena.root == NULL) {
+ arena_panic("cannot allocate system memory");
+ }
+ return arena;
+}
+
+void *arena_alloc(struct arena *arena, size_t size, size_t alignment, unsigned flags) {
+ // Alignment must be a power of two.
+ // See: https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
+ assert(alignment != 0 && (alignment & (alignment - 1)) == 0);
+ size_t padding = -(uintptr_t)(arena->root + arena->used) & (alignment - 1);
+
+ // If no more memory is available, we fall back to our error strategy.
+ assert(arena->capacity >= arena->used);
+ if (arena->used + padding + size > arena->capacity) {
+ if (flags & ARENA_NO_PANIC) {
+ return NULL;
+ } else {
+ arena_panic("out of preallocated memory");
+ }
+ }
+
+ // Reserve memory from arena.
+ void *ptr = arena->root + arena->used + padding;
+ arena->used += padding + size;
+
+ if (~flags & ARENA_NO_ZERO) {
+ memset(ptr, 0, size);
+ }
+
+ return ptr;
+}
+
+void arena_destroy(struct arena *arena) {
+ free(arena->root);
+}
diff --git a/src/arena.h b/src/arena.h
new file mode 100644
index 0000000..cd4aa17
--- /dev/null
+++ b/src/arena.h
@@ -0,0 +1,43 @@
+#ifndef ARENA_H
+#define ARENA_H
+
+//
+// This module defines a simple, fixed-size arena allocator.
+//
+
+#include // size_t
+
+struct arena {
+ void *root;
+ size_t capacity;
+ size_t used;
+};
+
+// Initialize an arena with the given `capacity`.
+// Panics on failure to allocate.
+struct arena arena_create(size_t capacity);
+
+// These flags control the behavior of `arena_alloc`.
+#define ARENA_NO_ZERO 1
+#define ARENA_NO_PANIC 2
+
+// Allocate `size` bytes in `arena`.
+// The resulting memory is zeroed unless ARENA_NO_ZERO is passed.
+// Unless ARENA_NO_PANIC is specified, the resulting pointer is always valid.
+void *arena_alloc(struct arena *arena, size_t size, size_t alignment, unsigned flags);
+
+// Free the memory associated with the arena using the underlying allocator.
+void arena_destroy(struct arena *arena);
+
+//
+// The `new` macro makes the basic allocation case simple. It uses a bit of
+// preprocessor magic to simulate default argument values.
+//
+
+#define new(...) newx(__VA_ARGS__,new4,new3,new2)(__VA_ARGS__)
+#define newx(a1, a2, a3, a4, a5, ...) a5
+#define new2(arena, typ) (typ *)arena_alloc(arena, sizeof(typ), _Alignof(typ), 0)
+#define new3(arena, typ, count) (typ *)arena_alloc(arena, count * sizeof(typ), _Alignof(typ), 0)
+#define new4(arena, typ, count, flags) (typ *)arena_alloc(arena, count * sizeof(typ), _Alignof(typ), flags)
+
+#endif
diff --git a/src/creole-test.c b/src/creole-test.c
new file mode 100644
index 0000000..18d24a2
--- /dev/null
+++ b/src/creole-test.c
@@ -0,0 +1,273 @@
+#include "creole.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+//#include
+
+#define RED "\x1b[31m"
+#define GREEN "\x1b[32m"
+#define YELLOW "\x1b[33m"
+#define CYAN "\x1b[96m"
+#define BOLD "\x1b[39;1m"
+#define DIM "\x1b[39;2m"
+#define CLEAR "\x1b[0m"
+
+#define LENGTH(a) (sizeof(a)/sizeof((a)[0]))
+
+#define CHUNK_SIZE 5
+
+int read_file(const char *file_path, char **out_buffer, size_t *out_length) {
+ assert(out_buffer != NULL && *out_buffer == NULL);
+ assert(file_path != NULL);
+
+ FILE *fp = fopen(file_path, "r");
+ if (fp == NULL) {
+ return -1;
+ }
+
+ char *buffer = NULL;
+ size_t allocated = 0;
+ size_t used = 0;
+ while (true) {
+ // Grow buffer, if needed.
+ if (used + CHUNK_SIZE > allocated) {
+ // Grow exponentially to guarantee O(log(n)) performance.
+ allocated = (allocated == 0) ? CHUNK_SIZE : allocated * 2;
+
+ // Overflow check. Some ANSI C compilers may optimize this away, though.
+ if (allocated <= used) {
+ free(buffer);
+ fclose(fp);
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ char *temp = realloc(buffer, allocated);
+ if (temp == NULL) {
+ int old_errno = errno;
+ free(buffer); // free() may not set errno
+ fclose(fp); // fclose() may set errno
+ errno = old_errno;
+ return -1;
+ }
+ buffer = temp;
+ }
+
+ size_t nread = fread(buffer + used, 1, CHUNK_SIZE, fp);
+ if (nread == 0) {
+ // End-of-file or errnor has occured.
+ // FIXME: Should we be checking (nread < CHUNK_SIZE)?
+ // https://stackoverflow.com/a/39322170
+ break;
+ }
+ used += nread;
+ }
+
+ if (ferror(fp)) {
+ int old_errno = errno;
+ free(buffer); // free() may not set errno
+ fclose(fp); // fclose() may set errno
+ errno = old_errno;
+ return -1;
+ }
+
+ // Reallocate to optimal size.
+ char *temp = realloc(buffer, used + 1);
+ if (temp == NULL) {
+ int old_errno = errno;
+ free(buffer); // free() may not set errno
+ fclose(fp); // fclose() may set errno
+ errno = old_errno;
+ return -1;
+ }
+ buffer = temp;
+
+ // Null-terminate the buffer. Note that buffers may still contain \0,
+ // so strlen(buffer) == length may not be true.
+ buffer[used] = '\0';
+
+ // Return buffer.
+ *out_buffer = buffer;
+ if (out_length != NULL) {
+ *out_length = used;
+ }
+ fclose(fp);
+ return 0;
+}
+
+// https://stackoverflow.com/a/779960
+char *replace(const char *orig, char *rep, char *with) {
+ assert(orig != NULL);
+ assert(rep != NULL);
+
+ char *tmp; // varies
+
+ int len_rep = strlen(rep); // length of rep (the string to remove)
+ if (len_rep == 0) {
+ errno = EINVAL; // empty rep causes infinite loop during count
+ return NULL;
+ }
+
+ int len_with; // length of with (the string to replace rep with)
+ if (with == NULL)
+ with = "";
+ len_with = strlen(with);
+
+ // count the number of replacements needed
+ const char *ins; // the next insert point
+ int count; // number of replacements
+ ins = orig;
+ for (count = 0; (tmp = strstr(ins, rep)) != NULL; ++count) {
+ ins = tmp + len_rep;
+ }
+
+ char *result; // the return string
+ tmp = result = malloc(strlen(orig) + (len_with - len_rep) * count + 1);
+ if (!result) {
+ return NULL;
+ }
+
+ // first time through the loop, all the variable are set correctly
+ // from here on,
+ // tmp points to the end of the result string
+ // ins points to the next occurrence of rep in orig
+ // orig points to the remainder of orig after "end of rep"
+ while (count--) {
+ ins = strstr(orig, rep);
+ int len_front = ins - orig;
+ tmp = strncpy(tmp, orig, len_front) + len_front;
+ tmp = strcpy(tmp, with) + len_with;
+ orig += len_front + len_rep; // move to next "end of rep"
+ }
+ strcpy(tmp, orig);
+ return result;
+}
+
+int print_escaped(FILE *fp, const char *string, size_t length) {
+ static struct {
+ char from;
+ const char *to;
+ } replacements[] = {
+ { '\t', "\\t" },
+ { '\n', "\\n" },
+ { '"', "\\\"" },
+ };
+
+ if (fputc('"', fp) == EOF) {
+ return -1;
+ }
+
+ for (size_t i = 0; i < length; ++i) {
+ for (size_t j = 0; j < LENGTH(replacements); ++j) {
+ if (string[i] == replacements[j].from) {
+ if (fprintf(fp, "%s", replacements[j].to) < 0) {
+ return -1;
+ }
+ goto next_char;
+ }
+ }
+ if (fputc(string[i], fp) == EOF) {
+ return -1;
+ }
+next_char:
+ ;
+ }
+
+ if (fputc('"', fp) == EOF) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ if (argc != 1) {
+ fprintf(stderr, "Usage: %s\n", argv[0]);
+ fprintf(stderr, "Takes no arguments, must be invoked in parent of test dir.\n");
+ return EXIT_FAILURE;
+ }
+
+ glob_t glob_result;
+ if (glob("./test/*.input.txt", GLOB_ERR, NULL, &glob_result) < 0) {
+ perror("Glob failed");
+ return EXIT_FAILURE;
+ }
+
+ unsigned ok = 0;
+ unsigned failures = 0;
+ unsigned errors = 0;
+
+ for (int i = 0; i < glob_result.gl_matchc; ++i) {
+ char *input_name = glob_result.gl_pathv[i];
+
+ int input_name_len = strlen(input_name);
+ int prefix_len = strlen("./test/");
+ int sufix_len = strlen(".input.txt");
+ printf("Running: " BOLD "%.*s" CLEAR "... ", input_name_len - prefix_len - sufix_len, input_name + prefix_len);
+
+ char *input_buffer = NULL;
+ size_t input_length;
+ if (read_file(input_name, &input_buffer, &input_length) < 0) {
+ printf(RED "internal error!" CLEAR "\n " CYAN "error:" CLEAR " reading %s: %s\n", input_name, strerror(errno));
+ errors += 1;
+ goto fail_input_buffer;
+ }
+
+ // TODO: replace() is a bit overkill. Just strcpy to buffer of size (strlen(input_name) - strlen(".input.txt") + strlen(".output.txt")).
+ char *output_name = replace(input_name, ".input.txt", ".output.txt");
+ if (output_name == NULL) {
+ printf(RED "internal error!" CLEAR "\n " CYAN "error:" CLEAR " generating output name: %s\n", strerror(errno));
+ errors += 1;
+ goto fail_output_name;
+ }
+ char *output_buffer = NULL;
+ size_t output_length;
+ if (read_file(output_name, &output_buffer, &output_length) < 0) {
+ printf(RED "internal error!" CLEAR "\n " CYAN "error:" CLEAR " reading %s: %s\n", output_name, strerror(errno));
+ errors += 1;
+ goto fail_output_buffer;
+ }
+
+ // Do actual render.
+ static char buffer[1024];
+ FILE *fp = fmemopen(buffer, sizeof(buffer), "wb");
+ render_creole(fp, input_buffer, input_length);
+ long buffer_length = ftell(fp);
+ fclose(fp);
+
+ bool success = strcmp(output_buffer, buffer) == 0;
+ if (success) {
+ ok += 1;
+ printf(GREEN "ok" CLEAR "\n");
+ } else {
+ failures += 1;
+ printf(RED "unexpected output!" CLEAR);
+ printf(CYAN "\n input: " CLEAR);
+ print_escaped(stdout, input_buffer, input_length);
+ printf(CYAN "\n want: " CLEAR);
+ print_escaped(stdout, output_buffer, output_length);
+ printf(CYAN"\n got: " CLEAR);
+ print_escaped(stdout, buffer, buffer_length); // TODO: rendered
+ putchar('\n');
+ }
+
+ free(output_buffer);
+fail_output_buffer:
+ free(output_name);
+fail_output_name:
+ free(input_buffer);
+fail_input_buffer:
+ ;
+ }
+
+ printf("Summary: " YELLOW "%u" CLEAR " errors, " RED "%u" CLEAR " failures and " GREEN "%u" CLEAR " successes\n", errors, failures, ok);
+
+ globfree(&glob_result);
+ return (failures == 0 && errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/creole.c b/src/creole.c
new file mode 100644
index 0000000..f69c543
--- /dev/null
+++ b/src/creole.c
@@ -0,0 +1,111 @@
+#include "creole.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LENGTH(x) (sizeof(x)/sizeof((x)[0]))
+
+#define DEBUG(...) (fprintf(stderr, __VA_ARGS__), fflush(stderr))
+
+void process(const char *begin, const char *end, bool new_block, FILE *out);
+int do_headers(const char *begin, const char *end, bool new_block, FILE *out);
+
+// A parser takes a (sub)string and returns the number of characters consumed, if any.
+//
+// The parameter `new_block` determines whether `begin` points to the beginning of a new block.
+// The sign of the return value determines whether a new block should begin, after the consumed text.
+typedef int (* parser_t)(const char *begin, const char *end, bool new_block, FILE *out);
+
+static parser_t parsers[] = { do_headers };
+
+int do_headers(const char *begin, const char *end, bool new_block, FILE *out) {
+ if (!new_block) { // Headers are block-level elements.
+ return 0;
+ }
+
+ if (*begin != '=') {
+ return 0;
+ }
+
+ unsigned level = 0;
+ while (*begin == '=') {
+ level += 1;
+ begin += 1;
+ }
+ DEBUG("level %d\n", level);
+
+ while (isspace(*begin)) {
+ begin += 1;
+ }
+
+ const char *stop = end;
+ while (stop + 1 != end && stop[1] != '\n') {
+ stop += 1;
+ }
+ while (*stop == '=') {
+ stop -= 1;
+ }
+
+ fprintf(out, "", level);
+ process(begin, stop, false, out);
+ fprintf(out, " ", level);
+
+ return -(stop - begin);
+}
+
+void process(const char *begin, const char *end, bool new_block, FILE *out) {
+ const char *p = begin;
+ while (p < end) {
+ // Eat all newlines if we're starting a block.
+ if (new_block) {
+ while (*p == '\n') {
+ p += 1;
+ if (p == end) {
+ return;
+ }
+ }
+ }
+
+ // Greedily try all parsers.
+ int affected;
+ for (unsigned i = 0; i < LENGTH(parsers); ++i) {
+ DEBUG("%p\n", parsers[i]);
+ affected = parsers[i](p, end, new_block, out);
+ if (affected) {
+ break;
+ }
+ }
+ if (affected) {
+ p += abs(affected);
+ } else {
+ fputc(*p, out);
+ p += 1;
+ }
+
+ if (p + 1 == end) {
+ // Don't print single newline at end.
+ if (*p == '\n') {
+ return;
+ }
+ } else {
+ // Determine whether we've reached a new block.
+ if (p[0] == '\n' && p[1] == '\n') {
+ // Double newline characters separate blocks;
+ // if we've found them, we're starting a new block
+ new_block = true;
+ } else {
+ // ...otherwise the parser gets to decide.
+ new_block = affected < 0;
+ }
+ }
+ }
+}
+
+void render_creole(FILE *out, const char *source, size_t source_length)
+{
+ process(source, source + source_length, true, out);
+}
diff --git a/src/creole.h b/src/creole.h
new file mode 100644
index 0000000..ac3e706
--- /dev/null
+++ b/src/creole.h
@@ -0,0 +1,15 @@
+#ifndef CREOLE_H
+#define CREOLE_H
+
+// Defines a module for rendering Wiki Creole [1] to a file. This functionality
+// of this module is based on the formal grammar [2] of Wiki Creole.
+//
+// [1]: http://www.wikicreole.org/wiki/Home
+// [2]: http://www.wikicreole.org/wiki/EBNFGrammarForWikiCreole1.0
+
+#include // size_t
+#include // FILE
+
+void render_creole(FILE *out, const char *source, size_t length);
+
+#endif
diff --git a/src/die.c b/src/die.c
new file mode 100644
index 0000000..529eb9b
--- /dev/null
+++ b/src/die.c
@@ -0,0 +1,58 @@
+#include "die.h"
+
+#include // assert
+#include // fprintf, vfprintf, fputc, stderr
+#include // exit, EXIT_FAILURE
+#include // va_*
+#include // git_*
+#include // strerror
+#include // errno
+
+void die(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ fputc('\n', stderr);
+
+#ifndef NDEBUG
+ git_libgit2_shutdown();
+#endif
+ exit(EXIT_FAILURE);
+}
+
+// Die but include the last git error.
+void noreturn die_git(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ const git_error *e = git_error_last();
+ assert(e != NULL && "die_git called without error");
+ fprintf(stderr, ": %s\n", e->message);
+
+#ifndef NDEBUG
+ git_libgit2_shutdown();
+#endif
+ exit(EXIT_FAILURE);
+}
+
+// Die but include errno information.
+void noreturn die_errno(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+
+ fprintf(stderr, ": %s\n", strerror(errno));
+
+#ifndef NDEBUG
+ git_libgit2_shutdown();
+#endif
+ exit(EXIT_FAILURE);
+}
diff --git a/src/die.h b/src/die.h
new file mode 100644
index 0000000..891d10f
--- /dev/null
+++ b/src/die.h
@@ -0,0 +1,31 @@
+#ifndef DIE_H
+#define DIE_H
+
+//
+// This module defines various utilities for ending program execution
+// abnormally.
+//
+
+#include // noreturn
+
+#ifdef __GNUC__
+#define _DIE_PRINTF_ATTR __attribute__((format(printf, 1, 2)))
+#else
+#define _DIE_PRINTF_ATTR
+#endif
+
+// Exit the program, displaying no extra information.
+_DIE_PRINTF_ATTR
+noreturn void die(const char *msg, ...);
+
+// Exit the program, displaying the last libgit error.
+// It is an error to invoke this if there has been no libgit error.
+_DIE_PRINTF_ATTR
+noreturn void die_git(const char *msg, ...);
+
+// Exit the program, displaying errno message.
+// It is NOT an error to invoke this if errno is 0, just pretty weird.
+_DIE_PRINTF_ATTR
+noreturn void die_errno(const char *msg, ...);
+
+#endif
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a6b438f
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,174 @@
+#include "arena.h"
+#include "die.h"
+#include "strutil.h"
+#include "creole.h"
+
+// #include
+#include // errno, EEXIST
+#include // git_*
+#include // false
+#include
+#include // EXIT_SUCCESS
+#include // mkdir
+#include // mode_t
+
+void xmkdir(const char *path, mode_t mode, bool exist_ok) {
+ if (mkdir(path, mode) < 0) {
+ if (exist_ok && errno == EEXIST) {
+ return;
+ } else {
+ die_errno("failed to mkdir %s", path);
+ }
+ }
+}
+
+void process_other_file(const char *path, const char *source, size_t source_len) {
+ FILE *out = fopen(path, "w");
+ if (out == NULL) {
+ die_errno("failed to open %s for writing", path);
+ }
+ if (fwrite(source, 1, source_len, out) < source_len) {
+ die_errno("failed to write content to %s\n", path);
+ }
+ fclose(out);
+}
+
+void process_markup_file(const char *path, const char *source, size_t source_len) {
+ FILE *out = fopen(path, "w");
+ if (out == NULL) {
+ die_errno("failed to open %s for writing", path);
+ }
+ int status = render_creole(out, source, source_len);
+ if (status != 0) {
+ fprintf(stderr, "warning: failed to parse: %s (status %d)\n", path, status);
+ }
+ fclose(out);
+}
+
+void process_dir(const char *path) {
+ xmkdir(path, 0755, false);
+}
+
+void list_tree(struct arena *a, struct git_repository *repo, struct git_tree *tree, const char *prefix) {
+ // Grab a snapshot of the arena.
+ // All memory allocated within the arena in this subcalltree will be freed.
+ // This is effectively the same as allocating a new arena for each call to list_tree.
+ struct arena snapshot = *a;
+
+ size_t tree_count = git_tree_entrycount(tree);
+ for (size_t i = 0; i < tree_count; ++i) {
+ // Read the entry.
+ const struct git_tree_entry *entry;
+ if ((entry = git_tree_entry_byindex(tree, i)) == NULL) {
+ die("read tree item");
+ }
+
+ // Construct path to entry.
+ const char *entry_out_path = joinpath(a, prefix, git_tree_entry_name(entry));
+ printf("Generating: %s\n", entry_out_path);
+
+ // entry->obj fail on submodules. just ignore them.
+ struct git_object *obj;
+ if (git_tree_entry_to_object(&obj, repo, entry) == 0) {
+ git_object_t type = git_object_type(obj);
+ switch (type) {
+ case GIT_OBJECT_BLOB: {
+ struct git_blob *blob = (struct git_blob *)obj;
+ const char *source = git_blob_rawcontent(blob);
+ if (source == NULL) {
+ die_git("get source for blob %s", git_oid_tostr_s(git_object_id(obj)));
+ }
+ size_t source_len = git_blob_rawsize(blob);
+ if (endswith(entry_out_path, ".md") && !git_blob_is_binary(blob)) {
+ process_markup_file(entry_out_path, source, source_len);
+ } else {
+ process_other_file(entry_out_path, source, source_len);
+ }
+ git_object_free(obj);
+ } break;
+ case GIT_OBJECT_TREE: {
+ process_dir(entry_out_path);
+ list_tree(a, repo, (struct git_tree *)obj, entry_out_path);
+ git_object_free(obj);
+ } break;
+ default: {
+ // Ignore whatever weird thing this is.
+ git_object_free(obj);
+ } break;
+ }
+ }
+ }
+
+ // Restore snapshot.
+ *a = snapshot;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 3) {
+ die("Usage: %s git-path out-path", argv[0]);
+ }
+ char *git_path = argv[1];
+ char *out_path = argv[2];
+
+ // Initialize libgit. Note that calling git_libgit2_shutdown is not
+ // necessary, as per this snippet from the documentation:
+ //
+ // > Usually you don’t need to call the shutdown function as the operating
+ // > system will take care of reclaiming resources, but if your
+ // > application uses libgit2 in some areas which are not usually active,
+ // > you can use
+ //
+ // That's good news!
+ if (git_libgit2_init() < 0) {
+ die_git("initialize libgit");
+ }
+
+ // Do not search outside the git repository. GIT_CONFIG_LEVEL_APP is the highest level currently.
+ // for (int i = 1; i <= GIT_CONFIG_LEVEL_APP; i++) {
+ // if (git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, i, "") < 0) {
+ // die_git("set search path");
+ // }
+ // }
+
+ // Don't require the repository to be owned by the current user.
+ git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0);
+
+ struct git_repository *repo;
+ if (git_repository_open_ext(&repo, git_path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL) < 0) {
+ die_git("open repository");
+ }
+
+ // Find HEAD.
+ struct git_object *head;
+ const struct git_oid *head_id;
+ if (git_revparse_single(&head, repo, "HEAD") < 0) {
+ die_git("parse HEAD");
+ }
+ head_id = git_object_id(head);
+ git_object_free(head);
+
+ // Get a handle to the tree at head.
+ struct git_commit *commit;
+ if (git_commit_lookup(&commit, repo, head_id) < 0) {
+ die_git("look up head commit");
+ }
+ struct git_tree *tree;
+ if (git_commit_tree(&tree, commit) < 0) {
+ die_git("get tree for commit %s", git_oid_tostr_s(git_commit_id(commit)));
+ }
+
+ // Create the initial output directory.
+ xmkdir(out_path, 0755, true);
+
+ struct arena a = arena_create(1024);
+ list_tree(&a, repo, tree, out_path);
+#ifndef NDEBUG
+ arena_destroy(&a);
+#endif
+
+#ifndef NDEBUG
+ git_libgit2_shutdown();
+#endif
+ return EXIT_SUCCESS;
+}
diff --git a/src/strutil.c b/src/strutil.c
new file mode 100644
index 0000000..9192989
--- /dev/null
+++ b/src/strutil.c
@@ -0,0 +1,58 @@
+#include "strutil.h"
+
+#include "arena.h" // struct arena, new
+#include // assert
+#include // va_*
+#include // bool, false
+#include // vsnprintf
+#include // strlen, strncmp
+
+int aprintf(struct arena *a, char **out, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ int ret = vaprintf(a, out, fmt, ap);
+ va_end(ap);
+ return ret;
+}
+
+int vaprintf(struct arena *a, char **out, const char *fmt, va_list args) {
+ // Calculate size.
+ va_list tmp;
+ va_copy(tmp, args);
+ int size = vsnprintf(NULL, 0, fmt, args);
+ va_end(tmp);
+
+ // If e.g. the format string was broken, we cannot continue.
+ if (size < 0) {
+ return -1;
+ }
+
+ // Arena allocation cannot fail.
+ *out = new(a, char, size + 1);
+
+ int t = vsnprintf(*out, size + 1, fmt, args);
+ assert(t == size);
+
+ return size;
+}
+
+char *joinpath(struct arena *a, const char *path_a, const char *path_b) {
+ char *out;
+ int ret = aprintf(a, &out, "%s/%s", path_a, path_b);
+ assert(ret > 0 && "should be infallible");
+ return out;
+}
+
+bool endswith(const char *haystack, const char *needle) {
+ assert(haystack != NULL);
+ assert(needle != NULL);
+
+ size_t haystack_len = strlen(haystack);
+ size_t needle_len = strlen(needle);
+
+ if (needle_len > haystack_len) {
+ return false;
+ }
+
+ return strncmp(haystack + (haystack_len - needle_len), needle, needle_len) == 0;
+}
diff --git a/src/strutil.h b/src/strutil.h
new file mode 100644
index 0000000..03f8294
--- /dev/null
+++ b/src/strutil.h
@@ -0,0 +1,26 @@
+#ifndef STRUTIL_H
+#define STRUTIL_H
+
+//
+// Defines various utilities for working with strings.
+//
+
+#include "arena.h" // struct arena
+#include // bool
+#include // va_list
+
+// Like asprintf except the allocation is made inside the given arena.
+// Panics on allocation failure.
+int aprintf(struct arena *a, char **out, const char *fmt, ...);
+
+// Same as aprintf, except takes a varargs list.
+int vaprintf(struct arena *a, char **out, const char *fmt, va_list args);
+
+// Join the two paths with a directory separator.
+// Result is allocated in arena.
+char *joinpath(struct arena *a, const char *path_a, const char *path_b);
+
+// Returns boolean indicating if `haystack` ends with `needle`.
+bool endswith(const char *haystack, const char *needle);
+
+#endif
diff --git a/test/>h6.input.txt b/test/>h6.input.txt
new file mode 100644
index 0000000..f422da3
--- /dev/null
+++ b/test/>h6.input.txt
@@ -0,0 +1 @@
+======= Header =
\ No newline at end of file
diff --git a/test/>h6.output.txt b/test/>h6.output.txt
new file mode 100644
index 0000000..1ab8a75
--- /dev/null
+++ b/test/>h6.output.txt
@@ -0,0 +1 @@
+======= Header =
\ No newline at end of file
diff --git a/test/basic-paragraph-markup.input.txt b/test/basic-paragraph-markup.input.txt
new file mode 100644
index 0000000..30d69f7
--- /dev/null
+++ b/test/basic-paragraph-markup.input.txt
@@ -0,0 +1 @@
+Basic paragraph test with <, >, & and "
\ No newline at end of file
diff --git a/test/basic-paragraph-markup.output.txt b/test/basic-paragraph-markup.output.txt
new file mode 100644
index 0000000..24cc6d6
--- /dev/null
+++ b/test/basic-paragraph-markup.output.txt
@@ -0,0 +1 @@
+Basic paragraph test with <, >, & and "
\ No newline at end of file
diff --git a/test/difficult-emphasis-#1.input.txt b/test/difficult-emphasis-#1.input.txt
new file mode 100644
index 0000000..f40a5e6
--- /dev/null
+++ b/test/difficult-emphasis-#1.input.txt
@@ -0,0 +1 @@
+// http://www.link.org //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#1.output.txt b/test/difficult-emphasis-#1.output.txt
new file mode 100644
index 0000000..4b94283
--- /dev/null
+++ b/test/difficult-emphasis-#1.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/difficult-emphasis-#10.input.txt b/test/difficult-emphasis-#10.input.txt
new file mode 100644
index 0000000..e2fff46
--- /dev/null
+++ b/test/difficult-emphasis-#10.input.txt
@@ -0,0 +1 @@
+// ftp //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#10.output.txt b/test/difficult-emphasis-#10.output.txt
new file mode 100644
index 0000000..d4e6163
--- /dev/null
+++ b/test/difficult-emphasis-#10.output.txt
@@ -0,0 +1 @@
+ ftp
\ No newline at end of file
diff --git a/test/difficult-emphasis-#11.input.txt b/test/difficult-emphasis-#11.input.txt
new file mode 100644
index 0000000..c990a82
--- /dev/null
+++ b/test/difficult-emphasis-#11.input.txt
@@ -0,0 +1 @@
+// fttpfptftpft //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#11.output.txt b/test/difficult-emphasis-#11.output.txt
new file mode 100644
index 0000000..a633f70
--- /dev/null
+++ b/test/difficult-emphasis-#11.output.txt
@@ -0,0 +1 @@
+ fttpfptftpft
\ No newline at end of file
diff --git a/test/difficult-emphasis-#12.input.txt b/test/difficult-emphasis-#12.input.txt
new file mode 100644
index 0000000..eb6c6be
--- /dev/null
+++ b/test/difficult-emphasis-#12.input.txt
@@ -0,0 +1 @@
+// ftp: //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#12.output.txt b/test/difficult-emphasis-#12.output.txt
new file mode 100644
index 0000000..bc36f4f
--- /dev/null
+++ b/test/difficult-emphasis-#12.output.txt
@@ -0,0 +1 @@
+ ftp:
\ No newline at end of file
diff --git a/test/difficult-emphasis-#13.input.txt b/test/difficult-emphasis-#13.input.txt
new file mode 100644
index 0000000..a2613a2
--- /dev/null
+++ b/test/difficult-emphasis-#13.input.txt
@@ -0,0 +1 @@
+// ftp://
\ No newline at end of file
diff --git a/test/difficult-emphasis-#13.output.txt b/test/difficult-emphasis-#13.output.txt
new file mode 100644
index 0000000..d152870
--- /dev/null
+++ b/test/difficult-emphasis-#13.output.txt
@@ -0,0 +1 @@
+// ftp://
\ No newline at end of file
diff --git a/test/difficult-emphasis-#14.input.txt b/test/difficult-emphasis-#14.input.txt
new file mode 100644
index 0000000..8fcc0a8
--- /dev/null
+++ b/test/difficult-emphasis-#14.input.txt
@@ -0,0 +1 @@
+// ftp:////
\ No newline at end of file
diff --git a/test/difficult-emphasis-#14.output.txt b/test/difficult-emphasis-#14.output.txt
new file mode 100644
index 0000000..2e8d283
--- /dev/null
+++ b/test/difficult-emphasis-#14.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/difficult-emphasis-#15.input.txt b/test/difficult-emphasis-#15.input.txt
new file mode 100644
index 0000000..fac5c31
--- /dev/null
+++ b/test/difficult-emphasis-#15.input.txt
@@ -0,0 +1 @@
+//fttpfptftpftt//
\ No newline at end of file
diff --git a/test/difficult-emphasis-#15.output.txt b/test/difficult-emphasis-#15.output.txt
new file mode 100644
index 0000000..f69d27c
--- /dev/null
+++ b/test/difficult-emphasis-#15.output.txt
@@ -0,0 +1 @@
+fttpfptftpftt
\ No newline at end of file
diff --git a/test/difficult-emphasis-#16.input.txt b/test/difficult-emphasis-#16.input.txt
new file mode 100644
index 0000000..69a62c9
--- /dev/null
+++ b/test/difficult-emphasis-#16.input.txt
@@ -0,0 +1 @@
+//ftp://link.org//
\ No newline at end of file
diff --git a/test/difficult-emphasis-#16.output.txt b/test/difficult-emphasis-#16.output.txt
new file mode 100644
index 0000000..b639d39
--- /dev/null
+++ b/test/difficult-emphasis-#16.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/difficult-emphasis-#2.input.txt b/test/difficult-emphasis-#2.input.txt
new file mode 100644
index 0000000..4164319
--- /dev/null
+++ b/test/difficult-emphasis-#2.input.txt
@@ -0,0 +1 @@
+// http //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#2.output.txt b/test/difficult-emphasis-#2.output.txt
new file mode 100644
index 0000000..775aeaf
--- /dev/null
+++ b/test/difficult-emphasis-#2.output.txt
@@ -0,0 +1 @@
+ http
\ No newline at end of file
diff --git a/test/difficult-emphasis-#3.input.txt b/test/difficult-emphasis-#3.input.txt
new file mode 100644
index 0000000..63b3f61
--- /dev/null
+++ b/test/difficult-emphasis-#3.input.txt
@@ -0,0 +1 @@
+// httphpthtpht //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#3.output.txt b/test/difficult-emphasis-#3.output.txt
new file mode 100644
index 0000000..2475a98
--- /dev/null
+++ b/test/difficult-emphasis-#3.output.txt
@@ -0,0 +1 @@
+ httphpthtpht
\ No newline at end of file
diff --git a/test/difficult-emphasis-#4.input.txt b/test/difficult-emphasis-#4.input.txt
new file mode 100644
index 0000000..59df379
--- /dev/null
+++ b/test/difficult-emphasis-#4.input.txt
@@ -0,0 +1 @@
+// http: //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#4.output.txt b/test/difficult-emphasis-#4.output.txt
new file mode 100644
index 0000000..6ec00bc
--- /dev/null
+++ b/test/difficult-emphasis-#4.output.txt
@@ -0,0 +1 @@
+ http:
\ No newline at end of file
diff --git a/test/difficult-emphasis-#5.input.txt b/test/difficult-emphasis-#5.input.txt
new file mode 100644
index 0000000..6b00e37
--- /dev/null
+++ b/test/difficult-emphasis-#5.input.txt
@@ -0,0 +1 @@
+// http://
\ No newline at end of file
diff --git a/test/difficult-emphasis-#5.output.txt b/test/difficult-emphasis-#5.output.txt
new file mode 100644
index 0000000..f0a2bf3
--- /dev/null
+++ b/test/difficult-emphasis-#5.output.txt
@@ -0,0 +1 @@
+// http://
\ No newline at end of file
diff --git a/test/difficult-emphasis-#6.input.txt b/test/difficult-emphasis-#6.input.txt
new file mode 100644
index 0000000..482f676
--- /dev/null
+++ b/test/difficult-emphasis-#6.input.txt
@@ -0,0 +1 @@
+// http:////
\ No newline at end of file
diff --git a/test/difficult-emphasis-#6.output.txt b/test/difficult-emphasis-#6.output.txt
new file mode 100644
index 0000000..5f4d7e4
--- /dev/null
+++ b/test/difficult-emphasis-#6.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/difficult-emphasis-#7.input.txt b/test/difficult-emphasis-#7.input.txt
new file mode 100644
index 0000000..75d6014
--- /dev/null
+++ b/test/difficult-emphasis-#7.input.txt
@@ -0,0 +1 @@
+//httphpthtphtt//
\ No newline at end of file
diff --git a/test/difficult-emphasis-#7.output.txt b/test/difficult-emphasis-#7.output.txt
new file mode 100644
index 0000000..d22e67a
--- /dev/null
+++ b/test/difficult-emphasis-#7.output.txt
@@ -0,0 +1 @@
+httphpthtphtt
\ No newline at end of file
diff --git a/test/difficult-emphasis-#8.input.txt b/test/difficult-emphasis-#8.input.txt
new file mode 100644
index 0000000..b3db2a6
--- /dev/null
+++ b/test/difficult-emphasis-#8.input.txt
@@ -0,0 +1 @@
+//http://link.org//
\ No newline at end of file
diff --git a/test/difficult-emphasis-#8.output.txt b/test/difficult-emphasis-#8.output.txt
new file mode 100644
index 0000000..336af88
--- /dev/null
+++ b/test/difficult-emphasis-#8.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/difficult-emphasis-#9.input.txt b/test/difficult-emphasis-#9.input.txt
new file mode 100644
index 0000000..36d9ea2
--- /dev/null
+++ b/test/difficult-emphasis-#9.input.txt
@@ -0,0 +1 @@
+// ftp://www.link.org //
\ No newline at end of file
diff --git a/test/difficult-emphasis-#9.output.txt b/test/difficult-emphasis-#9.output.txt
new file mode 100644
index 0000000..0608ab1
--- /dev/null
+++ b/test/difficult-emphasis-#9.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/dummy-without-corresponding-output.input.txt b/test/dummy-without-corresponding-output.input.txt
new file mode 100644
index 0000000..e69de29
diff --git a/test/emphasis.input.txt b/test/emphasis.input.txt
new file mode 100644
index 0000000..032289b
--- /dev/null
+++ b/test/emphasis.input.txt
@@ -0,0 +1 @@
+//Emphasis//
\ No newline at end of file
diff --git a/test/emphasis.output.txt b/test/emphasis.output.txt
new file mode 100644
index 0000000..93eaeba
--- /dev/null
+++ b/test/emphasis.output.txt
@@ -0,0 +1 @@
+Emphasis
\ No newline at end of file
diff --git a/test/h1.input.txt b/test/h1.input.txt
new file mode 100644
index 0000000..2000784
--- /dev/null
+++ b/test/h1.input.txt
@@ -0,0 +1 @@
+= Header =
\ No newline at end of file
diff --git a/test/h1.output.txt b/test/h1.output.txt
new file mode 100644
index 0000000..689a688
--- /dev/null
+++ b/test/h1.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/h2.input.txt b/test/h2.input.txt
new file mode 100644
index 0000000..a2bc486
--- /dev/null
+++ b/test/h2.input.txt
@@ -0,0 +1 @@
+== Header =
\ No newline at end of file
diff --git a/test/h2.output.txt b/test/h2.output.txt
new file mode 100644
index 0000000..7369147
--- /dev/null
+++ b/test/h2.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/h3.input.txt b/test/h3.input.txt
new file mode 100644
index 0000000..4bb4cc0
--- /dev/null
+++ b/test/h3.input.txt
@@ -0,0 +1 @@
+=== Header =
\ No newline at end of file
diff --git a/test/h3.output.txt b/test/h3.output.txt
new file mode 100644
index 0000000..8634f69
--- /dev/null
+++ b/test/h3.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/h4.input.txt b/test/h4.input.txt
new file mode 100644
index 0000000..0439f7f
--- /dev/null
+++ b/test/h4.input.txt
@@ -0,0 +1 @@
+==== Header =
\ No newline at end of file
diff --git a/test/h4.output.txt b/test/h4.output.txt
new file mode 100644
index 0000000..74ac160
--- /dev/null
+++ b/test/h4.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/h5.input.txt b/test/h5.input.txt
new file mode 100644
index 0000000..cbe5f09
--- /dev/null
+++ b/test/h5.input.txt
@@ -0,0 +1 @@
+===== Header
\ No newline at end of file
diff --git a/test/h5.output.txt b/test/h5.output.txt
new file mode 100644
index 0000000..91c8302
--- /dev/null
+++ b/test/h5.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/h6.input.txt b/test/h6.input.txt
new file mode 100644
index 0000000..f333b14
--- /dev/null
+++ b/test/h6.input.txt
@@ -0,0 +1 @@
+====== Header =
\ No newline at end of file
diff --git a/test/h6.output.txt b/test/h6.output.txt
new file mode 100644
index 0000000..1ce38f7
--- /dev/null
+++ b/test/h6.output.txt
@@ -0,0 +1 @@
+Header
\ No newline at end of file
diff --git a/test/horizontal-rule.input.txt b/test/horizontal-rule.input.txt
new file mode 100644
index 0000000..b86aa52
--- /dev/null
+++ b/test/horizontal-rule.input.txt
@@ -0,0 +1,3 @@
+Some text
+----
+Some more text
\ No newline at end of file
diff --git a/test/horizontal-rule.output.txt b/test/horizontal-rule.output.txt
new file mode 100644
index 0000000..05809bf
--- /dev/null
+++ b/test/horizontal-rule.output.txt
@@ -0,0 +1 @@
+Some text
Some more text
\ No newline at end of file
diff --git a/test/image.input.txt b/test/image.input.txt
new file mode 100644
index 0000000..82de5f6
--- /dev/null
+++ b/test/image.input.txt
@@ -0,0 +1 @@
+{{image.gif|my image}}
\ No newline at end of file
diff --git a/test/image.output.txt b/test/image.output.txt
new file mode 100644
index 0000000..421ea11
--- /dev/null
+++ b/test/image.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/inline-tt.input.txt b/test/inline-tt.input.txt
new file mode 100644
index 0000000..668057e
--- /dev/null
+++ b/test/inline-tt.input.txt
@@ -0,0 +1 @@
+Inline {{{tt}}} example {{{here}}}!
\ No newline at end of file
diff --git a/test/inline-tt.output.txt b/test/inline-tt.output.txt
new file mode 100644
index 0000000..4b26296
--- /dev/null
+++ b/test/inline-tt.output.txt
@@ -0,0 +1 @@
+Inline tt example here!
\ No newline at end of file
diff --git a/test/multi-line-emphasis.input.txt b/test/multi-line-emphasis.input.txt
new file mode 100644
index 0000000..7dbeaa4
--- /dev/null
+++ b/test/multi-line-emphasis.input.txt
@@ -0,0 +1,6 @@
+Bold and italics should //be
+able// to cross lines.
+
+But, should //not be...
+
+...able// to cross paragraphs.
\ No newline at end of file
diff --git a/test/multi-line-emphasis.output.txt b/test/multi-line-emphasis.output.txt
new file mode 100644
index 0000000..751f62e
--- /dev/null
+++ b/test/multi-line-emphasis.output.txt
@@ -0,0 +1,6 @@
+Bold and italics should be
+able to cross lines.
+
+But, should //not be...
+
+...able// to cross paragraphs.
\ No newline at end of file
diff --git a/test/named-link.input.txt b/test/named-link.input.txt
new file mode 100644
index 0000000..59ef88e
--- /dev/null
+++ b/test/named-link.input.txt
@@ -0,0 +1 @@
+[[MyPage|My page]]
\ No newline at end of file
diff --git a/test/named-link.output.txt b/test/named-link.output.txt
new file mode 100644
index 0000000..912d393
--- /dev/null
+++ b/test/named-link.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/named-url.input.txt b/test/named-url.input.txt
new file mode 100644
index 0000000..a71274b
--- /dev/null
+++ b/test/named-url.input.txt
@@ -0,0 +1 @@
+[[http://example.com/examplepage|Example Page]]
\ No newline at end of file
diff --git a/test/named-url.output.txt b/test/named-url.output.txt
new file mode 100644
index 0000000..ff2c2a6
--- /dev/null
+++ b/test/named-url.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/ordered-item-with-ordered-sublist.input.txt b/test/ordered-item-with-ordered-sublist.input.txt
new file mode 100644
index 0000000..181b2f8
--- /dev/null
+++ b/test/ordered-item-with-ordered-sublist.input.txt
@@ -0,0 +1,2 @@
+# Item
+## Subitem
\ No newline at end of file
diff --git a/test/ordered-item-with-ordered-sublist.output.txt b/test/ordered-item-with-ordered-sublist.output.txt
new file mode 100644
index 0000000..79cd7c0
--- /dev/null
+++ b/test/ordered-item-with-ordered-sublist.output.txt
@@ -0,0 +1,2 @@
+- Item
+- Subitem
\ No newline at end of file
diff --git a/test/ordered-sublist-without-initial-tag.input.txt b/test/ordered-sublist-without-initial-tag.input.txt
new file mode 100644
index 0000000..8e45942
--- /dev/null
+++ b/test/ordered-sublist-without-initial-tag.input.txt
@@ -0,0 +1 @@
+## Sublist item
\ No newline at end of file
diff --git a/test/ordered-sublist-without-initial-tag.output.txt b/test/ordered-sublist-without-initial-tag.output.txt
new file mode 100644
index 0000000..0587284
--- /dev/null
+++ b/test/ordered-sublist-without-initial-tag.output.txt
@@ -0,0 +1 @@
+## Sublist item
\ No newline at end of file
diff --git a/test/preformatted-block.input.txt b/test/preformatted-block.input.txt
new file mode 100644
index 0000000..0ce854b
--- /dev/null
+++ b/test/preformatted-block.input.txt
@@ -0,0 +1,3 @@
+{{{
+Preformatted block
+}}}
\ No newline at end of file
diff --git a/test/preformatted-block.output.txt b/test/preformatted-block.output.txt
new file mode 100644
index 0000000..42b0a91
--- /dev/null
+++ b/test/preformatted-block.output.txt
@@ -0,0 +1,2 @@
+Preformatted block
+
\ No newline at end of file
diff --git a/test/raw-url.input.txt b/test/raw-url.input.txt
new file mode 100644
index 0000000..8ebd56b
--- /dev/null
+++ b/test/raw-url.input.txt
@@ -0,0 +1 @@
+http://example.com/examplepage
\ No newline at end of file
diff --git a/test/raw-url.output.txt b/test/raw-url.output.txt
new file mode 100644
index 0000000..b0ec525
--- /dev/null
+++ b/test/raw-url.output.txt
@@ -0,0 +1 @@
+http://example.com/examplepage
\ No newline at end of file
diff --git a/test/simple-ordered-list.input.txt b/test/simple-ordered-list.input.txt
new file mode 100644
index 0000000..9cde6b0
--- /dev/null
+++ b/test/simple-ordered-list.input.txt
@@ -0,0 +1,2 @@
+# list item
+#list item 2
\ No newline at end of file
diff --git a/test/simple-ordered-list.output.txt b/test/simple-ordered-list.output.txt
new file mode 100644
index 0000000..34fb177
--- /dev/null
+++ b/test/simple-ordered-list.output.txt
@@ -0,0 +1,2 @@
+- list item
+- list item 2
\ No newline at end of file
diff --git a/test/simple-unordered-list.input.txt b/test/simple-unordered-list.input.txt
new file mode 100644
index 0000000..019ac85
--- /dev/null
+++ b/test/simple-unordered-list.input.txt
@@ -0,0 +1,2 @@
+* list item
+*list item 2
\ No newline at end of file
diff --git a/test/simple-unordered-list.output.txt b/test/simple-unordered-list.output.txt
new file mode 100644
index 0000000..5634c4b
--- /dev/null
+++ b/test/simple-unordered-list.output.txt
@@ -0,0 +1,2 @@
+- list item
+- list item 2
\ No newline at end of file
diff --git a/test/strong.input.txt b/test/strong.input.txt
new file mode 100644
index 0000000..e5d3ce8
--- /dev/null
+++ b/test/strong.input.txt
@@ -0,0 +1 @@
+**Strong**
\ No newline at end of file
diff --git a/test/strong.output.txt b/test/strong.output.txt
new file mode 100644
index 0000000..7ded6c8
--- /dev/null
+++ b/test/strong.output.txt
@@ -0,0 +1 @@
+Strong
\ No newline at end of file
diff --git a/test/tables.input.txt b/test/tables.input.txt
new file mode 100644
index 0000000..1038fa2
--- /dev/null
+++ b/test/tables.input.txt
@@ -0,0 +1,2 @@
+| A | B |
+| //C// | **D** \\ E |
\ No newline at end of file
diff --git a/test/tables.output.txt b/test/tables.output.txt
new file mode 100644
index 0000000..de0276a
--- /dev/null
+++ b/test/tables.output.txt
@@ -0,0 +1 @@
+ A B C D
E
\ No newline at end of file
diff --git a/test/two-preformatted-blocks.input.txt b/test/two-preformatted-blocks.input.txt
new file mode 100644
index 0000000..636f199
--- /dev/null
+++ b/test/two-preformatted-blocks.input.txt
@@ -0,0 +1,4 @@
+{{{
+Preformatted block
+}}}
+{{{Block 2}}}
\ No newline at end of file
diff --git a/test/two-preformatted-blocks.output.txt b/test/two-preformatted-blocks.output.txt
new file mode 100644
index 0000000..5f16814
--- /dev/null
+++ b/test/two-preformatted-blocks.output.txt
@@ -0,0 +1,2 @@
+Preformatted block
+
Block 2
\ No newline at end of file
diff --git a/test/unnamed-link.input.txt b/test/unnamed-link.input.txt
new file mode 100644
index 0000000..0b11afe
--- /dev/null
+++ b/test/unnamed-link.input.txt
@@ -0,0 +1 @@
+[[MyPage]]
\ No newline at end of file
diff --git a/test/unnamed-link.output.txt b/test/unnamed-link.output.txt
new file mode 100644
index 0000000..7ed65d2
--- /dev/null
+++ b/test/unnamed-link.output.txt
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/test/unnamed-url.input.txt b/test/unnamed-url.input.txt
new file mode 100644
index 0000000..ea80e5d
--- /dev/null
+++ b/test/unnamed-url.input.txt
@@ -0,0 +1 @@
+[[http://example.com/examplepage]]
\ No newline at end of file
diff --git a/test/unnamed-url.output.txt b/test/unnamed-url.output.txt
new file mode 100644
index 0000000..b0ec525
--- /dev/null
+++ b/test/unnamed-url.output.txt
@@ -0,0 +1 @@
+http://example.com/examplepage
\ No newline at end of file
diff --git a/test/unordered-item-with-ordered-sublist.input.txt b/test/unordered-item-with-ordered-sublist.input.txt
new file mode 100644
index 0000000..10ac242
--- /dev/null
+++ b/test/unordered-item-with-ordered-sublist.input.txt
@@ -0,0 +1,2 @@
+* Item
+*# Subitem
\ No newline at end of file
diff --git a/test/unordered-item-with-ordered-sublist.output.txt b/test/unordered-item-with-ordered-sublist.output.txt
new file mode 100644
index 0000000..57ed1da
--- /dev/null
+++ b/test/unordered-item-with-ordered-sublist.output.txt
@@ -0,0 +1,2 @@
+- Item
+- Subitem
\ No newline at end of file
diff --git a/test/unordered-item-with-unordered-sublist.input.txt b/test/unordered-item-with-unordered-sublist.input.txt
new file mode 100644
index 0000000..b4d3ecd
--- /dev/null
+++ b/test/unordered-item-with-unordered-sublist.input.txt
@@ -0,0 +1,2 @@
+* Item
+** Subitem
\ No newline at end of file
diff --git a/test/unordered-item-with-unordered-sublist.output.txt b/test/unordered-item-with-unordered-sublist.output.txt
new file mode 100644
index 0000000..9c4a46f
--- /dev/null
+++ b/test/unordered-item-with-unordered-sublist.output.txt
@@ -0,0 +1,2 @@
+- Item
+- Subitem
\ No newline at end of file
diff --git a/test/unordered-sublist-without-initial-tag.input.txt b/test/unordered-sublist-without-initial-tag.input.txt
new file mode 100644
index 0000000..f495ea5
--- /dev/null
+++ b/test/unordered-sublist-without-initial-tag.input.txt
@@ -0,0 +1 @@
+** Sublist item
\ No newline at end of file
diff --git a/test/unordered-sublist-without-initial-tag.output.txt b/test/unordered-sublist-without-initial-tag.output.txt
new file mode 100644
index 0000000..59d4091
--- /dev/null
+++ b/test/unordered-sublist-without-initial-tag.output.txt
@@ -0,0 +1 @@
+** Sublist item
\ No newline at end of file
diff --git "a/test/url\342\210\225emphasis-ambiguity.input.txt" "b/test/url\342\210\225emphasis-ambiguity.input.txt"
new file mode 100644
index 0000000..4c29ff9
--- /dev/null
+++ "b/test/url\342\210\225emphasis-ambiguity.input.txt"
@@ -0,0 +1 @@
+This is an //italic// text. This is a url: http://www.wikicreole.org. This is what can go wrong://this should be an italic text//.
\ No newline at end of file
diff --git "a/test/url\342\210\225emphasis-ambiguity.output.txt" "b/test/url\342\210\225emphasis-ambiguity.output.txt"
new file mode 100644
index 0000000..9be7ec8
--- /dev/null
+++ "b/test/url\342\210\225emphasis-ambiguity.output.txt"
@@ -0,0 +1 @@
+This is an italic text. This is a url: http://www.wikicreole.org. This is what can go wrong:this should be an italic text.
\ No newline at end of file
--
cgit v1.2.3