Ejemplo n.º 1
0
static VALUE rb_redcarpet_md_render(VALUE self, VALUE text)
{
	VALUE rb_rndr;
	struct buf *output_buf;
	struct sd_markdown *markdown;

	Check_Type(text, T_STRING);
	check_utf8_encoding(text);

	rb_rndr = rb_iv_get(self, "@renderer");
	Data_Get_Struct(self, struct sd_markdown, markdown);

	if (rb_respond_to(rb_rndr, rb_intern("preprocess")))
		text = rb_funcall(rb_rndr, rb_intern("preprocess"), 1, text);

	/* initialize buffers */
	output_buf = bufnew(128);

	/* render the magic */
	sd_markdown_render(
		output_buf,
		RSTRING_PTR(text),
		RSTRING_LEN(text),
		markdown);

	/* build the Ruby string */
	text = redcarpet_str_new(output_buf->data, output_buf->size);

	bufrelease(output_buf);

	if (rb_respond_to(rb_rndr, rb_intern("postprocess")))
		text = rb_funcall(rb_rndr, rb_intern("postprocess"), 1, text);

	return text;
}
Ejemplo n.º 2
0
/* sundown.markdown(input_string) ==> output_string */
static int sundown_markdown(lua_State *L) {
    const char *indoc;
    size_t indocSize;
    struct buf *inbuf;
    struct buf *outbuf;
    struct html_renderopt options;
    struct sd_callbacks callbacks;
    struct sd_markdown *markdown;

    /* read input_string */
    indoc = luaL_checklstring(L, 1, &indocSize);
    inbuf = bufnew(READ_UNIT);
    bufgrow(inbuf, indocSize);
    bufput(inbuf, indoc, indocSize);

    /* prepare for output_string */
    outbuf = bufnew(OUTPUT_UNIT);
    sdhtml_renderer(&callbacks, &options, 0);
    markdown = sd_markdown_new(0, 16, &callbacks, &options);

    /* write output_string */
    sd_markdown_render(outbuf, inbuf->data, inbuf->size, markdown);
    lua_pushlstring(L, (const char*) outbuf->data, outbuf->size);
    
    bufrelease(inbuf);
    bufrelease(outbuf);
    sd_markdown_free(markdown);
    
    return 1;
}
Ejemplo n.º 3
0
static QString markdown(QString in){
    struct buf *ib, *ob;
    struct sd_callbacks cbs;
    struct html_renderopt opts;
    struct sd_markdown *mkd;

    if(in.size() > 0){
        QByteArray qba = in.toUtf8();
        const char *txt = qba.constData();
        if(NULL == txt) qDebug() << "txt was null!";
        if(0 < qba.size()){
            ib = bufnew(qba.size());
            bufputs(ib,txt);
            ob = bufnew(64);
            sdhtml_renderer(&cbs,&opts,0);
            mkd = sd_markdown_new(0,16,&cbs,&opts);
            sd_markdown_render(ob,ib->data,ib->size,mkd);
            sd_markdown_free(mkd);
            return QString::fromUtf8(bufcstr(ob));
        }
        else
            qDebug() <<"qstrlen was null";
    }
    return "";
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
QByteArray md2html(const QByteArray &data, int ext)
{
    if (data.isEmpty()) {
        return data;
    }

    struct buf *ob;

    struct sd_callbacks callbacks;
    struct html_renderopt options;
    struct sd_markdown *markdown;

    /* performing markdown parsing */
    ob = bufnew(OUTPUT_UNIT);

    sdhtml_renderer(&callbacks, &options, 0);
    markdown = sd_markdown_new(ext, 16, &callbacks, &options);

    sd_markdown_render(ob, (uint8_t*)data.constData(), data.size(), markdown);
    sd_markdown_free(markdown);

    QByteArray out((char*)ob->data,ob->size);
    /* cleanup */
    bufrelease(ob);

    return out;
}
Ejemplo n.º 6
0
static PyObject *
snudown_md(PyObject *self, PyObject *args, PyObject *kwargs)
{
    static char *kwlist[] = {"text", "nofollow", "target", NULL};

    struct buf ib, *ob;
    PyObject *py_result;
    const char* result_text;

    memset(&ib, 0x0, sizeof(struct buf));

    /* Parse arguments */
    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|iz", kwlist,
                                     &ib.data, &ib.size, &_state.options.nofollow, &_state.options.target)) {
        return NULL;
    }

    /* Output buffer */
    ob = bufnew(128);

    /* do the magic */
    sd_markdown_render(ob, ib.data, ib.size, sundown);

    /* make a Python string */
    result_text = "";
    if (ob->data)
        result_text = (const char*)ob->data;
    py_result = Py_BuildValue("s#", result_text, (int)ob->size);

    /* Cleanup */
    bufrelease(ob);
    return py_result;
}
Ejemplo n.º 7
0
BOOL convMd2Html(LPCTSTR outpath, const CString& csspath)
{
	struct buf *ob;
	int ret;
	FILE *outf = NULL;
	unsigned int extensions = 0xff;
	char hHead[] = "<!DOCTYPE html>\n<html>\n<head>\n<meta charset=""utf-8"">\n";
	char hSym[] = "<link href=""%s"" rel=""stylesheet""></link>\n</head>\n<body>\n";
	char hTail[] = "</body>\n</html>";
	char *hCSS = NULL;

	struct sd_callbacks callbacks;
	struct html_renderopt options;
	struct sd_markdown *markdown;

	char *cssbuf = unicode2Utf8((LPCTSTR)csspath, csspath.GetLength());
	int aclen = strlen(cssbuf) + strlen(hSym);

	hCSS = (char*)malloc(aclen+1);
	memset(hCSS, 0, aclen);
	sprintf(hCSS, "<link href=""%s"" rel=""stylesheet""></link>\n</head>\n<body>\n", cssbuf);
	free(cssbuf);


	/* performing markdown parsing */
	ob = bufnew(OUTPUT_UNIT);

	//extensions = MKDEXT_TABLES|MKDEXT_FENCED_CODE|MKDEXT_AUTOLINK|MKDEXT_STRIKETHROUGH|MKDEXT_SUPERSCRIPT|MKDEXT_LAX_SPACING;

	sdhtml_renderer(&callbacks, &options, 0);
	markdown = sd_markdown_new(extensions, 16, &callbacks, &options);

	sd_markdown_render(ob, mib_utf8->data, mib_utf8->size, markdown);
	sd_markdown_free(markdown);

	outf = _tfopen(outpath, _T("w"));
	if(outf != NULL) {
		fwrite(hHead, 1, strlen(hHead), outf);
		fwrite(hCSS, 1, strlen(hCSS), outf);
		/* writing the result to stdout */
		ret = fwrite(ob->data, 1, ob->size, outf);
		fwrite(hTail, 1, strlen(hTail), outf);
		fclose(outf);
	}

	/* cleanup */
	free(hCSS);
	bufrelease(mib_utf8);
	bufrelease(ob);
	mib_utf8 = NULL;

	return TRUE;
}
Ejemplo n.º 8
0
bd_status_t
bd_render(struct buf *ob, const struct buf *ib)
{
	struct sd_callbacks callbacks;
	struct html_renderopt options;
	struct sd_markdown *markdown;

	unsigned int flags = HTML_USE_XHTML;
	sdhtml_renderer(&callbacks, &options, flags);
	markdown = sd_markdown_new(0, 16, &callbacks, &options);
	sd_markdown_render(ob, ib->data, ib->size, markdown);
	sd_markdown_free(markdown);
	return BD_SUCCESS;
}
Ejemplo n.º 9
0
/* 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;
}
Ejemplo n.º 10
0
/**
* returns html. NOTE that buffer NEEDS to be deallocated using bufrelease(output_buf);!!!
*/
static struct buf *ghmd_to_html(char const *text, const size_t textSize) {
    struct buf *output_buf;
    struct sd_markdown *md = g_markdown.md;

