greenmat 3.2.0.2 → 3.2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 640a48e0309d3ad49841f279024df4b6bfad325f
4
- data.tar.gz: 6d5c03df89ba11808bb57ca7c156336e4f300a92
3
+ metadata.gz: c56dac9366c03995652ee0bc85e40215c3abbb1f
4
+ data.tar.gz: 083dc87803beeb23ab9d2fcad89bc41b2cb96cfc
5
5
  SHA512:
6
- metadata.gz: 1c6ed914f324b245051525b3a8ff1189ac7a7144ad370254245448e0d857bcae235b1c202e9e66597a51df931f59c042ff2f345580c5765ba2160b81aa8a45fe
7
- data.tar.gz: e7479bf0e3e018566ea1640e09981d7eddb36d8eebe3e4b64946130abf0951220f08e923b1032769ad20d941a836cc0be129fe0b70ef729f460a2215987f4581
6
+ metadata.gz: f3dfbca086ab162186b40bfe338f147f9b63d3feb006265e0cf253c693375e9430e7bb825267b4e25d55dbe24d66574f7b257ba7f216b38f6e2cc4bd4d215333
7
+ data.tar.gz: b870e9c370fd479629b51c55425a6eee6723787fd9e70dde2d42807d52ea17d81f28d2998d40a7d8493c347a9786b8503845f7113dcef0433f75027a02d1846b
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## v3.2.2.0
6
+
7
+ * Update base Redcarpet version to 3.2.2.
8
+
5
9
  ## v3.2.0.2
6
10
 
7
11
  * Fix missing `greenmat/version` in the gem package.
data/README.md CHANGED
@@ -13,6 +13,39 @@ Greenmat follows Redcarpet's updates by merging the upstream changes.
13
13
  The version format is `MAJOR.MINOR.PATCH.FORK`.
14
14
  `MAJOR.MINOR.PATCH` is the same as the version of Redcarpet that Greenmat is based on. `FORK` is incremented on each release of Greenmat itself and reset to zero when any of `MAJOR.MINOR.PATCH` is bumped.
15
15
 
16
+ ## Development
17
+
18
+ ### Initial setup
19
+
20
+ Clone the Greenmat repository:
21
+
22
+ ```bash
23
+ $ git clone git@github.com:increments/greenmat.git
24
+ $ cd greenmat
25
+ ```
26
+
27
+ Set up git remote for Redcarpet as `upstream`:
28
+
29
+ ```bash
30
+ $ rake greenmat:setup_upstream
31
+ ```
32
+
33
+ ### Merging upstream changes
34
+
35
+ Run the following task to merge `upstream/master` branch into the current branch:
36
+
37
+ ```bash
38
+ $ rake greenmat:merge_upstream
39
+ ```
40
+
41
+ Note that this task does _not_ automatically commit the merge, so you need to commit the changes after checking each diff. Also it forces conflicting hunks to be auto-resolved cleanly by favoring upstream version.
42
+
43
+ If you want to merge a branch other than `upstream/master`, specify the name as `BRANCH` environment variable:
44
+
45
+ ```bash
46
+ $ rake greenmat:merge_upstream BRANCH=branch_name
47
+ ```
48
+
16
49
  ## Acknowledgment
17
50
 
18
51
  We appreciate Redcarpet project and the contributors for the great efforts!
@@ -1,7 +1,43 @@
1
1
  #!/usr/bin/env ruby
