New Upstream Release - ruby-rdiscount
Ready changes
Summary
Merged new upstream version: 2.2.7 (was: 2.1.8).
Resulting package
Built on 2023-02-26T06:12 (took 2m29s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-releases ruby-rdiscount-dbgsymapt install -t fresh-releases ruby-rdiscount
Diff
diff --git a/BUILDING b/BUILDING
index 29a72f3..759b653 100644
--- a/BUILDING
+++ b/BUILDING
@@ -20,7 +20,7 @@ the rake gather task to copy discount source files into the ext/ directory:
Submodule 'discount' (git://github.com/davidfstr/discount.git) registered for path 'discount'
Cloning into discount...
$ cd discount
- $ ./configure.sh --with-fenced-code --with-github-tags --with-dl=both
+ $ ./configure.sh
$ make # ensure it compiles
$ cd ..
$ rake gather
@@ -52,6 +52,9 @@ ext. This must be done manually. Here's a quick way to get the full list:
$ echo ext/*.c ext/*.h ext/*.rb ext/blocktags ext/VERSION | tr ' ' "\n" | sort
+(There is an old Rakefile target called "rdiscount.gemspec" that looks like it
+ is designed to perform an update of these files, however I haven't tested it.)
+
Build the RDiscount gem. If you get errors related to missing files
in ext, make sure you updated the gemspec correctly in the previous step.
diff --git a/README.markdown b/README.markdown
index 7d1beb8..6ab30fa 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,22 +1,22 @@
Discount Markdown Processor for Ruby
====================================
-[![Build Status](https://travis-ci.org/davidfstr/rdiscount.png)](https://travis-ci.org/davidfstr/rdiscount)
+[![Build Status](https://github.com/davidfstr/rdiscount/actions/workflows/main.yml/badge.svg)](https://github.com/davidfstr/rdiscount/actions/workflows/main.yml)
-Discount is an implementation of John Gruber's Markdown markup language in C. It
-implements all of the language described in [the markdown syntax document][1] and
+Discount is an implementation of John Gruber's Markdown markup language in C.
+It implements all of the language described in [the markdown syntax document][1] and
passes the [Markdown 1.0 test suite][2].
CODE: `git clone git://github.com/davidfstr/rdiscount.git`
-HOME: <http://dafoster.net/projects/rdiscount/>
-DOCS: <http://rdoc.info/github/davidfstr/rdiscount/master/RDiscount>
-BUGS: <http://github.com/davidfstr/rdiscount/issues>
+HOME: <https://dafoster.net/projects/rdiscount/>
+DOCS: <https://rdoc.info/github/davidfstr/rdiscount/master/RDiscount>
+BUGS: <https://github.com/davidfstr/rdiscount/issues>
-Discount was developed by [David Loren Parsons][3]. The Ruby extension
-is maintained by [David Foster][4].
+Discount was developed by [David Loren Parsons][3].
+The Ruby extension is maintained by [David Foster][4].
-[1]: http://daringfireball.net/projects/markdown/syntax
-[2]: http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip
-[3]: http://www.pell.portland.or.us/~orc
+[1]: https://daringfireball.net/projects/markdown/syntax
+[2]: https://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip
+[3]: https://www.pell.portland.or.us/~orc
[4]: https://github.com/davidfstr
INSTALL, HACKING
diff --git a/Rakefile b/Rakefile
index 0127296..6cc39a9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -53,7 +53,7 @@ task :man => 'man/rdiscount.1'
require 'rake/testtask'
Rake::TestTask.new('test:unit') do |t|
t.test_files = FileList['test/*_test.rb']
- t.ruby_opts += ['-rubygems'] if defined? Gem
+ t.ruby_opts += ['-r rubygems'] if defined? Gem
end
task 'test:unit' => [:build]
@@ -67,11 +67,20 @@ task 'test:conformance' => [:build] do |t|
print result
fail unless result.include? "; 0 failed."
end
+
+ # Allow to run this rake tasks multiple times
+ # https://medium.com/@shaneilske/invoke-a-rake-task-multiple-times-1bcb01dee9d9
+ ENV.delete("MARKDOWN_TEST_VER")
+ ENV.delete("RDISCOUNT_EXTENSIONS")
+ Rake::Task["test:conformance"].reenable
end
desc 'Run version 1.0 conformance suite'
task 'test:conformance:1.0' => [:build] do |t|
ENV['MARKDOWN_TEST_VER'] = '1.0'
+ # see https://github.com/Orc/discount/issues/261
+ # requires flags -f1.0,tabstop,nopants
+ ENV['RDISCOUNT_EXTENSIONS'] = "md1compat"
Rake::Task['test:conformance'].invoke
end
@@ -82,7 +91,7 @@ task 'test:conformance:1.0.3' => [:build] do |t|
end
desc 'Run unit and conformance tests'
-task :test => %w[test:unit test:conformance]
+task :test => %w[test:unit test:conformance:1.0 test:conformance:1.0.3]
desc 'Run benchmarks'
task :benchmark => :build do |t|
diff --git a/bin/rdiscount b/bin/rdiscount
index 3e3a638..8806789 100755
--- a/bin/rdiscount
+++ b/bin/rdiscount
@@ -1,7 +1,13 @@
#!/usr/bin/env ruby
-# Usage: rdiscount [<file>...]
-# Convert one or more Markdown files to HTML and write to standard output. With
+#
+# Usage: [env RDISCOUNT_EXTENSIONS='<extension>,...'] rdiscount [<file>...]
# no <file> or when <file> is '-', read Markdown source text from standard input.
+#
+# Convert one or more Markdown files to HTML and write to standard output.
+# With no <file> or when <file> is '-', read Markdown source text from
+# standard input. Optionally, the RDISCOUNT_EXTENSIONS environment variable
+# can specify a comma-separated list of extensions to enable in RDiscount.
+#
if ARGV.include?('--help')
File.read(__FILE__).split("\n").grep(/^# /).each do |line|
puts line[2..-1]
@@ -10,4 +16,5 @@ if ARGV.include?('--help')
end
require 'rdiscount'
-STDOUT.write(RDiscount.new(ARGF.read).to_html)
+extensions = ENV['RDISCOUNT_EXTENSIONS'].to_s.split(',').map{ |key| key.to_sym }
+STDOUT.write(RDiscount.new(ARGF.read, *extensions).to_html)
diff --git a/debian/changelog b/debian/changelog
index 7b99bce..97a8989 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+ruby-rdiscount (2.2.7-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Sun, 26 Feb 2023 06:10:46 -0000
+
ruby-rdiscount (2.1.8-2) unstable; urgency=medium
[ Cédric Boutillier ]
diff --git a/debian/patches/01_use-system-libmarkdown.patch b/debian/patches/01_use-system-libmarkdown.patch
index 7f6fb9c..7067298 100644
--- a/debian/patches/01_use-system-libmarkdown.patch
+++ b/debian/patches/01_use-system-libmarkdown.patch
@@ -5,9 +5,11 @@ Forwarded: not-needed
Author: Alessandro Ghedini <ghedo@debian.org>
Last-Update: 2014-04-12
---- a/Rakefile
-+++ b/Rakefile
-@@ -17,7 +17,7 @@
+Index: ruby-rdiscount.git/Rakefile
+===================================================================
+--- ruby-rdiscount.git.orig/Rakefile
++++ ruby-rdiscount.git/Rakefile
+@@ -17,7 +17,7 @@ file "ext/ruby-#{RUBYDIGEST}" do |f|
end
CLEAN.include "ext/ruby-*"
@@ -16,9 +18,11 @@ Last-Update: 2014-04-12
chdir('ext') { ruby 'extconf.rb' }
end
CLEAN.include 'ext/Makefile', 'ext/mkmf.log'
---- a/ext/extconf.rb
-+++ b/ext/extconf.rb
-@@ -2,6 +2,8 @@
+Index: ruby-rdiscount.git/ext/extconf.rb
+===================================================================
+--- ruby-rdiscount.git.orig/ext/extconf.rb
++++ ruby-rdiscount.git/ext/extconf.rb
+@@ -2,6 +2,8 @@ require 'mkmf'
dir_config('rdiscount')
diff --git a/ext/Csio.c b/ext/Csio.c
index 5706657..1b418e0 100644
--- a/ext/Csio.c
+++ b/ext/Csio.c
@@ -50,11 +50,11 @@ Cswrite(Cstring *iot, char *bfr, int size)
/* reparse() into a cstring
*/
void
-Csreparse(Cstring *iot, char *buf, int size, int flags)
+Csreparse(Cstring *iot, char *buf, int size, mkd_flag_t flags)
{
MMIOT f;
___mkd_initmmiot(&f, 0);
- ___mkd_reparse(buf, size, 0, &f, 0);
+ ___mkd_reparse(buf, size, flags, &f, 0);
___mkd_emblock(&f);
SUFFIX(*iot, T(f.out), S(f.out));
___mkd_freemmiot(&f, 0);
diff --git a/ext/VERSION b/ext/VERSION
index ebf14b4..541133c 100644
--- a/ext/VERSION
+++ b/ext/VERSION
@@ -1 +1 @@
-2.1.8
+2.2.7c
diff --git a/ext/amalloc.c b/ext/amalloc.c
index bb20ab6..922db77 100644
--- a/ext/amalloc.c
+++ b/ext/amalloc.c
@@ -5,10 +5,11 @@
#include <stdio.h>
#include <stdlib.h>
+#include "config.h"
#define MAGIC 0x1f2e3d4c
-struct alist { int magic, size; struct alist *next, *last; };
+struct alist { int magic, size, index; int *end; struct alist *next, *last; };
static struct alist list = { 0, 0, 0, 0 };
@@ -16,14 +17,32 @@ static int mallocs=0;
static int reallocs=0;
static int frees=0;
+static int index = 0;
+
+static void
+die(char *msg, int index)
+{
+ fprintf(stderr, msg, index);
+ abort();
+}
+
+
void *
-acalloc(int size, int count)
+acalloc(int count, int size)
{
- struct alist *ret = calloc(size + sizeof(struct alist), count);
+ struct alist *ret;
+
+ if ( size > 1 ) {
+ count *= size;
+ size = 1;
+ }
- if ( ret ) {
+ if ( ret = calloc(count + sizeof(struct alist) + sizeof(int), size) ) {
ret->magic = MAGIC;
ret->size = size * count;
+ ret->index = index ++;
+ ret->end = (int*)(count + (char*) (ret + 1));
+ *(ret->end) = ~MAGIC;
if ( list.next ) {
ret->next = list.next;
ret->last = &list;
@@ -54,6 +73,8 @@ afree(void *ptr)
struct alist *p2 = ((struct alist*)ptr)-1;
if ( p2->magic == MAGIC ) {
+ if ( ! (p2->end && *(p2->end) == ~MAGIC) )
+ die("goddam: corrupted memory block %d in free()!\n", p2->index);
p2->last->next = p2->next;
p2->next->last = p2->last;
++frees;
@@ -71,12 +92,16 @@ arealloc(void *ptr, int size)
struct alist save;
if ( p2->magic == MAGIC ) {
+ if ( ! (p2->end && *(p2->end) == ~MAGIC) )
+ die("goddam: corrupted memory block %d in realloc()!\n", p2->index);
save.next = p2->next;
save.last = p2->last;
- p2 = realloc(p2, sizeof(*p2) + size);
+ p2 = realloc(p2, sizeof(int) + sizeof(*p2) + size);
if ( p2 ) {
p2->size = size;
+ p2->end = (int*)(size + (char*) (p2 + 1));
+ *(p2->end) = ~MAGIC;
p2->next->last = p2;
p2->last->next = p2;
++reallocs;
diff --git a/ext/blocktags b/ext/blocktags
index e7bdf73..55ac4b4 100644
--- a/ext/blocktags
+++ b/ext/blocktags
@@ -17,6 +17,7 @@ static struct kw blocktags[] = {
{ "PRE", 3, 0 },
{ "WBR", 3, 0 },
{ "XMP", 3, 0 },
+ { "FORM", 4, 0 },
{ "NOBR", 4, 0 },
{ "STYLE", 5, 0 },
{ "TABLE", 5, 0 },
@@ -30,4 +31,4 @@ static struct kw blocktags[] = {
{ "BLOCKQUOTE", 10, 0 },
};
-#define NR_blocktags 29
+#define NR_blocktags 30
diff --git a/ext/config.h b/ext/config.h
index a009b20..4476ed5 100644
--- a/ext/config.h
+++ b/ext/config.h
@@ -7,15 +7,7 @@
/* tabs are four spaces */
#define TABSTOP 4
-/* enable fenced code blocks */
-#define WITH_FENCED_CODE 1
-
-/* include - and _ as acceptable characters in HTML tag names */
-#define WITH_GITHUB_TAGS 1
-
-/* enable discount and PHP Markdown Extra definition lists */
-#define USE_EXTRA_DL 1
-#define USE_DISCOUNT_DL 1
+#define DESTRUCTOR __attribute__((__destructor__))
/* these are setup by extconf.rb */
#if HAVE_RANDOM
diff --git a/ext/css.c b/ext/css.c
index 3eb30b3..fd9ede9 100644
--- a/ext/css.c
+++ b/ext/css.c
@@ -54,15 +54,13 @@ mkd_css(Document *d, char **res)
stylesheets(d->code, &f);
if ( (size = S(f)) > 0 ) {
+ /* null-terminate, then strdup() into a free()able memory
+ * chunk
+ */
EXPAND(f) = 0;
- /* HACK ALERT! HACK ALERT! HACK ALERT! */
- *res = T(f);/* we know that a T(Cstring) is a character pointer */
- /* so we can simply pick it up and carry it away, */
- /* leaving the husk of the Ctring on the stack */
- /* END HACK ALERT */
+ *res = strdup(T(f));
}
- else
- DELETE(f);
+ DELETE(f);
return size;
}
return EOF;
@@ -75,11 +73,13 @@ int
mkd_generatecss(Document *d, FILE *f)
{
char *res;
- int written = EOF, size = mkd_css(d, &res);
+ int written;
+ int size = mkd_css(d, &res);
- if ( size > 0 )
- written = fwrite(res, 1, size, f);
+ written = (size > 0) ? fwrite(res,1,size,f) : 0;
+
if ( res )
free(res);
+
return (written == size) ? size : EOF;
}
diff --git a/ext/cstring.h b/ext/cstring.h
index a03ba2c..f0b7410 100644
--- a/ext/cstring.h
+++ b/ext/cstring.h
@@ -72,6 +72,5 @@ typedef STRING(char) Cstring;
extern void Csputc(int, Cstring *);
extern int Csprintf(Cstring *, char *, ...);
extern int Cswrite(Cstring *, char *, int);
-extern void Csreparse(Cstring *, char *, int, int);
#endif/*_CSTRING_D*/
diff --git a/ext/docheader.c b/ext/docheader.c
index 073f6da..2bde634 100644
--- a/ext/docheader.c
+++ b/ext/docheader.c
@@ -17,7 +17,12 @@
static char *
onlyifset(Line *l)
{
- char *ret = T(l->text) + l->dle;
+ char *ret;
+
+ if ( l->dle < 0 || l->dle >= S(l->text) )
+ return 0;
+
+ ret = T(l->text) + l->dle;
return ret[0] ? ret : 0;
}
diff --git a/ext/dumptree.c b/ext/dumptree.c
index 0c0c01f..e3fbaf3 100644
--- a/ext/dumptree.c
+++ b/ext/dumptree.c
@@ -108,9 +108,18 @@ dumptree(Paragraph *pp, Stack *sp, FILE *f)
changepfx(sp, '`');
printpfx(sp, f);
- d = fprintf(f, "[%s", Pptype(pp->typ));
+ if ( pp->typ == HDR )
+ d += fprintf(f, "[h%d", pp->hnumber);
+ else
+ d = fprintf(f, "[%s", Pptype(pp->typ));
if ( pp->ident )
d += fprintf(f, " %s", pp->ident);
+
+#ifdef GITHUB_CHECKBOX
+ if ( pp->flags )
+ d += fprintf(f, " %x", pp->flags);
+#endif
+
if ( pp->align > 1 )
d += fprintf(f, ", <%s>", Begin[pp->align]);
@@ -134,7 +143,7 @@ dumptree(Paragraph *pp, Stack *sp, FILE *f)
int
-mkd_dump(Document *doc, FILE *out, int flags, char *title)
+mkd_dump(Document *doc, FILE *out, mkd_flag_t flags, char *title)
{
Stack stack;
@@ -145,7 +154,6 @@ mkd_dump(Document *doc, FILE *out, int flags, char *title)
dumptree(doc->code, &stack, out);
DELETE(stack);
- mkd_cleanup(doc);
return 0;
}
return -1;
diff --git a/ext/extconf.rb b/ext/extconf.rb
index 0eef0f5..442eef7 100644
--- a/ext/extconf.rb
+++ b/ext/extconf.rb
@@ -8,7 +8,7 @@ HAVE_RAND = have_func('rand')
HAVE_SRAND = have_func('srand')
def sized_int(size, types)
- types.find { |type| check_sizeof(type) == 4 } ||
+ types.find { |type| check_sizeof(type) == size } ||
abort("no int with size #{size}")
end
@@ -33,6 +33,7 @@ open(File.join(File.dirname(__FILE__), "ruby-config.h"), "wb") do |f|
end
$defs.push("-DVERSION=\\\"#{VERSION}\\\"")
+$defs.push("-DBRANCH=\"\"")
# Post XCode 5.1 the command line tools on OS X treat unrecognised
# command line options as errors and it's been seen that
@@ -42,4 +43,8 @@ if /darwin|mac os/.match RbConfig::CONFIG['host_os']
$DLDFLAGS.gsub!("-multiply_definedsuppress", "")
end
+if /mswin/.match RbConfig::CONFIG['host_os']
+ $defs.push("-Dinline=__inline")
+end
+
create_makefile('rdiscount')
diff --git a/ext/flags.c b/ext/flags.c
index cc1c589..78ff3dc 100644
--- a/ext/flags.c
+++ b/ext/flags.c
@@ -2,7 +2,7 @@
#include "markdown.h"
struct flagnames {
- DWORD flag;
+ mkd_flag_t flag;
char *name;
};
@@ -30,12 +30,20 @@ static struct flagnames flagnames[] = {
{ MKD_NODLIST, "!DLIST" },
{ MKD_EXTRA_FOOTNOTE, "FOOTNOTE" },
{ MKD_NOSTYLE, "!STYLE" },
+ { MKD_NODLDISCOUNT, "!DLDISCOUNT" },
+ { MKD_DLEXTRA, "DLEXTRA" },
+ { MKD_FENCEDCODE, "FENCEDCODE" },
+ { MKD_IDANCHOR, "IDANCHOR" },
+ { MKD_GITHUBTAGS, "GITHUBTAGS" },
+ { MKD_URLENCODEDANCHOR, "URLENCODEDANCHOR" },
+ { MKD_LATEX, "LATEX" },
+ { MKD_EXPLICITLIST, "EXPLICITLIST" },
};
#define NR(x) (sizeof x/sizeof x[0])
void
-mkd_flags_are(FILE *f, DWORD flags, int htmlplease)
+mkd_flags_are(FILE *f, mkd_flag_t flags, int htmlplease)
{
int i;
int not, set, even=1;
diff --git a/ext/generate.c b/ext/generate.c
index 2516a09..3899f18 100644
--- a/ext/generate.c
+++ b/ext/generate.c
@@ -56,16 +56,16 @@ peek(MMIOT *f, int i)
i += (f->isp-1);
- return (i >= 0) && (i < S(f->in)) ? T(f->in)[i] : EOF;
+ return (i >= 0) && (i < S(f->in)) ? (unsigned char)T(f->in)[i] : EOF;
}
/* pull a byte from the input buffer
*/
-static inline int
+static inline unsigned int
pull(MMIOT *f)
{
- return ( f->isp < S(f->in) ) ? T(f->in)[f->isp++] : EOF;
+ return ( f->isp < S(f->in) ) ? (unsigned char)T(f->in)[f->isp++] : EOF;
}
@@ -108,8 +108,10 @@ isthisnonword(MMIOT *f, int i)
/* return/set the current cursor position
+ * (when setting the current cursor position we also need to flush the
+ * last character written cache)
*/
-#define mmiotseek(f,x) (f->isp = x)
+#define mmiotseek(f,x) ((f->isp = x), (f->last = 0))
#define mmiottell(f) (f->isp)
@@ -129,7 +131,7 @@ static void
Qchar(int c, MMIOT *f)
{
block *cur;
-
+
if ( S(f->Q) == 0 ) {
cur = &EXPAND(f->Q);
memset(cur, 0, sizeof *cur);
@@ -139,7 +141,7 @@ Qchar(int c, MMIOT *f)
cur = &T(f->Q)[S(f->Q)-1];
EXPAND(cur->b_text) = c;
-
+
}
@@ -178,6 +180,16 @@ Qprintf(MMIOT *f, char *fmt, ...)
}
+/* Qanchor() prints out a suitable-for-id-tag version of a string
+ */
+static void
+Qanchor(struct line *p, MMIOT *f)
+{
+ mkd_string_to_anchor(T(p->text), S(p->text),
+ (mkd_sta_function_t)Qchar, f, 1, f);
+}
+
+
/* Qem()
*/
static void
@@ -197,13 +209,13 @@ Qem(MMIOT *f, char c, int count)
/* generate html from a markup fragment
*/
void
-___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc)
+___mkd_reparse(char *bfr, int size, mkd_flag_t flags, MMIOT *f, char *esc)
{
MMIOT sub;
struct escaped e;
___mkd_initmmiot(&sub, f->footnotes);
-
+
sub.flags = f->flags | flags;
sub.cb = f->cb;
sub.ref_prefix = f->ref_prefix;
@@ -219,11 +231,16 @@ ___mkd_reparse(char *bfr, int size, int flags, MMIOT *f, char *esc)
push(bfr, size, &sub);
pushc(0, &sub);
S(sub.in)--;
-
+
text(&sub);
___mkd_emblock(&sub);
-
+
Qwrite(T(sub.out), S(sub.out), f);
+ /* inherit the last character printed from the reparsed
+ * text; this way superscripts can work when they're
+ * applied to something embedded in a link
+ */
+ f->last = sub.last;
___mkd_freemmiot(&sub, f->footnotes);
}
@@ -263,7 +280,7 @@ puturl(char *s, int size, MMIOT *f, int display)
if ( !( ispunct(c) || isspace(c) ) )
Qchar('\\', f);
}
-
+
if ( c == '&' )
Qstring("&", f);
else if ( c == '<' )
@@ -431,7 +448,7 @@ linkybroket(MMIOT *f, int image, Footnote *p)
if ( good ) {
if ( peek(f, 1) == ')' )
pull(f);
-
+
___mkd_tidy(&p->link);
}
@@ -456,7 +473,7 @@ linkyurl(MMIOT *f, int image, Footnote *p)
if ( c == '<' ) {
pull(f);
- if ( !(f->flags & MKD_1_COMPAT) )
+ if ( !is_flag_set(f->flags, MKD_1_COMPAT) )
return linkybroket(f,image,p);
mayneedtotrim=1;
}
@@ -477,12 +494,12 @@ linkyurl(MMIOT *f, int image, Footnote *p)
}
if ( peek(f, 1) == ')' )
pull(f);
-
+
___mkd_tidy(&p->link);
-
+
if ( mayneedtotrim && (T(p->link)[S(p->link)-1] == '>') )
--S(p->link);
-
+
return 1;
}
@@ -578,12 +595,12 @@ static void
printlinkyref(MMIOT *f, linkytype *tag, char *link, int size)
{
char *edit;
-
- if ( f->flags & IS_LABEL )
+
+ if ( is_flag_set(f->flags, IS_LABEL) )
return;
-
+
Qstring(tag->link_pfx, f);
-
+
if ( tag->kind & IS_URL ) {
if ( f->cb && f->cb->e_url && (edit = (*f->cb->e_url)(link, size, f->cb->e_data)) ) {
puturl(edit, strlen(edit), f, 0);
@@ -623,7 +640,7 @@ extra_linky(MMIOT *f, Cstring text, Footnote *ref)
{
if ( ref->flags & REFERENCED )
return 0;
-
+
if ( f->flags & IS_LABEL )
___mkd_reparse(T(text), S(text), linkt.flags, f, 0);
else {
@@ -637,6 +654,32 @@ extra_linky(MMIOT *f, Cstring text, Footnote *ref)
} /* extra_linky */
+
+/* check a url (or url fragment to see that it begins with a known good
+ * protocol (or no protocol at all)
+ */
+static int
+safelink(Cstring link)
+{
+ char *p, *colon;
+
+ if ( T(link) == 0 ) /* no link; safe */
+ return 1;
+
+ p = T(link);
+ if ( (colon = memchr(p, ':', S(link))) == 0 )
+ return 1; /* no protocol specified: safe */
+
+ if ( !isalpha(*p) ) /* protocol/method is [alpha][alnum or '+.-'] */
+ return 1;
+ while ( ++p < colon )
+ if ( !(isalnum(*p) || *p == '.' || *p == '+' || *p == '-') )
+ return 1;
+
+ return isautoprefix(T(link), S(link));
+}
+
+
/* print out a linky (or fail if it's Not Allowed)
*/
static int
@@ -648,12 +691,10 @@ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
if ( image )
tag = &imaget;
else if ( tag = pseudo(ref->link) ) {
- if ( f->flags & (MKD_NO_EXT|MKD_SAFELINK) )
+ if ( is_flag_set(f->flags, MKD_NO_EXT) || is_flag_set(f->flags, MKD_SAFELINK) )
return 0;
}
- else if ( (f->flags & MKD_SAFELINK) && T(ref->link)
- && (T(ref->link)[0] != '/')
- && !isautoprefix(T(ref->link), S(ref->link)) )
+ else if ( is_flag_set(f->flags, MKD_SAFELINK) && !safelink(ref->link) )
/* if MKD_SAFELINK, only accept links that are local or
* a well-known protocol
*/
@@ -664,7 +705,7 @@ linkyformat(MMIOT *f, Cstring text, int image, Footnote *ref)
if ( f->flags & tag->flags )
return 0;
- if ( f->flags & IS_LABEL )
+ if ( is_flag_set(f->flags, IS_LABEL) )
___mkd_reparse(T(text), S(text), tag->flags, f, 0);
else if ( tag->link_pfx ) {
printlinkyref(f, tag, T(ref->link), S(ref->link));
@@ -700,7 +741,7 @@ linkylinky(int image, MMIOT *f)
int start = mmiottell(f);
Cstring name;
Footnote key, *ref;
-
+
int status = 0;
int extra_footnote = 0;
@@ -718,7 +759,7 @@ linkylinky(int image, MMIOT *f)
if ( isspace(peek(f,1)) )
pull(f);
-
+
if ( peek(f,1) == '[' ) {
pull(f); /* consume leading '[' */
goodlink = linkylabel(f, &key.tag);
@@ -728,12 +769,12 @@ linkylinky(int image, MMIOT *f)
* require a second []
*/
mmiotseek(f, implicit_mark);
- goodlink = !(f->flags & MKD_1_COMPAT);
+ goodlink = !is_flag_set(f->flags, MKD_1_COMPAT);
- if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' )
+ if ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && (!image) && S(name) && T(name)[0] == '^' )
extra_footnote = 1;
}
-
+
if ( goodlink ) {
if ( !S(key.tag) ) {
DELETE(key.tag);
@@ -777,7 +818,7 @@ cputc(int c, MMIOT *f)
}
}
-
+
/*
* convert an email address to a string of nonsense
*/
@@ -786,7 +827,7 @@ mangle(char *s, int len, MMIOT *f)
{
while ( len-- > 0 ) {
#if DEBIAN_GLITCH
- Qprintf(f, "d;", *((unsigned char*)(s++)) );
+ Qprintf(f, "&#%02d;", *((unsigned char*)(s++)) );
#else
Qstring("&#", f);
Qprintf(f, COINTOSS() ? "x%02x;" : "%02d;", *((unsigned char*)(s++)) );
@@ -819,7 +860,7 @@ matchticks(MMIOT *f, int tickchar, int ticks, int *endticks)
{
int size, count, c;
int subsize=0, subtick=0;
-
+
*endticks = ticks;
for (size = 0; (c=peek(f,size+ticks)) != EOF; size ++) {
if ( (c == tickchar) && ( count = nrticks(size+ticks,tickchar,f)) ) {
@@ -852,7 +893,7 @@ code(MMIOT *f, char *s, int length)
int i,c;
for ( i=0; i < length; i++ )
- if ( (c = s[i]) == MKD_EOLN) /* ^C: expand back to 2 spaces */
+ if ( (c = s[i]) == MKD_EOLN) /* expand back to 2 spaces */
Qstring(" ", f);
else if ( c == '\\' && (i < length-1) && escaped(f, s[i+1]) )
cputc(s[++i], f);
@@ -860,7 +901,6 @@ code(MMIOT *f, char *s, int length)
cputc(c, f);
} /* code */
-
/* delspan() -- write out a chunk of text, blocking with <del>...</del>
*/
static void
@@ -871,6 +911,41 @@ delspan(MMIOT *f, int size)
Qstring("</del>", f);
}
+#ifdef TYPORA
+/* subspan() -- write out a chunk of text, blocking with <sub>...</sub>
+ */
+static void
+subspan(MMIOT *f, int size)
+{
+ Qstring("<sub>", f);
+ ___mkd_reparse(cursor(f)-1, size, 0, f, 0);
+ Qstring("</sub>", f);
+}
+
+
+/* supspan() -- write out a chunk of text, blocking with <sup>...</sup>
+ */
+static void
+supspan(MMIOT *f, int size)
+{
+ Qstring("<sup>", f);
+ ___mkd_reparse(cursor(f)-1, size, 0, f, 0);
+ Qstring("</sup>", f);
+}
+
+
+/* highlightspan() -- write out a chunk of text, blocking with <mark>...</mark>
+ */
+static void
+highlightspan(MMIOT *f, int size)
+{
+ Qstring("<mark>", f);
+ ___mkd_reparse(cursor(f)-1, size, 0, f, 0);
+ Qstring("</mark>", f);
+}
+#endif
+
+
/* codespan() -- write out a chunk of text as code, trimming one
* space off the front and/or back as appropriate.
@@ -882,7 +957,7 @@ codespan(MMIOT *f, int size)
if ( size > 1 && peek(f, size-1) == ' ' ) --size;
if ( peek(f,i) == ' ' ) ++i, --size;
-
+
Qstring("<code>", f);
code(f, cursor(f)+(i-1), size);
Qstring("</code>", f);
@@ -897,12 +972,12 @@ forbidden_tag(MMIOT *f)
{
int c = toupper(peek(f, 1));
- if ( f->flags & MKD_NOHTML )
+ if ( is_flag_set(f->flags, MKD_NOHTML) )
return 1;
- if ( c == 'A' && (f->flags & MKD_NOLINKS) && !isthisalnum(f,2) )
+ if ( c == 'A' && is_flag_set(f->flags, MKD_NOLINKS) && !isthisalnum(f,2) )
return 1;
- if ( c == 'I' && (f->flags & MKD_NOIMAGE)
+ if ( c == 'I' && is_flag_set(f->flags, MKD_NOIMAGE)
&& strncasecmp(cursor(f)+1, "MG", 2) == 0
&& !isthisalnum(f,4) )
return 1;
@@ -919,17 +994,17 @@ static int
maybe_address(char *p, int size)
{
int ok = 0;
-
+
for ( ;size && (isalnum(*p) || strchr("._-+*", *p)); ++p, --size)
;
if ( ! (size && *p == '@') )
return 0;
-
+
--size, ++p;
if ( size && *p == '.' ) return 0;
-
+
for ( ;size && (isalnum(*p) || strchr("._-+", *p)); ++p, --size )
if ( *p == '.' && size > 1 ) ok = 1;
@@ -948,8 +1023,8 @@ process_possible_link(MMIOT *f, int size)
int address= 0;
int mailto = 0;
char *text = cursor(f);
-
- if ( f->flags & MKD_NOLINKS ) return 0;
+
+ if ( is_flag_set(f->flags, MKD_NOLINKS) ) return 0;
if ( (size > 7) && strncasecmp(text, "mailto:", 7) == 0 ) {
/* if it says it's a mailto, it's a mailto -- who am
@@ -984,6 +1059,17 @@ process_possible_link(MMIOT *f, int size)
} /* process_possible_link */
+/*
+ * check if a character is one of the things the reference implementation considers valid for starting
+ * a html(ish) tag
+ */
+static inline int
+is_a_strict_tag_prefix(int c)
+{
+ return isalpha(c) || (c == '/') || (c == '!') || (c == '$') || (c == '?');
+}
+
+
/* a < may be just a regular character, the start of an embedded html
* tag, or the start of an <automatic link>. If it's an automatic
* link, we also need to know if it's an email address because if it
@@ -993,56 +1079,56 @@ process_possible_link(MMIOT *f, int size)
static int
maybe_tag_or_link(MMIOT *f)
{
- int c, size;
- int maybetag = 1;
+ int c, size=0;
- if ( f->flags & MKD_TAGTEXT )
+ if ( is_flag_set(f->flags, MKD_TAGTEXT) )
return 0;
- for ( size=0; (c = peek(f, size+1)) != '>'; size++) {
- if ( c == EOF )
- return 0;
- else if ( c == '\\' ) {
- maybetag=0;
- if ( peek(f, size+2) != EOF )
- size++;
+ c = peek(f, 1);
+
+
+ if ( is_a_strict_tag_prefix(c) ) {
+ /* By decree of Markdown.pl *this is a tag* and we want to absorb everything up
+ * to the next '>', unless interrupted by another '<' OR a '`', at which point
+ * we kick it back to the caller as plain old text.
+ */
+ size=1;
+ while ( (c=peek(f,size+1)) != '>' ) {
+ if ( c == EOF || c == '<' )
+ return 0;
+ if ( is_flag_set(f->flags, MKD_STRICT) ) {
+ if ( c == '`' )
+ return 0;
+ }
+ size++;
}
- else if ( isspace(c) )
- break;
-#if WITH_GITHUB_TAGS
- else if ( ! (c == '/' || c == '-' || c == '_' || isalnum(c) ) )
-#else
- else if ( ! (c == '/' || isalnum(c) ) )
-#endif
- maybetag=0;
}
- if ( size ) {
- if ( maybetag || (size >= 3 && strncmp(cursor(f), "!--", 3) == 0) ) {
+ if ( size > 0 ) {
+ if ( process_possible_link(f, size) ) {
+ shift(f, size+1);
+ return 1;
+ }
+ else {
+ int i;
- /* It is not a html tag unless we find the closing '>' in
- * the same block.
- */
- while ( (c = peek(f, size+1)) != '>' )
- if ( c == EOF )
- return 0;
- else
- size++;
-
if ( forbidden_tag(f) )
return 0;
- Qchar('<', f);
- while ( ((c = peek(f, 1)) != EOF) && (c != '>') )
- Qchar(pull(f), f);
- return 1;
- }
- else if ( !isspace(c) && process_possible_link(f, size) ) {
+ for ( i=0; i <= size+1; i++ ) {
+ c = peek(f,i);
+
+ if ( (c == '&') && (i > 0) )
+ Qstring("&", f);
+ else
+ Qchar(c, f);
+ }
+
shift(f, size+1);
return 1;
}
}
-
+
return 0;
}
@@ -1064,6 +1150,8 @@ maybe_autolink(MMIOT *f)
if ( peek(f, size+2) != EOF )
++size;
}
+ else if ( c & 0x80 ) /* HACK: ignore utf-8 extended characters */
+ continue;
else if ( isspace(c) || strchr("'\"()[]{}<>`", c) || c == MKD_EOLN )
break;
}
@@ -1164,7 +1252,9 @@ smartypants(int c, int *flags, MMIOT *f)
{
int i;
- if ( f->flags & (MKD_NOPANTS|MKD_TAGTEXT|IS_LABEL) )
+ if ( is_flag_set(f->flags, MKD_NOPANTS)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || is_flag_set(f->flags, IS_LABEL) )
return 0;
for ( i=0; i < NRSMART; i++)
@@ -1208,6 +1298,27 @@ smartypants(int c, int *flags, MMIOT *f)
} /* smartypants */
+/* process latex with arbitrary 2-character ( $$ .. $$, \[ .. \], \( .. \)
+ * delimiters
+ */
+static int
+mathhandler(MMIOT *f, int e1, int e2)
+{
+ int i = 0;
+
+ while(peek(f, ++i) != EOF) {
+ if (peek(f, i) == e1 && peek(f, i+1) == e2) {
+ cputc(peek(f,-1), f);
+ cputc(peek(f, 0), f);
+ while ( i-- > -1 )
+ cputc(pull(f), f);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
/* process a body of text encased in some sort of tick marks. If it
* works, generate the output and return 1, otherwise just return 0 and
* let the caller figure it out.
@@ -1235,7 +1346,7 @@ tickhandler(MMIOT *f, int tickchar, int minticks, int allow_space, spanhandler s
return 0;
}
-#define tag_text(f) (f->flags & MKD_TAGTEXT)
+#define tag_text(f) is_flag_set(f->flags, MKD_TAGTEXT)
static void
@@ -1246,7 +1357,7 @@ text(MMIOT *f)
int smartyflags = 0;
while (1) {
- if ( (f->flags & MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) )
+ if ( is_flag_set(f->flags, MKD_AUTOLINK) && isalpha(peek(f,1)) && !tag_text(f) )
maybe_autolink(f);
c = pull(f);
@@ -1274,7 +1385,7 @@ text(MMIOT *f)
else
Qchar(c, f);
break;
-
+
case '!': if ( peek(f,1) == '[' ) {
pull(f);
if ( tag_text(f) || !linkylinky(1, f) )
@@ -1283,13 +1394,35 @@ text(MMIOT *f)
else
Qchar(c, f);
break;
+
case '[': if ( tag_text(f) || !linkylinky(0, f) )
Qchar(c, f);
break;
+
+#ifdef TYPORA
+ case '=': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || ! tickhandler(f,c,2,0, highlightspan))
+ Qchar(c, f);
+ break;
+
+ /* A^B^ -> A<sup>B</sup> */
+ case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || ! tickhandler(f,c,1,0, supspan))
+ Qchar(c, f);
+ break;
+#else /* !TYPORA */
/* A^B -> A<sup>B</sup> */
- case '^': if ( (f->flags & (MKD_NOSUPERSCRIPT|MKD_STRICT|MKD_TAGTEXT))
- || (isthisnonword(f,-1) && peek(f,-1) != ')')
- || isthisspace(f,1) )
+ case '^': if ( is_flag_set(f->flags, MKD_NOSUPERSCRIPT)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || (f->last == 0)
+ || ((ispunct(f->last) || isspace(f->last))
+ && f->last != ')')
+ || isthisspace(f,1) )
Qchar(c,f);
else {
char *sup = cursor(f);
@@ -1320,11 +1453,11 @@ text(MMIOT *f)
Qstring("</sup>", f);
}
break;
+#endif /* TYPORA */
case '_':
/* Underscores don't count if they're in the middle of a word */
- if ( !(f->flags & (MKD_NORELAXED|MKD_STRICT))
- && isthisalnum(f,-1)
- && isthisalnum(f,1) ) {
+ if ( !(is_flag_set(f->flags, MKD_NORELAXED) || is_flag_set(f->flags, MKD_STRICT))
+ && isthisalnum(f,-1) && isthisalnum(f,1) ) {
Qchar(c, f);
break;
}
@@ -1344,8 +1477,17 @@ text(MMIOT *f)
Qem(f,c,rep);
}
break;
-
- case '~': if ( (f->flags & (MKD_NOSTRIKETHROUGH|MKD_TAGTEXT|MKD_STRICT)) || ! tickhandler(f,c,2,0, delspan) )
+
+#ifdef TYPORA
+#define ticktick(f,c) (tickhandler(f,c,2,0,delspan) || tickhandler(f,c,1,0,subspan))
+#else
+#define ticktick(f,c) tickhandler(f,c,2,0,delspan)
+#endif
+
+ case '~': if ( is_flag_set(f->flags, MKD_NOSTRIKETHROUGH)
+ || is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_TAGTEXT)
+ || !ticktick(f,c) )
Qchar(c, f);
break;
@@ -1365,28 +1507,35 @@ text(MMIOT *f)
Qchar('\\', f);
shift(f, -1);
}
-
+
break;
- case '^': if ( f->flags & (MKD_STRICT|MKD_NOSUPERSCRIPT) ) {
+ case '^': if ( is_flag_set(f->flags, MKD_STRICT)
+ || is_flag_set(f->flags, MKD_NOSUPERSCRIPT) ) {
Qchar('\\', f);
shift(f,-1);
break;
}
Qchar(c, f);
break;
-
+
case ':': case '|':
- if ( f->flags & MKD_NOTABLES ) {
+ if ( is_flag_set(f->flags, MKD_NOTABLES) ) {
Qchar('\\', f);
shift(f,-1);
break;
}
Qchar(c, f);
break;
-
+
case EOF: Qchar('\\', f);
break;
-
+
+ case '[':
+ case '(': if ( is_flag_set(f->flags, MKD_LATEX)
+ && mathhandler(f, '\\', (c =='(')?')':']') )
+ break;
+ /* else fall through to default */
+
default: if ( escaped(f,c) ||
strchr(">#.-+{}]![*_\\()`", c) )
Qchar(c, f);
@@ -1398,8 +1547,12 @@ text(MMIOT *f)
}
break;
- case '<': if ( !maybe_tag_or_link(f) )
- Qstring("<", f);
+ case '<': if ( !maybe_tag_or_link(f) ) {
+ if ( is_flag_set(f->flags, MKD_STRICT) && is_a_strict_tag_prefix(peek(f,1)) )
+ Qchar(c, f);
+ else
+ Qstring("<", f);
+ }
break;
case '&': j = (peek(f,1) == '#' ) ? 2 : 1;
@@ -1412,7 +1565,16 @@ text(MMIOT *f)
Qchar(c, f);
break;
- default: Qchar(c, f);
+ case '$': if ( is_flag_set(f->flags, MKD_LATEX) && (peek(f, 1) == '$') ) {
+ pull(f);
+ if ( mathhandler(f, '$', '$') )
+ break;
+ Qchar('$', f);
+ }
+ /* fall through to default */
+
+ default: f->last = c;
+ Qchar(c, f);
break;
}
}
@@ -1426,26 +1588,22 @@ text(MMIOT *f)
static void
printheader(Paragraph *pp, MMIOT *f)
{
-#if WITH_ID_ANCHOR
- Qprintf(f, "<h%d", pp->hnumber);
- if ( f->flags & MKD_TOC ) {
- Qstring(" id=\"", f);
- mkd_string_to_anchor(T(pp->text->text),
- S(pp->text->text),
- (mkd_sta_function_t)Qchar, f, 1);
- Qchar('"', f);
- }
- Qchar('>', f);
-#else
- if ( f->flags & MKD_TOC ) {
- Qstring("<a name=\"", f);
- mkd_string_to_anchor(T(pp->text->text),
- S(pp->text->text),
- (mkd_sta_function_t)Qchar, f, 1);
- Qstring("\"></a>\n", f);
+ if ( is_flag_set(f->flags, MKD_IDANCHOR) ) {
+ Qprintf(f, "<h%d", pp->hnumber);
+ if ( is_flag_set(f->flags, MKD_TOC) ) {
+ Qstring(" id=\"", f);
+ Qanchor(pp->text, f);
+ Qchar('"', f);
+ }
+ Qchar('>', f);
+ } else {
+ if ( is_flag_set(f->flags, MKD_TOC) ) {
+ Qstring("<a name=\"", f);
+ Qanchor(pp->text, f);
+ Qstring("\"></a>\n", f);
+ }
+ Qprintf(f, "<h%d>", pp->hnumber);
}
- Qprintf(f, "<h%d>", pp->hnumber);
-#endif
push(T(pp->text->text), S(pp->text->text), f);
text(f);
Qprintf(f, "</h%d>", pp->hnumber);
@@ -1471,7 +1629,7 @@ splat(Line *p, char *block, Istring align, int force, MMIOT *f)
___mkd_tidy(&p->text);
if ( T(p->text)[S(p->text)-1] == '|' )
--S(p->text);
-
+
Qstring("<tr>\n", f);
while ( idx < S(p->text) ) {
first = idx;
@@ -1532,7 +1690,7 @@ printtable(Paragraph *pp, MMIOT *f)
for (p=T(dash->text), start=dash->dle; start < S(dash->text); ) {
char first, last;
int end;
-
+
last=first=0;
for (end=start ; (end < S(dash->text)) && p[end] != '|'; ++ end ) {
if ( p[end] == '\\' )
@@ -1574,9 +1732,10 @@ printtable(Paragraph *pp, MMIOT *f)
static int
printblock(Paragraph *pp, MMIOT *f)
{
- Line *t = pp->text;
static char *Begin[] = { "", "<p>", "<p style=\"text-align:center;\">" };
static char *End[] = { "", "</p>","</p>" };
+ Line *t = pp->text;
+ int align = pp->align;
while (t) {
if ( S(t->text) ) {
@@ -1596,9 +1755,9 @@ printblock(Paragraph *pp, MMIOT *f)
}
t = t->next;
}
- Qstring(Begin[pp->align], f);
+ Qstring(Begin[align], f);
text(f);
- Qstring(End[pp->align], f);
+ Qstring(End[align], f);
return 1;
}
@@ -1608,8 +1767,44 @@ printcode(Line *t, char *lang, MMIOT *f)
{
int blanks;
+ if ( f->cb->e_codefmt ) {
+ /* external code block formatter; copy the text into a buffer,
+ * call the formatter to style it, then dump that styled text
+ * directly to the queue
+ */
+ char *text;
+ char *fmt;
+ int size, copy_p;
+ Line *p;
+
+ for (size=0, p = t; p; p = p->next )
+ size += 1+S(p->text);
+
+ text = malloc(1+size);
+
+ for ( copy_p = 0; t ; t = t->next ) {
+ memcpy(text+copy_p, T(t->text), S(t->text));
+ copy_p += S(t->text);
+ text[copy_p++] = '\n';
+ }
+ text[copy_p] = 0;
+
+ fmt = (*(f->cb->e_codefmt))(text, copy_p, (lang && lang[0]) ? lang : 0);
+ free(text);
+
+ if ( fmt ) {
+ Qwrite(fmt, strlen(fmt), f);
+ if ( f->cb->e_free )
+ (*(f->cb->e_free))(fmt, f->cb->e_data);
+ return;
+ }
+ /* otherwise the external formatter failed and we need to
+ * fall back to the traditional codeblock format
+ */
+ }
+
Qstring("<pre><code", f);
- if (lang) {
+ if (lang && lang[0]) {
Qstring(" class=\"", f);
Qstring(lang, f);
Qstring("\"", f);
@@ -1634,7 +1829,7 @@ static void
printhtml(Line *t, MMIOT *f)
{
int blanks;
-
+
for ( blanks=0; t ; t = t->next )
if ( S(t->text) ) {
for ( ; blanks; --blanks )
@@ -1649,17 +1844,57 @@ printhtml(Line *t, MMIOT *f)
static void
-htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
+htmlify_paragraphs(Paragraph *p, MMIOT *f)
{
___mkd_emblock(f);
- if ( block )
- Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
- ___mkd_emblock(f);
while (( p = display(p, f) )) {
___mkd_emblock(f);
Qstring("\n\n", f);
}
+}
+
+
+#ifdef GITHUB_CHECKBOX
+static void
+li_htmlify(Paragraph *p, char *arguments, mkd_flag_t flags, MMIOT *f)
+{
+ ___mkd_emblock(f);
+
+ Qprintf(f, "<li");
+ if ( arguments )
+ Qprintf(f, " %s", arguments);
+ if ( flags & GITHUB_CHECK )
+ Qprintf(f, " class=\"github_checkbox\"");
+ Qprintf(f, ">");
+#if CHECKBOX_AS_INPUT
+ if ( flags & GITHUB_CHECK ) {
+ Qprintf(f, "<input disabled=\"\" type=\"checkbox\"");
+ if ( flags & IS_CHECKED )
+ Qprintf(f, " checked=\"checked\"");
+ Qprintf(f, "/>");
+ }
+#else
+ if ( flags & GITHUB_CHECK )
+ Qprintf(f, flags & IS_CHECKED ? "☑" : "☐");
+#endif
+
+ htmlify_paragraphs(p, f);
+
+ Qprintf(f, "</li>");
+ ___mkd_emblock(f);
+}
+#endif
+
+
+static void
+htmlify(Paragraph *p, char *block, char *arguments, MMIOT *f)
+{
+ ___mkd_emblock(f);
+ if ( block )
+ Qprintf(f, arguments ? "<%s %s>" : "<%s>", block, arguments);
+
+ htmlify_paragraphs(p, f);
if ( block )
Qprintf(f, "</%s>", block);
@@ -1701,7 +1936,11 @@ listdisplay(int typ, Paragraph *p, MMIOT* f)
Qprintf(f, ">\n");
for ( ; p ; p = p->next ) {
+#ifdef GITHUB_CHECKBOX
+ li_htmlify(p->down, p->ident, p->flags, f);
+#else
htmlify(p->down, "li", p->ident, f);
+#endif
Qchar('\n', f);
}
@@ -1716,7 +1955,7 @@ static Paragraph*
display(Paragraph *p, MMIOT *f)
{
if ( !p ) return 0;
-
+
switch ( p->typ ) {
case STYLE:
case WHITESPACE:
@@ -1725,15 +1964,15 @@ display(Paragraph *p, MMIOT *f)
case HTML:
printhtml(p->text, f);
break;
-
+
case CODE:
printcode(p->text, p->lang, f);
break;
-
+
case QUOTE:
htmlify(p->down, p->ident ? "div" : "blockquote", p->ident, f);
break;
-
+
case UL:
case OL:
case AL:
@@ -1759,7 +1998,7 @@ display(Paragraph *p, MMIOT *f)
case SOURCE:
htmlify(p->down, 0, 0, f);
break;
-
+
default:
printblock(p, f);
break;
@@ -1780,17 +2019,17 @@ mkd_extra_footnotes(MMIOT *m)
return;
Csprintf(&m->out, "\n<div class=\"footnotes\">\n<hr/>\n<ol>\n");
-
+
for ( i=1; i <= m->footnotes->reference; i++ ) {
for ( j=0; j < S(m->footnotes->note); j++ ) {
t = &T(m->footnotes->note)[j];
if ( (t->refnumber == i) && (t->flags & REFERENCED) ) {
- Csprintf(&m->out, "<li id=\"%s:%d\">\n<p>",
+ Csprintf(&m->out, "<li id=\"%s:%d\">\n",
p_or_nothing(m), t->refnumber);
- Csreparse(&m->out, T(t->title), S(t->title), 0);
+ htmlify(t->text, 0, 0, m);
Csprintf(&m->out, "<a href=\"#%sref:%d\" rev=\"footnote\">↩</a>",
p_or_nothing(m), t->refnumber);
- Csprintf(&m->out, "</p></li>\n");
+ Csprintf(&m->out, "</li>\n");
}
}
}
@@ -1805,23 +2044,26 @@ int
mkd_document(Document *p, char **res)
{
int size;
-
+
if ( p && p->compiled ) {
if ( ! p->html ) {
htmlify(p->code, 0, 0, p->ctx);
- if ( p->ctx->flags & MKD_EXTRA_FOOTNOTE )
+ if ( is_flag_set(p->ctx->flags, MKD_EXTRA_FOOTNOTE) )
mkd_extra_footnotes(p->ctx);
p->html = 1;
+ size = S(p->ctx->out);
+
+ if ( (size == 0) || T(p->ctx->out)[size-1] ) {
+ /* Add a null byte at the end of the generated html,
+ * but pretend it doesn't exist.
+ */
+ EXPAND(p->ctx->out) = 0;
+ --S(p->ctx->out);
+ }
}
- size = S(p->ctx->out);
-
- if ( (size == 0) || T(p->ctx->out)[size-1] )
- EXPAND(p->ctx->out) = 0;
-
*res = T(p->ctx->out);
- return size;
+ return S(p->ctx->out);
}
return EOF;
}
-
diff --git a/ext/gethopt.c b/ext/gethopt.c
new file mode 100644
index 0000000..4c6e4ce
--- /dev/null
+++ b/ext/gethopt.c
@@ -0,0 +1,286 @@
+/*
+ * gehopt; options processing with both single-character and whole-word
+ * options both introduced with -
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "gethopt.h"
+
+
+void
+hoptset(ctx, argc, argv)
+struct h_context *ctx;
+int argc;
+char **argv;
+{
+ memset(ctx, 0, sizeof *ctx);
+ ctx->argc = argc;
+ ctx->argv = argv;
+ ctx->optind = 1;
+}
+
+
+char *
+hoptarg(ctx)
+struct h_context *ctx;
+{
+ return ctx->optarg;
+}
+
+int
+hoptind(ctx)
+struct h_context *ctx;
+{
+ return ctx->optind;
+}
+
+char
+hoptopt(ctx)
+struct h_context *ctx;
+{
+ return ctx->optopt;
+}
+
+
+int
+hopterr(ctx,val)
+struct h_context *ctx;
+{
+ int old = ctx->opterr;
+
+ ctx->opterr = !!val;
+ return old;
+}
+
+
+struct h_opt *
+gethopt(ctx, opts, nropts)
+struct h_context *ctx;
+struct h_opt *opts;
+int nropts;
+{
+ int i;
+ int dashes;
+
+
+ if ( (ctx == 0) || ctx->optend || (ctx->optind >= ctx->argc) )
+ return 0;
+
+ ctx->optarg = 0;
+ ctx->optopt = 0;
+
+ if ( ctx->optchar == 0) {
+ /* check for leading -
+ */
+ if ( ctx->argv[ctx->optind][0] != '-' ) {
+ /* out of arguments */
+ ctx->optend = 1;
+ return 0;
+ }
+
+ if ( ctx->argv[ctx->optind][1] == 0
+ || strcmp(ctx->argv[ctx->optind], "--") == 0 ) {
+ /* option list finishes with - or -- token
+ */
+ ctx->optend = 1;
+ ctx->optind++;
+ return 0;
+ }
+
+ dashes = 1;
+ if ( ctx->argv[ctx->optind][dashes] == '-' ) {
+ /* support GNU-style long option double-dash prefix
+ * (if gethopt is passed an unknown option with a double-dash
+ * prefix, it won't match a word and then the second dash
+ * will be scanned as if it was a regular old single-character
+ * option.)
+ */
+ dashes = 2;
+ }
+
+ for ( i=0; i < nropts; i++ ) {
+ if ( ! opts[i].optword )
+ continue;
+
+ if (strcmp(opts[i].optword, dashes+(ctx->argv[ctx->optind]) ) == 0 ) {
+ if ( opts[i].opthasarg ) {
+ if ( ctx->argc > ctx->optind ) {
+ ctx->optarg = ctx->argv[ctx->optind+1];
+ ctx->optind += 2;
+ }
+ else {
+ /* word argument with required arg at end of
+ *command line
+ */
+ if ( ctx->opterr )
+ fprintf(stderr,
+ "%s: option requires an argument -- %s\n",
+ ctx->argv[0], opts[i].optword);
+ ctx->optind ++;
+ return HOPTERR;
+ }
+ }
+ else {
+ ctx->optind ++;
+ }
+ return &opts[i];
+ }
+ }
+ ctx->optchar = 1;
+ }
+
+ ctx->optopt = ctx->argv[ctx->optind][ctx->optchar++];
+
+ if ( !ctx->optopt ) {
+ /* fell off the end of this argument */
+ ctx->optind ++;
+ ctx->optchar = 0;
+ return gethopt(ctx, opts, nropts);
+ }
+
+ for ( i=0; i<nropts; i++ ) {
+ if ( opts[i].optchar == ctx->optopt ) {
+ /* found a single-char option!
+ */
+ if ( opts[i].opthasarg ) {
+ if ( ctx->argv[ctx->optind][ctx->optchar] ) {
+ /* argument immediately follows this options (-Oc)
+ */
+ ctx->optarg = &ctx->argv[ctx->optind][ctx->optchar];
+ ctx->optind ++;
+ ctx->optchar = 0;
+ }
+ else if ( ctx->optind < ctx->argc-1 ) {
+ /* argument is next arg (-O c)
+ */
+ ctx->optarg = &ctx->argv[ctx->optind+1][0];
+ ctx->optind += 2;
+ ctx->optchar = 0;
+ }
+ else {
+ /* end of arg string (-O); set optarg to null, return
+ * (should it opterr on me?)
+ */
+ ctx->optarg = 0;
+ ctx->optind ++;
+ ctx->optchar = 0;
+ if ( ctx->opterr )
+ fprintf(stderr,
+ "%s: option requires an argument -- %c\n",
+ ctx->argv[0], opts[i].optchar);
+ return HOPTERR;
+ }
+ }
+ else {
+ if ( !ctx->argv[ctx->optind][ctx->optchar] ) {
+ ctx->optind ++;
+ ctx->optchar = 0;
+ }
+ }
+ return &opts[i];
+ }
+ }
+ if ( ctx->opterr )
+ fprintf(stderr, "%s: illegal option -- %c\n", ctx->argv[0], ctx->optopt);
+ return HOPTERR;
+}
+
+
+void
+hoptusage(char *pgm, struct h_opt opts[], int nropts, char *arguments)
+{
+ int i;
+ int optcount;
+
+ fprintf(stderr, "usage: %s", pgm);
+
+ /* print out the options that don't have flags first */
+
+ for ( optcount=i=0; i < nropts; i++ ) {
+ if ( opts[i].optchar && !opts[i].opthasarg) {
+ if (optcount == 0 )
+ fputs(" [-", stderr);
+ fputc(opts[i].optchar, stderr);
+ optcount++;
+ }
+ }
+ if ( optcount )
+ fputc(']', stderr);
+
+ /* print out the options WITH flags */
+ for ( i = 0; i < nropts; i++ )
+ if ( opts[i].optchar && opts[i].opthasarg)
+ fprintf(stderr, " [-%c %s]", opts[i].optchar, opts[i].opthasarg);
+
+ /* print out the long options */
+ for ( i = 0; i < nropts; i++ )
+ if ( opts[i].optword ) {
+ fprintf(stderr, " [-%s", opts[i].optword);
+ if ( opts[i].opthasarg )
+ fprintf(stderr, " %s", opts[i].opthasarg);
+ fputc(']', stderr);
+ }
+
+ /* print out the arguments string, if any */
+
+ if ( arguments )
+ fprintf(stderr, " %s", arguments);
+
+ /* and we're done */
+ fputc('\n', stderr);
+}
+
+
+#if DEBUG
+struct h_opt opts[] = {
+ { 0, "css", 0, 1, "css file" },
+ { 1, "header", 0, 1, "header file" },
+ { 2, 0, 'a', 0, "option a (no arg)" },
+ { 3, 0, 'b', 1, "option B (with arg)" },
+ { 4, "help", '?', 0, "help message" },
+} ;
+
+#define NROPT (sizeof opts/sizeof opts[0])
+
+
+int
+main(argc, argv)
+char **argv;
+{
+ struct h_opt *ret;
+ struct h_context ctx;
+ int i;
+
+
+ hoptset(&ctx, argc, argv);
+ hopterr(&ctx, 1);
+
+ while (( ret = gethopt(&ctx, opts, NROPT) )) {
+
+ if ( ret != HOPTERR ) {
+ if ( ret->optword )
+ printf("%s", ret->optword);
+ else
+ printf("%c", ret->optchar);
+
+ if ( ret->opthasarg ) {
+ if ( hoptarg(&ctx) )
+ printf(" with argument \"%s\"", hoptarg(&ctx));
+ else
+ printf(" with no argument?");
+ }
+ printf(" (%s)\n", ret->optdesc);
+ }
+ }
+
+ argc -= hoptind(&ctx);
+ argv += hoptind(&ctx);
+
+ for ( i=0; i < argc; i++ )
+ printf("%d: %s\n", i, argv[i]);
+ return 0;
+}
+
+#endif /*DEBUG*/
diff --git a/ext/gethopt.h b/ext/gethopt.h
new file mode 100644
index 0000000..65817b1
--- /dev/null
+++ b/ext/gethopt.h
@@ -0,0 +1,43 @@
+/*
+ * gethopt; options processing with both single-character and whole-work
+ * options both introduced with -
+ */
+
+#ifndef __GETHOPT_D
+#define __GETHOPT_D
+
+#include <stdio.h>
+#include <string.h>
+
+
+struct h_opt {
+ int option;
+ char *optword;
+ char optchar;
+ char *opthasarg;
+ char *optdesc;
+} ;
+
+#define HOPTERR ((struct h_opt*)-1)
+
+struct h_context {
+ char **argv;
+ int argc;
+ int optchar;
+ int optind;
+ char *optarg;
+ char optopt;
+ int opterr:1;
+ int optend:1;
+} ;
+
+extern char *hoptarg(struct h_context *);
+extern int hoptind(struct h_context *);
+extern char hoptopt(struct h_context *);
+extern void hoptset(struct h_context *, int, char **);
+extern int hopterr(struct h_context *, int);
+extern struct h_opt *gethopt(struct h_context *, struct h_opt*, int);
+
+extern void hoptusage(char *, struct h_opt*, int, char *);
+
+#endif/*__GETHOPT_D*/
diff --git a/ext/github_flavoured.c b/ext/github_flavoured.c
index 12ed609..019f783 100644
--- a/ext/github_flavoured.c
+++ b/ext/github_flavoured.c
@@ -30,7 +30,7 @@ gfm_populate(getc_func getc, void* ctx, int flags)
if ( !a ) return 0;
- a->tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP;
+ a->tabstop = is_flag_set(flags, MKD_TABSTOP) ? 4 : TABSTOP;
CREATE(line);
@@ -59,16 +59,17 @@ gfm_populate(getc_func getc, void* ctx, int flags)
DELETE(line);
- if ( (pandoc == 3) && !(flags & (MKD_NOHEADER|MKD_STRICT)) ) {
+ if ( (pandoc == 3) && !(is_flag_set(flags, MKD_NOHEADER)
+ || is_flag_set(flags, MKD_STRICT)) ) {
/* the first three lines started with %, so we have a header.
* clip the first three lines out of content and hang them
* off header.
*/
Line *headers = T(a->content);
- a->title = headers; __mkd_header_dle(a->title);
- a->author= headers->next; __mkd_header_dle(a->author);
- a->date = headers->next->next; __mkd_header_dle(a->date);
+ a->title = headers; __mkd_trim_line(a->title, 1);
+ a->author= headers->next; __mkd_trim_line(a->author, 1);
+ a->date = headers->next->next; __mkd_trim_line(a->date, 1);
T(a->content) = headers->next->next->next;
}
@@ -80,7 +81,7 @@ gfm_populate(getc_func getc, void* ctx, int flags)
/* convert a block of text into a linked list
*/
Document *
-gfm_string(const char *buf, int len, DWORD flags)
+gfm_string(const char *buf, int len, mkd_flag_t flags)
{
struct string_stream about;
@@ -94,7 +95,7 @@ gfm_string(const char *buf, int len, DWORD flags)
/* convert a file into a linked list
*/
Document *
-gfm_in(FILE *f, DWORD flags)
+gfm_in(FILE *f, mkd_flag_t flags)
{
return gfm_populate((getc_func)fgetc, f, flags & INPUT_MASK);
}
diff --git a/ext/h1title.c b/ext/h1title.c
new file mode 100644
index 0000000..89fd9d2
--- /dev/null
+++ b/ext/h1title.c
@@ -0,0 +1,36 @@
+#include <stdio.h>
+#include "markdown.h"
+
+static Paragraph *
+mkd_h1(Paragraph *p)
+{
+ Paragraph *found;
+
+ while ( p ) {
+ if ( p->typ == HDR && p->hnumber == 1 )
+ return p;
+ if ( p->down && (found = mkd_h1(p->down)) )
+ return found;
+ p = p->next;
+ }
+ return 0;
+}
+
+char *
+mkd_h1_title(Document *doc, int flags)
+{
+ Paragraph *title;
+
+ if (doc && (title = mkd_h1(doc->code)) ) {
+ char *generated;
+ int size;
+
+ /* assert that a H1 header is one line long, so that's
+ * the only thing needed
+ */
+ size = mkd_line(T(title->text->text),
+ S(title->text->text), &generated, flags|MKD_TAGTEXT);
+ if ( size ) return generated;
+ }
+ return 0;
+}
diff --git a/ext/html5.c b/ext/html5.c
index de91b90..870589c 100644
--- a/ext/html5.c
+++ b/ext/html5.c
@@ -13,7 +13,6 @@ mkd_with_html5_tags()
mkd_define_tag("ASIDE", 0);
mkd_define_tag("FOOTER", 0);
mkd_define_tag("HEADER", 0);
- mkd_define_tag("HGROUP", 0);
mkd_define_tag("NAV", 0);
mkd_define_tag("SECTION", 0);
mkd_define_tag("ARTICLE", 0);
diff --git a/ext/markdown.c b/ext/markdown.c
index f43f514..81243d8 100644
--- a/ext/markdown.c
+++ b/ext/markdown.c
@@ -147,12 +147,12 @@ typedef struct _flo {
#define floindex(x) (x.i)
-static int
+static unsigned int
flogetc(FLO *f)
{
if ( f && f->t ) {
if ( f->i < S(f->t->text) )
- return T(f->t->text)[f->i++];
+ return (unsigned char)T(f->t->text)[f->i++];
f->t = f->t->next;
f->i = 0;
return flogetc(f);
@@ -170,28 +170,26 @@ splitline(Line *t, int cutpoint)
tmp->next = t->next;
t->next = tmp;
- tmp->dle = t->dle;
SUFFIX(tmp->text, T(t->text)+cutpoint, S(t->text)-cutpoint);
+ EXPAND(tmp->text) = 0;
+ S(tmp->text)--;
+
S(t->text) = cutpoint;
}
}
#define UNCHECK(l) ((l)->flags &= ~CHECKED)
-#ifdef WITH_FENCED_CODE
-# define UNLESS_FENCED(t) if (fenced) { \
+#define UNLESS_FENCED(t) if (fenced) { \
other = 1; l->count += (c == ' ' ? 0 : -1); \
} else { t; }
-#else
-# define UNLESS_FENCED(t) t;
-#endif
/*
* walk a line, seeing if it's any of half a dozen interesting regular
* types.
*/
static void
-checkline(Line *l)
+checkline(Line *l, mkd_flag_t flags)
{
int eol, i;
int dashes = 0, spaces = 0,
@@ -210,23 +208,30 @@ checkline(Line *l)
for (i=l->dle; i<eol; i++) {
register int c = T(l->text)[i];
+ int is_fence_char = 0;
if ( c != ' ' ) l->count++;
switch (c) {
case '-': UNLESS_FENCED(dashes = 1); break;
case ' ': UNLESS_FENCED(spaces = 1); break;
- case '=': equals = 1; break;
+ case '=': UNLESS_FENCED(equals = 1); break;
case '_': UNLESS_FENCED(underscores = 1); break;
case '*': stars = 1; break;
-#if WITH_FENCED_CODE
- case '~': if (other) return; fenced = 1; tildes = 1; break;
- case '`': if (other) return; fenced = 1; backticks = 1; break;
-#endif
default:
- other = 1;
- l->count--;
- if (!fenced) return;
+ if ( is_flag_set(flags, MKD_FENCEDCODE) ) {
+ switch (c) {
+ case '~': if (other) return; is_fence_char = 1; tildes = 1; break;
+ case '`': if (other) return; is_fence_char = 1; backticks = 1; break;
+ }
+ if (is_fence_char) {
+ fenced = 1;
+ break;
+ }
+ }
+ other = 1;
+ l->count--;
+ if (!fenced) return;
}
}
@@ -242,28 +247,32 @@ checkline(Line *l)
if ( stars || underscores ) { l->kind = chk_hr; }
else if ( dashes ) { l->kind = chk_dash; }
else if ( equals ) { l->kind = chk_equal; }
-#if WITH_FENCED_CODE
else if ( tildes ) { l->kind = chk_tilde; }
else if ( backticks ) { l->kind = chk_backtick; }
-#endif
}
+/* markdown only does special handling of comments if the comment end
+ * is at the end of a line
+ */
static Line *
commentblock(Paragraph *p, int *unclosed)
{
Line *t, *ret;
char *end;
- for ( t = p->text; t ; t = t->next) {
- if ( end = strstr(T(t->text), "-->") ) {
- splitline(t, 3 + (end - T(t->text)) );
- ret = t->next;
- t->next = 0;
- return ret;
- }
+ for ( t = p->text; t ; t = t->next) {
+ if ( end = strstr(T(t->text), "-->") ) {
+ if ( nextnonblank(t, 3 + (end - T(t->text))) < S(t->text) )
+ continue;
+ /*splitline(t, 3 + (end - T(t->text)) );*/
+ ret = t->next;
+ t->next = 0;
+ return ret;
+ }
}
+
*unclosed = 1;
return t;
@@ -307,8 +316,8 @@ htmlblock(Paragraph *p, struct kw *tag, int *unclosed)
else {
if ( closing = (c == '/') ) c = flogetc(&f);
- for ( i=0; i < tag->size; c=flogetc(&f) ) {
- if ( tag->id[i++] != toupper(c) )
+ for ( i=0; i < tag->size; i++, c=flogetc(&f) ) {
+ if ( tag->id[i] != toupper(c) )
break;
}
@@ -372,10 +381,10 @@ iscode(Line *t)
static inline int
-ishr(Line *t)
+ishr(Line *t, mkd_flag_t flags)
{
if ( ! (t->flags & CHECKED) )
- checkline(t);
+ checkline(t, flags);
if ( t->count > 2 )
return t->kind == chk_hr || t->kind == chk_dash || t->kind == chk_equal;
@@ -384,7 +393,7 @@ ishr(Line *t)
static int
-issetext(Line *t, int *htyp)
+issetext(Line *t, int *htyp, mkd_flag_t flags)
{
Line *n;
@@ -394,7 +403,7 @@ issetext(Line *t, int *htyp)
if ( (n = t->next) ) {
if ( !(n->flags & CHECKED) )
- checkline(n);
+ checkline(n, flags);
if ( n->kind == chk_dash || n->kind == chk_equal ) {
*htyp = SETEXT;
@@ -406,7 +415,7 @@ issetext(Line *t, int *htyp)
static int
-ishdr(Line *t, int *htyp)
+ishdr(Line *t, int *htyp, mkd_flag_t flags)
{
/* ANY leading `#`'s make this into an ETX header
*/
@@ -417,27 +426,28 @@ ishdr(Line *t, int *htyp)
/* And if not, maybe it's a SETEXT header instead
*/
- return issetext(t, htyp);
+ return issetext(t, htyp, flags);
}
static inline int
-end_of_block(Line *t)
+end_of_block(Line *t, mkd_flag_t flags)
{
int dummy;
if ( !t )
return 0;
- return ( (S(t->text) <= t->dle) || ishr(t) || ishdr(t, &dummy) );
+ return ( (S(t->text) <= t->dle) || ishr(t, flags) || ishdr(t, &dummy, flags) );
}
static Line*
-is_discount_dt(Line *t, int *clip)
+is_discount_dt(Line *t, int *clip, mkd_flag_t flags)
{
-#if USE_DISCOUNT_DL
- if ( t && t->next
+ if ( !is_flag_set(flags, MKD_NODLDISCOUNT)
+ && t
+ && t->next
&& (S(t->text) > 2)
&& (t->dle == 0)
&& (T(t->text)[0] == '=')
@@ -447,9 +457,8 @@ is_discount_dt(Line *t, int *clip)
return t;
}
else
- return is_discount_dt(t->next, clip);
+ return is_discount_dt(t->next, clip, flags);
}
-#endif
return 0;
}
@@ -463,15 +472,15 @@ is_extra_dd(Line *t)
static Line*
-is_extra_dt(Line *t, int *clip)
+is_extra_dt(Line *t, int *clip, mkd_flag_t flags)
{
-#if USE_EXTRA_DL
-
- if ( t && t->next && S(t->text) && T(t->text)[0] != '='
+ if ( is_flag_set(flags, MKD_DLEXTRA)
+ && t
+ && t->next && S(t->text) && T(t->text)[0] != '='
&& T(t->text)[S(t->text)-1] != '=') {
Line *x;
- if ( iscode(t) || end_of_block(t) )
+ if ( iscode(t) || end_of_block(t, flags) )
return 0;
if ( (x = skipempty(t->next)) && is_extra_dd(x) ) {
@@ -479,52 +488,52 @@ is_extra_dt(Line *t, int *clip)
return t;
}
- if ( x=is_extra_dt(t->next, clip) )
+ if ( x=is_extra_dt(t->next, clip, flags) )
return x;
}
-#endif
return 0;
}
static Line*
-isdefinition(Line *t, int *clip, int *kind)
+isdefinition(Line *t, int *clip, int *kind, mkd_flag_t flags)
{
Line *ret;
*kind = 1;
- if ( ret = is_discount_dt(t,clip) )
+ if ( ret = is_discount_dt(t,clip,flags) )
return ret;
*kind=2;
- return is_extra_dt(t,clip);
+ return is_extra_dt(t,clip,flags);
}
static int
-islist(Line *t, int *clip, DWORD flags, int *list_type)
+islist(Line *t, int *clip, mkd_flag_t flags, int *list_type)
{
int i, j;
char *q;
- if ( end_of_block(t) )
+ if ( end_of_block(t, flags) )
return 0;
- if ( !(flags & (MKD_NODLIST|MKD_STRICT)) && isdefinition(t,clip,list_type) )
+ if ( !(is_flag_set(flags, MKD_NODLIST) || is_flag_set(flags, MKD_STRICT))
+ && isdefinition(t,clip,list_type,flags) )
return DL;
if ( strchr("*-+", T(t->text)[t->dle]) && isspace(T(t->text)[t->dle+1]) ) {
i = nextnonblank(t, t->dle+1);
*clip = (i > 4) ? 4 : i;
*list_type = UL;
- return AL;
+ return is_flag_set(flags, MKD_EXPLICITLIST) ? UL : AL;
}
if ( (j = nextblank(t,t->dle)) > t->dle ) {
if ( T(t->text)[j-1] == '.' ) {
- if ( !(flags & (MKD_NOALPHALIST|MKD_STRICT))
- && (j == t->dle + 2)
+ if ( !(is_flag_set(flags, MKD_NOALPHALIST) || is_flag_set(flags, MKD_STRICT))
+ && (j == t->dle + 2)
&& isalpha(T(t->text)[t->dle]) ) {
j = nextnonblank(t,j);
*clip = (j > 4) ? 4 : j;
@@ -602,8 +611,7 @@ codeblock(Paragraph *p)
Line *t = p->text, *r;
for ( ; t; t = r ) {
- CLIP(t->text,0,4);
- t->dle = mkd_firstnonblank(t);
+ __mkd_trim_line(t,4);
if ( !( (r = skipempty(t->next)) && iscode(r)) ) {
___mkd_freeLineRange(t,r);
@@ -615,12 +623,14 @@ codeblock(Paragraph *p)
}
-#ifdef WITH_FENCED_CODE
static int
-iscodefence(Line *r, int size, line_type kind)
+iscodefence(Line *r, int size, line_type kind, mkd_flag_t flags)
{
+ if ( !is_flag_set(flags, MKD_FENCEDCODE) )
+ return 0;
+
if ( !(r->flags & CHECKED) )
- checkline(r);
+ checkline(r, flags);
if ( kind )
return (r->kind == kind) && (r->count >= size);
@@ -628,42 +638,42 @@ iscodefence(Line *r, int size, line_type kind)
return (r->kind == chk_tilde || r->kind == chk_backtick) && (r->count >= size);
}
+
static Paragraph *
-fencedcodeblock(ParagraphRoot *d, Line **ptr)
+fencedcodeblock(ParagraphRoot *d, Line **ptr, mkd_flag_t flags)
{
Line *first, *r;
Paragraph *ret;
first = (*ptr);
-
+
/* don't allow zero-length code fences
- */
- if ( (first->next == 0) || iscodefence(first->next, first->count, 0) )
+ */
+ if ( (first->next == 0) || iscodefence(first->next, first->count, 0, flags) )
return 0;
/* find the closing fence, discard the fences,
- * return a Paragraph with the contents
- */
+ * return a Paragraph with the contents
+ */
for ( r = first; r && r->next; r = r->next )
- if ( iscodefence(r->next, first->count, first->kind) ) {
+ if ( iscodefence(r->next, first->count, first->kind, flags) ) {
(*ptr) = r->next->next;
ret = Pp(d, first->next, CODE);
- if (S(first->text) - first->count > 0) {
- char *lang_attr = T(first->text) + first->count;
- while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
- ret->lang = strdup(lang_attr);
- }
- else {
- ret->lang = 0;
- }
- ___mkd_freeLine(first);
- ___mkd_freeLine(r->next);
- r->next = 0;
- return ret;
+ if (S(first->text) - first->count > 0) {
+ char *lang_attr = T(first->text) + first->count;
+ while ( *lang_attr != 0 && *lang_attr == ' ' ) lang_attr++;
+ ret->lang = strdup(lang_attr);
+ }
+ else {
+ ret->lang = 0;
}
+ ___mkd_freeLine(first);
+ ___mkd_freeLine(r->next);
+ r->next = 0;
+ return ret;
+ }
return 0;
}
-#endif
static int
@@ -685,11 +695,11 @@ centered(Line *first, Line *last)
static int
-endoftextblock(Line *t, int toplevelblock, DWORD flags)
+endoftextblock(Line *t, int toplevelblock, mkd_flag_t flags)
{
int z;
- if ( end_of_block(t) || isquote(t) )
+ if ( end_of_block(t, flags) || isquote(t) )
return 1;
/* HORRIBLE STANDARDS KLUDGES:
@@ -705,7 +715,7 @@ endoftextblock(Line *t, int toplevelblock, DWORD flags)
static Line *
-textblock(Paragraph *p, int toplevel, DWORD flags)
+textblock(Paragraph *p, int toplevel, mkd_flag_t flags)
{
Line *t, *next;
@@ -740,12 +750,12 @@ szmarkerclass(char *p)
#define iscsschar(c) (isalpha(c) || (c == '-') || (c == '_') )
static int
-isdivmarker(Line *p, int start, DWORD flags)
+isdivmarker(Line *p, int start, mkd_flag_t flags)
{
char *s;
int last, i;
- if ( flags & (MKD_NODIVQUOTE|MKD_STRICT) )
+ if ( is_flag_set(flags, MKD_NODIVQUOTE) || is_flag_set(flags, MKD_STRICT) )
return 0;
start = nextnonblank(p, start);
@@ -779,7 +789,7 @@ isdivmarker(Line *p, int start, DWORD flags)
* way the markdown sample web form at Daring Fireball works.
*/
static Line *
-quoteblock(Paragraph *p, DWORD flags)
+quoteblock(Paragraph *p, mkd_flag_t flags)
{
Line *t, *q;
int qp;
@@ -796,9 +806,8 @@ quoteblock(Paragraph *p, DWORD flags)
/* clip next space, if any */
if ( T(t->text)[qp] == ' ' )
qp++;
- CLIP(t->text, 0, qp);
+ __mkd_trim_line(t,qp);
UNCHECK(t);
- t->dle = mkd_firstnonblank(t);
}
q = skipempty(t->next);
@@ -840,16 +849,40 @@ typedef int (*linefn)(Line *);
* marker, but multiple paragraphs need to start with a 4-space indent.
*/
static Line *
-listitem(Paragraph *p, int indent, DWORD flags, linefn check)
+listitem(Paragraph *p, int indent, mkd_flag_t flags, linefn check)
{
Line *t, *q;
int clip = indent;
int z;
+#ifdef GITHUB_CHECKBOX
+ int firstpara = 1;
+ int ischeck;
+#define CHECK_NOT 0
+#define CHECK_NO 1
+#define CHECK_YES 2
+#endif
for ( t = p->text; t ; t = q) {
- CLIP(t->text, 0, clip);
UNCHECK(t);
- t->dle = mkd_firstnonblank(t);
+ __mkd_trim_line(t, clip);
+
+#ifdef GITHUB_CHECKBOX
+ if ( firstpara ) {
+ ischeck = CHECK_NOT;
+ if ( strncmp(T(t->text)+t->dle, "[ ]", 3) == 0 )
+ ischeck = CHECK_NO;
+ else if ( strncasecmp(T(t->text)+t->dle, "[x]", 3) == 0 )
+ ischeck = CHECK_YES;
+
+ if ( ischeck != CHECK_NOT ) {
+ __mkd_trim_line(t, 3);
+ p->flags |= GITHUB_CHECK;
+ if ( ischeck == CHECK_YES )
+ p->flags |= IS_CHECKED;
+ }
+ firstpara = 0;
+ }
+#endif
/* even though we had to trim a long leader off this item,
* the indent for trailing paragraphs is still 4...
@@ -878,9 +911,9 @@ listitem(Paragraph *p, int indent, DWORD flags, linefn check)
indent = clip ? clip : 2;
}
- if ( (q->dle < indent) && (ishr(q) || islist(q,&z,flags,&z)
+ if ( (q->dle < indent) && (ishr(q,flags) || islist(q,&z,flags,&z)
|| (check && (*check)(q)))
- && !issetext(q,&z) ) {
+ && !issetext(q,&z,flags) ) {
q = t->next;
t->next = 0;
return q;
@@ -902,7 +935,7 @@ definition_block(Paragraph *top, int clip, MMIOT *f, int kind)
while (( labels = q )) {
- if ( (q = isdefinition(labels, &z, &kind)) == 0 )
+ if ( (q = isdefinition(labels, &z, &kind, f->flags)) == 0 )
break;
if ( (text = skipempty(q->next)) == 0 )
@@ -998,6 +1031,28 @@ tgood(char c)
}
+/*
+ * eat lines for a markdown extra footnote
+ */
+static Line *
+extrablock(Line *p)
+{
+ Line *np;
+
+ while ( p && p->next ) {
+ np = p->next;
+
+ if ( np->dle < 4 && np->dle < S(np->text) ) {
+ p->next = 0;
+ return np;
+ }
+ __mkd_trim_line(np,4);
+ p = np;
+ }
+ return 0;
+}
+
+
/*
* add a new (image or link) footnote to the footnote table
*/
@@ -1013,20 +1068,31 @@ addfootnote(Line *p, MMIOT* f)
CREATE(foot->tag);
CREATE(foot->link);
CREATE(foot->title);
+ foot->text = 0;
foot->flags = foot->height = foot->width = 0;
+ /* keep the footnote label */
for (j=i=p->dle+1; T(p->text)[j] != ']'; j++)
EXPAND(foot->tag) = T(p->text)[j];
-
EXPAND(foot->tag) = 0;
S(foot->tag)--;
+
+ /* consume the closing ]: */
j = nextnonblank(p, j+2);
- if ( (f->flags & MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
- /* need to consume all lines until non-indented block? */
- while ( j < S(p->text) )
- EXPAND(foot->title) = T(p->text)[j++];
- goto skip_to_end;
+ if ( is_flag_set(f->flags, MKD_EXTRA_FOOTNOTE) && (T(foot->tag)[0] == '^') ) {
+ /* markdown extra footnote: All indented lines past this point;
+ * the first line includes the footnote reference, so we need to
+ * snip that out as we go.
+ */
+ foot->flags |= EXTRA_FOOTNOTE;
+ __mkd_trim_line(p,j);
+
+ np = extrablock(p);
+
+ foot->text = compile(p, 0, f);
+
+ return np;
}
while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
@@ -1037,8 +1103,7 @@ addfootnote(Line *p, MMIOT* f)
if ( T(p->text)[j] == '=' ) {
sscanf(T(p->text)+j, "=%dx%d", &foot->width, &foot->height);
- while ( (j < S(p->text)) && !isspace(T(p->text)[j]) )
- ++j;
+ j = nextblank(p, j);
j = nextnonblank(p,j);
}
@@ -1068,7 +1133,6 @@ addfootnote(Line *p, MMIOT* f)
--S(foot->title);
}
-skip_to_end:
___mkd_freeLine(p);
return np;
}
@@ -1106,6 +1170,22 @@ consume(Line *ptr, int *eaten)
}
+typedef ANCHOR(Line) Cache;
+
+static void
+uncache(Cache *cache, ParagraphRoot *d, MMIOT *f)
+{
+ Paragraph *p;
+
+ if ( T(*cache) ) {
+ E(*cache)->next = 0;
+ p = Pp(d, 0, SOURCE);
+ p->down = compile(T(*cache), 1, f);
+ T(*cache) = E(*cache) = 0;
+ }
+}
+
+
/*
* top-level compilation; break the document into
* style, html, and source blocks with footnote links
@@ -1115,25 +1195,21 @@ static Paragraph *
compile_document(Line *ptr, MMIOT *f)
{
ParagraphRoot d = { 0, 0 };
- ANCHOR(Line) source = { 0, 0 };
+ Cache source = { 0, 0 };
Paragraph *p = 0;
struct kw *tag;
int eaten, unclosed;
+ int previous_was_break = 1;
while ( ptr ) {
- if ( !(f->flags & MKD_NOHTML) && (tag = isopentag(ptr)) ) {
+ if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) {
int blocktype;
/* If we encounter a html/style block, compile and save all
* of the cached source BEFORE processing the html/style.
*/
- if ( T(source) ) {
- E(source)->next = 0;
- p = Pp(&d, 0, SOURCE);
- p->down = compile(T(source), 1, f);
- T(source) = E(source) = 0;
- }
+ uncache(&source, &d, f);
- if ( f->flags & MKD_NOSTYLE )
+ if (is_flag_set(f->flags, MKD_NOSTYLE) )
blocktype = HTML;
else
blocktype = strcmp(tag->id, "STYLE") == 0 ? STYLE : HTML;
@@ -1144,6 +1220,7 @@ compile_document(Line *ptr, MMIOT *f)
p->down = compile(p->text, 1, f);
p->text = 0;
}
+ previous_was_break = 1;
}
else if ( isfootnote(ptr) ) {
/* footnotes, like cats, sleep anywhere; pull them
@@ -1151,23 +1228,27 @@ compile_document(Line *ptr, MMIOT *f)
* later processing
*/
ptr = consume(addfootnote(ptr, f), &eaten);
+ previous_was_break = 1;
+ }
+ else if ( previous_was_break && iscodefence(ptr,3,0,f->flags)) {
+ uncache(&source, &d, f);
+ if ( !fencedcodeblock(&d, &ptr, f->flags) ) /* just source */
+ goto attach;
}
else {
+ attach:
/* source; cache it up to wait for eof or the
* next html/style block
*/
ATTACH(source,ptr);
+ previous_was_break = blankline(ptr);
ptr = ptr->next;
}
}
- if ( T(source) ) {
- /* if there's any cached source at EOF, compile
- * it now.
- */
- E(source)->next = 0;
- p = Pp(&d, 0, SOURCE);
- p->down = compile(T(source), 1, f);
- }
+ /* if there's any cached source at EOF, compile
+ * it now.
+ */
+ uncache(&source, &d, f);
return T(d);
}
@@ -1187,7 +1268,7 @@ actually_a_table(MMIOT *f, Line *pp)
int c;
/* tables need to be turned on */
- if ( f->flags & (MKD_STRICT|MKD_NOTABLES) )
+ if ( is_flag_set(f->flags, MKD_STRICT) || is_flag_set(f->flags, MKD_NOTABLES) )
return 0;
/* tables need three lines */
@@ -1242,10 +1323,11 @@ compile(Line *ptr, int toplevel, MMIOT *f)
ptr = consume(ptr, ¶);
while ( ptr ) {
+
if ( iscode(ptr) ) {
p = Pp(&d, ptr, CODE);
- if ( f->flags & MKD_1_COMPAT) {
+ if ( is_flag_set(f->flags, MKD_1_COMPAT) ) {
/* HORRIBLE STANDARDS KLUDGE: the first line of every block
* has trailing whitespace trimmed off.
*/
@@ -1254,11 +1336,9 @@ compile(Line *ptr, int toplevel, MMIOT *f)
ptr = codeblock(p);
}
-#if WITH_FENCED_CODE
- else if ( iscodefence(ptr,3,0) && (p=fencedcodeblock(&d, &ptr)) )
+ else if ( iscodefence(ptr,3,0,f->flags) && (p=fencedcodeblock(&d, &ptr, f->flags)) )
/* yay, it's already done */ ;
-#endif
- else if ( ishr(ptr) ) {
+ else if ( ishr(ptr, f->flags) ) {
p = Pp(&d, 0, HR);
r = ptr;
ptr = ptr->next;
@@ -1280,18 +1360,44 @@ compile(Line *ptr, int toplevel, MMIOT *f)
p->down = compile(p->text, 1, f);
p->text = 0;
}
- else if ( ishdr(ptr, &hdr_type) ) {
+ else if ( ishdr(ptr, &hdr_type, f->flags) ) {
p = Pp(&d, ptr, HDR);
ptr = headerblock(p, hdr_type);
}
else {
- p = Pp(&d, ptr, MARKUP);
- ptr = textblock(p, toplevel, f->flags);
- /* tables are a special kind of paragraph */
- if ( actually_a_table(f, p->text) )
- p->typ = TABLE;
+ /* either markup or an html block element
+ */
+ struct kw *tag;
+ int unclosed = 1;
+
+ p = Pp(&d, ptr, MARKUP); /* default to regular markup,
+ * then check if it's an html
+ * block. If it IS an html
+ * block, htmlblock() will
+ * populate this paragraph &
+ * all we need to do is reset
+ * the paragraph type to HTML,
+ * otherwise the paragraph
+ * remains empty and ready for
+ * processing with textblock()
+ */
+
+ if ( !is_flag_set(f->flags, MKD_NOHTML) && (tag = isopentag(ptr)) ) {
+ /* possibly an html block
+ */
+
+ ptr = htmlblock(p, tag, &unclosed);
+ if ( ! unclosed ) {
+ p->typ = HTML;
+ }
+ }
+ if ( unclosed ) {
+ ptr = textblock(p, toplevel, f->flags);
+ /* tables are a special kind of paragraph */
+ if ( actually_a_table(f, p->text) )
+ p->typ = TABLE;
+ }
}
-
if ( (para||toplevel) && !p->align )
p->align = PARA;
@@ -1316,19 +1422,30 @@ compile(Line *ptr, int toplevel, MMIOT *f)
* prepare and compile `text`, returning a Paragraph tree.
*/
int
-mkd_compile(Document *doc, DWORD flags)
+mkd_compile(Document *doc, mkd_flag_t flags)
{
if ( !doc )
return 0;
- if ( doc->compiled )
- return 1;
+ flags &= USER_FLAGS;
+
+ if ( doc->compiled ) {
+ if ( doc->ctx->flags == flags && !doc->dirty)
+ return 1;
+ else {
+ doc->compiled = doc->dirty = 0;
+ if ( doc->code)
+ ___mkd_freeParagraph(doc->code);
+ if ( doc->ctx->footnotes )
+ ___mkd_freefootnotes(doc->ctx);
+ }
+ }
doc->compiled = 1;
memset(doc->ctx, 0, sizeof(MMIOT) );
doc->ctx->ref_prefix= doc->ref_prefix;
doc->ctx->cb = &(doc->cb);
- doc->ctx->flags = flags & USER_FLAGS;
+ doc->ctx->flags = flags;
CREATE(doc->ctx->in);
doc->ctx->footnotes = malloc(sizeof doc->ctx->footnotes[0]);
doc->ctx->footnotes->reference = 0;
diff --git a/ext/markdown.h b/ext/markdown.h
index 8edd302..5cb897f 100644
--- a/ext/markdown.h
+++ b/ext/markdown.h
@@ -1,22 +1,22 @@
#ifndef _MARKDOWN_D
#define _MARKDOWN_D
+#include "config.h"
#include "cstring.h"
-/* reference-style links (and images) are stored in an array
- * of footnotes.
+#ifdef HAVE_INTTYPES_H
+# include <inttypes.h>
+#elif HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+/* flags, captured into a named type
*/
-typedef struct footnote {
- Cstring tag; /* the tag for the reference link */
- Cstring link; /* what this footnote points to */
- Cstring title; /* what it's called (TITLE= attribute) */
- int height, width; /* dimensions (for image link) */
- int dealloc; /* deallocation needed? */
- int refnumber;
- int flags;
-#define EXTRA_BOOKMARK 0x01
-#define REFERENCED 0x02
-} Footnote;
+typedef DWORD mkd_flag_t;
+
+#define is_flag_set(flags, item) ((flags) & (item))
+#define set_flag(flags, item) ((flags) |= (item))
+#define clear_flag(flags, item) ((flags) &= ~(item))
/* each input line is read into a Line, which contains the line,
* the offset of the first non-space character [this assumes
@@ -49,16 +49,38 @@ typedef struct paragraph {
struct paragraph *down; /* recompiled contents of this paragraph */
struct line *text; /* all the text in this paragraph */
char *ident; /* %id% tag for QUOTE */
- char *lang; /* lang attribute for CODE */
+ char *lang; /* lang attribute for CODE */
enum { WHITESPACE=0, CODE, QUOTE, MARKUP,
HTML, STYLE, DL, UL, OL, AL, LISTITEM,
HDR, HR, TABLE, SOURCE } typ;
enum { IMPLICIT=0, PARA, CENTER} align;
int hnumber; /* <Hn> for typ == HDR */
+#if GITHUB_CHECKBOX
+ int flags;
+#define GITHUB_CHECK 0x01
+#define IS_CHECKED 0x02
+#endif
} Paragraph;
enum { ETX, SETEXT }; /* header types */
+/* reference-style links (and images) are stored in an array
+ * of footnotes.
+ */
+typedef struct footnote {
+ Cstring tag; /* the tag for the reference link */
+ Cstring link; /* what this footnote points to */
+ Cstring title; /* what it's called (TITLE= attribute) */
+ Paragraph *text; /* EXTRA_FOOTNOTE content */
+
+ int height, width; /* dimensions (for image link) */
+ int dealloc; /* deallocation needed? */
+ int refnumber;
+ int flags;
+#define EXTRA_FOOTNOTE 0x01
+#define REFERENCED 0x02
+} Footnote;
+
typedef struct block {
enum { bTEXT, bSTAR, bUNDER } b_type;
@@ -78,7 +100,9 @@ typedef struct callback_data {
void *e_data; /* private data for callbacks */
mkd_callback_t e_url; /* url edit callback */
mkd_callback_t e_flags; /* extra href flags callback */
+ mkd_callback_t e_anchor; /* callback for anchor types */
mkd_free_t e_free; /* edit/flags callback memory deallocator */
+ mkd_callback_t e_codefmt; /* codeblock formatter (for highlighting) */
} Callback_data;
@@ -101,11 +125,12 @@ typedef struct mmiot {
Cstring out;
Cstring in;
Qblock Q;
+ char last; /* last text character added to out */
int isp;
struct escaped *esc;
char *ref_prefix;
struct footnote_list *footnotes;
- DWORD flags;
+ mkd_flag_t flags;
#define MKD_NOLINKS 0x00000001
#define MKD_NOIMAGE 0x00000002
#define MKD_NOPANTS 0x00000004
@@ -129,15 +154,23 @@ typedef struct mmiot {
#define MKD_NODLIST 0x00100000
#define MKD_EXTRA_FOOTNOTE 0x00200000
#define MKD_NOSTYLE 0x00400000
-#define IS_LABEL 0x08000000
-#define USER_FLAGS 0x0FFFFFFF
+#define MKD_NODLDISCOUNT 0x00800000
+#define MKD_DLEXTRA 0x01000000
+#define MKD_FENCEDCODE 0x02000000
+#define MKD_IDANCHOR 0x04000000
+#define MKD_GITHUBTAGS 0x08000000
+#define MKD_URLENCODEDANCHOR 0x10000000
+#define IS_LABEL 0x20000000
+#define MKD_LATEX 0x40000000
+#define MKD_EXPLICITLIST 0x80000000
+#define USER_FLAGS 0xFFFFFFFF
#define INPUT_MASK (MKD_NOHEADER|MKD_TABSTOP)
Callback_data *cb;
} MMIOT;
-#define MKD_EOLN 3
+#define MKD_EOLN '\r'
/*
@@ -156,6 +189,7 @@ typedef struct document {
ANCHOR(Line) content; /* uncompiled text, not valid after compile() */
Paragraph *code; /* intermediate code generated by compile() */
int compiled; /* set after mkd_compile() */
+ int dirty; /* flags or callbacks changed */
int html; /* set after (internal) htmlify() */
int tabstop; /* for properly expanding tabs (ick) */
char *ref_prefix;
@@ -175,7 +209,7 @@ struct string_stream {
extern int mkd_firstnonblank(Line *);
-extern int mkd_compile(Document *, DWORD);
+extern int mkd_compile(Document *, mkd_flag_t);
extern int mkd_document(Document *, char **);
extern int mkd_generatehtml(Document *, FILE *);
extern int mkd_css(Document *, char **);
@@ -184,19 +218,19 @@ extern int mkd_generatecss(Document *, FILE *);
extern int mkd_xml(char *, int , char **);
extern int mkd_generatexml(char *, int, FILE *);
extern void mkd_cleanup(Document *);
-extern int mkd_line(char *, int, char **, DWORD);
-extern int mkd_generateline(char *, int, FILE*, DWORD);
+extern int mkd_line(char *, int, char **, mkd_flag_t);
+extern int mkd_generateline(char *, int, FILE*, mkd_flag_t);
#define mkd_text mkd_generateline
extern void mkd_basename(Document*, char *);
typedef int (*mkd_sta_function_t)(const int,const void*);
-extern void mkd_string_to_anchor(char*,int, mkd_sta_function_t, void*, int);
+extern void mkd_string_to_anchor(char*,int, mkd_sta_function_t, void*, int, MMIOT *);
-extern Document *mkd_in(FILE *, DWORD);
-extern Document *mkd_string(const char*,int, DWORD);
+extern Document *mkd_in(FILE *, mkd_flag_t);
+extern Document *mkd_string(const char*, int, mkd_flag_t);
-extern Document *gfm_in(FILE *, DWORD);
-extern Document *gfm_string(const char*,int, DWORD);
+extern Document *gfm_in(FILE *, mkd_flag_t);
+extern Document *gfm_string(const char*,int, mkd_flag_t);
extern void mkd_initialize();
extern void mkd_shlib_destructor();
@@ -214,14 +248,19 @@ extern void ___mkd_initmmiot(MMIOT *, void *);
extern void ___mkd_freemmiot(MMIOT *, void *);
extern void ___mkd_freeLineRange(Line *, Line *);
extern void ___mkd_xml(char *, int, FILE *);
-extern void ___mkd_reparse(char *, int, int, MMIOT*, char*);
+extern void ___mkd_reparse(char *, int, mkd_flag_t, MMIOT*, char*);
extern void ___mkd_emblock(MMIOT*);
extern void ___mkd_tidy(Cstring *);
extern Document *__mkd_new_Document();
extern void __mkd_enqueue(Document*, Cstring *);
-extern void __mkd_header_dle(Line *);
+extern void __mkd_trim_line(Line *, int);
extern int __mkd_io_strget(struct string_stream *);
+/* utility function to do some operation and exit the current function
+ * if it fails
+ */
+#define DO_OR_DIE(op) if ( (op) == EOF ) return EOF; else 1
+
#endif/*_MARKDOWN_D*/
diff --git a/ext/mkdio.c b/ext/mkdio.c
index fd6105d..fa44c1d 100644
--- a/ext/mkdio.c
+++ b/ext/mkdio.c
@@ -16,6 +16,7 @@
typedef ANCHOR(Line) LineAnchor;
+
/* create a new blank Document
*/
Document*
@@ -73,13 +74,19 @@ __mkd_enqueue(Document* a, Cstring *line)
}
-/* trim leading blanks from a header line
+/* trim leading characters from a line, then adjust the dle.
*/
void
-__mkd_header_dle(Line *p)
+__mkd_trim_line(Line *p, int clip)
{
- CLIP(p->text, 0, 1);
- p->dle = mkd_firstnonblank(p);
+ if ( clip >= S(p->text) ) {
+ S(p->text) = p->dle = 0;
+ T(p->text)[0] = 0;
+ }
+ else if ( clip > 0 ) {
+ CLIP(p->text, 0, clip);
+ p->dle = mkd_firstnonblank(p);
+ }
}
@@ -88,7 +95,7 @@ __mkd_header_dle(Line *p)
typedef int (*getc_func)(void*);
Document *
-populate(getc_func getc, void* ctx, int flags)
+populate(getc_func getc, void* ctx, mkd_flag_t flags)
{
Cstring line;
Document *a = __mkd_new_Document();
@@ -97,7 +104,7 @@ populate(getc_func getc, void* ctx, int flags)
if ( !a ) return 0;
- a->tabstop = (flags & MKD_TABSTOP) ? 4 : TABSTOP;
+ a->tabstop = is_flag_set(flags, MKD_TABSTOP) ? 4 : TABSTOP;
CREATE(line);
@@ -121,16 +128,16 @@ populate(getc_func getc, void* ctx, int flags)
DELETE(line);
- if ( (pandoc == 3) && !(flags & (MKD_NOHEADER|MKD_STRICT)) ) {
+ if ( (pandoc == 3) && !(is_flag_set(flags, MKD_NOHEADER) || is_flag_set(flags, MKD_STRICT)) ) {
/* the first three lines started with %, so we have a header.
* clip the first three lines out of content and hang them
* off header.
*/
Line *headers = T(a->content);
- a->title = headers; __mkd_header_dle(a->title);
- a->author= headers->next; __mkd_header_dle(a->author);
- a->date = headers->next->next; __mkd_header_dle(a->date);
+ a->title = headers; __mkd_trim_line(a->title, 1);
+ a->author= headers->next; __mkd_trim_line(a->author, 1);
+ a->date = headers->next->next; __mkd_trim_line(a->date, 1);
T(a->content) = headers->next->next->next;
}
@@ -142,7 +149,7 @@ populate(getc_func getc, void* ctx, int flags)
/* convert a file into a linked list
*/
Document *
-mkd_in(FILE *f, DWORD flags)
+mkd_in(FILE *f, mkd_flag_t flags)
{
return populate((getc_func)fgetc, f, flags & INPUT_MASK);
}
@@ -164,7 +171,7 @@ __mkd_io_strget(struct string_stream *in)
/* convert a block of text into a linked list
*/
Document *
-mkd_string(const char *buf, int len, DWORD flags)
+mkd_string(const char *buf, int len, mkd_flag_t flags)
{
struct string_stream about;
@@ -183,22 +190,20 @@ mkd_generatehtml(Document *p, FILE *output)
char *doc;
int szdoc;
- if ( (szdoc = mkd_document(p, &doc)) != EOF ) {
- if ( p->ctx->flags & MKD_CDATA )
- mkd_generatexml(doc, szdoc, output);
- else
- fwrite(doc, szdoc, 1, output);
- putc('\n', output);
- return 0;
- }
- return -1;
+ DO_OR_DIE( szdoc = mkd_document(p,&doc) );
+ if ( is_flag_set(p->ctx->flags, MKD_CDATA) )
+ DO_OR_DIE( mkd_generatexml(doc, szdoc, output) );
+ else if ( fwrite(doc, szdoc, 1, output) != 1 )
+ return EOF;
+ DO_OR_DIE( putc('\n', output) );
+ return 0;
}
/* convert some markdown text to html
*/
int
-markdown(Document *document, FILE *out, int flags)
+markdown(Document *document, FILE *out, mkd_flag_t flags)
{
if ( mkd_compile(document, flags) ) {
mkd_generatehtml(document, out);
@@ -209,55 +214,103 @@ markdown(Document *document, FILE *out, int flags)
}
-/* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
+/* anchor_format a string, returning the formatted string in malloc()ed space
+ * MKD_URLENCODEDANCHOR is now perverted to being a html5 anchor
+ *
+ * !labelformat: print all characters
+ * labelformat && h4anchor: prefix nonalpha label with L,
+ * expand all nonalnum, _, ':', '.' to hex
+ * except space which maps to -
+ * labelformat && !h4anchor:expand space to -, other isspace() & '%' to hex
*/
-void
-mkd_string_to_anchor(char *s, int len, mkd_sta_function_t outchar,
- void *out, int labelformat)
+static char *
+mkd_anchor_format(char *s, int len, int labelformat, mkd_flag_t flags)
{
-#if WITH_URLENCODED_ANCHOR
- static const unsigned char hexchars[] = "0123456789abcdef";
-#endif
+ char *res;
unsigned char c;
+ int i, needed, out = 0;
+ int h4anchor = !is_flag_set(flags, MKD_URLENCODEDANCHOR);
+ static const unsigned char hexchars[] = "0123456789abcdef";
- int i, size;
- char *line;
+ needed = (labelformat ? (4*len) : len) + 2 /* 'L', trailing null */;
- size = mkd_line(s, len, &line, IS_LABEL);
+ if ( (res = malloc(needed)) == NULL )
+ return NULL;
+
+ if ( h4anchor && labelformat && !isalpha(s[0]) )
+ res[out++] = 'L';
+
-#if !WITH_URLENCODED_ANCHOR
- if ( labelformat && (size>0) && !isalpha(line[0]) )
- (*outchar)('L',out);
-#endif
- for ( i=0; i < size ; i++ ) {
- c = line[i];
+ for ( i=0; i < len ; i++ ) {
+ c = s[i];
if ( labelformat ) {
- if ( isalnum(c) || (c == '_') || (c == ':') || (c == '-') || (c == '.' ) )
- (*outchar)(c, out);
- else
-#if WITH_URLENCODED_ANCHOR
- {
- (*outchar)('%', out);
- (*outchar)(hexchars[c >> 4 & 0xf], out);
- (*outchar)(hexchars[c & 0xf], out);
+ if ( h4anchor
+ ? (isalnum(c) || (c == '_') || (c == ':') || (c == '.' ) )
+ : !(isspace(c) || c == '%') )
+ res[out++] = c;
+ else if ( c == ' ' )
+ res[out++] = '-';
+ else {
+ res[out++] = h4anchor ? '-' : '%';
+ res[out++] = hexchars[c >> 4 & 0xf];
+ res[out++] = hexchars[c & 0xf];
+ if ( h4anchor )
+ res[out++] = '-';
}
-#else
- (*outchar)('.', out);
-#endif
}
else
- (*outchar)(c,out);
+ res[out++] = c;
}
-
- if (line)
- free(line);
+
+ res[out++] = 0;
+ return res;
+} /* mkd_anchor_format */
+
+
+/* write out a Cstring, mangled into a form suitable for `<a href=` or `<a id=`
+ */
+void
+mkd_string_to_anchor(char *s, int len, mkd_sta_function_t outchar,
+ void *out, int labelformat,
+ MMIOT *f)
+{
+ char *res;
+ char *line;
+ int size;
+
+ int i;
+
+ size = mkd_line(s, len, &line, IS_LABEL);
+
+ if ( !line )
+ return;
+
+ if ( f->cb->e_anchor )
+ res = (*(f->cb->e_anchor))(line, size, f->cb->e_data);
+ else
+ res = mkd_anchor_format(line, size, labelformat, f->flags);
+
+ free(line);
+
+ if ( !res )
+ return;
+
+ for ( i=0; res[i]; i++ )
+ (*outchar)(res[i], out);
+
+ if ( f->cb->e_anchor ) {
+ if ( f->cb->e_free )
+ (*(f->cb->e_free))(res, f->cb->e_data);
+ }
+ else
+ free(res);
}
/* ___mkd_reparse() a line
*/
static void
-mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
+mkd_parse_line(char *bfr, int size, MMIOT *f, mkd_flag_t flags)
{
___mkd_initmmiot(f, 0);
f->flags = flags & USER_FLAGS;
@@ -269,7 +322,7 @@ mkd_parse_line(char *bfr, int size, MMIOT *f, int flags)
/* ___mkd_reparse() a line, returning it in malloc()ed memory
*/
int
-mkd_line(char *bfr, int size, char **res, DWORD flags)
+mkd_line(char *bfr, int size, char **res, mkd_flag_t flags)
{
MMIOT f;
int len;
@@ -277,15 +330,14 @@ mkd_line(char *bfr, int size, char **res, DWORD flags)
mkd_parse_line(bfr, size, &f, flags);
if ( len = S(f.out) ) {
- /* kludge alert; we know that T(f.out) is malloced memory,
- * so we can just steal it away. This is awful -- there
- * should be an opaque method that transparently moves
- * the pointer out of the embedded Cstring.
- */
EXPAND(f.out) = 0;
- *res = T(f.out);
- T(f.out) = 0;
- S(f.out) = ALLOCATED(f.out) = 0;
+ /* strdup() doesn't use amalloc(), so in an amalloc()ed
+ * build this copies the string safely out of our memory
+ * paranoia arena. In a non-amalloc world, it's a spurious
+ * memory allocation, but it avoids unintentional hilarity
+ * with amalloc()
+ */
+ *res = strdup(T(f.out));
}
else {
*res = 0;
@@ -299,18 +351,19 @@ mkd_line(char *bfr, int size, char **res, DWORD flags)
/* ___mkd_reparse() a line, writing it to a FILE
*/
int
-mkd_generateline(char *bfr, int size, FILE *output, DWORD flags)
+mkd_generateline(char *bfr, int size, FILE *output, mkd_flag_t flags)
{
MMIOT f;
+ int status;
mkd_parse_line(bfr, size, &f, flags);
- if ( flags & MKD_CDATA )
- mkd_generatexml(T(f.out), S(f.out), output);
+ if ( is_flag_set(flags, MKD_CDATA) )
+ status = mkd_generatexml(T(f.out), S(f.out), output) != EOF;
else
- fwrite(T(f.out), S(f.out), 1, output);
+ status = fwrite(T(f.out), S(f.out), 1, output) == S(f.out);
___mkd_freemmiot(&f, 0);
- return 0;
+ return status ? 0 : EOF;
}
@@ -319,8 +372,11 @@ mkd_generateline(char *bfr, int size, FILE *output, DWORD flags)
void
mkd_e_url(Document *f, mkd_callback_t edit)
{
- if ( f )
+ if ( f ) {
+ if ( f->cb.e_url != edit )
+ f->dirty = 1;
f->cb.e_url = edit;
+ }
}
@@ -329,8 +385,24 @@ mkd_e_url(Document *f, mkd_callback_t edit)
void
mkd_e_flags(Document *f, mkd_callback_t edit)
{
- if ( f )
+ if ( f ) {
+ if ( f->cb.e_flags != edit )
+ f->dirty = 1;
f->cb.e_flags = edit;
+ }
+}
+
+
+/* set the anchor formatter
+ */
+void
+mkd_e_anchor(Document *f, mkd_callback_t format)
+{
+ if ( f ) {
+ if ( f->cb.e_anchor != format )
+ f->dirty = 1;
+ f->cb.e_anchor = format;
+ }
}
@@ -339,8 +411,11 @@ mkd_e_flags(Document *f, mkd_callback_t edit)
void
mkd_e_free(Document *f, mkd_free_t dealloc)
{
- if ( f )
+ if ( f ) {
+ if ( f->cb.e_free != dealloc )
+ f->dirty = 1;
f->cb.e_free = dealloc;
+ }
}
@@ -349,8 +424,23 @@ mkd_e_free(Document *f, mkd_free_t dealloc)
void
mkd_e_data(Document *f, void *data)
{
- if ( f )
+ if ( f ) {
+ if ( f->cb.e_data != data )
+ f->dirty = 1;
f->cb.e_data = data;
+ }
+}
+
+
+/* set the code block display callback
+ */
+void
+mkd_e_code_format(Document *f, mkd_callback_t codefmt)
+{
+ if ( f && (f->cb.e_codefmt != codefmt) ) {
+ f->dirty = 1;
+ f->cb.e_codefmt = codefmt;
+ }
}
@@ -359,6 +449,9 @@ mkd_e_data(Document *f, void *data)
void
mkd_ref_prefix(Document *f, char *data)
{
- if ( f )
+ if ( f ) {
+ if ( f->ref_prefix != data )
+ f->dirty = 1;
f->ref_prefix = data;
+ }
}
diff --git a/ext/mkdio.h b/ext/mkdio.h
index 91d7fca..9775f2c 100644
--- a/ext/mkdio.h
+++ b/ext/mkdio.h
@@ -3,9 +3,11 @@
#include <stdio.h>
+#include <inttypes.h>
+
typedef void MMIOT;
-typedef unsigned int mkd_flag_t;
+typedef uint32_t mkd_flag_t;
/* line builder for markdown()
*/
@@ -30,12 +32,10 @@ void mkd_cleanup(MMIOT*);
/* markup functions
*/
-int mkd_dump(MMIOT*, FILE*, int, char*);
+int mkd_dump(MMIOT*, FILE*, mkd_flag_t, char*);
int markdown(MMIOT*, FILE*, mkd_flag_t);
int mkd_line(char *, int, char **, mkd_flag_t);
-typedef int (*mkd_sta_function_t)(const int,const void*);
-void mkd_string_to_anchor(char *, int, mkd_sta_function_t, void*, int);
-int mkd_xhtmlpage(MMIOT*,int,FILE*);
+int mkd_xhtmlpage(MMIOT*,mkd_flag_t,FILE*);
/* header block access
*/
@@ -67,6 +67,8 @@ typedef void (*mkd_free_t)(char*, void*);
void mkd_e_url(void *, mkd_callback_t);
void mkd_e_flags(void *, mkd_callback_t);
+void mkd_e_anchor(void *, mkd_callback_t);
+void mkd_e_code_format(void*, mkd_callback_t);
void mkd_e_free(void *, mkd_free_t );
void mkd_e_data(void *, void *);
@@ -106,6 +108,15 @@ void mkd_ref_prefix(MMIOT*, char*);
#define MKD_NODLIST 0x00100000 /* forbid definition lists */
#define MKD_EXTRA_FOOTNOTE 0x00200000 /* enable markdown extra-style footnotes */
#define MKD_NOSTYLE 0x00400000 /* don't extract <style> blocks */
+#define MKD_NODLDISCOUNT 0x00800000 /* disable discount-style definition lists */
+#define MKD_DLEXTRA 0x01000000 /* enable extra-style definition lists */
+#define MKD_FENCEDCODE 0x02000000 /* enabled fenced code blocks */
+#define MKD_IDANCHOR 0x04000000 /* use id= anchors for TOC links */
+#define MKD_GITHUBTAGS 0x08000000 /* allow dash and underscore in element names */
+#define MKD_URLENCODEDANCHOR 0x10000000 /* urlencode non-identifier chars instead of replacing with dots */
+#define MKD_LATEX 0x40000000 /* handle embedded LaTeX escapes */
+#define MKD_EXPLICITLIST 0x80000000 /* don't combine numbered/bulletted lists */
+
#define MKD_EMBED MKD_NOLINKS|MKD_NOIMAGE|MKD_TAGTEXT
/* special flags for mkd_in() and mkd_string()
diff --git a/ext/mktags.c b/ext/mktags.c
index 0d4680a..f1c619e 100644
--- a/ext/mktags.c
+++ b/ext/mktags.c
@@ -3,6 +3,7 @@
#include <stdio.h>
#define __WITHOUT_AMALLOC 1
+#include "config.h"
#include "cstring.h"
#include "tags.h"
@@ -41,6 +42,7 @@ typedef int (*stfu)(const void*,const void*);
/* load in the standard collection of html tags that markdown supports
*/
+int
main()
{
int i;
@@ -65,6 +67,7 @@ main()
KW("H6");
KW("LISTING");
KW("NOBR");
+ KW("FORM");
KW("UL");
KW("P");
KW("OL");
diff --git a/ext/notspecial.c b/ext/notspecial.c
new file mode 100644
index 0000000..8e85410
--- /dev/null
+++ b/ext/notspecial.c
@@ -0,0 +1,44 @@
+/*
+ * check a filename to see if it's a (fifo|character special|socket) object
+ * (if a stat() function doesn't exist, we can't stat so we'll just return
+ * true no matter what.)
+ */
+
+#include "config.h"
+
+#if HAVE_STAT && HAS_ISCHR && HAS_ISFIFO && HAS_ISSOCK
+#include <sys/stat.h>
+
+int
+notspecial(char *file)
+{
+ struct stat info;
+
+ if ( stat(file, &info) != 0 )
+ return 1;
+
+ return !( S_ISCHR(info.st_mode) || S_ISFIFO(info.st_mode) || S_ISSOCK(info.st_mode) );
+}
+#else
+int
+notspecial(char *file)
+{
+ return 1;
+}
+#endif
+
+
+#if DEBUG
+
+#include <stdio.h>
+
+int
+main(argc, argv)
+char **argv;
+{
+ int i;
+
+ for ( i=1; i < argc; i++ )
+ printf("%s is %sspecial\n", argv[i], notspecial(argv[i]) ? "not " : "");
+}
+#endif
diff --git a/ext/pgm_options.c b/ext/pgm_options.c
index 161917e..3d364ac 100644
--- a/ext/pgm_options.c
+++ b/ext/pgm_options.c
@@ -8,7 +8,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
-#include <unistd.h>
#include <mkdio.h>
#include <errno.h>
#include <string.h>
@@ -25,15 +24,15 @@ static struct _opt {
char *name;
char *desc;
int off;
- int skip;
+ int skip; /* this opt is a synonym */
int sayenable;
mkd_flag_t flag;
} opts[] = {
{ "tabstop", "default (4-space) tabstops", 0, 0, 1, MKD_TABSTOP },
{ "image", "images", 1, 0, 1, MKD_NOIMAGE },
{ "links", "links", 1, 0, 1, MKD_NOLINKS },
- { "relax", "emphasis inside words", 1, 1, 1, MKD_STRICT },
- { "strict", "emphasis inside words", 0, 0, 1, MKD_STRICT },
+ { "relax", "Markdown.pl compatibility", 1, 1, 1, MKD_STRICT },
+ { "strict", "Markdown.pl compatibility", 0, 0, 1, MKD_STRICT },
{ "tables", "tables", 1, 0, 1, MKD_NOTABLES },
{ "header", "pandoc-style headers", 1, 0, 1, MKD_NOHEADER },
{ "html", "raw html", 1, 0, 1, MKD_NOHTML },
@@ -55,6 +54,15 @@ static struct _opt {
{ "footnotes", "markdown extra footnotes", 0, 0, 1, MKD_EXTRA_FOOTNOTE },
{ "footnote", "markdown extra footnotes", 0, 1, 1, MKD_EXTRA_FOOTNOTE },
{ "style", "extract style blocks", 1, 0, 1, MKD_NOSTYLE },
+ { "dldiscount", "discount-style definition lists", 1, 0, 1, MKD_NODLDISCOUNT },
+ { "dlextra", "extra-style definition lists", 0, 0, 1, MKD_DLEXTRA },
+ { "fencedcode", "fenced code blocks", 0, 0, 1, MKD_FENCEDCODE },
+ { "idanchor", "id= anchors in TOC", 0, 0, 1, MKD_IDANCHOR },
+ { "githubtags", "permit - and _ in element names", 0, 0, 0, MKD_GITHUBTAGS },
+ { "urlencodedanchor", "html5-style anchors", 0, 0, 0, MKD_URLENCODEDANCHOR },
+ { "html5anchor", "html5-style anchors", 0, 1, 0, MKD_URLENCODEDANCHOR },
+ { "latex", "handle LaTeX escapes", 0, 0, 1, MKD_LATEX },
+ { "explicitlist", "do not merge adjacent numeric/bullet lists", 0, 0, 1, MKD_EXPLICITLIST },
} ;
#define NR(x) (sizeof x / sizeof x[0])
@@ -76,7 +84,7 @@ sort_by_flag(struct _opt *a, struct _opt *b)
void
-show_flags(int byname)
+show_flags(int byname, int verbose)
{
int i;
@@ -84,14 +92,14 @@ show_flags(int byname)
qsort(opts, NR(opts), sizeof(opts[0]), (stfu)sort_by_name);
for (i=0; i < NR(opts); i++)
- if ( ! opts[i].skip )
+ if ( verbose || !opts[i].skip )
fprintf(stderr, "%16s : %s\n", opts[i].name, opts[i].desc);
}
else {
qsort(opts, NR(opts), sizeof(opts[0]), (stfu)sort_by_flag);
for (i=0; i < NR(opts); i++)
- if ( ! opts[i].skip ) {
+ if ( !opts[i].skip ) {
fprintf(stderr, "%08lx : ", (long)opts[i].flag);
if ( opts[i].sayenable )
fprintf(stderr, opts[i].off ? "disable " : "enable ");
@@ -101,7 +109,7 @@ show_flags(int byname)
}
-int
+char *
set_flag(mkd_flag_t *flags, char *optionstring)
{
int i;
@@ -132,7 +140,7 @@ set_flag(mkd_flag_t *flags, char *optionstring)
*flags &= ~opts[i].flag;
}
else
- return 0;
+ return arg;
}
- return 1;
+ return 0;
}
diff --git a/ext/pgm_options.h b/ext/pgm_options.h
index b7544cd..566023f 100644
--- a/ext/pgm_options.h
+++ b/ext/pgm_options.h
@@ -3,7 +3,7 @@
#include <mkdio.h>
-int set_flag(mkd_flag_t *flags, char *optionstring);
-void show_flags(int byname);
+char *set_flag(mkd_flag_t *flags, char *optionstring);
+void show_flags(int byname, int verbose);
#endif/*PGM_OPTIONS_D*/
diff --git a/ext/rdiscount.c b/ext/rdiscount.c
index 3bef140..e7ade6c 100644
--- a/ext/rdiscount.c
+++ b/ext/rdiscount.c
@@ -14,6 +14,9 @@ typedef struct {
* The following flags are handled specially:
* - MKD_TABSTOP: Always set.
* - MKD_NOHEADER: Always set.
+ * - MKD_DLEXTRA: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
+ * - MKD_FENCEDCODE: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
+ * - MKD_GITHUBTAGS: Always set. (For compatibility with RDiscount 2.1.8 and earlier.)
* - MKD_NOPANTS: Set unless the "smart" accessor returns true.
*
* See rb_rdiscount__get_flags() for the detailed implementation.
@@ -31,11 +34,36 @@ static AccessorFlagPair ACCESSOR_2_FLAG[] = {
{ "no_pseudo_protocols", MKD_NO_EXT },
{ "no_superscript", MKD_NOSUPERSCRIPT },
{ "no_strikethrough", MKD_NOSTRIKETHROUGH },
+ { "latex", MKD_LATEX },
+ { "explicitlist", MKD_EXPLICITLIST },
+ { "md1compat", MKD_1_COMPAT },
{ NULL, 0 } /* sentinel */
};
static VALUE rb_cRDiscount;
+int rb_rdiscount__get_flags(VALUE ruby_obj)
+{
+ AccessorFlagPair *entry;
+
+ /* compile flags */
+ int flags = MKD_TABSTOP | MKD_NOHEADER | MKD_DLEXTRA | MKD_FENCEDCODE | MKD_GITHUBTAGS;
+
+ /* The "smart" accessor turns OFF the MKD_NOPANTS flag. */
+ if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue ) {
+ flags = flags | MKD_NOPANTS;
+ }
+
+ /* Handle standard flags declared in ACCESSOR_2_FLAG */
+ for ( entry = ACCESSOR_2_FLAG; entry->accessor_name; entry++ ) {
+ if ( rb_funcall(ruby_obj, rb_intern(entry->accessor_name), 0) == Qtrue ) {
+ flags = flags | entry->flag;
+ }
+ }
+
+ return flags;
+}
+
static VALUE
rb_rdiscount_to_html(int argc, VALUE *argv, VALUE self)
{
@@ -114,28 +142,6 @@ rb_rdiscount_toc_content(int argc, VALUE *argv, VALUE self)
return buf;
}
-int rb_rdiscount__get_flags(VALUE ruby_obj)
-{
- AccessorFlagPair *entry;
-
- /* compile flags */
- int flags = MKD_TABSTOP | MKD_NOHEADER;
-
- /* The "smart" accessor turns OFF the MKD_NOPANTS flag. */
- if ( rb_funcall(ruby_obj, rb_intern("smart"), 0) != Qtrue ) {
- flags = flags | MKD_NOPANTS;
- }
-
- /* Handle standard flags declared in ACCESSOR_2_FLAG */
- for ( entry = ACCESSOR_2_FLAG; entry->accessor_name; entry++ ) {
- if ( rb_funcall(ruby_obj, rb_intern(entry->accessor_name), 0) == Qtrue ) {
- flags = flags | entry->flag;
- }
- }
-
- return flags;
-}
-
void Init_rdiscount()
{
diff --git a/ext/resource.c b/ext/resource.c
index 9795db8..0b2dd06 100644
--- a/ext/resource.c
+++ b/ext/resource.c
@@ -65,6 +65,7 @@ ___mkd_freefootnote(Footnote *f)
DELETE(f->tag);
DELETE(f->link);
DELETE(f->title);
+ if ( f->text) ___mkd_freeParagraph(f->text);
}
diff --git a/ext/setup.c b/ext/setup.c
index 988a6aa..b889f18 100644
--- a/ext/setup.c
+++ b/ext/setup.c
@@ -31,7 +31,7 @@ mkd_initialize()
}
-void
+void DESTRUCTOR
mkd_shlib_destructor()
{
mkd_deallocate_tags();
diff --git a/ext/tags.c b/ext/tags.c
index 792ef92..7e7e15f 100644
--- a/ext/tags.c
+++ b/ext/tags.c
@@ -1,5 +1,7 @@
/* block-level tags for passing html blocks through the blender
*/
+#include "config.h"
+
#define __WITHOUT_AMALLOC 1
#include "cstring.h"
#include "tags.h"
diff --git a/ext/toc.c b/ext/toc.c
index 8037e36..1cdddec 100644
--- a/ext/toc.c
+++ b/ext/toc.c
@@ -25,12 +25,14 @@ mkd_toc(Document *p, char **doc)
Cstring res;
int size;
int first = 1;
+ extern void Csreparse(Cstring *, char *, int, mkd_flag_t);
+
if ( !(doc && p && p->ctx) ) return -1;
*doc = 0;
- if ( ! (p->ctx->flags & MKD_TOC) ) return 0;
+ if ( ! is_flag_set(p->ctx->flags, MKD_TOC) ) return 0;
CREATE(res);
RESERVE(res, 100);
@@ -38,7 +40,7 @@ mkd_toc(Document *p, char **doc)
for ( tp = p->code; tp ; tp = tp->next ) {
if ( tp->typ == SOURCE ) {
for ( srcp = tp->down; srcp; srcp = srcp->next ) {
- if ( srcp->typ == HDR && srcp->text ) {
+ if ( (srcp->typ == HDR) && srcp->text ) {
while ( last_hnumber > srcp->hnumber ) {
if ( (last_hnumber - srcp->hnumber) > 1 )
@@ -62,11 +64,11 @@ mkd_toc(Document *p, char **doc)
Csprintf(&res, "%*s<li><a href=\"#", srcp->hnumber, "");
mkd_string_to_anchor(T(srcp->text->text),
S(srcp->text->text),
- (mkd_sta_function_t)Csputc, &res,1);
+ (mkd_sta_function_t)Csputc,
+ &res,1,p->ctx);
Csprintf(&res, "\">");
- mkd_string_to_anchor(T(srcp->text->text),
- S(srcp->text->text),
- (mkd_sta_function_t)Csputc, &res,0);
+ Csreparse(&res, T(srcp->text->text),
+ S(srcp->text->text), IS_LABEL);
Csprintf(&res, "</a>");
first = 0;
@@ -82,16 +84,12 @@ mkd_toc(Document *p, char **doc)
}
if ( (size = S(res)) > 0 ) {
+ /* null-terminate & strdup into a free()able memory chunk
+ */
EXPAND(res) = 0;
- /* HACK ALERT! HACK ALERT! HACK ALERT! */
- *doc = T(res); /* we know that a T(Cstring) is a character pointer
- * so we can simply pick it up and carry it away,
- * leaving the husk of the Ctring on the stack
- * END HACK ALERT
- */
+ *doc = strdup(T(res));
}
- else
- DELETE(res);
+ DELETE(res);
return size;
}
diff --git a/ext/version.c b/ext/version.c
index 8e48410..0fbd81a 100644
--- a/ext/version.c
+++ b/ext/version.c
@@ -1,30 +1,13 @@
#include "config.h"
-char markdown_version[] = VERSION
+char markdown_version[] = BRANCH VERSION
#if 4 != 4
" TAB=4"
#endif
#if USE_AMALLOC
" DEBUG"
#endif
-#if USE_DISCOUNT_DL
-# if USE_EXTRA_DL
- " DL=BOTH"
-# else
- " DL=DISCOUNT"
-# endif
-#elif USE_EXTRA_DL
- " DL=EXTRA"
-#else
- " DL=NONE"
-#endif
-#if WITH_ID_ANCHOR
- " ID-ANCHOR"
-#endif
-#if WITH_GITHUB_TAGS
- " GITHUB-TAGS"
-#endif
-#if WITH_FENCED_CODE
- " FENCED-CODE"
+#if GITHUB_CHECKBOX
+ " GITHUB_CHECKBOX"
#endif
;
diff --git a/ext/xml.c b/ext/xml.c
index 5e58389..f71b05c 100644
--- a/ext/xml.c
+++ b/ext/xml.c
@@ -47,9 +47,9 @@ mkd_generatexml(char *p, int size, FILE *out)
c = *p++;
if ( entity = mkd_xmlchar(c) )
- fputs(entity, out);
+ DO_OR_DIE( fputs(entity, out) );
else
- fputc(c, out);
+ DO_OR_DIE( fputc(c, out) );
}
return 0;
}
@@ -74,9 +74,10 @@ mkd_xml(char *p, int size, char **res)
else
Csputc(c, &f);
}
- /* HACK ALERT! HACK ALERT! HACK ALERT! */
- *res = T(f); /* we know that a T(Cstring) is a character pointer */
- /* so we can simply pick it up and carry it away, */
- return S(f); /* leaving the husk of the Ctring on the stack */
- /* END HACK ALERT */
+ /* null terminate, strdup() into a free()able memory block,
+ * and return the size of everything except the null terminator
+ */
+ EXPAND(f) = 0;
+ *res = strdup(T(f));
+ return S(f)-1;
}
diff --git a/ext/xmlpage.c b/ext/xmlpage.c
index 96ed2b7..2003cdd 100644
--- a/ext/xmlpage.c
+++ b/ext/xmlpage.c
@@ -5,44 +5,39 @@
* The redistribution terms are provided in the COPYRIGHT file that must
* be distributed with this source code.
*/
-#include "config.h"
#include <stdio.h>
#include <stdlib.h>
-#include <ctype.h>
-
-#include "cstring.h"
-#include "markdown.h"
-#include "amalloc.h"
+#include <markdown.h>
int
-mkd_xhtmlpage(Document *p, int flags, FILE *out)
+mkd_xhtmlpage(Document *p, mkd_flag_t flags, FILE *out)
{
char *title;
extern char *mkd_doc_title(Document *);
if ( mkd_compile(p, flags) ) {
- fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
- fprintf(out, "<!DOCTYPE html "
- " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
- " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n");
-
- fprintf(out, "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n");
+ DO_OR_DIE( fprintf(out, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<!DOCTYPE html "
+ " PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
+ " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n") );
- fprintf(out, "<head>\n");
- if ( title = mkd_doc_title(p) )
- fprintf(out, "<title>%s</title>\n", title);
- mkd_generatecss(p, out);
- fprintf(out, "</head>\n");
-
- fprintf(out, "<body>\n");
- mkd_generatehtml(p, out);
- fprintf(out, "</body>\n");
- fprintf(out, "</html>\n");
+ DO_OR_DIE( fprintf(out, "<head>\n") );
+ DO_OR_DIE( fprintf(out, "<title>") );
+ if ( title = mkd_doc_title(p) ) {
+ DO_OR_DIE( fprintf(out, "%s", title) );
+ }
+ DO_OR_DIE( fprintf(out, "</title>\n") );
+ DO_OR_DIE( mkd_generatecss(p, out) );
+ DO_OR_DIE( fprintf(out, "</head>\n"
+ "<body>\n") );
- mkd_cleanup(p);
+ DO_OR_DIE( mkd_generatehtml(p, out) );
+ DO_OR_DIE( fprintf(out, "</body>\n"
+ "</html>\n") );
return 0;
}
- return -1;
+ return EOF;
}
diff --git a/lib/rdiscount.rb b/lib/rdiscount.rb
index 40e9053..f0f6de3 100644
--- a/lib/rdiscount.rb
+++ b/lib/rdiscount.rb
@@ -24,7 +24,7 @@
# end
#
class RDiscount
- VERSION = '2.1.8'
+ VERSION = '2.2.7'
# Original Markdown formatted text.
attr_reader :text
@@ -75,6 +75,15 @@ class RDiscount
# Disable strikethrough processing.
attr_accessor :no_strikethrough
+ # Keep LaTeX inside $$ intact.
+ attr_accessor :latex
+
+ # Don't merge adjacent list into a single list.
+ attr_accessor :explicitlist
+
+ # Not documented: run in markdown 1 compat mode (only used for MarkdownTest1.0)
+ attr_accessor :md1compat
+
# Create a RDiscount Markdown processor. The +text+ argument
# should be a string containing Markdown text. Additional arguments may be
# supplied to set various processing options:
@@ -95,6 +104,8 @@ class RDiscount
# * <tt>:no_pseudo_protocols</tt> - Do not process pseudo-protocols.
# * <tt>:no_superscript</tt> - Disable superscript processing.
# * <tt>:no_strikethrough</tt> - Disable strikethrough processing.
+ # * <tt>:latex</tt> - Keep LaTeX inside $$ intact.
+ # * <tt>:explicitlist</tt> - Don't merge adjacent list into a single list.
#
def initialize(text, *extensions)
@text = text
diff --git a/metadata.yml b/metadata.yml
deleted file mode 100644
index 0459094..0000000
--- a/metadata.yml
+++ /dev/null
@@ -1,99 +0,0 @@
---- !ruby/object:Gem::Specification
-name: rdiscount
-version: !ruby/object:Gem::Version
- version: 2.1.8
-platform: ruby
-authors:
-- Ryan Tomayko
-- David Loren Parsons
-- Andrew White
-- David Foster
-autorequire:
-bindir: bin
-cert_chain: []
-date: 2015-02-01 00:00:00.000000000 Z
-dependencies: []
-description:
-email: davidfstr@gmail.com
-executables:
-- rdiscount
-extensions:
-- ext/extconf.rb
-extra_rdoc_files:
-- COPYING
-files:
-- BUILDING
-- COPYING
-- README.markdown
-- Rakefile
-- bin/rdiscount
-- ext/Csio.c
-- ext/VERSION
-- ext/amalloc.c
-- ext/amalloc.h
-- ext/basename.c
-- ext/blocktags
-- ext/config.h
-- ext/css.c
-- ext/cstring.h
-- ext/docheader.c
-- ext/dumptree.c
-- ext/emmatch.c
-- ext/extconf.rb
-- ext/flags.c
-- ext/generate.c
-- ext/github_flavoured.c
-- ext/html5.c
-- ext/markdown.c
-- ext/markdown.h
-- ext/mkdio.c
-- ext/mkdio.h
-- ext/mktags.c
-- ext/pgm_options.c
-- ext/pgm_options.h
-- ext/rdiscount.c
-- ext/resource.c
-- ext/setup.c
-- ext/tags.c
-- ext/tags.h
-- ext/toc.c
-- ext/version.c
-- ext/xml.c
-- ext/xmlpage.c
-- lib/markdown.rb
-- lib/rdiscount.rb
-- man/markdown.7
-- man/rdiscount.1
-- man/rdiscount.1.ronn
-- rdiscount.gemspec
-- test/benchmark.rb
-- test/benchmark.txt
-- test/markdown_test.rb
-- test/rdiscount_test.rb
-homepage: http://dafoster.net/projects/rdiscount/
-licenses:
-- BSD
-metadata: {}
-post_install_message:
-rdoc_options: []
-require_paths:
-- lib
-required_ruby_version: !ruby/object:Gem::Requirement
- requirements:
- - - "!="
- - !ruby/object:Gem::Version
- version: 1.9.2
-required_rubygems_version: !ruby/object:Gem::Requirement
- requirements:
- - - ">="
- - !ruby/object:Gem::Version
- version: '0'
-requirements: []
-rubyforge_project: wink
-rubygems_version: 2.2.2
-signing_key:
-specification_version: 4
-summary: Fast Implementation of Gruber's Markdown in C
-test_files:
-- test/markdown_test.rb
-- test/rdiscount_test.rb
diff --git a/rdiscount.gemspec b/rdiscount.gemspec
index df75979..daa19cc 100644
--- a/rdiscount.gemspec
+++ b/rdiscount.gemspec
@@ -1,12 +1,11 @@
Gem::Specification.new do |s|
s.name = 'rdiscount'
- s.version = '2.1.8'
+ s.version = '2.2.7'
s.summary = "Fast Implementation of Gruber's Markdown in C"
- s.date = '2015-02-01'
- s.email = 'davidfstr@gmail.com'
+ s.email = 'david@dafoster.net'
s.homepage = 'http://dafoster.net/projects/rdiscount/'
- s.authors = ["Ryan Tomayko", "David Loren Parsons", "Andrew White", "David Foster"]
- s.license = "BSD"
+ s.authors = ["Ryan Tomayko", "David Loren Parsons", "Andrew White", "David Foster", "l33tname"]
+ s.license = "BSD-3-Clause"
# = MANIFEST =
s.files = %w[
BUILDING
@@ -15,13 +14,12 @@ Gem::Specification.new do |s|
Rakefile
bin/rdiscount
discount
- ext/Csio.c
- ext/VERSION
ext/amalloc.c
ext/amalloc.h
ext/basename.c
ext/blocktags
ext/config.h
+ ext/Csio.c
ext/css.c
ext/cstring.h
ext/docheader.c
@@ -30,13 +28,17 @@ Gem::Specification.new do |s|
ext/extconf.rb
ext/flags.c
ext/generate.c
+ ext/gethopt.c
+ ext/gethopt.h
ext/github_flavoured.c
+ ext/h1title.c
ext/html5.c
ext/markdown.c
ext/markdown.h
ext/mkdio.c
ext/mkdio.h
ext/mktags.c
+ ext/notspecial.c
ext/pgm_options.c
ext/pgm_options.h
ext/rdiscount.c
@@ -45,6 +47,7 @@ Gem::Specification.new do |s|
ext/tags.c
ext/tags.h
ext/toc.c
+ ext/VERSION
ext/version.c
ext/xml.c
ext/xmlpage.c
@@ -65,7 +68,4 @@ Gem::Specification.new do |s|
s.extensions = ["ext/extconf.rb"]
s.executables = ["rdiscount"]
s.require_paths = ["lib"]
- s.rubyforge_project = 'wink'
- # Ruby 1.9.2 has a known bug in mkmf. Ruby 1.9.3 or later is fine.
- s.required_ruby_version = '!= 1.9.2'
end
diff --git a/test/markdown_test.rb b/test/markdown_test.rb
index f93b513..1291300 100644
--- a/test/markdown_test.rb
+++ b/test/markdown_test.rb
@@ -138,7 +138,6 @@ class MarkdownTest < Test::Unit::TestCase
Dir["#{MARKDOWN_TEST_DIR}/Tests/*.text"].each do |text_file|
basename = File.basename(text_file).sub(/\.text$/, '')
- html_file = text_file.sub(/text$/, 'html')
method_name = basename.gsub(/[-,()]/, '').gsub(/\s+/, '_').downcase
define_method "test_#{method_name}" do
diff --git a/test/rdiscount_test.rb b/test/rdiscount_test.rb
index 73a9aa2..f54f812 100644
--- a/test/rdiscount_test.rb
+++ b/test/rdiscount_test.rb
@@ -7,7 +7,7 @@ require 'rdiscount'
class RDiscountTest < Test::Unit::TestCase
def test_that_version_looks_valid
- if not /^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?$/ =~ RDiscount::VERSION
+ if not (/^[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?$/) =~ RDiscount::VERSION
assert false, 'Expected RDiscount::VERSION to be a 3 or 4 component version string but found ' + RDiscount::VERSION.to_s
end
end
@@ -54,24 +54,24 @@ class RDiscountTest < Test::Unit::TestCase
def test_that_generate_toc_sets_toc_ids
rd = RDiscount.new("# Level 1\n\n## Level 2", :generate_toc)
assert rd.generate_toc
- assert_equal %(<a name="Level.1"></a>\n<h1>Level 1</h1>\n\n<a name="Level.2"></a>\n<h2>Level 2</h2>\n), rd.to_html
+ assert_equal %(<a name="Level-1"></a>\n<h1>Level 1</h1>\n\n<a name="Level-2"></a>\n<h2>Level 2</h2>\n), rd.to_html
end
def test_should_get_the_generated_toc
rd = RDiscount.new("# Level 1\n\n## Level 2", :generate_toc)
- exp = %(<ul>\n <li><a href=\"#Level.1\">Level 1</a>\n <ul>\n <li><a href=\"#Level.2\">Level 2</a></li>\n </ul>\n </li>\n</ul>)
+ exp = %(<ul>\n <li><a href=\"#Level-1\">Level 1</a>\n <ul>\n <li><a href=\"#Level-2\">Level 2</a></li>\n </ul>\n </li>\n</ul>)
assert_equal exp, rd.toc_content.strip
end
def test_toc_should_escape_apostropes
rd = RDiscount.new("# A'B\n\n## C", :generate_toc)
- exp = %(<ul>\n <li><a href=\"#A.B\">A'B</a>\n <ul>\n <li><a href=\"#C\">C</a></li>\n </ul>\n </li>\n</ul>)
+ exp = %(<ul>\n <li><a href=\"#A-27-B\">A'B</a>\n <ul>\n <li><a href=\"#C\">C</a></li>\n </ul>\n </li>\n</ul>)
assert_equal exp, rd.toc_content.strip
end
def test_toc_should_escape_question_marks
rd = RDiscount.new("# A?B\n\n## C", :generate_toc)
- exp = %(<ul>\n <li><a href=\"#A.B\">A?B</a>\n <ul>\n <li><a href=\"#C\">C</a></li>\n </ul>\n </li>\n</ul>)
+ exp = %(<ul>\n <li><a href=\"#A-3f-B\">A?B</a>\n <ul>\n <li><a href=\"#C\">C</a></li>\n </ul>\n </li>\n</ul>)
assert_equal exp, rd.toc_content.strip
end
@@ -139,12 +139,6 @@ EOS
end
def test_that_tags_can_have_dashes_and_underscores
- if RDiscount::VERSION.start_with? "2.0.7"
- # Skip test for 2.0.7.x series due to upstream behavioral change in
- # Discount 2.0.7. This test can be fixed in Discount 2.1.5 using the
- # WITH_GITHUB_TAGS compile-time flag.
- return
- end
rd = RDiscount.new("foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b>")
assert_equal "<p>foo <asdf-qwerty>bar</asdf-qwerty> and <a_b>baz</a_b></p>\n", rd.to_html
end
@@ -249,7 +243,47 @@ EOS
</dl>
EOS
end
-
+
+ def test_latex_passtrough_dont_render_link
+ rd = RDiscount.new("$$[(1+2)*3-4](1-2)$$", :latex)
+ assert_equal "<p>$$[(1+2)*3-4](1-2)$$</p>\n", rd.to_html
+ end
+
+ def test_that_emphasis_beside_international_characters_detected
+ rd = RDiscount.new(%(*foo ä bar*))
+ assert_equal %(<p><em>foo ä bar</em></p>\n), rd.to_html
+
+ rd = RDiscount.new(%(*ä foobar*))
+ assert_equal %(<p><em>ä foobar</em></p>\n), rd.to_html
+
+ rd = RDiscount.new(%(*foobar ä*))
+ assert_equal %(<p><em>foobar ä</em></p>\n), rd.to_html
+ end
+
+ def test_taht
+ rd = RDiscount.new(<<EOS, :explicitlist)
+- Bullet
+- Bullet
+
+1. Numbered
+2. Numbered
+EOS
+
+ assert_equal <<EOS, rd.to_html
+<ul>
+<li>Bullet</li>
+<li>Bullet</li>
+</ul>
+
+
+<ol>
+<li>Numbered</li>
+<li>Numbered</li>
+</ol>
+
+EOS
+ end
+
def test_that_extra_definition_lists_work
rd = RDiscount.new(<<EOS)
tag1
@@ -262,15 +296,4 @@ EOS
</dl>
EOS
end
-
- def test_that_emphasis_beside_international_characters_detected
- rd = RDiscount.new(%(*foo ä bar*))
- assert_equal %(<p><em>foo ä bar</em></p>\n), rd.to_html
-
- rd = RDiscount.new(%(*ä foobar*))
- assert_equal %(<p><em>ä foobar</em></p>\n), rd.to_html
-
- rd = RDiscount.new(%(*foobar ä*))
- assert_equal %(<p><em>foobar ä</em></p>\n), rd.to_html
- end
end
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/lib/debug/.build-id/53/cf3b3a61681b93c617594cf821918cdf9b56de.debug -rw-r--r-- root/root /usr/share/rubygems-integration/3.1.0/specifications/rdiscount-2.2.7.gemspec
Files in first set of .debs but not in second
-rw-r--r-- root/root /usr/lib/debug/.build-id/9d/8098b2bb82cede82d261f9805eb2891ad3604f.debug -rw-r--r-- root/root /usr/share/rubygems-integration/3.1.0/specifications/rdiscount-2.1.8.gemspec
No differences were encountered between the control files of package ruby-rdiscount
Control files of package ruby-rdiscount-dbgsym: lines which differ (wdiff format)
Build-Ids: 9d8098b2bb82cede82d261f9805eb2891ad3604f 53cf3b3a61681b93c617594cf821918cdf9b56de