    /* initialize buffers */
    output_buf = bufnew(256);

    /* render the magic */
    sd_markdown_render(output_buf, text, textSize, md);

    //printf((char const *) output_buf->data);
    //printf("size %d\n", (int) output_buf->size);
    //printf("asize %d\n", (int) output_buf->asize);

    return output_buf;
}
Ejemplo n.º 11
0
static VALUE rb_redcarpet_md_render(VALUE self, VALUE text)
{
	VALUE rb_rndr;
	struct buf *output_buf;
	struct sd_markdown *markdown;

	Check_Type(text, T_STRING);

	rb_rndr = rb_iv_get(self, "@renderer");
	Data_Get_Struct(self, struct sd_markdown, markdown);

	if (rb_respond_to(rb_rndr, rb_intern("preprocess")))
		text = rb_funcall(rb_rndr, rb_intern("preprocess"), 1, text);
	if (NIL_P(text))
		return Qnil;

#ifdef HAVE_RUBY_ENCODING_H
	{
		struct rb_redcarpet_rndr *renderer;
		Data_Get_Struct(rb_rndr, struct rb_redcarpet_rndr, renderer);
		renderer->options.active_enc = rb_enc_get(text);
	}
#endif

	/* initialize buffers */
	output_buf = bufnew(128);

	/* render the magic */
	sd_markdown_render(
		output_buf,
		(const uint8_t*)RSTRING_PTR(text),
		RSTRING_LEN(text),
		markdown);

	/* build the Ruby string */
	text = rb_enc_str_new((const char*)output_buf->data, output_buf->size, rb_enc_get(text));

	bufrelease(output_buf);

	if (rb_respond_to(rb_rndr, rb_intern("postprocess")))
		text = rb_funcall(rb_rndr, rb_intern("postprocess"), 1, text);

	return text;
}
Ejemplo n.º 12
0
QString markDown(QString text) {
  struct sd_callbacks callbacks;
  struct html_renderopt options;
  sdhtml_renderer(&callbacks, &options, 0);

  struct sd_markdown* markdown = sd_markdown_new(0, 16, &callbacks, &options);

  struct buf* ob = bufnew(64);
  // QByteArray ba = text.toLocal8Bit();
  QByteArray ba = text.toUtf8();

  sd_markdown_render(ob, (const unsigned char*)ba.constData(), ba.size(),
                     markdown);
  sd_markdown_free(markdown);

  // QString ret = QString::fromLocal8Bit((char*)ob->data, ob->size);
  QString ret = QString::fromUtf8((char*)ob->data, ob->size);
  bufrelease(ob);

  return ret.trimmed();
}
Ejemplo n.º 13
0
static Rboolean render_to_html(struct buf *ib, struct buf *ob,
                                  SEXP Soptions, SEXP Sextensions)
{
   struct sd_callbacks callbacks;
   struct html_renderopt renderopt;
   unsigned int exts=0, options=0;
   struct sd_markdown *markdown;
   struct buf *htmlbuf;
   Rboolean toc = FALSE, smarty = FALSE;

   /* Marshal extensions */
   if (isString(Sextensions))
   {
      int i;
      for (i = 0; i < LENGTH(Sextensions); i++)
      {
         if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "NO_INTRA_EMPHASIS") == 0)
            exts |= MKDEXT_NO_INTRA_EMPHASIS;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "TABLES") == 0)
            exts |= MKDEXT_TABLES;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "FENCED_CODE") == 0)
            exts |= MKDEXT_FENCED_CODE;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "AUTOLINK") == 0)
            exts |= MKDEXT_AUTOLINK;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "STRIKETHROUGH") == 0)
            exts |= MKDEXT_STRIKETHROUGH;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "LAX_SPACING") == 0)
            exts |= MKDEXT_LAX_SPACING;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "SPACE_HEADERS") == 0)
            exts |= MKDEXT_SPACE_HEADERS;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "SUPERSCRIPT") == 0)
            exts |= MKDEXT_SUPERSCRIPT;
         else if (strcasecmp(CHAR(STRING_ELT(Sextensions,i)),
                        "LATEX_MATH") == 0)
            exts |= MKDEXT_LATEX_MATH;
      }
   }

   /* Marshal HTML options */
   if (isString(Soptions))
   {
      int i;
      for (i = 0; i < LENGTH(Soptions); i++)
      {
         if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SKIP_HTML") == 0)
            options |= HTML_SKIP_HTML;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SKIP_STYLE") == 0)
            options |= HTML_SKIP_STYLE;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SKIP_IMAGES") == 0)
            options |= HTML_SKIP_IMAGES;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SKIP_LINKS") == 0)
            options |= HTML_SKIP_LINKS;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SAFELINK") == 0)
            options |= HTML_SAFELINK;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "TOC") == 0)
         {
            options |= HTML_TOC;
            toc = TRUE;
         }
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "HARD_WRAP") == 0)
            options |= HTML_HARD_WRAP;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "USE_XHTML") == 0)
            options |= HTML_USE_XHTML;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "ESCAPE") == 0)
            options |= HTML_ESCAPE;
         else if (strcasecmp(CHAR(STRING_ELT(Soptions,i)),
                        "SMARTYPANTS") == 0)
            smarty = TRUE;
      }
   }

   htmlbuf = bufnew(OUTPUT_UNIT);
   if (!htmlbuf)
   {
      RMD_WARNING_NOMEM;
      return FALSE;
   }

   if (toc==TRUE)
   {
      struct buf *tocbuf = bufnew(OUTPUT_UNIT);

      if (!tocbuf)
      {
         RMD_WARNING_NOMEM;
         return FALSE;
      }

      sdhtml_toc_renderer(&callbacks, &renderopt);
      markdown = sd_markdown_new(exts,16,&callbacks,(void *)&renderopt);
      if (!markdown)
      {
         RMD_WARNING_NOMEM;
         return FALSE;
      }
      
      sd_markdown_render(tocbuf, ib->data, ib->size, markdown);
      sd_markdown_free(markdown);

      bufputs(htmlbuf,"<div id=\"toc\">\n");
      bufputs(htmlbuf,"<div id=\"toc_header\">Table of Contents</div>\n");
      bufput(htmlbuf,tocbuf->data,tocbuf->size);
      bufputs(htmlbuf,"</div>\n");
      bufputs(htmlbuf,"\n");
      bufrelease(tocbuf);
   }

   sdhtml_renderer(&callbacks, &renderopt, options);

   markdown = sd_markdown_new(exts,16,&callbacks,(void *)&renderopt);
   if (!markdown)
   {
      RMD_WARNING_NOMEM;
      return FALSE;
   }

   sd_markdown_render(htmlbuf, ib->data, ib->size, markdown);

   sd_markdown_free(markdown);

   if (smarty==TRUE)
   {
      struct buf *smartybuf = bufnew(OUTPUT_UNIT);
      if (!smartybuf)
      {
         RMD_WARNING_NOMEM;
         return FALSE;
      }
      sdhtml_smartypants(smartybuf,htmlbuf->data,htmlbuf->size);
      bufrelease(htmlbuf);
      htmlbuf = smartybuf;
   }

   bufput(ob,htmlbuf->data,htmlbuf->size);

   bufrelease(htmlbuf);

   return TRUE;
}
Ejemplo n.º 14
0
static PyObject *
snudown_md(PyObject *self, PyObject *args, PyObject *kwargs)
{
	static char *kwlist[] = {"text", "nofollow", "target", "toc_id_prefix", "renderer", "enable_toc", NULL};

	struct buf ib, *ob;
	PyObject *py_result;
	const char* result_text;
	int renderer = RENDERER_USERTEXT;
	int enable_toc = 0;
	struct snudown_renderer _snudown;
	int nofollow = 0;
	char* target = NULL;
	char* toc_id_prefix = NULL;
	unsigned int flags;

	memset(&ib, 0x0, sizeof(struct buf));

	/* Parse arguments */
	if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|izzii", kwlist,
				&ib.data, &ib.size, &nofollow,
				&target, &toc_id_prefix, &renderer, &enable_toc)) {
		return NULL;
	}

	if (renderer < 0 || renderer >= RENDERER_COUNT) {
		PyErr_SetString(PyExc_ValueError, "Invalid renderer");
		return NULL;
	}

	_snudown = sundown[renderer];

	struct snudown_renderopt *options = &(_snudown.state->options);
	options->nofollow = nofollow;
	options->target = target;

	/* Output buffer */
	ob = bufnew(128);

	flags = options->html.flags;

	if (enable_toc) {
		_snudown.toc_state->options.html.toc_id_prefix = toc_id_prefix;
		sd_markdown_render(ob, ib.data, ib.size, _snudown.toc_renderer);
		_snudown.toc_state->options.html.toc_id_prefix = NULL;

		options->html.flags |= HTML_TOC;
	}

	options->html.toc_id_prefix = toc_id_prefix;

	/* do the magic */
	sd_markdown_render(ob, ib.data, ib.size, _snudown.main_renderer);

	options->html.toc_id_prefix = NULL;
	options->html.flags = flags;

	/* make a Python string */
	result_text = "";
	if (ob->data)
		result_text = (const char*)ob->data;
	py_result = Py_BuildValue("s#", result_text, (int)ob->size);

	/* Cleanup */
	bufrelease(ob);
	return py_result;
}
Ejemplo n.º 15
0
int main(int argc, const char * argv[])
{
	struct buf					ib, *ob;
	int							enable_toc = 0;
	struct snudown_renderer		renderer;
	int							nofollow = 0;
	char*						target = NULL;
	char*						domain = NULL;
	char*						toc_id_prefix = NULL;
	unsigned int				flags;
	FILE *						fp;
	
	init_default_renderer();

	memset(&ib, 0x0, sizeof(struct buf));
	
	nofollow = false;
	target = "_blank";
	domain = "lightnet.is";
	toc_id_prefix = "";
	enable_toc = false;
	
	renderer = sundown[RENDERER_USERTEXT];
	
	struct snudown_renderopt *options = &(renderer.state->options);
	options->nofollow = nofollow;
	options->target = target;
	options->domain = domain;
	
	/* Set the input buffer */
	ib.data = (unsigned char*)	"First line:\nSecond Line:\nThird Line\n\n"
								"[link away](http://google.com)\n[link here](http://lightnet.is/space/lightnet)\n\n"
								"![5-Cell aka Pentachoron](http://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Schlegel_wireframe_5-cell.png/250px-Schlegel_wireframe_5-cell.png)";
	ib.size = strlen((const char*) ib.data);
	
	/* Output buffer */
	ob = bufnew(128);
	
	flags = options->html.flags;
	
	if (enable_toc) {
		renderer.toc_state->options.html.toc_id_prefix = toc_id_prefix;
		sd_markdown_render(ob, ib.data, ib.size, renderer.toc_renderer);
		renderer.toc_state->options.html.toc_id_prefix = NULL;
		
		options->html.flags |= HTML_TOC;
	}
	
	options->html.toc_id_prefix = toc_id_prefix;
	
	/* do the magic */
	sd_markdown_render(ob, ib.data, ib.size, renderer.main_renderer);
	
	options->html.toc_id_prefix = NULL;
	options->html.flags = flags;
	
	fp = fopen( "markdown.html", "w+" );

	fprintf( fp, "<HTML>\n<BODY>\n");
	fprintf( fp, "%s", (const char*) ob->data);
	fprintf( fp, "\n<HTML>\n<BODY>\n");
	
	fclose( fp );
	
	printf( "%s", (const char*) ob->data );
	
	/* Cleanup */
	bufrelease(ob);

	return 0;
}
Ejemplo n.º 16
0
int markdown_convert(strarg_t const dst, strarg_t const src) {
	int file = -1, fd = -1;
	byte_t const *in = NULL;
	struct buf *out = NULL;

	file = open(dst, O_CREAT | O_EXCL | O_RDWR, 0400);
	if(file < 0) {
		fprintf(stderr, "Can't create %s: %s\n", dst, strerror(errno));
		goto err;
	}

	fd = open(src, O_RDONLY, 0000);
	if(fd < 0) {
		fprintf(stderr, "Can't open %s: %s\n", src, strerror(errno));
		goto err;
	}

	struct stat stats[1];
	int rc = fstat(fd, stats);
	if(rc < 0) {
		fprintf(stderr, "Can't stat %s: %s\n", src, strerror(errno));
		goto err;
	}
	size_t const size = stats->st_size;
	if(size > CONVERT_MAX) {
		fprintf(stderr, "File too large %s: %zu\n", src, size);
		goto err;
	}

	in = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
	close(fd); fd = -1;
	if(MAP_FAILED == in) {
		fprintf(stderr, "Can't read %s: %s\n", src, strerror(errno));
		goto err;
	}


#ifdef MARKDOWN_STANDALONE
// TODO: chroot, drop privileges?
#endif

	unsigned int const rflags =
		HTML_ESCAPE |
		HTML_HARD_WRAP |
		0;
	unsigned int const mflags =
		MKDEXT_AUTOLINK |
		MKDEXT_FENCED_CODE |
		MKDEXT_NO_INTRA_EMPHASIS |
		MKDEXT_SUPERSCRIPT |
		0;
	size_t const nesting = 10;

	struct sd_callbacks callbacks[1];
	struct markdown_state state[1];
	sdhtml_renderer(callbacks, &state->opts, rflags);
	state->link = callbacks->link;
	state->autolink = callbacks->autolink;
	callbacks->link = markdown_link;
	callbacks->autolink = markdown_autolink;

	struct sd_markdown *parser = sd_markdown_new(mflags, nesting, callbacks, state);
	out = bufnew(1024 * 8); // Sundown grows this as needed.
	sd_markdown_render(out, in, size, parser);
	sd_markdown_free(parser); parser = NULL;

	// TODO: How is this supposed to work? Aren't we only writing the first 8K?
	// TODO: Are we even freeing `out`?

	size_t written = 0;
	for(;;) {
		ssize_t const r = TEMP_FAILURE_RETRY(write(file, out->data+written, out->size-written));
		if(r < 0) {
			fprintf(stderr, "Can't write %s: %s\n", dst, strerror(errno));
			goto err;
		}
		written += (size_t)r;
		if(written >= out->size) break;
	}
	if(fdatasync(file) < 0) {
		fprintf(stderr, "Can't sync %s: %s\n", dst, strerror(errno));
		goto err;
	}

	int const close_err = close(file); file = -1;
	if(close_err < 0) {
		fprintf(stderr, "Error while closing %s: %s\n", dst, strerror(errno));
		goto err;
	}
	munmap((byte_t *)in, size); in = NULL;
	bufrelease(out); out = NULL;
	return 0;

err:
	unlink(dst);
	close(file); file = -1;
	munmap((byte_t *)in, size); in = NULL;
	bufrelease(out); out = NULL;
	return -1;
}