2
- lib_path = File.expand_path('../../lib', __FILE__)
3
- $:.unshift(lib_path)
2
+ # Usage: greenmat [--parse-<extension>...] [--render-<extension>...] [--smarty] [<file>...]
3
+ # Convert one or more Markdown files to HTML and write to standard output. With
4
+ # no <file> or when <file> is '-', read Markdown source text from standard input.
5
+ # With <extension>s, perform additional Markdown processing before writing output.
6
+ # With --smarty, use the SmartyHTML renderer
7
+ if ARGV.include?('--help') or ARGV.include?('-h')
8
+ File.read(__FILE__).split("\n").grep(/^# /).each do |line|
9
+ puts line[2..-1]
10
+ end
11
+ exit 0
12
+ end
4
13
 
5
- require 'greenmat/cli'
14
+ require 'greenmat'
6
15
 
7
- Greenmat::CLI.process(ARGV)
16
+ if ARGV.include?('--version') or ARGV.include?('-v')
17
+ puts "Greenmat #{Greenmat::VERSION}"
18
+ exit 0
19
+ end
20
+
21
+ root = File.expand_path('../../', __FILE__)
22
+ $:.unshift File.expand_path('lib', root)
23
+
24
+ render_extensions = {}
25
+ parse_extensions = {}
26
+ renderer = Greenmat::Render::HTML
27
+
28
+ ARGV.delete_if do |arg|
29
+ if arg =~ /^--render-([\w-]+)$/
30
+ arg = $1.gsub('-', '_')
31
+ render_extensions[arg.to_sym] = true
32
+ elsif arg =~ /^--parse-([\w-]+)$/
33
+ arg = $1.gsub('-', '_')
34
+ parse_extensions[arg.to_sym] = true
35
+ elsif arg == '--smarty'
36
+ renderer = Greenmat::Render::SmartyHTML
37
+ else
38
+ false
39
+ end
40
+ end
41
+
42
+ render = renderer.new(render_extensions)
43
+ STDOUT.write(Greenmat::Markdown.new(render, parse_extensions).render(ARGF.read))
@@ -94,14 +94,14 @@ bufnew(size_t unit)
94
94
 
95
95
  /* bufnullterm: NULL-termination of the string array */
96
96
  const char *
97
- bufcstr(const struct buf *buf)
97
+ bufcstr(struct buf *buf)
98
98
  {
99
99
  assert(buf && buf->unit);
100
100
 
101
101
  if (buf->size < buf->asize && buf->data[buf->size] == 0)
102
102
  return (char *)buf->data;
103
103
 
104
- if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == BUF_OK) {
104
+ if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {
105
105
  buf->data[buf->size] = 0;
106
106
  return (char *)buf->data;
107
107
  }
@@ -118,7 +118,7 @@ bufprintf(struct buf *buf, const char *fmt, ...)
118
118
 
119
119
  assert(buf && buf->unit);
120
120
 
121
- if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < BUF_OK)
121
+ if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0)
122
122
  return;
123
123
 
124
124
  va_start(ap, fmt);
@@ -136,7 +136,7 @@ bufprintf(struct buf *buf, const char *fmt, ...)
136
136
  }
137
137
 
138
138
  if ((size_t)n >= buf->asize - buf->size) {
139
- if (bufgrow(buf, buf->size + n + 1) < BUF_OK)
139
+ if (bufgrow(buf, buf->size + n + 1) < 0)
140
140
  return;
141
141
 
142
142
  va_start(ap, fmt);
@@ -156,7 +156,7 @@ bufput(struct buf *buf, const void *data, size_t len)
156
156
  {
157
157
  assert(buf && buf->unit);
158
158
 
159
- if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < BUF_OK)
159
+ if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)
160
160
  return;
161
161
 
162
162
  memcpy(buf->data + buf->size, data, len);
@@ -177,7 +177,7 @@ bufputc(struct buf *buf, int c)
177
177
  {
178
178
  assert(buf && buf->unit);
179
179
 
180
- if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < BUF_OK)
180
+ if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)
181
181
  return;
182
182
 
183
183
  buf->data[buf->size] = c;
@@ -55,7 +55,7 @@ int bufgrow(struct buf *, size_t);
55
55
  struct buf *bufnew(size_t) __attribute__ ((malloc));
56
56
 
57
57
  /* bufnullterm: NUL-termination of the string array (making a C-string) */
58
- const char *bufcstr(const struct buf *);
58
+ const char *bufcstr(struct buf *);
59
59
 
60
60
  /* bufprefix: compare the beginning of a buffer with a string */
61
61
  int bufprefix(const struct buf *buf, const char *prefix);
@@ -125,9 +125,13 @@ static VALUE rb_greenmat_md_render(VALUE self, VALUE text)
125
125
  if (NIL_P(text))
126
126
  return Qnil;
127
127
 
128
- struct rb_greenmat_rndr *renderer;
129
- Data_Get_Struct(rb_rndr, struct rb_greenmat_rndr, renderer);
130
- renderer->options.active_enc = rb_enc_get(text);
128
+ #ifdef HAVE_RUBY_ENCODING_H
129
+ {
130
+ struct rb_greenmat_rndr *renderer;
131
+ Data_Get_Struct(rb_rndr, struct rb_greenmat_rndr, renderer);
132
+ renderer->options.active_enc = rb_enc_get(text);
133
+ }
134
+ #endif
131
135
 
