diff --git a/src/include/util.h b/src/include/util.h index 20d2fd0..0eca6dd 100644 --- a/src/include/util.h +++ b/src/include/util.h @@ -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 diff --git a/src/inlines.c b/src/inlines.c index 2d967d2..92e1886 100644 --- a/src/inlines.c +++ b/src/inlines.c @@ -1,5 +1,6 @@ #include +#include #include 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("= 0) { + fputs(" title='", out); + writeescaped(data + titlestart, titleend - titlestart, out); + fputc('\'', out); + } + fputc('>', out); + writeescaped(data + textstart, textend - textstart, out); + fputs("", out); + return i; +} + static void writeescaped(char *data, size_t len, FILE *out) { int i; for (i = 0; i < len; ++i) diff --git a/src/util.c b/src/util.c index 51261b6..d72a19a 100644 --- a/src/util.c +++ b/src/util.c @@ -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; +}