inline links
This commit is contained in:
@@ -33,5 +33,6 @@ void freestring(struct string *str);
|
||||
int appendcharstring(struct string *str, char c);
|
||||
int appendstrstring(struct string *str, char *s);
|
||||
void resetstring(struct string *str);
|
||||
int isctrl(int c);
|
||||
|
||||
#endif
|
||||
|
||||
152
src/inlines.c
152
src/inlines.c
@@ -1,5 +1,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <util.h>
|
||||
#include <inlines.h>
|
||||
|
||||
static const char *punctuation = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
|
||||
@@ -11,9 +12,11 @@ struct escape escapes[] = {
|
||||
{'<', "<"},
|
||||
{'>', ">"},
|
||||
{'&', "&"},
|
||||
{'"', """},
|
||||
};
|
||||
|
||||
static int writecodespan(char *data, int i, size_t len, FILE *out);
|
||||
static int writelink(char *data, int i, size_t len, FILE *out);
|
||||
static void writeescaped(char *data, size_t len, FILE *out);
|
||||
static void writechescape(char c, FILE *out);
|
||||
|
||||
@@ -27,6 +30,8 @@ void writedata(char *data, size_t len, FILE *out) {
|
||||
int newi;
|
||||
if ((newi = writecodespan(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if ((newi = writelink(data, i, len, out)) >= 0)
|
||||
goto special;
|
||||
if (data[i] == '\\') {
|
||||
if (strchr(punctuation, data[i + 1]) == NULL)
|
||||
writechescape('\\', out);
|
||||
@@ -69,6 +74,153 @@ static int writecodespan(char *data, int i, size_t len, FILE *out) {
|
||||
return end + ticklen;
|
||||
}
|
||||
|
||||
static int writelink(char *data, int i, size_t len, FILE *out) {
|
||||
int textstart, textend, titlestart, titleend, linkstart, linkend;
|
||||
int count;
|
||||
enum {
|
||||
INITIAL,
|
||||
GETTEXT,
|
||||
GETDESTSTART,
|
||||
GETDESTDETERMINE,
|
||||
GETDESTPOINTY,
|
||||
GETDESTNORMAL,
|
||||
GETTITLEDETERMINE,
|
||||
GETTITLEDOUBLEQUOTE,
|
||||
GETTITLESINGLEQUOTE,
|
||||
GETTITLEPAREN,
|
||||
NEARLYTHERE,
|
||||
DONE
|
||||
} state;
|
||||
state = INITIAL;
|
||||
for (; i < len; ++i) {
|
||||
switch (state) {
|
||||
case INITIAL:
|
||||
if (data[i] != '[')
|
||||
return -1;
|
||||
state = GETTEXT;
|
||||
count = 1;
|
||||
textstart = i + 1;
|
||||
break;
|
||||
case GETTEXT:
|
||||
if (data[i] == '[')
|
||||
++count;
|
||||
if (data[i] == ']')
|
||||
--count;
|
||||
if (count == 0) {
|
||||
textend = i;
|
||||
linkstart = i;
|
||||
state = GETDESTSTART;
|
||||
}
|
||||
break;
|
||||
case GETDESTSTART:
|
||||
if (data[i] != '(')
|
||||
return -1;
|
||||
state = GETDESTDETERMINE;
|
||||
break;
|
||||
case GETDESTDETERMINE:
|
||||
if (data[i] == '<') {
|
||||
state = GETDESTPOINTY;
|
||||
linkstart = i + 1;
|
||||
}
|
||||
else {
|
||||
state = GETDESTNORMAL;
|
||||
linkstart = i--;
|
||||
count = 0;
|
||||
}
|
||||
break;
|
||||
case GETDESTPOINTY:
|
||||
if (data[i] == '<' && data[i - 1] != '\\')
|
||||
return -1;
|
||||
if (data[i] == '>' && data[i - 1] != '\\') {
|
||||
linkend = i;
|
||||
state = GETTITLEDETERMINE;
|
||||
}
|
||||
break;
|
||||
case GETDESTNORMAL:
|
||||
if (data[i] == '(')
|
||||
++count;
|
||||
if (data[i] == ')')
|
||||
--count;
|
||||
if (count < 0) {
|
||||
state = GETTITLEDETERMINE;
|
||||
linkend = i--;
|
||||
break;
|
||||
}
|
||||
if (count != 0)
|
||||
break;
|
||||
if (isctrl(data[i]) || data[i] == ' ') {
|
||||
state = GETTITLEDETERMINE;
|
||||
linkend = i;
|
||||
}
|
||||
break;
|
||||
case GETTITLEDETERMINE:
|
||||
switch (data[i]) {
|
||||
case '"':
|
||||
state = GETTITLEDOUBLEQUOTE;
|
||||
titlestart = i + 1;
|
||||
break;
|
||||
case '\'':
|
||||
state = GETTITLESINGLEQUOTE;
|
||||
titlestart = i + 1;
|
||||
break;
|
||||
case '(':
|
||||
state = GETTITLEPAREN;
|
||||
count = 1;
|
||||
titlestart = i + 1;
|
||||
break;
|
||||
default:
|
||||
--i;
|
||||
titlestart = titleend = -1;
|
||||
state = NEARLYTHERE;
|
||||
}
|
||||
break;
|
||||
case GETTITLEDOUBLEQUOTE:
|
||||
if (data[i] == '"' && data[i - 1] != '\\') {
|
||||
titleend = i;
|
||||
state = NEARLYTHERE;
|
||||
}
|
||||
break;
|
||||
case GETTITLESINGLEQUOTE:
|
||||
if (data[i] == '\'' && data[i - 1] != '\\') {
|
||||
titleend = i;
|
||||
state = NEARLYTHERE;
|
||||
}
|
||||
break;
|
||||
case GETTITLEPAREN:
|
||||
if (data[i] == '(' || data[i] == ')') {
|
||||
if (data[i - 1] != '\\') {
|
||||
titleend = i;
|
||||
state = NEARLYTHERE;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEARLYTHERE:
|
||||
if (data[i] != ')')
|
||||
return -1;
|
||||
state = DONE;
|
||||
break;
|
||||
case DONE:
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
done:
|
||||
if (state != DONE)
|
||||
return -1;
|
||||
fputs("<a", out);
|
||||
fputs(" href='", out);
|
||||
writeescaped(data + linkstart, linkend - linkstart, out);
|
||||
fputc('\'', out);
|
||||
if (titlestart >= 0) {
|
||||
fputs(" title='", out);
|
||||
writeescaped(data + titlestart, titleend - titlestart, out);
|
||||
fputc('\'', out);
|
||||
}
|
||||
fputc('>', out);
|
||||
writeescaped(data + textstart, textend - textstart, out);
|
||||
fputs("</a>", out);
|
||||
return i;
|
||||
}
|
||||
|
||||
static void writeescaped(char *data, size_t len, FILE *out) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i)
|
||||
|
||||
@@ -77,3 +77,7 @@ int appendstrstring(struct string *str, char *s) {
|
||||
void resetstring(struct string *str) {
|
||||
str->len = 0;
|
||||
}
|
||||
|
||||
int isctrl(int c) {
|
||||
return (0 <= c && c <= 0x1f) || c == 0x7f;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user