132
136
  /* initialize buffers */
133
137
  output_buf = bufnew(128);
@@ -472,30 +472,25 @@ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
472
472
  static VALUE rb_greenmat_htmltoc_init(int argc, VALUE *argv, VALUE self)
473
473
  {
474
474
  struct rb_greenmat_rndr *rndr;
475
- unsigned int render_flags = HTML_TOC;
476
- VALUE hash, nesting_level = Qnil;
475
+ int nesting_level = 6;
476
+ VALUE hash, key = Qnil;
477
477
 
478
478
  Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
479
479
 
480
480
  if (rb_scan_args(argc, argv, "01", &hash) == 1) {
481
481
  Check_Type(hash, T_HASH);
482
482
 
483
- /* escape_html */
484
- if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
485
- render_flags |= HTML_ESCAPE;
483
+ key = CSTR2SYM("nesting_level");
486
484
 
487
- /* Nesting level */
488
- nesting_level = rb_hash_aref(hash, CSTR2SYM("nesting_level"));
485
+ if (RTEST(rb_hash_aref(hash, key))) {
486
+ Check_Type(rb_hash_aref(hash, key), T_FIXNUM);
487
+ nesting_level = NUM2INT(rb_hash_aref(hash, key));
488
+ }
489
489
  }
490
490
 
491
- sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, render_flags);
491
+ sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, nesting_level);
492
492
  rb_greenmat__overload(self, rb_cRenderHTML_TOC);
493
493
 
494
- if (!(NIL_P(nesting_level)))
495
- rndr->options.html.toc_data.nesting_level = NUM2INT(nesting_level);
496
- else
497
- rndr->options.html.toc_data.nesting_level = 6;
498
-
499
494
  return Qnil;
500
495
  }
501
496
 
@@ -17,6 +17,7 @@
17
17
 
18
18
  #include "markdown.h"
19
19
  #include "html.h"
20
+ #include "ruby.h"
20
21
  #include <string.h>
21
22
  #include <stdlib.h>
22
23
  #include <stdio.h>
