A few bug fixes and a feature enhancement.

The bug fixes (bugs reported by Mike Schiraldi @ reddit) are
1. If -fautolink is turned on, a naked @ becomes a mailto:
   link.  Fixed by only triggering `maybe_tag_or_link()` on
   alphabetic characters.
2. If -fnohtml is set, forced linebreaks become <br/>.
   Fixed by filtering out nonprinting nonwhitespace characters
   on the input and using ^C as the <br/> generator character
   instead of converting the forced linebreak directly to <br/>.
3. inline links (via []()) don't allow spaces or escaped ) characters.
   Fixed by rewriting `linkyurl()` so it builds a new `Cstring` for
   link.link (which needs to be deleted after use, so I added a new
   `deallocate` field to the bookmark record for `linkylinky` to use,)
   changing the link gobbler to absorb input until a terminator instead
   of until whitespace or a terminator) and allowing \ to escape ), =,
   or ".
4. Change `Qchar` to take an integer instead of a char.

The feature enhancement is the `abbr:` pseudo-protocol, which I had not
implemented because it collided with the old 'eat until whitespace' version
of `linkyurl()`
This commit is contained in:
David Parsons
2009-06-05 22:18:17 -07:00
parent ade41b33d7
commit 2cb51b7e38
7 changed files with 143 additions and 62 deletions
+51 -35
View File
@@ -114,7 +114,7 @@ shift(MMIOT *f, int i)
/* Qchar()
*/
static void
Qchar(char c, MMIOT *f)
Qchar(int c, MMIOT *f)
{
block *cur;
@@ -393,8 +393,8 @@ linkylabel(MMIOT *f, int *sizep)
* `text`, where I extract until I reach a ')' or
* whitespace.
*/
static char*
linkyurl(MMIOT *f, int *sizep)
static int
linkyurl(MMIOT *f, Footnote *p, char *fin)
{
int size = 0;
char *ptr;
@@ -403,27 +403,31 @@ linkyurl(MMIOT *f, int *sizep)
if ( (c = eatspace(f)) == EOF )
return 0;
ptr = cursor(f);
/* if I do (title:blah blah blah) embedded links, I need to subvert
* linkyurl to do a lookahead for the pseudo-protocol, then snarf
* up everything up to the terminating ')'
*/
p->dealloc = 1;
CREATE(p->link);
if ( c == '<' ) {
pull(f);
ptr++;
ptr = cursor(f);
if ( (size = parenthetical('<', '>', f)) == EOF )
return 0;
SUFFIX(p->link, ptr, size);
}
else {
for ( ; ((c=pull(f)) != ')') && !isspace(c); size++)
if ( c == EOF ) return 0;
if ( c == ')' )
shift(f, -1);
while (1) {
if ( (c = pull(f)) == EOF )
return 0;
else if ( strchr(fin, c) ) {
shift(f, -1);
break;
}
if ( (c == '\\') && strchr(fin, peek(f,1)) )
c = pull(f);
EXPAND(p->link) = c;
}
___mkd_tidy(&p->link);
}
*sizep = size;
return ptr;
return 1;
}
@@ -497,6 +501,7 @@ linkykey(int image, Footnote *val, MMIOT *f)
{
Footnote *ret;
Cstring mylabel;
Cstring *url;
int here;
memset(val, 0, sizeof *val);
@@ -509,7 +514,7 @@ linkykey(int image, Footnote *val, MMIOT *f)
switch ( pull(f) ) {
case '(':
/* embedded link */
if ( (T(val->link) = linkyurl(f,&S(val->link))) == 0 )
if ( !linkyurl(f, val, image ? "='\")" : "'\")") )
return 0;
if ( image && !linkysize(f, &val->height, &val->width) )
@@ -592,12 +597,14 @@ typedef struct linkytype {
char *text_pfx; /* text prefix (eg: ">" */
char *text_sfx; /* text suffix (eg: "</a>" */
int flags; /* reparse flags */
int kind; /* tag is url or something else? */
#define IS_URL 0x01
} linkytype;
static linkytype imaget = { 0, 0, "<img src=\"", "\"",
1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG };
1, " alt=\"", "\" />", DENY_IMG|INSIDE_TAG, IS_URL };
static linkytype linkt = { 0, 0, "<a href=\"", "\"",
0, ">", "</a>", DENY_A };
0, ">", "</a>", DENY_A, IS_URL };
/*
* pseudo-protocols for [][];
@@ -607,9 +614,10 @@ static linkytype linkt = { 0, 0, "<a href=\"", "\"",
* raw: just dump the link without any processing
*/
static linkytype specials[] = {
{ "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0 },
{ "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0 },
{ "raw:", 4, 0, 0, 0, 0, 0, 0 },
{ "id:", 3, "<a id=\"", "\"", 0, ">", "</a>", 0, IS_URL },
{ "class:", 6, "<span class=\"", "\"", 0, ">", "</span>", 0, 0 },
{ "raw:", 4, 0, 0, 0, 0, 0, 0, 0 },
{ "abbr:", 5, "<abbr title=\"", "\"", 0, ">", "</abbr>", 0, 0 },
} ;
#define NR(x) (sizeof x / sizeof x[0])
@@ -642,7 +650,8 @@ linkylinky(int image, MMIOT *f)
linkytype *tag;
if ( !linkykey(image, &link, f) ) {
mmiotseek(f, start);
failed: mmiotseek(f, start);
if ( link.dealloc ) DELETE(link.link);
return 0;
}
@@ -654,22 +663,25 @@ linkylinky(int image, MMIOT *f)
*/
if ( (f->flags & SAFELINK) && (T(link.link)[0] != '/')
&& !isautoprefix(T(link.link)) ) {
mmiotseek(f, start);
return 0;
goto failed;
}
tag = &linkt;
}
if ( f->flags & tag-> flags ) {
mmiotseek(f, start);
return 0;
}
if ( f->flags & tag->flags )
goto failed;
if ( tag->link_pfx ) {
Qstring(tag->link_pfx, f);
if ( f->base && (T(link.link)[tag->szpat] == '/') )
puturl(f->base, strlen(f->base), f);
puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
if ( tag->kind & IS_URL ) {
if ( f->base && (T(link.link)[tag->szpat] == '/') )
puturl(f->base, strlen(f->base), f);
puturl(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
}
else
___mkd_reparse(T(link.link) + tag->szpat, S(link.link) - tag->szpat, INSIDE_TAG, f);
Qstring(tag->link_sfx, f);
if ( tag->WxH && link.height && link.width ) {
@@ -690,6 +702,7 @@ linkylinky(int image, MMIOT *f)
else
Qwrite(T(link.link) + tag->szpat, S(link.link) - tag->szpat, f);
if ( link.dealloc ) DELETE(link.link);
return 1;
}
@@ -957,7 +970,7 @@ text(MMIOT *f)
int smartyflags = 0;
while (1) {
if (f->flags & AUTOLINK)
if ( (f->flags & AUTOLINK) && isalpha(peek(f,1)) )
maybe_tag_or_link(f, EOF);
c = pull(f);
@@ -970,6 +983,9 @@ text(MMIOT *f)
switch (c) {
case 0: break;
case 3: Qstring("<br/>", f);
break;
case '>': if ( tag_text(f) )
Qstring("&gt;", f);
else
@@ -1185,10 +1201,10 @@ printblock(Paragraph *pp, MMIOT *f)
if ( S(t->text) > 2 && T(t->text)[S(t->text)-2] == ' '
&& T(t->text)[S(t->text)-1] == ' ') {
push(T(t->text), S(t->text)-2, f);
push("<br/>\n", 6, f);
push("\003\n", 2, f);
}
else {
___mkd_tidy(t);
___mkd_tidy(&t->text);
push(T(t->text), S(t->text), f);
if ( t->next )
push("\n", 1, f);
+4 -4
View File
@@ -127,10 +127,10 @@ skipempty(Line *p)
void
___mkd_tidy(Line *t)
___mkd_tidy(Cstring *t)
{
while ( S(t->text) && isspace(T(t->text)[S(t->text)-1]) )
--S(t->text);
while ( S(*t) && isspace(T(*t)[S(*t)-1]) )
--S(*t);
}
@@ -871,7 +871,7 @@ compile(Line *ptr, int toplevel, MMIOT *f)
/* HORRIBLE STANDARDS KLUDGE: the first line of every block
* has trailing whitespace trimmed off.
*/
___mkd_tidy(p->text);
___mkd_tidy(&p->text->text);
}
ptr = codeblock(p);
+2 -1
View File
@@ -11,6 +11,7 @@ typedef struct footnote {
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? */
} Footnote;
/* each input line is read into a Line, which contains the line,
@@ -138,6 +139,6 @@ extern void ___mkd_freeLineRange(Line *, Line *);
extern void ___mkd_xml(char *, int, FILE *);
extern void ___mkd_reparse(char *, int, int, MMIOT*);
extern void ___mkd_emblock(MMIOT*);
extern void ___mkd_tidy(Line *);
extern void ___mkd_tidy(Cstring *);
#endif/*_MARKDOWN_D*/
+1 -1
View File
@@ -113,7 +113,7 @@ populate(getc_func getc, void* ctx, int flags)
queue(a, &line);
S(line) = 0;
}
else
else if ( isprint(c) || isspace(c) || (c & 0x80) )
EXPAND(line) = c;
}
+38
View File
@@ -0,0 +1,38 @@
./echo 'Reddit-style embedded links'
rc=0
./echo -n ' single link ...................... '
V="http://www.pell.portland.or.us/~orc/Code/discount"
Q=`echo "$V" | ./markdown -fautolink | grep -i '<a href=' | wc -l`
if [ ${Q:-0} -eq 1 ]; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' link surrounded by text .......... '
V="here http://it is?"
Q=`echo "$V" | ./markdown -fautolink`
case "$Q" in
'<p>here <a href="http://it">http://it</a> is?</p>') ./echo "ok" ;;
*) ./echo "FAILED"
rc=1;;
esac
./echo -n ' naked @ .......................... '
V="@"
Q=`echo "$V" | ./markdown -fautolink`
case "$Q" in
'<p>@</p>') ./echo "ok" ;;
*) ./echo "FAILED"
rc=1;;
esac
exit $rc
+36 -21
View File
@@ -12,15 +12,6 @@ else
rc=1
fi
./echo -n ' quote link title with () ......... '
if ./echo '[hehehe](url (link title))' | ./markdown | grep -i 'title="link title"' >/dev/null; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' url contains + ................... '
if ./echo '[hehehe](u+rl)' | ./markdown | grep -i '+' >/dev/null; then
@@ -48,6 +39,33 @@ else
rc=1
fi
./echo -n ' url contains whitespace .......... '
if ./echo '[hehehe](r u)' | ./markdown | grep -i '"r%20u"' >/dev/null; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' url contains whitespace & title .. '
if ./echo '[hehehe](r u "there")' | ./markdown | grep -i '"r%20u"' >/dev/null; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' url contains escaped ) ........... '
if ./echo '[hehehe](u\))' | ./markdown | grep -i '"u)"' >/dev/null; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' label contains < ................. '
if ./echo '![he<he<he](url)' | ./markdown | grep -i '&lt;' >/dev/null; then
@@ -116,6 +134,15 @@ else
rc=1
fi
./echo -n ' pseudo-protocol "abbr:" .......... '
if ./echo '[foo](abbr:bar)' | ./markdown | fgrep '<abbr title="bar">' >/dev/null; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
./echo -n ' nested [][]s ..................... '
count=`./echo '[[z](y)](x)' | ./markdown | tr '>' '\n' | grep -i '<a href' | wc -l`
@@ -144,16 +171,4 @@ else
rc=1
fi
./echo -n ' Reddit-style embedded links ...... '
V="http://www.pell.portland.or.us/~orc/Code/discount"
Q=`echo "$V" | ./markdown -fautolink | grep -i '<a href=' | wc -l`
if [ ${Q:-0} -eq 1 ]; then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
exit $rc
+11
View File
@@ -82,4 +82,15 @@ else
./echo "ok"
fi
./echo -n ' markdown <br/> with -fnohtml ..... '
count=`echo "foo " | ./markdown -fnohtml | grep '<p>foo<br/>' | wc -l`
if [ $count -eq 1 ] ;then
./echo "ok"
else
./echo "FAILED"
rc=1
fi
exit $rc