@@ -124,7 +125,7 @@ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, v
124
125
  if (lang && lang->size) {
125
126
  size_t i, cls;
126
127
  if (options->flags & HTML_PRETTIFY) {
127
- BUFPUTSL(ob, "<pre><code class=\"prettyprint lang-");
128
+ BUFPUTSL(ob, "<pre><code class=\"prettyprint ");
128
129
  cls++;
129
130
  } else {
130
131
  BUFPUTSL(ob, "<pre><code class=\"");
@@ -264,51 +265,17 @@ rndr_linebreak(struct buf *ob, void *opaque)
264
265
  return 1;
265
266
  }
266
267
 
267
- char *header_anchor(const struct buf *buffer)
268
+ char *header_anchor(struct buf *text)
268
269
  {
269
- size_t i, j, k, size = buffer->size;
270
+ VALUE str = rb_str_new2(bufcstr(text));
271
+ VALUE space_regex = rb_reg_new(" +", 2 /* length */, 0);
272
+ VALUE tags_regex = rb_reg_new("<\\/?[^>]*>", 10, 0);
270
273
 
271
- char text[size];
272
- strcpy(text, bufcstr(buffer));
274
+ VALUE heading = rb_funcall(str, rb_intern("gsub"), 2, space_regex, rb_str_new2("-"));
275
+ heading = rb_funcall(heading, rb_intern("gsub"), 2, tags_regex, rb_str_new2(""));
276
+ heading = rb_funcall(heading, rb_intern("downcase"), 0);
273
277
 
274
- char raw_string[size];
275
-
276
- /* Strip down the inline HTML markup if needed */
277
- if (strchr(text, '<') < strchr(text, '>')) {
278
- char* part = strtok(text, "<>");
279
-
280
- /* Once every two times, the yielded token is the
281
- content of a HTML tag so we don't need to copy it */
282
- for (k = 0; part != NULL; k++) {
283
- if (k == 0)
284
- strcpy(raw_string, part);
285
- else if (k % 2 == 0)
286
- strcat(raw_string, part);
287
-
288
- part = strtok(NULL, "<>");
289
- }
290
-
291
- size = strlen(raw_string);
292
- } else {
293
- strcpy(raw_string, text);
294
- }
295
-
296
- char* heading = malloc(size * sizeof(char));
297
-
298
- /* Dasherize the string removing extra white spaces
299
- and stripped chars */
300
- for (i = 0, j = 0; i < size; ++i, ++j) {
301
- while ((i+1) < size && STRIPPED_CHAR(raw_string[i]) && STRIPPED_CHAR(raw_string[i+1]))
302
- i++;
303
-
304
- if (STRIPPED_CHAR(raw_string[i]))
305
- heading[j] = '-';
306
- else
307
- heading[j] = tolower(raw_string[i]);
308
- }
309
-
310
- heading[j++] = '\0';
311
- return heading;
278
+ return StringValueCStr(heading);
312
279
  }
313
280
 
314
281
  static void
@@ -428,30 +395,15 @@ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
428
395
  static void
429
396
  rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
430
397
  {
431
- size_t org, size;
432
- struct html_renderopt *options = opaque;
433
-
434
- if (!text)
435
- return;
436
-
437
- size = text->size;
438
- while (size > 0 && text->data[size - 1] == '\n')
439
- size--;
440
-
441
- for (org = 0; org < size && text->data[org] == '\n'; ++org)
442
-
443
- if (org >= size)
444
- return;
445
-
446
- /* Remove style tags if the `:no_styles` option is enabled */
447
- if ((options->flags & HTML_SKIP_STYLE) != 0 &&
448
- sdhtml_is_tag(text->data, size, "style"))
449
- return;
450
-
451
- if (ob->size)
452
- bufputc(ob, '\n');
453
-
454
- bufput(ob, text->data + org, size - org);
398
+ size_t org, sz;
399
+ if (!text) return;
400
+ sz = text->size;
401
+ while (sz > 0 && text->data[sz - 1] == '\n') sz--;
402
+ org = 0;
403
+ while (org < sz && text->data[org] == '\n') org++;
404
+ if (org >= sz) return;
405
+ if (ob->size) bufputc(ob, '\n');
406
+ bufput(ob, text->data + org, sz - org);
455
407
  bufputc(ob, '\n');
456
408
  }
457
409
 
@@ -477,9 +429,7 @@ static int
477
429
  rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
478
430
  {
479
431
  struct html_renderopt *options = opaque;
480
-
481
- if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
482
- return 0;
432
+ if (!link || !link->size) return 0;
483
433
 
484
434
  BUFPUTSL(ob, "<img src=\"");
485
435
  escape_href(ob, link->data, link->size);
@@ -502,7 +452,7 @@ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
502
452
  struct html_renderopt *options = opaque;
503
453
 
504
454
  /* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
505
- It doesn't see if there are any valid tags, just escape all of them. */
455
+ * It doens't see if there are any valid tags, just escape all of them. */
506
456
  if((options->flags & HTML_ESCAPE) != 0) {
507
457
  escape_html(ob, text->data, text->size);
508
458
  return 1;
@@ -686,14 +636,7 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
686
636
  }
687
637
 
688
638
  bufprintf(ob, "<a href=\"#%s\">", header_anchor(text));
689
-
690
- if (text) {
691
- if (options->flags & HTML_ESCAPE)
692
- escape_html(ob, text->data, text->size);
693
- else
694
- bufput(ob, text->data, text->size);
695
- }
696
-
639
+ if (text) escape_html(ob, text->data, text->size);
697
640
  BUFPUTSL(ob, "</a>\n");
698
641
  }
699
642
  }
@@ -718,7 +661,7 @@ toc_finalize(struct buf *ob, void *opaque)
718
661
  }
719
662
 
720
663
  void
721
- sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
664
+ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, int nesting_level)
722
665
  {
723
666
  static const struct sd_callbacks cb_default = {
724
667
  NULL,
@@ -759,7 +702,8 @@ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *optio
759
702
  };
760
703
 
761
704
  memset(options, 0x0, sizeof(struct html_renderopt));
762
- options->flags = render_flags;
705
+ options->flags = HTML_TOC;
706
+ options->toc_data.nesting_level = nesting_level;
763
707
 
764
708
  memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
765
709
  }
@@ -65,17 +65,11 @@ extern void
65
65
  sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);
66
66
 
67
67
  extern void
68
- sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);
68
+ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, int nesting_level);
69
69
 
70
70
  extern void
71
71
  sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size);
72
72
 
73
- /* header method used internally in Greenmat */
74
- char *header_anchor(const struct buf *buffer);
75
-
76
- #define STRIPPED_CHARS " -&+$,/:;=?@\"#{}|^~[]`\\*()%.!'"
77
- #define STRIPPED_CHAR(x) (strchr(STRIPPED_CHARS, x) != NULL)
78
-
79
73
  #ifdef __cplusplus
80
74
  }
81
75
  #endif
@@ -1176,7 +1176,7 @@ char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset
1176
1176
  title_e--;
1177
1177
 
1178
1178
  /* checking for closing quote presence */
1179
- if (data[title_e] != '\'' && data[title_e] != '"') {
1179
+ if (data[title_e] != '\'' && data[title_e] != '"') {
1180
1180
  title_b = title_e = 0;
1181
1181
  link_e = i;
1182
1182
  }
@@ -1605,7 +1605,7 @@ prefix_oli(uint8_t *data, size_t size)
1605
1605
  return i + 2;
1606
1606
  }
1607
1607
 
1608
- /* prefix_uli • returns unordered list item prefix */
1608
+ /* prefix_uli • returns ordered list item prefix */
1609
1609
  static size_t
1610
1610
  prefix_uli(uint8_t *data, size_t size)
1611
1611
  {
@@ -1677,7 +1677,7 @@ parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
1677
1677
  static size_t
1678
1678
  parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);
1679
1679
 
1680
- /* parse_paragraph • handles parsing of a regular paragraph */
1680
+ /* parse_blockquote • handles parsing of a regular paragraph */
1681
1681
  static size_t
1682
1682
  parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
1683
1683
  {
@@ -2858,7 +2858,6 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2858
2858
 
2859
2859
  struct buf *text;
2860
2860
  size_t beg, end;
2861
- int in_fence = 0;
2862
2861
 
2863
2862
  text = bufnew(64);
2864
2863
  if (!text)
@@ -2870,8 +2869,7 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2870
2869
  /* reset the references table */
2871
2870
  memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
2872
2871
 
2873
- int footnotes_enabled = md->ext_flags & MKDEXT_FOOTNOTES;
2874
- int codefences_enabled = md->ext_flags & MKDEXT_FENCED_CODE;
2872
+ int footnotes_enabled = md->ext_flags & MKDEXT_FOOTNOTES;
2875
2873
 
2876
2874
  /* reset the footnotes lists */
2877
2875
  if (footnotes_enabled) {
@@ -2887,13 +2885,10 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2887
2885
  if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)
2888
2886
  beg += 3;
2889
2887
 
2890
- while (beg < doc_size) { /* iterating over lines */
2891
- if (codefences_enabled && (is_codefence(document + beg, doc_size - beg, NULL) != 0))
2892
- in_fence = !in_fence;
2893
-
2894
- if (!in_fence && footnotes_enabled && is_footnote(document, beg, doc_size, &end, &md->footnotes_found))
2888
+ while (beg < doc_size) /* iterating over lines */
2889
+ if (footnotes_enabled && is_footnote(document, beg, doc_size, &end, &md->footnotes_found))
2895
2890
  beg = end;
2896
- else if (!in_fence && is_ref(document, beg, doc_size, &end, md->refs))
2891
+ else if (is_ref(document, beg, doc_size, &end, md->refs))
2897
2892
  beg = end;
2898
2893
  else { /* skipping to the next line */
2899
2894
  end = beg;
@@ -2913,7 +2908,6 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2913
2908
 
2914
2909
  beg = end;
2915
2910
  }
2916
- }
2917
2911
 
2918
2912
  /* pre-grow the output buffer to minimize allocations */
2919
2913
  bufgrow(ob, MARKDOWN_GROW(text->size));
@@ -2924,7 +2918,7 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2924
2918
 
2925
2919
  if (text->size) {
2926
2920
  /* adding a final newline if not already present */
2927
- if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
2921
+ if (text->data[text->size - 1] != '\n' && text->data[text->size - 1] != '\r')
2928
2922
  bufputc(text, '\n');
2929
2923
 
2930
2924
  parse_block(ob, md, text->data, text->size);
@@ -2937,9 +2931,6 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
2937
2931
  if (md->cb.doc_footer)
2938
2932
  md->cb.doc_footer(ob, md->opaque);
2939
2933
 
2940
- /* Null-terminate the buffer */
2941
- bufcstr(ob);
2942
-
2943
2934
  /* clean-up */
2944
2935
  bufrelease(text);
2945
2936
  free_link_refs(md->refs);